Vue3的监视计算属性(toRefs,生命周期,Hook)

155 阅读3分钟

setup的细节

1.setup执行的时机

  • 在beforeCreate之前执行(一次),此时组件对象还没有创建
  • this的值为undefined,不能通过this访问data/computed/methods/props
  • 其实所有的composition API 相关的回调函数中也都不可以

2.setup的返回值

  • 一般都会返回一个对象,为模板提供数据,模板中可以直接使用返回的对象中的属性和方法
  • 返回对象中的属性会与data函数返回对象的属性合并成为组件对象的属性
  • 返回对象中的方法会与methos中的方法合并成为组件对象的属性
  • 如果有重名,setup优先,现在是报错了
  • 注意:
    1. 一般不要混合使用:methods中可以访问setup提供的属性和方法,但在setup方法中不能访问data和methods
    2. setup不能是一个async函数:因为返回值不再是return的对象,而是Promise,模板看不到return对象中的属性数据
  • setup 的参数
    • setup(props,context)/setup(props,{attars,slots,emit})
    • props: 包含props配置声明且传入了所有属性的对象
    • attars:包含没有在props配置中声明的属性的对象,相当于 this.$attrs
    • slots:包含所有传入的插槽内容的对象,相当于this.$slot
    • emit: 用来分发自定义事件的函数,相当于this.$emit
<template>
    
  <div>{{obj}}</div>
  <button @click="updateObj">更新数据</button>

</template>

<script lang="ts">
  import {defineComponent} from 'vue'

  export default defineComponent({
    name:'APP',     
      setup(){
          //ref用来定义复杂类型的响应式数据
         const obj = ref({
             name:'小宇',
             wife:{
                 name:'小甜甜',   
                 cars:['奥迪','宝马','奔驰']
             }
         })
             const updateObj = ()=>{
        //obj.value.name+='==='
        obj.value.wife.cars[2]='马自达'
    }
    return{
        obj,
        updateObj
    }
      }
  })

</script>


<style scoped>
  
</style>

计算属性

<template>
    <h2>
        computed计算属性
    </h2>
    <h2>{{count2}}</h2>
    <h2>{{count3}}</h2>
 <button @click='upodateCount'></button>

</template>

<script lang="ts">
  import {defineComponent,computed} from 'vue'

  export default defineComponent({
    name:'APP',         
    setup(){
        const count = ref(10)
        //计算属性用法1,vue3中computed返回的是Ref类型的数据
        const count2 = computed(()=>{
            return count.value+10
        })
        //计算属性用法2
        const count3 = computed({
            get(){
                console.log('进入到了get')
                return count.value*10
            },
            set(val:number){
                  console.log('进入到了set')
                count.value = val
            }
        })
        //方法:
        const updateCount=()=>{
            count3.value=1000
        },
        console.log(count2)
        return{
            count2
            count3
        }
    }
  })

</script>


<style scoped>
  
</style>

监视属性

Watch()

侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数

watch 是懒侦听的,即仅在侦听源发生变化时才执行回调函数

watchEffect()

  • watch的套路式:既要指明监视的属性,也要指明监视的回调

  • watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,就监视哪个属性

  • watchEffect有点像computed:

    • 但computed注重的是计算出来的值(回调函数的返回值),所以必须要写返回值
    • 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值
    //watchEffect 所指定的回调中用到的数据只要发生变化,则直接重新执行回调
    watchEffect(()=>{
        const x1 = sum.value
        const x2 = person.age
        console.log('watchEffect配置的回调执行了')
    })
    

立即执行一个函数,同时响应式的追踪其依赖

<template>
  <h2>watch和computed及watchEffect函数</h2>
  <fieldset>
    <legend>姓名操作</legend>
    姓氏:<input type="text" placeholder="请输入姓氏" v-model="user.firstName" /><br />
    名字:<input type="text" placeholder="请输入名字" v-model="user.lastName" /><br />
  </fieldset>

  <fieldset>
    <legend>计算属性和监视的演示</legend>
    姓名:<input type="text" placeholder="显示姓名" v-model="fullName1" /><br />
    姓名:<input type="text" placeholder="显示姓名" v-model="fullName2" /><br />
    姓名:<input type="text" placeholder="显示姓名" v-model="fullName3" /><br />
  </fieldset>
</template>
<script lang="ts">
import {
  computed,
  defineComponent,
  reactive,
  ref,
  watch,
  watchEffect
} from 'vue'
export default defineComponent({
  name: 'App',
  setup() {
    // 定义响应式对象,存储姓氏和名字
    const user = reactive({
      firstName: '东方',
      lastName: '不败'
    })

    // 通过计算属性的方式实现 第一个姓名的操作效果
    const fullName1 = computed(() => {
      // get的简写形式
      return user.firstName + '_' + user.lastName
    })

    // 通过计算属性的方式实现  第二个姓名的操作效果
    const fullName2 = computed({
      get() {
        return user.firstName + '_' + user.lastName
      },
      set(val: string) {
        // ts的语法
        const names = val.split('_')
        user.firstName = names[0]
        user.lastName = names[1]
      }
    })

    // 通过监视的方式实现第三个姓名的操作
    const fullName3 = ref('')
    // 使用watch进行数据监视
     watch(user, ({ firstName, lastName }) => {
       fullName3.value = firstName + '_' + lastName
     },{immediate:true,deep:true}) // 默认执行一次,深度监视

    // watchEffect(()=>{
    //   fullName3.value = user.firstName + '_' + user.lastName
    // }) // 默认就会执行一次

    // watchEffect(() => {
    //   const names = fullName3.value.split('_')
    //   user.firstName = names[0]
    //   user.lastName = names[1]
    // })

    // watch也可以同时监视多个数据
    // watch([user.firstName, user.lastName, fullName3], () => {
    //   console.log('=====')
    // })
    
    // watch另一种写法
    // watch([()=>user.firstName, ()=>user.lastName, fullName3], () => {
    //  console.log('=====')
    // })
    return {
      user,
      fullName1,
      fullName2,
      fullName3
    }
  }
})
</script>

toRefs()

把一个响应式对象转换为普通对象,该普通对象的每个property都是一个ref

应用:当从和成函数返回响应式对象时,toRef非常有用,这样消费组件可以在不丢失响应式的情况下对返回的对象进行分解使用

问题:

reactive对象取出的所有属性值都是非响应式的

解决:利用toRefs可以将一个响应式reactive对象的所有原始属性转换为响应式ref属性

作用:创建一个ref对象,其value值指向另一个对象的某个属性

语法:const name = toRef(person,'name')

应用:要将响应式对象中某个属性单独提供给外部使用时

扩展:toRefs 与 toRef功能一致,但是可以批量创建多个ref对象,语法:toRefs(person)

vue3的生命周期

1.vue3中是支持vue2中的声明周期钩子的,vue2中的beforeDestroy的destroyed两个周期钩子在vue3中已经替换了

vue2中的beforeDestroy替换成了onBeforeUnmount (销毁之前)

destroy替换成了onUnmounted (销毁之后)

2.生命周期对比

  • beforeCreate-> 使用setup()`
  • created -> 使用 setup()
  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  • beforeUpdate -> onBeforeUpdate
  • updated -> onUpdated
  • beforeDestroy -> onBeforeUnmount
  • destroyed -> onUnmounted
  • errorCaptured -> onErrorCaptured

//2.0中为 beforeDestroy 和 destroyed

  • beforeUnmount -> onBeforeUnmount
  • unMounted -> onUnmounted

理解自定义Hook

  • 作用:对多个组件重复的功能进行提取封装
  • 使用Vue3的组合API封装的可复用的功能函数
  • 自定义hook的作用类似于vue2中的mixin技术
  • 自定义Hook的优势:很清楚复用功能代码的来源,更清楚易懂