Vue学习笔记11-Composition API(二)

166 阅读4分钟

1.Reactive 判断的API

isProxy:检查对象是否由reactive或者readonly创建的proxy

isReactive:检查独享是否由reactive创建的响应式代理

isReadonly:检查是否由readonly创建的只读代理

toRow:返回reactive或readonly代理的原始对象

shallowReactive:创建一个响应式代理,它跟踪自身的property响应性,但不执行嵌套对象的深层响应式转化,(深层还是原生对象)

shallowReadonly:创建一个proxy,使其自身的property为只读,但不执行嵌套对象的只读转换

2.toRefs

如果我们使用ES6的解构语法,对reactive返回的对象进行结构获取值,那么之后无论是修改解构后的变量,还是修改 reactive返回的对象,数据都不再是响应式的

const state = reactive({
    name:'wjm',
    age:18
})
const {name,age} = state

Vue为我们提供了一个toRefs函数,可以将reactive返回的对象中的所有属性都转成ref 那么我们再次进行解构之后,解构出来的name和age都是ref的

const {name,age} = toRefs(state)

这种做法相当于在state.name和ref.value之间建立了连接,任何一个修改都会引起另一个变化

3.toRef

只转换一个属性为ref

const state = reactive({
    name:'wjm',
    age:18
})
//普通解构
let {name} = state
//refs解构,接受一个响应式对象,将state中的所有属性转换成ref,返回对象,然后解构
let {name} = toRefs(state)
//toRef,只转换name属性,返回单个ref对象
let name = toRef(state,'name')

unRef:如果参数是ref对象,返回内部值,否则直接返回

val = unRef(val)等同于val = isRef(val)?val.value:val

4.shallowRef 与 triggerRef

shallowRef:创建一个浅层的ref对象

当我们不希望使一个对象具有深层响应式时,可以使用shallowRef,这样,我们改变深层的值的时候,不会触发视图更新

同时,Vue3提供了triggerRef方法,接受一个响应式对象,触发该对象的响应式更新,比如:

<script>
import {ref,shallowRef,triggerRef} from 'vue'
export default {
  name: '',
 setup(props,context) {
     const info = shallowRef({
         name:"wjm",
         friends:['abc','cba','joey']
     })
     const changeFriends = ()=>{
         info.value.friends.pop()
         //手动触发info响应式更新
         triggerRef(info)
     }

     return {
         info,
         changeFriends
     }
 }
}
</script>

5.customRef

customRef:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显示控制,返回一个带有get和set的对象

useDebounceRef.js

import {customRef} from 'vue'

// 自定义ref
export default function(value){
    let timer = null
    return customRef((track,trigger)=>{
        return {
            get(){
                //收集依赖
                track()
                return value
            },
            set(newValue){
                clearTimeout(timer)
                timer = setTimeout(() => {
                    value = newValue
                    // 触发依赖,更新视图
                    trigger() 
                }, 2000);
            }
        }
    })
}

app.vue

<template>
  <div>
      <input type="text" v-model="message">
      {{message}}
  </div>
</template>

<script>
// import {ref,customRef} from 'vue'
import useDebounceRef from '../hook/useDebounceRef'
export default {
  name: '',
  setup(){
      const message = useDebounceRef('hello world')
      return {
          message
      }
  }
}
</script>

6.computed 计算属性

在vue3中,我们可以在setup中使用computed方法来编写一个计算属性 computed方法接受一个getter函数或一个包含set、get的对象

<script>
import {ref,computed} from 'vue'
export default {
    setup(){
        const firstName = ref('kobe')
        const lastName = ref('bryant')

        // computed函数返回的是一个ref对象
        // 用法1:传入一个getter
        // const fullName = computed(()=>firstName.value +" "+ lastName.value)

        // 用法2:传入一个对象,包含getter,setter
        const fullName = computed({
            get:()=>firstName.value + lastName.value,
            set(newValue){
                const names = newValue.split(' ')
                firstName = names[0]
                lastName = names[1]
            }
        })

        const changeFullName = ()=>{
            fullName.value = 'james wu'
        }
        return {
            fullName,
            changeFullName
        }
    }
}
</script>

7. watchEffect、watch 侦听数据

在optionAPI中,我们可以通过watch来侦听data和props的数据的变化

在compositionAPI中,我们使用 watchEffect和watch来实现侦听数据变化

watchEffect接受一个回调函数,该函数默认会先执行一次,分析依赖了哪些数据,在这些数据改变时,会执行注册的回调

watch.vue

<script>
import {ref,watchEffect} from 'vue'
export default {
    setup(){
        const name = ref('wjm')
        const age = ref(18)

        const changeName = ()=>{
            name.value = 'kittle'
        }

        // watchEffect默认会先执行一次,分析依赖了哪些数据,在这些数据改变时,会执行注册的回调
        watchEffect(()=>{
            console.log('now name is:',name.value)
        })
        const changeAge = ()=>age.value++

        return {
            name,
            age,
            changeAge,
            changeName 
        }
    }
 
}
</script>

watchEffect会返回一个停止侦听函数stop,执行后会停止该监听

const stop = watchEffect(()=>{
            console.log('now name is:',name.value)
        })
        
 if(age > 25){
 
 }

watchEffect的清除副作用

比如我们需要在侦听函数中发送请求,但是在请求还没哟到达的时候,我们停止了侦听器,或者侦听器函数被再次执行了,那么上一次的请求就应该被取消掉,这个时候我们就可以清除上一次的副作用;

我们可以在watchEffect传入的函数回调中,可以获取到一个参数,onInvalidate

当副作用即将重新执行或者侦听器被停止时,会执行onInvalidate函数传入的回调函数,我们可以在传入的回调函数中执行一些清理操作

watchEffect((onInvalidate)=>{
            const timer = setTimeout(() => {
                console.log('now name is:',name.value)
            }, 2000);

            onInvalidate(()=>{
                //在这个回调中执行一些清理操作
                clearTimeout(timer)
            })
            
        })

watch:等同于optionAPI中的watch,是惰性的,第一次挂载不会触发

import {ref,reactive,watch} from 'vue'
export default {
  setup(){
      const info = reactive({name:'why',age:18})

    //   第一种写法:传入getter函数和回调函数
      watch(()=>info.name,(newValue,oldValue)=>{
          console.log('newValue:',newValue,'oldValue:',oldValue)
      })

    //   第二种:传入一个可响应对象:reactive/ref对象
    //   reactive对象:拿到的newValue和oldValue都是reactive对象
    //      如果
    //   ref对象:获取到的newValue和oldValue都是value,不是ref对象
      watch(info,(newValue,oldValue)=>{
          console.log('newValue:',newValue,'oldValue:',oldValue)
      })
      const changeData = ()=>{
          info.name = 'kobe'
      }

      return {
          info,
          changeData
      }
  }
}
</script> 

第一种监听方式:watch接受两个参数,get函数和回调函数

第二种监听方式:watch接受两个参数,响应式对象(reactive/ref)和回调函数,根据传入的值的类型不同,回调中拿到的值的类型也不同,具体见代码;

watch函数的第一个参数可以接受一个数组,这样,回调中的newValue和oldValue也分别是一个数组,类型可以可不相同

watch的选项

  1. 深度侦听:当watch传入的是响应式对象,默认深度监听,其他情况中,我们可以给watch传入第三个参数,配置对象
    watch(info,(newValue,oldValue)=>{
          console.log('newValue:',newValue,'oldValue:',oldValue)
      },
      {     
          deep:true, //深度监听
          immediate:true //立即执行
      })

8.在setup中使用ref

在vue2中我们可以使用this.$refs.xxx获取dom,但在vue3中,setup中不能绑定this,因此我们不能使用这种方式

可以使用ref函数进行绑定

<template>
  <div>
      <h2 ref="title">哈哈哈</h2>
  </div>
</template>

<script>
import {ref,watchEffect} from 'vue'
export default {
    setup(){
        //调用ref,传入null,dom挂载后会把对应的dom传给title对象
        const title = ref(null)

        watchEffect(()=>{
            console.log(title.value)
        },{
            //flush的默认值为pre,意为在挂载,更新时都会执行,
            // flush:"pre",
            // flush:'post'
        })

        return {
            title
        }
    }
}
</script>