91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

vue中的mounted鉤子怎么用

發布時間:2022-03-24 12:17:05 來源:億速云 閱讀:893 作者:小新 欄目:編程語言

這篇文章主要為大家展示了“vue中的mounted鉤子怎么用”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“vue中的mounted鉤子怎么用”這篇文章吧。

vue中的mounted鉤子怎么用

注:閱讀本文需要對vue的patch流程有較清晰的理解,如果不清楚patch流程,建議先了解清楚這個流程再閱讀本文,否則可能會感覺云里霧里。

聊之前我們先看一個場景

<div id="app">
    <aC />
    <bC />
</div>

如上所示,App.vue文件里面有兩個子組件,互為兄弟關系

組件里面自各有created和mounted兩個生命周期鉤子,a表示組件名 C是created的縮寫

// a組件
created() {
    console.log('aC')
  },
mounted() {
  debugger
  console.log('aM')
},

// b組件
created() {
    console.log('bC')
  },
mounted() {
  debugger
  console.log('bM')
},

請問打印順序是什么?各位讀者可以先腦補一下,后面看看對不對。

如果對vue patch流程比較熟悉的讀者,可能會認為順序是aC→aM→bC→BM,也就是a組件先創建,再掛載,然后到b組件重復以上流程。因為從patch的方法里面可以知道,組件created后,再走到insert進父容器的過程,是一個同步的流程,只有這個流程走完后,才會遍歷到b組件,走b組件的渲染流程。

實際上瀏覽器打印出來的順序是aC→bC→aM→bM,也就是兩個created先執行,才到mounted執行,和上面的分析相悖。這里先說原因,子組件從created到insert進父容器的過程還是同步的,但是insert進父容器后,也可以理解為子組件mounted,并沒有馬上調用mounted生命周期鉤子。下面從源碼角度分析一下:

先大概回顧一下子組件渲染流程,patch函數調用createElm創建真實element,createElm里面通過createComponent判斷當前vnode是否組件vnode,是則進入組件渲染流程

function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {
    var i = vnode.data;
    if (isDef(i)) {
      var isReactivated = isDef(vnode.componentInstance) && i.keepAlive;
      if (isDef(i = i.hook) && isDef(i = i.init)) {
        i(vnode, false /* hydrating */);
      }
      if (isDef(vnode.componentInstance)) {
        initComponent(vnode, insertedVnodeQueue);
				// 最終組件創建完后會走到這里 把組件對應的el插入到父節點
        insert(parentElm, vnode.elm, refElm);
        if (isTrue(isReactivated)) {
          reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm);
        }
        return true
      }
    }
  }

createComponent里面就把組件對應的el插入到父節點,最后會返回到patch調用棧,調用

invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch);

因為子組件有vnode.parent所以會走一個分支,但是我們也看看第二個分支調用的insert是什么

function invokeInsertHook (vnode, queue, initial) {
    // delay insert hooks for component root nodes, invoke them after the
    // element is really inserted
    if (isTrue(initial) && isDef(vnode.parent)) {
      vnode.parent.data.pendingInsert = queue;
    } else {
      for (var i = 0; i < queue.length; ++i) {
        queue[i].data.hook.insert(queue[i]);
      }
    }
  }

這個insert是掛在vnode.data.hook上,在組件創建過程中,createComponent方法里面有一個調用

installComponentHooks,在這里把insert鉤子注入了。這個方法實際定義在componentVNodeHooks對象里面,可以看到這個insert里面調用了callHook(componentInstance, 'mounted'),這里實際上就是調用子組件的mounted生命周期。

insert: function insert (vnode) {
    var context = vnode.context;
    var componentInstance = vnode.componentInstance;
    if (!componentInstance._isMounted) {
      componentInstance._isMounted = true;
      callHook(componentInstance, 'mounted');
    }
    if (vnode.data.keepAlive) {
      if (context._isMounted) {
        // vue-router#1212
        // During updates, a kept-alive component's child components may
        // change, so directly walking the tree here may call activated hooks
        // on incorrect children. Instead we push them into a queue which will
        // be processed after the whole patch process ended.
        queueActivatedComponent(componentInstance);
      } else {
        activateChildComponent(componentInstance, true /* direct */);
      }
    }
  },

再來看看這個方法,子組件走第一個分支,僅僅執行了一行代碼vnode.parent.data.pendingInsert = queue , 這個queue實際是在patch 開始時候,定義的insertedVnodeQueue。這里的邏輯就是把當前的insertedVnodeQueue,掛在parent的vnode data的pendingInsert上。

function invokeInsertHook (vnode, queue, initial) {
    // delay insert hooks for component root nodes, invoke them after the
    // element is really inserted
    if (isTrue(initial) && isDef(vnode.parent)) {
      vnode.parent.data.pendingInsert = queue;
    } else {
      for (var i = 0; i < queue.length; ++i) {
        queue[i].data.hook.insert(queue[i]);
      }
    }
  }

// 在patch 開始時候 定義了insertedVnodeQueue為一個空數組
var insertedVnodeQueue = [];

源碼里面再搜索insertedVnodeQueue ,可以看到有這樣一段邏輯,initComponent還是在createComponent里面調用的

function initComponent (vnode, insertedVnodeQueue) {
    if (isDef(vnode.data.pendingInsert)) {
      insertedVnodeQueue.push.apply(insertedVnodeQueue, vnode.data.pendingInsert);
      vnode.data.pendingInsert = null;
    }
    vnode.elm = vnode.componentInstance.$el;
    if (isPatchable(vnode)) {
			// ??注意這個方法 
      invokeCreateHooks(vnode, insertedVnodeQueue);
      setScope(vnode);
    } else {
      // empty component root.
      // skip all element-related modules except for ref (#3455)
      registerRef(vnode);
      // make sure to invoke the insert hook
      insertedVnodeQueue.push(vnode);
    }
  }

insertedVnodeQueue.push.apply(insertedVnodeQueue, vnode.data.pendingInsert) 重點看這一行代碼,把 vnode.data.pendingInsert這個數組每一項push到當前vnode的insertedVnodeQueue中,注意這里是通過apply的方式,所以是把 vnode.data.pendingInsert這個數組每一項都push,而不是push pendingInsert這個列表進去。也就是說在這里,組件把他的子組件的insertedVnodeQueue里面的item收集了,因為渲染是一個深度遞歸的過程,所有最后根組件的insertedVnodeQueue能拿到所有子組件的insertedVnodeQueue里面的每一項。

從invokeInsertHook的queue[i].data.hook.insert(queue[i]) 這一行可以看出,insertedVnodeQueue里面的item應該是vnode。源碼中搜索insertedVnodeQueue.push ,可以發現是invokeCreateHooks這個方法把當前vnode push了進去。

function invokeCreateHooks (vnode, insertedVnodeQueue) {
    for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) {
      cbs.create[i$1](emptyNode, vnode);
    }
    i = vnode.data.hook; // Reuse variable
    if (isDef(i)) {
      if (isDef(i.create)) { i.create(emptyNode, vnode); }
	     // 把當前vnode push 到了insertedVnodeQueue
      if (isDef(i.insert)) { insertedVnodeQueue.push(vnode); }
    }
  }

對于組件vnode來說,這個方法還是在initComponent中調用的。

到這里就很清晰,子組件insert進父節點后,并不會馬上調用mounted鉤子,而是把組件對應到vnode插入到父vnode的insertedVnodeQueue中,層層遞歸,最終根組件拿到所有子組件的vnode,再依次循環遍歷,調用vnode的insert鉤子,從而調用了mounted鉤子。這里是先進先出的,第一個被push進去的第一個被拿出來調用,所以最深的那個子組件的mounted先執行。最后附上一張源碼調試的圖,可以清晰的看到根組件的insertedVnodeQueue是什么內容。

vue中的mounted鉤子怎么用

至于為什么vue要這樣設計,是因為掛載是先子后父的,子組件插入到了父節點,但是父節點還沒有真正插入到頁面中,如果這時候立馬調用子組件的mounted,對框架使用者來說可能會造成困惑,因為子組件調用mounted的時候并沒有真正渲染到頁面中,而且此時也肯定也無法通過document.querySelector的方式操作dom。

以上是“vue中的mounted鉤子怎么用”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

桂平市| 金沙县| 灌云县| 安义县| 玉林市| 大足县| 西华县| 乌兰察布市| 唐海县| 象州县| 吉安县| 拉萨市| 安泽县| 留坝县| 武乡县| 章丘市| 城步| 环江| 松溪县| 英超| 蓬莱市| 年辖:市辖区| 玉溪市| 元氏县| 印江| 山东| 泰安市| 呼玛县| 巩留县| 康定县| 垦利县| 平定县| 沙田区| 大埔县| 巢湖市| 江永县| 江华| 临邑县| 伊通| 汝阳县| 静宁县|