天天看點

vue3 基礎-API-響應式 ref, reactive

響應式變量 ref, reactive, toRefs

上篇咱介紹了 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' })}

繼續閱讀