vue3.0——监听属性、Vue3生命周期函数、Teleport、属性传值、自定义事件、状态驱动的动态 CSS、注册组件、异步组件、占位组件 Suspense

一、监听属性-watch

  • 与vue2.x中的watch配置功能一致

  • 注意

    • 监视reactive定义的响应式数据时,oldvalue无法正确获取,强制开始了深度监视(deep的配置失效)

    • 监视reactive定义的响应式数据的某一个值时:deep配置有效

1.监听ref定义的响应式数据

  示例:

<script setup>
import { ref, reactive, watch } from "vue";

//监听一个数据
let msg = ref("今天星期二");
function changemsg() {
  msg.value = "Today is Tuesday";
}
watch(msg, (newv, oldv) => {
  console.log(newv, oldv, "msg发生了变化");
});

</script>
  
  <template>
      <div>
       <p>{{msg}}</p>
       <button @click="changemsg">change-msg</button>
      </div>   
  </template>

  结果显示:

2.监听reactive定义的一个响应式数据的全部属性

  示例:

<script setup>
import { reactive, watch } from "vue";

let user = reactive({name: "haha",age: 21,shopping: { cloth: 2, shooes: 1 }});
watch(user, function(newv, oldv) {
  console.log("修改了name/age/shopping属性");
  console.log(newv, oldv);
});
function changecloth() {
  user.shopping.cloth = 3;
}
function changeshopping() {
  user.shopping="no-shopping";
}
function changename() {
  user.name = "xixi";
}
function changeage() {
  user.age = 21;
  console.log("点击了changeage按钮,修改了age,但是没有触发watch")
}
</script>
      
      <template>
          <div class="box1">
            <p>{{user}}</p>
            <button @click="changecloth">changecloth</button><br /><br />
            <button @click="changeshopping">changeshopping</button><br /><br />
            <button @click="changename">changename</button>
            <button @click="changeage">changeage</button><br /><br />
          </div>
      </template>

 显示结果:

二、Vue3生命周期

  • 什么是生命周期?

    Vue中每个组件都是独立的,每个组件都有一个属于它的生命周期,从一个组件创建、数据初始化、挂载、更新、销毁、这就是一个组件所谓的生命中周期

  • Vue2.x中的生命周期

beforeCreate   created

beforeMount    mounted

beforeUpdate  updated

beforeDestroy  destroyed

activated    deactivated   errorCaptured

  • Vue3.x的生命周期

    在Vue3.x中,新增了一个setup生命周期函数,setup执行的时机是在beforeCreate之前,因为在这个函数中不能通过this来获取实例的;同时为了命名的统一,将beforeDestory改名为beforeUnmount,destoryed改名为unmounted

beforeCreate  created(建议使用setup代替)

setup

beforeMount     mounted

beforeUpdate   updated

beforeUnmount   unmounted

  • vue3新增了生命周期钩子,我们可以通过在生命周期函数前加on来访问组件的生命周期

    Composition API 形式的生命周期钩子

onBeforeMount  onMounted

onBeforeUpdate onUpdated

onBeforeUnmount onUnmounted

onErrorCaptured onRenderTracked onRenderTriggered

  示例:

  App.vue

<script setup>
import {onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted,ref} from "vue"
import Box from "./Box.vue";

   let msg =ref("哈哈");
    function changemsg() {
      msg.value = "修改了msg,更新了页面";
    }
  onBeforeMount(function() {
		console.log("beforeMount--app组件挂载前执行")
	})
	onMounted(function() {
		console.log("onMounted--app组件挂载了以后执行")
	})
  onBeforeUpdate(function() {
		console.log("onBeforeUpdate--app组件更新前执行")
	})
	onUpdated(function(){
		console.log("onUpdated--app组件更新以后执行")
	})
	onBeforeUnmount(function() {
		console.log("onBeforeUnmount--app组件销毁前执行")
	})
  onUnmounted(function() {
		console.log("onUnmounted--app组件销毁了以后执行")
	})
</script>

<template>
	<div class="app">
       <p>{{msg}}</p>
       <button @click="changemsg">修改app的msg</button>
       <Box></Box>
	</div>
</template>

  Box.vue

<script>
  import {onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted,ref} from 'vue'
   
  export default {
    setup () {
      onBeforeMount (() => {console.log("触发了beforeMount")})
      onMounted (() => {console.log("触发了mounted")})
      onBeforeUpdate (() => {console.log("触发了beforeUpdate")}) 
      onUpdated (() => {console.log("触发了updated")}) 
      onBeforeUnmount (() => {console.log("触发了beforeDestroy")}) 
      onUnmounted (() => {console.log("触发了destroyed")})
      let msg =ref("嘻嘻");
       function changemsg() {
          msg.value = "修改了msg,更新了页面"
       }
      return {msg,changemsg}
    }
  }
  </script>
     
<template>
	<div class="box">
     <p>{{msg}}</p>
     <button @click="changemsg">修改box的msg</button>
	</div>
</template>

  显示结果:

 三、Teleport

  • Vue 鼓励我们通过将 UI 和相关行为封装到组件中来构建 UI。我们可以将它们嵌套在另一个内部,以构建一个组成应用程序 UI 的树。
  • 然而,有时组件模板的一部分逻辑上属于该组件,而从技术角度来看,最好将模板的这一部分移动到 DOM 中 Vue app 之外的其他位置
  • to属性:放到指定位置

  示例:

  App.vue

<script setup>
  import Box from "./Box.vue"
</script>

<template>
	<div class="app">
    <h1>app</h1>
        <Box></Box>
	</div>
</template>

  Box.vue

<script setup>
  import {ref} from "vue"
  let msg=ref("haha")
  let changemsg=()=>{
    msg.value="xixi"
  }
</script> 
  
  <template>
    <div>
      <h2>box</h2>
      <teleport to="body">
        <div>
          <p @click="changemsg">{{msg}}</p>
          <button @click="changemsg">change-msg</button>
        </div>
      </teleport>
    </div>
  </template>

  显示结果:

四、 属性传值

  示例:

  App.vue

<script setup>
  import Box from "./Box.vue"
  let haha="哈哈"
</script>

<template>
    <div>	
      <Box :name="haha" :age=21 @myclick="fn"></Box>
    </div>
</template>

  Box.vue

<script>
  export default {
     props:["name","age"],
     setup(props){
         let fn=()=>{console.log(props)}//必须在组件中注册属性不然setup函数收不到数据
         return {fn}
     }    
  }
</script>
<template>
    <div>
       <p>{{name}}:{{age}}</p>
        <button @click="fn">look</button>
    </div>
</template>

  结果显示:

 五、自定义事件

  • 事件名

    事件名提供了自动的大小写转换即如果在子组件中触发一个以 camelCase (驼峰式命名) 命名的事件,可以在父组件中添加一个 kebab-case (短横线分隔命名) 的监听器

  示例:

  App.vue

<script setup>
  import MyBox from "./MyBox.vue"
  function mysum(arg1,arg2){
		let sum=arg1+arg2
    console.log(sum)
	}
</script>

<template>
	<div>	
	    <h1>app</h1>
		  <my-box @my-click="mysum"></my-box>
	</div>
</template>

  MyBox.vue

<script setup>
	import {ref,watch,defineEmits} from "vue"
	let count=ref(1)
	let fm=()=>{
		count.value++
	}
	let emit=defineEmits()
	watch(count,(v)=>{
		if(v==3){
        //触发自定义事件的同时给父组件传值
		   	emit("myClick",5,10)
		}
	})
</script>

<template>
	<div class="box">
		<p>{{count}}</p>
		<button @click="fm">点两下打印结果</button>
	</div>
</template>

结果显示:

 六、状态驱动的动态 CSS

  • 单文件组件的 <style> 标签可以通过 v-bind 这一 CSS 函数将 CSS 的值关联到动态的组件状态上

  示例:

<script setup>
	import {ref} from "vue"
	
	let color = ref("lightpink")
	let ishow=ref("block")
	let flag=0
	let change=()=>{
		if(flag==0){
			color.value="lightblue"
		    ishow.value="none"
			flag=1
		}else{
			color.value="lightpink"
		    ishow.value="block"
			flag=0
		}
	}	
</script>
<template>
	<div>
		<h1 class="box1">我出现啦</h1>
		<button @click="change">点击切换显隐</button>
	</div>
</template>

<style scoped lang="scss">
	.box1 {
		color: v-bind(color);
		display: v-bind(ishow);
	}
</style>

  结果显示:

 七、注册组件

  1.组件内部注册:

<script>
    import Box1 from "./Box1.vue"
    export defult{
        components:{
            Box1
        },
        setup(){}
    }
</script>
<template>
    <Box1></Box1>	
< /template>

  2.vue3组件内部组合式API setup语法糖中注册组件

<script setup>
    import Box1 from "./Box1.vue"  
    //只需要导入 不用写注册代码  会在打包的时候自动帮我们注册 
</script>
<template>
    <Box1></Box1>   
</template>

  3.注册全局组件

//main.js文件:
import { createApp} from 'vue'
import App from './App.vue'
const app=createApp(App)
​
import Box1 from "./Box1.vue"
app.component(Box1.name,Box1)
​
app.mount('#app')//注意 一定要在app.mount之前注册全局组件 否则使用不了
​
//App.vue文件:
<template>
    <Box1></Box1>   
< /template>

  4.定义同步组件:

//Box1.vue文件:
<script>
  import {defineComponent} from "vue"
  export default defineComponent({
        data(){
            return {}
        },
        methods:{},     
        setup(){
        }       
    });
</script>

  5.定义异步组件:

  (1)局部异步组件

      组件内部:

<script>
    import {defineAsyncComponent} from "vue"
    let Box1 = defineAsyncComponent(() => import("./Box1.vue")) 
    //注意3.2之后不用引入defineAsyncComponent
    export default {
        components: {
            Box1
        },
        setup() {}
    }
</script>

     setup语法糖:

<script setup>
    import {defineAsyncComponent} from "vue"
    let Box3=defineAsyncComponent(()=>import("./Box3.vue"))
   //注意3.2之后不用引入defineAsyncComponent,而且这个变量名直接就是注册的组件名(打包时自动注册的)
</script>

 (2)全局异步组件

//main.js文件:
import { createApp,defineAsyncComponent} from 'vue'
import App from './App.vue'
const app=createApp(App)
let Box1=defineAsyncComponent(()=>import("./Box1.vue"))
app.component("Box1",Box1)
//注意 一定要在app.mount之前注册全局组件 否则使用不了
app.mount('#app')

八、占位组件Suspense

  • 等待异步组件时渲染一些额外的内容,让应用有更好的用户体验

    <suspense> 组件有两个插槽。它们都只接收一个直接子节点。

  • default 插槽里的节点会尽可能展示出来。如果不能,则展示 fallback 插槽里的节点。

  示例:

   App.vue

<script setup>
  import Box1 from "./Box1.vue"
  import Box2 from "./Box2.vue"
  import {defineAsyncComponent} from "vue"
  let Box3=defineAsyncComponent(()=>import("./Box3.vue"))

</script>
<template>
<div>
  <Box1></Box1>
  <Box2></Box2>
  <suspense>
    <template #default>
      <Box3></Box3>
    </template>
    <template #fallback>
      <div>loading....</div>
    </template>
  </suspense>
  <Box4></Box4>
</div>
</template>

   Box2.vue

<template>
	<div>
		<h3>box2</h3>
		<p>{{msg}}</p>
	</div>
</template>

<script>
	import {ref,defineComponent} from "vue"
	let Box2=defineComponent({		
		setup(){
			let msg=ref("box2--msg")
			return {msg}
		}		
	})
	export default Box2;
</script>

   Box3.vue

<template>
	<div>
		<h3>加载box3</h3>
	</div>
</template>