<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>

          vue2.x高階問題,你能答多少

          共 12232字,需瀏覽 25分鐘

           ·

          2021-05-12 21:55

          有句老話說,在父母那里我們永遠(yuǎn)是孩子,同樣在各位大佬這里,我永遠(yuǎn)是菜雞??????。不管怎樣,學(xué)習(xí)的激情永遠(yuǎn)不可磨滅。答案如有錯誤,感謝指教??

          首發(fā)個人博客

          種一棵樹,最好的時機是十年前,其次是現(xiàn)在

          vue源碼中值得學(xué)習(xí)的點

          1. 柯里化: 一個函數(shù)原本有多個參數(shù), 只傳入一個參數(shù), 生成一個新函數(shù), 由新函數(shù)接收剩下的參數(shù)來運行得到結(jié)構(gòu)
          2. 偏函數(shù): 一個函數(shù)原本有多個參數(shù), 只傳入一部分參數(shù), 生成一個新函數(shù), 由新函數(shù)接收剩下的參數(shù)來運行得到結(jié)構(gòu)
          3. 高階函數(shù): 一個函數(shù)參數(shù)是一個函數(shù), 該函數(shù)對參數(shù)這個函數(shù)進行加工, 得到一個函數(shù), 這個加工用的函數(shù)就是高階函數(shù)
          4. ...

          vue 響應(yīng)式系統(tǒng)

          簡述:vue 初始化時會用Object.defineProperty()給data中每一個屬性添加gettersetter,同時創(chuàng)建depwatcher進行依賴收集派發(fā)更新,最后通過diff算法對比新老vnode差異,通過patch即時更新DOM

          簡易圖解:

          詳細(xì)版本

          可以參考下圖片引用地址: 圖解 Vue 響應(yīng)式原理

          Vue的數(shù)據(jù)為什么頻繁變化但只會更新一次

          1. 檢測到數(shù)據(jù)變化
          2. 開啟一個隊列
          3. 在同一事件循環(huán)中緩沖所有數(shù)據(jù)改變
          4. 如果同一個 watcher (watcherId相同)被多次觸發(fā),只會被推入到隊列中一次

          不優(yōu)化,每一個數(shù)據(jù)變化都會執(zhí)行: setter->Dep->Watcher->update->run

          優(yōu)化后:執(zhí)行順序update -> queueWatcher -> 維護觀察者隊列(重復(fù)id的Watcher處理) -> waiting標(biāo)志位處理 -> 處理$nextTick(在為微任務(wù)或者宏任務(wù)中異步更新DOM)

          vue使用Object.defineProperty() 的缺陷

          數(shù)組的length屬性被初始化configurable false,所以想要通過get/set方法來監(jiān)聽length屬性是不可行的。

          vue中通過重寫了七個能改變原數(shù)組的方法來進行數(shù)據(jù)監(jiān)聽

          對象還是使用Object.defineProperty()添加get和set來監(jiān)聽

          參考

          Vue.nextTick()原理

          在下次DOM更新循環(huán)結(jié)束之后執(zhí)行延遲回調(diào)。在修改數(shù)據(jù)之后立即使用這個方法,獲取更新后的DOM。

          源碼實現(xiàn):Promise > MutationObserver > setImmediate > setTimeout

          參考文章:淺析Vue.nextTick()原理

          computed 的實現(xiàn)原理

          computed 本質(zhì)是一個惰性求值的觀察者computed watcher。其內(nèi)部通過 this.dirty 屬性標(biāo)記計算屬性是否需要重新求值。

          • 當(dāng) computed 的依賴狀態(tài)發(fā)生改變時,就會通知這個惰性的 watcher,computed watcher 通過 this.dep.subs.length 判斷有沒有訂閱者,
          • 有的話,會重新計算,然后對比新舊值,如果變化了,會重新渲染。(Vue 想確保不僅僅是計算屬性依賴的值發(fā)生變化,而是當(dāng)計算屬性最終計算的值發(fā)生變化時才會觸發(fā)渲染 watcher 重新渲染,本質(zhì)上是一種優(yōu)化。)
          • 沒有的話,僅僅把 this.dirty = true (當(dāng)計算屬性依賴于其他數(shù)據(jù)時,屬性并不會立即重新計算,只有之后其他地方需要讀取屬性的時候,它才會真正計算,即具備 lazy(懶計算)特性。)

          watch 的理解

          watch沒有緩存性,更多的是觀察的作用,可以監(jiān)聽某些數(shù)據(jù)執(zhí)行回調(diào)。當(dāng)我們需要深度監(jiān)聽對象中的屬性時,可以打開deep:true選項,這樣便會對對象中的每一項進行監(jiān)聽。這樣會帶來性能問題,優(yōu)化的話可以使用字符串形式監(jiān)聽

          注意:Watcher : 觀察者對象 , 實例分為渲染 watcher (render watcher),計算屬性 watcher (computed watcher),偵聽器 watcher(user watcher)三種

          vue diff 算法

          • 只對比父節(jié)點相同的新舊子節(jié)點(比較的是Vnode),時間復(fù)雜度只有O(n)
          • 在 diff 比較的過程中,循環(huán)從兩邊向中間收攏

          新舊節(jié)點對比過程

          1、先到 不需要移動的相同節(jié)點,借助key值找到可復(fù)用的節(jié)點是,消耗最小

          2、再找相同但是需要移動的節(jié)點,消耗第二小

          3、最后找不到,才會去新建刪除節(jié)點,保底處理

          注意:新舊節(jié)點對比過程,不會對這兩棵Vnode樹進行修改,而是以比較的結(jié)果直接對 真實DOM 進行修改

          Vue的patch是即時的,并不是打包所有修改最后一起操作DOM(React則是將更新放入隊列后集中處理)

          參考文章:Vue 虛擬dom diff原理詳解

          vue 渲染過程

          1. 調(diào)用 compile 函數(shù),生成 render 函數(shù)字符串 ,編譯過程如下:
          • parse 使用大量的正則表達式對template字符串進行解析,將標(biāo)簽、指令、屬性等轉(zhuǎn)化為抽象語法樹AST。模板 -> AST (最消耗性能)
          • optimize 遍歷AST,找到其中的一些靜態(tài)節(jié)點并進行標(biāo)記,方便在頁面重渲染的時候進行diff比較時,直接跳過這一些靜態(tài)節(jié)點,優(yōu)化runtime的性能
          • generate 將最終的AST轉(zhuǎn)化為render函數(shù)字符串
          1. 調(diào)用 new Watcher 函數(shù),監(jiān)聽數(shù)據(jù)的變化,當(dāng)數(shù)據(jù)發(fā)生變化時,Render 函數(shù)執(zhí)行生成 vnode 對象
          2. 調(diào)用 patch 方法,對比新舊 vnode 對象,通過 DOM diff 算法,添加、修改、刪除真正的 DOM 元素

          結(jié)合源碼,談一談vue生命周期

          vue 生命周期官方圖解

          Vue 中的 key 到底有什么用?

          key 是給每一個 vnode 的唯一 id,依靠 key,我們的 diff 操作可以更準(zhǔn)確、更快速

          更準(zhǔn)確 : 因為帶 key 就不是就地復(fù)用了,在 sameNode 函數(shù) a.key === b.key 對比中可以避免就地復(fù)用的情況。所以會更加準(zhǔn)確,如果不加 key,會導(dǎo)致之前節(jié)點的狀態(tài)被保留下來,會產(chǎn)生一系列的 bug。

          更快速 : key 的唯一性可以被 Map 數(shù)據(jù)結(jié)構(gòu)充分利用,相比于遍歷查找的時間復(fù)雜度 O(n),Map 的時間復(fù)雜度僅僅為 O(1),源碼如下:

          function createKeyToOldIdx(children, beginIdx, endIdx) {
            let i, key;
            const map = {};
            for (i = beginIdx; i <= endIdx; ++i) {
              key = children[i].key;
              if (isDef(key)) map[key] = i;
            }
            return map;

          注意:在沒有key的情況下,會更快。感謝評論區(qū)老哥fengyangyang的提醒:引用官網(wǎng)的話:key 的特殊 attribute 主要用在 Vue 的虛擬 DOM 算法,在新舊 nodes 對比時辨識 VNodes。如果不使用 key,Vue 會使用一種最大限度減少動態(tài)元素并且盡可能的嘗試就地修改/復(fù)用相同類型元素的算法。而使用 key 時,它會基于 key 的變化重新排列元素順序,并且會移除 key 不存在的元素。

          vue-router 路由模式有幾種

          默認(rèn)值: "hash" (瀏覽器環(huán)境) | "abstract" (Node.js 環(huán)境)

          可選值: "hash" | "history" | "abstract"

          配置路由模式:

          • hash: 使用 URL hash 值來作路由。支持所有瀏覽器,包括不支持 HTML5 History Api 的瀏覽器。
          • history: 依賴 HTML5 History API 和服務(wù)器配置。
          • abstract: 支持所有 JavaScript 運行環(huán)境,如 Node.js 服務(wù)器端。如果發(fā)現(xiàn)沒有瀏覽器的 API,路由會自動強制進入這個模式.

          說一說keep-alive實現(xiàn)原理

          定義

          keep-alive組件接受三個屬性參數(shù):include、excludemax

          • include 指定需要緩存的組件name集合,參數(shù)格式支持String, RegExp, Array。當(dāng)為字符串的時候,多個組件名稱以逗號隔開。
          • exclude 指定不需要緩存的組件name集合,參數(shù)格式和include一樣。
          • max 指定最多可緩存組件的數(shù)量,超過數(shù)量刪除第一個。參數(shù)格式支持String、Number。

          原理

          keep-alive實例會緩存對應(yīng)組件的VNode,如果命中緩存,直接從緩存對象返回對應(yīng)VNode

          LRU(Least recently used)算法根據(jù)數(shù)據(jù)的歷史訪問記錄來進行淘汰數(shù)據(jù),其核心思想是“如果數(shù)據(jù)最近被訪問過,那么將來被訪問的幾率也更高”。(墨菲定律:越擔(dān)心的事情越會發(fā)生)

          對對象屬性訪問的解析方法

          eg:訪問 a.b.c.d

          函數(shù)柯里化 + 閉包 + 遞歸

           function createGetValueByPath( path ) {
                let paths = path.split( '.' ); // [ xxx, yyy, zzz ]
                
                return function getValueByPath( obj ) {
                  let res = obj;
                  let prop;
                  while( prop = paths.shift() ) {
                    res = res[ prop ];
                  }
                  return res;
                }
              }
              
              let getValueByPath = createGetValueByPath( 'a.b.c.d' );
              
              var o = {
                a: {
                  b: {
                    c: {
                      d: {
                        e: '正確了'
                      }
                    }
                  }
                }
              };
              var res = getValueByPath( o );
              console.log( res ); 

          vue中針對7個數(shù)組方法的重寫

          Vue 通過原型攔截的方式重寫了數(shù)組的 7 個方法,首先獲取到這個數(shù)組的Observer。如果有新的值,就調(diào)用 observeArray 對新的值進行監(jiān)聽,然后調(diào)用 notify,通知 render watcher,執(zhí)行 update

          const arrayProto = Array.prototype;
          export const arrayMethods = Object.create(arrayProto);
          const methodsToPatch = [
            "push",
            "pop",
            "shift",
            "unshift",
            "splice",
            "sort",
            "reverse"
          ];

          methodsToPatch.forEach(function(method) {
            // cache original method
            const original = arrayProto[method];
            def(arrayMethods, method, function mutator(...args) {
              const result = original.apply(this, args);
              const ob = this.__ob__;
              let inserted;
              switch (method) {
                case "push":
                case "unshift":
                  inserted = args;
                  break;
                case "splice":
                  inserted = args.slice(2);
                  break;
              }
              if (inserted) ob.observeArray(inserted);
              // notify change
              ob.dep.notify();
              return result;
            });
          });

          Observer.prototype.observeArray = function observeArray(items) {
            for (var i = 0, l = items.length; i < l; i++) {
              observe(items[i]);
            }
          }; 

          vue處理響應(yīng)式 defineReactive 實現(xiàn)

           // 簡化后的版本 
              function defineReactive( target, key, value, enumerable ) {
                // 折中處理后, this 就是 Vue 實例
                let that = this;

                // 函數(shù)內(nèi)部就是一個局部作用域, 這個 value 就只在函數(shù)內(nèi)使用的變量 ( 閉包 )
                if ( typeof value === 'object' && value != null && !Array.isArray( value ) ) {
                  // 是非數(shù)組的引用類型
                  reactify( value ); // 遞歸
                }

                Object.defineProperty( target, key, {
                  configurable: true,
                  enumerable: !!enumerable,

                  get () {
                    console.log( `讀取 ${key} 屬性` ); // 額外
                    return value;
                  },
                  set ( newVal ) {
                    console.log( `設(shè)置 ${key} 屬性為: ${newVal}` ); // 額外

                    value = reactify( newVal );

                  }
                } );
              } 

          vue響應(yīng)式 reactify 實現(xiàn)

          // 將對象 o 響應(yīng)式化
              function reactify( o, vm ) {
                let keys = Object.keys( o );

                for ( let i = 0; i < keys.length; i++ ) {
                  let key = keys[ i ]; // 屬性名
                  let value = o[ key ];
                  if ( Array.isArray( value ) ) {
                    // 數(shù)組
                    value.__proto__ = array_methods; // 數(shù)組就響應(yīng)式了
                    for ( let j = 0; j < value.length; j++ ) {
                      reactify( value[ j ], vm ); // 遞歸
                    }
                  } else {
                    // 對象或值類型
                    defineReactive.call( vm, o, key, value, true );
                  }
                }
              } 

          為什么訪問data屬性不需要帶data

          vue中訪問屬性代理this.data.xxx 轉(zhuǎn)換 this.xxx的實現(xiàn)

           /** 將 某一個對象的屬性 訪問 映射到 對象的某一個屬性成員上 */
              function proxy( target, prop, key ) {
                Object.defineProperty( target, key, {
                  enumerable: true,
                  configurable: true,
                  get () {
                    return target[ prop ][ key ];
                  },
                  set ( newVal ) {
                    target[ prop ][ key ] = newVal;
                  }
                } );
              } 

          原文地址

          • https://juejin.cn/post/6921911974611664903]
          瀏覽 102
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  豆花视频A片无码资源 | 中文字幕一区二区三区在线观看 | 日韩无码中文字幕 | 男插女青青影院 | 男女啪啪免费网站入口 |