<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 解構(gòu)賦值失去響應(yīng)式引發(fā)的思考!

          共 13112字,需瀏覽 27分鐘

           ·

          2022-07-24 18:51

          image.png

          前言

          vue3發(fā)布以來經(jīng)歷兩年風(fēng)頭正盛,現(xiàn)在大有和react 平分天下的勢頭,我們知道他是基于proxy 實(shí)現(xiàn)響應(yīng)式的能力, 解決了vue2所遺留下來的一些問題,同時也正由于proxy的特性,也提高了運(yùn)行時的性能

          凡事有利有弊, proxy雖然無敵,但是他也有本身的局限,從而產(chǎn)生一些我認(rèn)為的弊端(其實(shí)就是不符合js語言的自然書寫方式,有的人覺得就是個特殊寫法,他不屬于弊端)

          • 1、 原始值的響應(yīng)式系統(tǒng)的實(shí)現(xiàn) 導(dǎo)致必須將他包裝為一個對象, 通過 .value 的方式訪問
          • 2、 ES6 解構(gòu),不能隨意使用。會破壞他的響應(yīng)式特性

          好奇心驅(qū)使,研究琢磨了一下,為什么他會造成這兩個弊端

          原始值的響應(yīng)式系統(tǒng)的實(shí)現(xiàn)

          在理解原始值的響應(yīng)式系統(tǒng)的實(shí)現(xiàn),我們先來溫習(xí)一下proxy 的能力!

          const obj = {
            name'win'
          }

          const handler = {
            getfunction(target, key){
              console.log('get--', key)
              return Reflect.get(...arguments)  
            },
            setfunction(target, key, value){
              console.log('set--', key, '=', value)
              return Reflect.set(...arguments)
            }
          }

          const data = new Proxy(obj, handler)
          data.name = 'ten'
          console.log(data.name,'data.name22')

          復(fù)制代碼

          上述代碼中,我們發(fā)現(xiàn),proxy 的使用本身就是對于 對象的攔截, 通過new Proxy 的返回值,攔截了obj 對象

          如此一來,當(dāng)你 訪問對象中的值的時候,他會觸發(fā) get 方法, 當(dāng)你修改對象中的值的時候 他會觸發(fā) set方法

          但是到了原始值的時候,他沒有對象啊,咋辦呢,new proxy 排不上用場了。

          無奈之下,我們只能包裝一下了,所以就有了使用.value訪問了

          我們來看看具體實(shí)現(xiàn)

          import { reactive } from "./reactive";
          import { trackEffects, triggerEffects } from './effect'

          export const isObject = (value) => {
              return typeof value === 'object' && value !== null
          }

          // 將對象轉(zhuǎn)化為響應(yīng)式的
          function toReactive(value{
              return isObject(value) ? reactive(value) : value
          }

          class RefImpl {
              public _value;
              public dep = new Set// 依賴收集
              public __v_isRef = true// 是ref的標(biāo)識
              // rawValue 傳遞進(jìn)來的值
              constructor(public rawValue, public _shallow) {
                  // 1、判斷如果是對象 使用reactive將對象轉(zhuǎn)為響應(yīng)式的
                  // 淺ref不需要再次代理
                  this._value = _shallow ? rawValue : toReactive(rawValue);
              }
              get value() {
                  // 取值的時候依賴收集
                  trackEffects(this.dep)
                  return this._value;
              }
              set value(newVal) {
                  if (newVal !== this.rawValue) {
                      // 2、set的值不等于初始值 判斷新值是否是對象 進(jìn)行賦值
                      this._value = this._shallow ? newVal : toReactive(newVal);
                      // 賦值完 將初始值變?yōu)楸敬蔚?/span>
                      this.rawValue = newVal
                      triggerEffects(this.dep)
                  }
              }
          }

          復(fù)制代碼

          上述代碼,就是對于原始值,的包裝,他被包裝為一個對象,通過get valueset value 方法來進(jìn)行原始值的訪問,從而導(dǎo)致必須有.value 的操作 ,這其實(shí)也是個無奈的選擇

          相當(dāng)于兩瓶毒藥,你得選一瓶 魚與熊掌不可兼得

          為什么ES6 解構(gòu),不能隨意使用會破壞他的響應(yīng)式特性

          第一個問題終于整明白了,那么我們來看看最重要的第二個問題,為什么結(jié)構(gòu)賦值,會破壞響應(yīng)式特性

          proxy背景

          在開始之前,我們先來討論一下為什么要更改響應(yīng)式方案

          vue2 基于Object.defineProperty  ,但是他有很多缺陷,比如 無法監(jiān)聽數(shù)組基于下標(biāo)的修改,不支持 Map、Set、WeakMap 和 WeakSet等缺陷 ,

          其實(shí)這些也也不耽誤我們開發(fā), vue2到現(xiàn)在還是主流,

          我的理解就是與時俱進(jìn)新一代的版本,一定要緊跟語言的特性,一定要符合新時代的書寫風(fēng)格,雖然proxy相對于Object.defineProperty 有很多進(jìn)步, 但是也不是一點(diǎn)缺點(diǎn)都沒有,你比如說 不兼容IE

          天底下的事情,哪有完美的呢?

          尤大的魄力就在于,舍棄一點(diǎn)現(xiàn)在,博一個未來!

          實(shí)現(xiàn)原理

          在理解了背景之后,我們再來假模假式的溫習(xí)一下proxy 原理,雖然這個都被講爛了。

          但是,寫水文,講究什么:倆字-連貫

          image.png
                  const obj = {
                      count1
                  };
                  const proxy = new Proxy(obj, {
                      get(target, key, receiver) {
                          console.log("這里是get");
                          return Reflect.get(target, key, receiver);
                      },
                      set(target, key, value, receiver) {
                          console.log("這里是set");
                          return Reflect.set(target, key, value, receiver);
                      }
                  });
                  
                  console.log(proxy)
                  console.log(proxy.count)
          復(fù)制代碼

          以上代碼就是Proxy的具體使用方式,通過和Reflect 的配合, 就能實(shí)現(xiàn)對于對象的攔截

          image.png

          如此依賴,就能實(shí)現(xiàn)響應(yīng)式了,大家可以發(fā)現(xiàn),這個obj的整個對象就被攔截了,但是你發(fā)現(xiàn)對象在嵌套深一層

          比如:


              const obj = {
                      count1,
                      b: {
                          c2
                      }
                  };
                  
                  
               console.log(proxy.b)
               console.log(proxy.b.c)
          復(fù)制代碼

          他就無法攔截了,我們必須要來個包裝

              const obj = {
                      a: {
                          count1
                      }
                  };
                  
                  function reactive(obj{
                      return new Proxy(obj, {
                          get(target, key, receiver) {
                              console.log("這里是get");
                              // 判斷如果是個對象在包裝一次,實(shí)現(xiàn)深層嵌套的響應(yīng)式
                              if (typeof target[key] === "object") {
                                  return reactive(target[key]);
                              };
                              return Reflect.get(target, key, receiver);
                          },
                          set(target, key, value, receiver) {
                              console.log("這里是set");
                              return Reflect.set(target, key, value, receiver);
                          }
                      });
                  };
                  const proxy = reactive(obj);
          復(fù)制代碼

          好了,原理搞完了,我們來正式研究一下

          現(xiàn)在列舉一下我知道的響應(yīng)式失去的幾個情況:

          • 1、解構(gòu) props 對象,因?yàn)樗鼤ロ憫?yīng)式
          • 2、 直接賦值reactive響應(yīng)式對象
          • 3、 vuex中組合API賦值

          解構(gòu) props 對象,因?yàn)樗鼤ロ憫?yīng)式

                 const obj = {
                      a: {
                          count1
                      },
                      b1
                  };
                      
                      //reactive 是上文中的reactive
                     const proxy = reactive(obj);
                  const {
                      a,
                      b
                  } = proxy;
                  console.log(a)
                  console.log(b)
                  console.log(a.count)
                  
          復(fù)制代碼
          image.png

          上述代碼中,我們發(fā)現(xiàn), 解構(gòu)賦值,b 不會觸發(fā)響應(yīng)式a如果你訪問的時候,會觸發(fā)響應(yīng)式

          這是為什么呢?

          別急我們一個個解釋?

          先來討論為什么解構(gòu)賦值,會丟失響應(yīng)式呢?

          我們知道解構(gòu)賦值,區(qū)分原始類型的賦值,和引用類型的賦值,

          原始類型的賦值相當(dāng)于按值傳遞引用類型的值就相當(dāng)于按引用傳遞

          就相當(dāng)于

             // 假設(shè)a是個響應(yīng)式對象
            const a={ b:1}
            // c 此時就是一個值跟當(dāng)前的a 已經(jīng)不沾邊了
            const c=a.b

          // 你直接訪問c就相當(dāng)于直接訪問這個值 也就繞過了 a 對象的get ,也就像原文中說的失去響應(yīng)式
          復(fù)制代碼

          那為啥a 具備響應(yīng)式呢?

          因?yàn)?code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">a 是引用類型,我們還記得上述代碼中的一個判斷嗎。如果他是個object 那么就重新包裝為響應(yīng)式

          正式由于當(dāng)前特性,導(dǎo)致,如果是引用類型, 你再去訪問其中的內(nèi)容的時候并不會失去響應(yīng)式

            // 假設(shè)a是個響應(yīng)式對象
           const a={ b:{c:3}}
           // 當(dāng)你訪問a.b的時候就已經(jīng)重新初始化響應(yīng)式了,此時的c就已經(jīng)是個代理的對象
           const c=a.b

          // 你直接訪問c就相當(dāng)于訪問一個響應(yīng)式對象,所以并不會失去響應(yīng)式
          復(fù)制代碼

          以上就大致解釋了為什么解構(gòu)賦值,可能會失去響應(yīng)式,我猜的文檔中懶得解釋其中緣由,索性就定了個規(guī)矩,您啊!

          就別用了,省的以為是vue的bug,提前改變用戶的使用習(xí)慣!不慣著

          直接賦值reactive響應(yīng)式對象

          我們最初使用vue3的時候,指定會寫出以下代碼

           const vue = reactive({ a1 })
           vue = { b2 }

          復(fù)制代碼

          然后就發(fā)出疑問reactive不是響應(yīng)式的嗎?為啥我賦值了以后,他的響應(yīng)式就沒了 ,接著破口大罵,垃圾vue

          其實(shí)啊,這就是您對于js 原生的概念不清除,其實(shí)尤大 已經(jīng)做了最大的努力,來防止你進(jìn)行錯誤操作了

          比如,由于解構(gòu)賦值的問題, 他直接禁止了reactive的解構(gòu)賦值

          image.png

          當(dāng)你用解構(gòu)賦值操作的時候,他直接禁用了

          那有人又問了, 為啥props 不給禁用了呢?

          因?yàn)槟愕膒rops 的數(shù)據(jù)可能不是響應(yīng)式的啊,不是響應(yīng)式的,我得能啊,尤大他也不能干涉用戶使用新語法啊

          所以還是那句話:框架現(xiàn)在的呈現(xiàn),其實(shí)充滿了取舍,有時候真是兩瓶毒藥,挑一瓶!

          回歸正題,我們再來說說 原生js 語法

          首先需要確認(rèn)的是,原生js 的引用類型的賦值,其實(shí)是 按照引用地址賦值!

           // 當(dāng)reactive 之后返回一個代理對象的地址被vue 存起來,
           // 用一個不恰當(dāng)?shù)谋扔鱽碚f,就是這個地址具備響應(yīng)式的能力
           const vue = reactive({ a1 })
           
           //  而當(dāng)你對于vue重新賦值的時候不是將新的對象賦值給那個地址,而是將vue 換了個新地址
           // 而此時新地址不具備響應(yīng)式,可不就失去響應(yīng)式了嗎
           vue = { b2 }

          復(fù)制代碼

          以上就是reactive失去響應(yīng)式的解釋,所以這個也是很多用戶罵罵咧咧的原因。不符合他的使用習(xí)慣了,這都是被vue2 培養(yǎng)起來的一代

          在這里我要替,尤大說句公道話,人家又沒收你錢,還因?yàn)樗阌锌陲埑?/code>,

          您自己不能與時俱進(jìn),擁抱新事物,那是您沒能耐

          這是典型的端起碗吃肉,放下筷子罵娘

          vuex中組合API賦值

          在vuex 用賦值也可能會失去響應(yīng)式


          import { computed } from 'vue'
          import { useStore } from 'vuex'

          export default {
            setup () {
              const store = useStore()
              return {
                // 在 computed 函數(shù)中訪問 state
                count: computed(() => store.state.count),

                // 在 computed 函數(shù)中訪問 getter
                double: computed(() => store.getters.double)
              }
            }
          }

          復(fù)制代碼

          以上代碼中我們發(fā)現(xiàn)store.getters.double 必須用computed 包裹起來,其實(shí)道理是一樣的,也是變量賦值的原因,在這里我們就不再贅述!

          最后

          本文為,在使用vue3過程中,采坑后的一些心得,以及探究,希望對各位大佬有幫助,能讓各位大佬在工作中升職加薪!

          最后,最后,推廣下我的vue源碼解析,分析的可能不太好,但是都是一行行看的,可能您看了哪天能想起個一句半句, 有所幫助!

          源碼解析地址[1]

          歡迎star

          關(guān)于本文

          作者:老驥farmer

          https://juejin.cn/post/7114596904926740493


          最后


          歡迎關(guān)注【前端瓶子君】??ヽ(°▽°)ノ?
          回復(fù)「算法」,加入前端編程源碼算法群,每日一道面試題(工作日),第二天瓶子君都會很認(rèn)真的解答喲!
          回復(fù)「交流」,吹吹水、聊聊技術(shù)、吐吐槽!
          回復(fù)「閱讀」,每日刷刷高質(zhì)量好文!
          如果這篇文章對你有幫助,在看」是最大的支持
           》》面試官也在看的算法資料《《
          “在看和轉(zhuǎn)發(fā)”就是最大的支持



          瀏覽 67
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <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>
                  欧美精品一区二区三区四区 | 99免费国产视频 | www.天天操.com | 国产精品国产三级国产专播IOS | 久久久7777 |