• Vue3响应式核心API 使用注意点


    1,和 vue2 的对比

    vue2 的响应式原理通过 defineProperty 实现,vue3 通过 Proxy 实现。

    但这里不讨论原理,讨论的是 vue3 新增 API 的使用和注意点。因为 vue3 将响应式 API 暴露了出来,是一套独立的数据响应式系统,和组件没有关系,这样也就能够实现单一状态管理

    • vue2 的响应式数据放在 data(){} 中,最终会被注入到组件实例上。
    • vue3 的响应式数据,是通过暴露出的响应式API来实现的,最终通过 setup() 返回。

    2,核心 API 介绍

    vue3 中的响应式数据,有2种格式:

    1. reactive 返回的 Proxy 对象,可直接访问属性。
    2. refcomputed 返回的 Ref 对象,需要通过 .value 访问属性。

    1,reactive 和 readonly

    官网参考

    先说几点重要的:

    1. 都只能用于对象类型。
    2. 返回的代理对象和原始对象不相等。
    3. vue3 为保证访问代理的一致性,对同一个原始对象调用 reactive() 会总是返回同样的代理对象,而对一个已存在的代理对象调用 reactive() 会返回其本身(下面有例子说明)。
    4. readonly 的唯一区别是返回的代理对象是只读的,修改属性会报错。

    看下面的例子

    import { reactive, readonly } from 'vue'
    
    const origin = { a: 1, b: 2 }
    const state = reactive(origin)
    console.log(state === reactive(origin)) // true 相同的代理对象
    console.log(state === reactive(state)) // true 返回自身
    
    const stateOnly = readonly(state)
    console.log(stateOnly === state) // false
    
    state.a++
    console.log(stateOnly.a) // 2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    虽然 stateOnly 是只读的。但因为代理的是 state,所以当 state 被修改时 stateOnly 也会被修改。

    另外,stateOnly 代理–> state 代理–> { a: 1, b: 2 },所以 stateOnly !== state

    简单应用

    import { readonly, reactive } from 'vue'
    
    /**
     * 返回1个对象和2个方法,
     * 对象是是响应式的,不允许直接修改。只能通过提供的方法修改指定属性。
     * @returns Object
     */
    function useUser() {
      const userOrigin = reactive({})
      const user = readonly(userOrigin)
      const setUserName = (name) => {
        userOrigin.name = name
      }
      const setUserAge = (age) => {
        userOrigin.age = age
      }
      return {
        user,
        setUserName,
        setUserAge
      }
    }
    
    const { user, setUserName, setUserAge } = useUser()
    
    console.log(user)
    setUserName('下雪天的夏风')
    setUserAge(18)
    console.log(user)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    2,ref

    官网参考

    可以代理任何数据,因为它是把这些数据都放到了一个对象的 .value 属性上,并返回这个对象。

    注意,这个对象不是 Proxy 对象,而是 Ref 对象。区别在上面已经说明了。

    如果 ref() 的参数是Proxy 对象,则直接将它放到 .value 属性上。

    const state = reactive({ a: 1, b: 2 })
    const stateRef = ref(state)
    
    console.log(stateRef.value === state) // true
    
    • 1
    • 2
    • 3
    • 4

    使用 .value 的原因是,无法直接检测到普通变量的访问或修改。所以通过 gettersetter 方法来拦截对象属性的 get 和 set 操作。

    ref 的大致原理如下:ref 原理参考

    const myRef = {
      _value: 0,
      get value() {
        // 触发依赖收集,重新渲染
        track()
        return this._value
      },
      set value(newValue) {
        this._value = newValue
        // 通知依赖它的对象更新
        trigger()
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    3,监听数据

    watchEffect

    官网参考

    这个函数会立即运行,同时响应式地追踪其依赖,并在依赖更改时重新执行。

    也就是说,可以同时监听多个。只要该函数中使用到的响应式数据被修改了,这个函数就会重新执行。

    在 vue3 中,大多数情况下监听数据使用 watchEffect() 就够了。

    watch

    官网参考

    watch 的使用方式和 vue2 中差别不大,和 watchEffect 的区别是:

    1. watch 默认是懒侦听,即仅在侦听源发生变化时才执行回调函数,可以指定配置项 { immediate: true }来立即执行一次。
    2. 可以获取到旧值。
    3. 可以更加明确的知道,是哪个状态的修改导致了 watch 的重新执行。

    注意点:

    watch 不能监听普通数据,监听来源只能是下面4种:

    1. 一个函数,返回一个值
    2. 一个 ref
    3. 一个响应式对象
    4. 由以上类型的值组成的数组

    看下面的例子:

    import { reactive, watch } from 'vue'
    
    const state = reactive({ a: 1, b: 2 })
    watch(state.a, () => {
      console.log('变化了')
    })
    
    state.a++ // watch 函数并不会执行。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    state.a 是普通的数据,不是响应式的,所以无法监听。

    import { reactive, watch } from 'vue'
    
    const state = reactive({ a: 1, b: 2 })
    watch(() => state.a, () => {
      console.log('变化了')
    })
    
    state.a++ // 变化了
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    如果 watch 的第一个参数是函数,则会调用该函数来收集依赖。也就是说,用到的 state.a 中的 state 是响应式数据,所以能被监听到了。

    但如果是监听的是 const count = ref(0) ,就可以直接监听 count 而不使用函数的方式返回。因为传递的是对象。所以可以监听到 value 的值。

    4,判断和转换

    判断

    API含义
    isProxy判断数据是否reactivereadonly 创建的
    isReactive判断数据是否reactive创建的
    isReadonly判断数据是否readonly创建的
    isRef判断数据是否是ref对象

    转换(toRefs)

    更多参考工具函数

    1,unref()

    如果参数是 ref,则返回内部值,否则返回参数本身。

    val = isRef(val) ? val.value : val
    
    • 1

    2,toRefs

    这个比较重要。

    toRefs 会将一个响应式对象的所有属性转换为 ref 格式,然后包装到一个普通对象中返回。

    看下面的例子:

    import { reactive, toRefs } from 'vue'
    
    const state = reactive({ a: 1, b: 2 })
    const stateRef = toRefs(state)
    console.log(stateRef) // {a: refObj, b:refObj},所以将 stateRef 展开也是响应式的。
    console.log(stateRef.a.value) // 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    应用1

    当父级传递的数据比较多,但只想用其中的某个,并且需要保持响应式:

    <template>
      <h1>{{ msg }}h1>
      <h1>{{ msg2Alias }}h1>
    template>
    
    <script setup>
    import { toRef, toRefs, computed } from "vue";
    const props = defineProps(["msg", "msg2"]);
    
    // 下面3种方式等价,父级修改 msg2 时,msg2Alias会同步修改。
    const {  msg2: msg2Alias } = toRefs(props);
    const msg2Alias = toRef(props, "msg2");
    const msg2Alias = computed(() => props.msg2);
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    应用2

    当使用方法返回的是代理对象时,可以解构而不失去响应式

    import { reactive, toRefs } from "vue";
    
    // composition function
    function usePos(){
      const pos = reactive({x:0, y:0});
      return pos;
    }
    
    setup(){
      const {x, y} = usePos(); // lost reactivity
      const {x, y} = toRefs(usePos()); // reactivity
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    以上。

  • 相关阅读:
    提质增效,降本增益,企业如何构建数据驱动的高效组织?
    用R语言和python进行社交网络中的社区检测
    ThreadLocal优化
    【MySQL】【牛客-SQL进阶挑战】02 表与索引操作
    阿里云:云服务器ECS搭建
    java毕业设计超市商品管理mybatis+源码+调试部署+系统+数据库+lw
    Docker镜像构建之Dockerfile
    带你走进脚本世界,ijkplayer之【init-ios.sh】脚本分析
    基于Python Django的公务员考试信息管理系统
    那Java自学,该怎么开始,要怎么学?
  • 原文地址:https://blog.csdn.net/qq_40147756/article/details/134267664