vue3 基础-API-响应式 ref, reactive

上篇咱介绍了 CompositionAPI, 其核心思想是直接在函数作用域内定义响应式状态变量,并将从多个函数中得到的状态组合起来处理复杂问题.

然后初介绍了 setup 函数的作用, 即其是在 created 实例完全初始化之前调用的, 因此不能用 this, 它的主要作用就可以是管理我们接下来要介绍的 API. 在项目中通常扮演一个"任务调度" 的角色, 对整个单页面的逻辑起一个核心调度的作用.

响应式变量

首先, 注意上面这里有个关键词叫 响应式状态变量 那我们先用一个案例来认识该现象.

<!DOCTYPE html>
<html lang="en">

<head>
  <title>响应式变量</title>
  <script src="https://unpkg.com/vue@3"></script>
</head>

<body>
  <div id="root"></div>
  <script>
    const app = Vue.createApp({
      template: `<div>{{name}}</div>`,

      setup (props, context) {
        let name = "youge"
        // 延时器的作用是 2秒后, 将 name 的值 改为 "cj"
        setTimeout(() => {
          name = 'cj'
        }, 2000);

        return { name }
      }
    })
    const vm = app.mount('#root')

  </script>
</body>

</html>

经过 2 秒后, 发现页面的数据并没有改变 !, 这就引入了一个响应式变量的概念.

Vue3 是使用 Proxy, 它可以劫持整个data对象,然后递归返回属性的值的代理即可实现响应式;但是它的兼容性不是很好;

Vue2 是使用 Object.defineProperty,它只能劫持对象的属性,所以它需要深度遍历data中的每个属性,这种方式对于数组很不友好,而且对象观测后,新增的属性就不是响应式的,不过可以用Vue.set()来添加新的属性;

总之结论就是在 vue 中, dom 的更新由数据驱动, 将数据进行 Proxy 封装, 当数据变化时, 会自动触发模板 dom 的更新啦.

ref

像 ref, reactive 他们都是将一个 vue 将数据变量封装为响应式变量的方法啦 ( ref 的底层也是 reactive) .

在使用中呢, ref 用来处理基础类型的数据 (number, string, null, boolean, undefined) 将其变为响应式.

<!DOCTYPE html>
<html lang="en">

<head>
  <title>ref</title>
  <script src="https://unpkg.com/vue@3"></script>
</head>

<body>
  <div id="root"></div>
  <script>
    const app = Vue.createApp({
      // 当时 ref 时, 这里不需要写成 name.value, 直接 name
      template: `<div>{{name}}</div>`,

      setup (props, context) {

        const { ref } = Vue 
        // proxy: 将 'youge' 封装成 proxy({value: 'youge'}) 的响应式
        let name = ref("youge")
        // 延时器的作用是 2秒后, 将 name 的值 改为 "cj"
        setTimeout(() => {
          // ref 底层也是 reactive, 其实是一个对象
          name.value = 'cj'
        }, 2000);

        return { name }
      }
    })
    const vm = app.mount('#root')

  </script>
</body>

</html>

再来一波关键点的复述, 这个蛮重要的其实, 就先不探究原理, 重在使用轮子哈:

// 1. 通过结构的方式从 Vue 中引入 ref
// 2. 响应变量 = ref(基础类型变量) 即完成包装
// 3. 通过 响应变量.value = 'xxx' 即完成数据变更
// 4. setup 直接 return 响应变量, 模板便可用, 而不用 .value 的方式

const { ref } = Vue 

let name = ref("youge")

name.value = 'newValue'

`<div>{{name}}</div>`,

reactive

同 ref 一样的作用 ( ref 基于 reactive) 用来将引用类型数据 ( object, array, function ... ) 等给封装为响应式变量啦.

当然普通类型也是可以的, 就 reactive 的适普性会更强哦. 还是同上的例子, 我们用 reactive 来改写一波:

<!DOCTYPE html>
<html lang="en">

<head>
  <title>reactive</title>
  <script src="https://unpkg.com/vue@3"></script>
</head>

<body>
  <div id="root"></div>
  <script>
    const app = Vue.createApp({
      template: `
      <div>{{nameObj}}</div>
      <div>{{nameObj.name}}</div>
      `,

      setup (props, context) {

        const { reactive } = Vue 
        // proxy: 将 { name: 'youge' } 封装成 proxy({name: 'youge'}) 的响应式
        let nameObj = reactive({ name: 'youge' })
        setTimeout(() => {
          nameObj.name = 'cj'
        }, 2000);

        return { nameObj }
      }
    })
    const vm = app.mount('#root')

  </script>
</body>

</html>

当然数组也是一样的啦.

setup (props, context) {
    const { reactive } = Vue 
    
    let arr = reactive(['a', 'b', 'c'])
    
    setTimeout(() => {
        arr[1] = 'cj'
    }, 2000);

    return { arr }
}

这样通过 ref 和 reactive 将数据封装为响应式变量,则就可以代替掉原来的 data ( ) 方法啦. 这个会更加通用和方便维护的哦.

readonly

即对响应式变量进行 "只读" 的限定哈.

setup (props, context) {

    const { reactive, readonly } = Vue 
    let arr = reactive(['a', 'b', 'c'])
    
    // 只要用了 readonly 就不能修改啦, 会警告的
    const copyArr = readonly(arr)
    setTimeout(() => {
        arr[1] = 'cj'
        copyArr[0] = 666
    }, 2000);

    return { arr, copyArr }
}

toRefs

它的作用其实就是将咱响应式变量中的值, 通过结构的方式获取到时也是一个响应式变量的值, 可以被模板直接引用, 就不用在模板中写类似 xxxObj.xxx 的写法啦.

一句话: 将 reactive 的数据转为 ref 数据, 也是响应式的啦.

在本例中, proxy( { name: 'youge' }) 中的值给再包装为 { name: proxy( { value: 'youge' })} 这样.

<!DOCTYPE html>
<html lang="en">

<head>
  <title>toRefs</title>
  <script src="https://unpkg.com/vue@3"></script>
</head>

<body>
  <div id="root"></div>
  <script>
    const app = Vue.createApp({
      template: `
      <div>{{name}} : {{age}}</div>
      `,

      setup(props, context) {

        const { reactive, toRefs } = Vue
        let dataObj = reactive({ name: 'youge', age: 18 })

        setTimeout(() => {
          dataObj.name = 'cj'
          dataObj.age = 26
        }, 2000);

      // toRefs: proxy({name: 'cj', age: 18}) 转为: 

      // name: proxy({value: 'cj'}), 
      // age:  proxy({value: 18})

      // 然后直接来一波结构赋值
      const { name, age } = toRefs(dataObj)

        return { name, age }
      }
    })
    const vm = app.mount('#root')

  </script>
</body>

</html>

小结

  • ref 和 reactive 都是对 js 数据类型进行响应式的封装, ref 针对基础类型, reactive 针对引用类型
  • ref 原理: proxy: 将 'youge' 封装成 proxy({value: 'youge'}) 的响应式
  • reactive 原理: proxy: 将 { name: 'youge' } 封装成 proxy({name: 'youge'}) 的响应式
  • toRefs 的作用: 将 proxy({name: 'cj' }) 转化为 { name: proxy({ value: 'cj' })}
posted @ 2022-10-06 15:28  致于数据科学家的小陈  阅读(122)  评论(0编辑  收藏  举报