<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Vue3 中有場(chǎng)景是 reactive 能做而 ref 做不了的嗎?

          共 7402字,需瀏覽 15分鐘

           ·

          2022-08-01 00:33

          前言

          如果你使用過(guò) Vue3,你知道的,在 Vue3 中有兩個(gè)非常常用的響應(yīng)式 API:reactive 和 ref。它們會(huì)把我們想要追蹤的數(shù)據(jù)變成響應(yīng)式。

          而且我們?cè)谑褂脮r(shí)一直被告知 ref 用于創(chuàng)建基礎(chǔ)類型的響應(yīng)式,也可以創(chuàng)建引用類型的響應(yīng)式。而對(duì)于引用類型,底層也是轉(zhuǎn)換為 reactive 來(lái)進(jìn)行響應(yīng)式處理。那既然這樣為撒還需要 reactive ,全部使用 ref 不就行了嗎?

          雖然 ref 創(chuàng)建的響應(yīng)式數(shù)據(jù)在腳本中需要通過(guò) .value 才能訪問(wèn)到呀!但是這里肯定影響不大。并且在模板中會(huì)自動(dòng)添加上 .value,所以模板中不需要通過(guò) .value 訪問(wèn)。

          既然這二者基本沒差別,但是還是暴露了 reactive 這個(gè) API,難道有什么場(chǎng)景是 reactive 能做而 ref 做不了的?

          image.png

          簡(jiǎn)單了解 ref & reactive

          我們先簡(jiǎn)單了解一下這兩個(gè) API。

          reactive

          返回對(duì)象的響應(yīng)式副本,響應(yīng)式轉(zhuǎn)換是“深層”的——它影響所有嵌套 property。我們一般這樣寫。

          const obj = reactive({ count0 })

          并且可以直接使用。

          const count = obj.count

          ref

          接受一個(gè)內(nèi)部值并返回一個(gè)響應(yīng)式且可變的 ref 對(duì)象。ref 對(duì)象僅有一個(gè)  .value property,指向該內(nèi)部值。我們一般是這樣寫。

          const data = ref(xxx)

          引用的時(shí)候,一般會(huì)通過(guò)data.value的方式引用。

          const dataValue = data.value

          通過(guò)跟蹤 Vue3 的源代碼可以證明,當(dāng)我們調(diào)用 ref 方法來(lái)定義響應(yīng)式數(shù)據(jù)時(shí),當(dāng)參數(shù)為對(duì)象類型時(shí),其實(shí)里面用的是 reactive 方法。也就是說(shuō)上面的 data.value ,事實(shí)上是 reactive 方法創(chuàng)造出來(lái)的。

          reactive 能做的 ref 也能做,并且還是用 reactive 做的

          我們通過(guò)源碼來(lái)看看 ref 的源碼實(shí)現(xiàn)。

          源碼分析版本:3.2.36

          function ref(value{
              return createRef(value, false);
          }

          ref 函數(shù)跳轉(zhuǎn)到 createRef 函數(shù)。

          function createRef(rawValue, shallow{
              ...
              return new RefImpl(rawValue, shallow);
          }

          createRef 函數(shù)返回的是 RefImpl 類的實(shí)例,換句話說(shuō),ref 創(chuàng)建出來(lái)的響應(yīng)式就是 RefImpl 實(shí)例對(duì)象。

          const count = ref(1);
          console.log(count);
          image.png

          我們重點(diǎn)來(lái)看看這個(gè) RefImpl 類。

          class RefImpl {
              constructor(value, __v_isShallow) {
                  ...
                  this._value = __v_isShallow ? value : toReactive(value);
              }
              get value() {
                  trackRefValue(this);
                  return this._value;
              }
              set value(newVal) {
                  newVal = this.__v_isShallow ? newVal : toRaw(newVal);
                  if (hasChanged(newVal, this._rawValue)) {
                      this._rawValue = newVal;
                      this._value = this.__v_isShallow ? newVal : toReactive(newVal);
                      triggerRefValue(this, newVal);
                  }
              }
          }

          __v_isShallow 參數(shù)在這里默認(rèn)是 false,這里也順帶講一嘴,當(dāng)我們?cè)谑褂?shallowRef 時(shí),這個(gè)參數(shù)為 true。

          function shallowRef(value{
              return createRef(value, true);
          }

          Ref 與 Reactive 創(chuàng)建的都是遞歸響應(yīng)的,將每一層的 json 數(shù)據(jù)解析成一個(gè) proxy 對(duì)象,shallowRef 與 shallowReactive 創(chuàng)建的是非遞歸的響應(yīng)對(duì)象,shallowReactive 創(chuàng)建的數(shù)據(jù)第一層數(shù)據(jù)改變會(huì)重新渲染 dom。

           var state = shallowReactive({
              a:'a',
              gf:{
                 b:'b',
                 f:{
                    c:'c',
                    s:{d:'d'}
                 }
              }
           });
          // 改變第一層的數(shù)據(jù)會(huì)導(dǎo)致頁(yè)面重新渲染
          state.a = '1'
          // 如果不改變第一層,只改變其他的數(shù)據(jù)頁(yè)面不會(huì)重新渲染
          state.gf.b = 2

          通過(guò) shallowRef 創(chuàng)建的響應(yīng)式對(duì)象,需要修改整個(gè) value 才能重新渲染 dom。

          var state = shallowRef({
             a:'a',
              gf:{
                 b:'b',
                 f:{
                    c:'c',
                    s:{d:'d'}
                 }
              }
          });

          // 不會(huì)重新渲染
          state.value.a = 1
          // 要修改整個(gè) value 才能重新渲染
          state.value = {
              a:'1',
              gf:{
                 b:'2',
                 f:{
                    c:'3',
                    s:{d:'d'}
                 }
              }
          }

          如果想更新 shallowRef 的某一層數(shù)據(jù),并且想觸發(fā)渲染,可以使用 triggerRef。

          var state = shallowRef({
             a:'a',
              gf:{
                 b:'b',
                 f:{
                    c:'c',
                    s:{d:'d'}
                 }
              }
          })
          state.value.gf.f.s.d = 4
          triggerRef(state)

          所以這里會(huì)走到 toReactive(value) 函數(shù)。

          const isObject = (val) => val !== null && typeof val === 'object';
          const toReactive = (value) => isObject(value) ? reactive(value) : value;

          可以看到,如果傳入的參數(shù)是一個(gè)對(duì)象的話,返回值將會(huì)繼續(xù)調(diào)用 reactive 方法來(lái)進(jìn)行包裹,reactive 最終會(huì)通過(guò) Proxy 來(lái)進(jìn)行實(shí)現(xiàn)響應(yīng)攔截,返回的也是一個(gè) Proxy 對(duì)象,但在這里不重要,我們只需要知道當(dāng) ref 的參數(shù)為對(duì)象時(shí),用的就是 reactive 方法。

          const data = reactive({
            count1,
          });
          console.log(data);
          const data_ref = ref({
            count1,
          });
          console.log(data_ref);

          結(jié)果顯然,讓對(duì) ref 傳入對(duì)象作為參數(shù)時(shí)和傳入基本類型作為參數(shù)返回結(jié)果情況是不一樣的。

          基本類型返回值value就是具體的值,對(duì)象類型返回值value是 reactive 方法創(chuàng)建的 proxy 對(duì)象。

          通過(guò)源碼來(lái)看,其實(shí)也證明了,在 Vue3 中,如果是把對(duì)象類型的數(shù)據(jù)弄成響應(yīng)式,reactive 和 ref 都可以,且 ref 內(nèi)部是通過(guò) r eactive 來(lái)支持的。

          也就是說(shuō),你 reactive 能做的,我 ref 也能做。

          ref 能做,但是 reactive 不能做

          其實(shí)通過(guò)上面的例子就能知道有什么是 reactive 不能做的呢?很明顯,reactive 不支持對(duì)基本類型數(shù)據(jù)響應(yīng)式,也就是說(shuō)基本類型數(shù)據(jù)不能直接作為 reactive 的參數(shù)來(lái)使用。

          簡(jiǎn)單看看源碼。

          function reactive(target{
              ...
              return createReactiveObject(...);
          }

          reactive 函數(shù)跳轉(zhuǎn)到 createReactiveObject 函數(shù)。

          const isObject = (val) => val !== null && typeof val === 'object';

          function createReactiveObject(...{
              if (!isObject(target)) {
                  {
                      console.warn(`value cannot be made reactive: ${String(target)}`);
                  }
                  return target;
              }
              ...
              const proxy = new Proxy(...);
              proxyMap.set(target, proxy);
              return proxy;
          }

          createReactiveObject 一開始就會(huì)判斷 target 是否是對(duì)象,如果不是對(duì)象就會(huì)直接 ?? 提示返回。如果是對(duì)象就會(huì)把 target 用 Proxy 變成響應(yīng)式對(duì)象。

          const data = reactive(10);
          image.png

          總結(jié)

          我們通過(guò)源碼來(lái)分析了兩個(gè)響應(yīng)式 API,發(fā)現(xiàn) Vue3 中有沒有 reactive 能做而 ref 做不了的場(chǎng)景?

          結(jié)論是:沒有

          簡(jiǎn)單來(lái)說(shuō) ref 是在 reactive 上在進(jìn)行了封裝進(jìn)行了增強(qiáng),所以在 Vue3 中 reactive 能做的,ref 也能做,reactive 不能做的,ref 也能做。


          如果覺得這篇文章還不錯(cuò)

          點(diǎn)擊下面卡片關(guān)注我

          來(lái)個(gè)【分享、點(diǎn)贊、在看】三連支持一下吧。


          參考

          • https://v3.cn.vuejs.org/guide/reactivity.html#%E4%BB%80%E4%B9%88%E6%98%AF%E5%93%8D%E5%BA%94%E6%80%A7
          • https://github.com/vuejs/docs-next-zh-cn
          • https://www.zhihu.com/question/467331620
          • https://v3.cn.vuejs.org/api/basic-reactivity.html
          • https://v3.cn.vuejs.org/api/refs-api.html#ref
          • https://www.zhihu.com/question/429036806


          瀏覽 72
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  伊人婷婷色激情 | 91福利视频网站 | 久久久成人电影视频 | 日韩成人av电影网站 | 丁香五月天导航 |