[vue 源碼系列] ref 與 $refs 如何關(guān)聯(lián)

先問大家一個簡單的問題:

還有人記得 jquery 里面的 data 方法是如何讓 DOM 節(jié)點綁定對應(yīng)的數(shù)據(jù)對象的嗎

有時候我們做節(jié)點關(guān)聯(lián)設(shè)計的思路其實有一點類似,但是在 vue 里面多了很多概念,比如:

1、vnode: 如何生成的,包含子父關(guān)系、屬性 data
2、內(nèi)置的 ref 對象的 create 如何注冊
3、生命周期:解析到根節(jié)點之后獲取 outerHTML 再一步一步解析子元素


用慣 vue 的人都會很熟悉地:

使用 ref 來注冊引用信息,再通過 $refs 對象就可以做關(guān)聯(lián)

但是我們看看它們是如何關(guān)聯(lián)上的呢?

代碼片段來自 2.5.16 版本:

1、需要初始化 $refs,默認是一個空對象

我們看到在函數(shù) initLifecycle 上會往 vm 上設(shè)置一個 key 為 $refs 值為一個對象

function initLifecycle (vm) {
  vm.$refs = {};
}

2、獲取元素上的 ref 值:

在函數(shù) registerRef 上,它接受 2 個參數(shù):

  • vnode
  • isRemoval
function registerRef (vnode, isRemoval) {}

直接通過 vnode.data 獲?。?/p>

var key = vnode.data.ref;

然后獲取 $refs

在這之前需要獲取 vm

從 vnode 上下文 context 獲取

var vm = vnode.context;

然后很簡單的就是 vm.$refs

var refs = vm.$refs;

ref 其實是什么呢?

DOM 節(jié)點或組件實例

這里的:

  • componentInstance -- 組件實例
  • elm -- DOM 節(jié)點
var ref = vnode.componentInstance || vnode.elm;

這里需要處理一下 v-for 一起用的情況,官網(wǎng)也提過:

對應(yīng)的引用信息是包含 DOM 節(jié)點或組件實例的數(shù)組

if (vnode.data.refInFor) {}

情況一:如果不是數(shù)組格式,強制轉(zhuǎn)換一下,外層套一個數(shù)組

判斷方式:Array.isArray

if (!Array.isArray(refs[key])) {
  refs[key] = [ref];
}

情況二:看數(shù)組里面是否存在當(dāng)前這個 ref,如果不存在,push 進去

if (refs[key].indexOf(ref) < 0) {
  refs[key].push(ref);
}

如果不是和 v-for 一起用:直接設(shè)置對象的 key 和 value:

refs[key] = ref;

最后一個問題,官網(wǎng)提到了:

ref 注冊時間 -- 因為 ref 本身是作為渲染結(jié)果被創(chuàng)建的,在初始渲染的時候你不能訪問它們 - 它們還不存在

那我們看看:

1、它到底是在什么時機綁定的
2、vnode 是如何產(chǎn)生的

最開始我們從 _init 開始

Vue.prototype._init = function (options) {
  // vm.$mount
  if (vm.$options.el) {
      vm.$mount(vm.$options.el);
    }
}

生成 vnode 最核心的部分:

實例化 VNode

function _createElement (
  var vnode;
  if (typeof tag === 'string') {
    // ...
    vnode = new VNode(
        config.parsePlatformTagName(tag), data, children,
        undefined, undefined, context
      );
  }
}

我們以如下代碼為例:

<div id="app">
    <img ref="imgbox" src="https://vuejs.org/images/logo.png" alt="Vue logo">
  </div>

我們的 VNode 如下:

最外層 app 轉(zhuǎn)換的 vnode:

children:[VNode]
data: {
  attrs: {
    id: "app"
  }
}
tag: "div"

子 vnode 如下:

data: {
  ref: "imgbox",
  attrs: {
    src:"https://vuejs.org/images/logo.png",
    alt:"Vue logo"
  }
}
tag: "img"

內(nèi)置了一個 ref 對象,里面有 create 函數(shù),調(diào)用了 registerRef

var ref = {
  create: function create (_, vnode) {
    registerRef(vnode);
  }
}

在函數(shù) invokeCreateHooks 調(diào)用 create

注意兩點:

1、cbs 是什么?
2、create又是什么,和 ref 對象的 create 有什么關(guān)系

function invokeCreateHooks (vnode, insertedVnodeQueue) {
  for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) {
      cbs.create[i$1](emptyNode, vnode);
    }
}

后面會提到:hooks

var hooks = ['create', 'activate', 'update', 'remove', 'destroy'];

核心部分:createPatchFunction,往 cbs 里面放置對應(yīng)的函數(shù)

function createPatchFunction (backend) {
  var cbs = {};

  var modules = backend.modules;

  for (i = 0; i < hooks.length; ++i) {
    cbs[hooks[i]] = [];
    for (j = 0; j < modules.length; ++j) {
      // ...
      cbs[hooks[i]].push(modules[j][hooks[i]]);
    }
  }
}

那誰調(diào)用了 createPatchFunction 函數(shù)呢:

var modules = platformModules.concat(baseModules);

var patch = createPatchFunction({ nodeOps: nodeOps, modules: modules });

我們發(fā)現(xiàn) baseModules 關(guān)聯(lián)了 ref

var baseModules = [
  ref,
  directives
]
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容