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

先問大家一個(gè)簡單的問題:

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

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

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


用慣 vue 的人都會(huì)很熟悉地:

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

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

代碼片段來自 2.5.16 版本:

1、需要初始化 $refs惭墓,默認(rèn)是一個(gè)空對象

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

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

2坛梁、獲取元素上的 ref 值:

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

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

直接通過 vnode.data 獲壤靶住:

var key = vnode.data.ref;

然后獲取 $refs

在這之前需要獲取 vm

從 vnode 上下文 context 獲取

var vm = vnode.context;

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

var refs = vm.$refs;

ref 其實(shí)是什么呢划咐?

DOM 節(jié)點(diǎn)或組件實(shí)例

這里的:

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

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

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

if (vnode.data.refInFor) {}

情況一:如果不是數(shù)組格式钧萍,強(qiáng)制轉(zhuǎn)換一下褐缠,外層套一個(gè)數(shù)組

判斷方式:Array.isArray

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

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

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

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

refs[key] = ref;

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

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

那我們看看:

1弛秋、它到底是在什么時(shí)機(jī)綁定的
2器躏、vnode 是如何產(chǎn)生的

最開始我們從 _init 開始

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

生成 vnode 最核心的部分:

實(shí)例化 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)置了一個(gè) ref 對象,里面有 create 函數(shù)蟹略,調(diào)用了 registerRef

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

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

注意兩點(diǎn):

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);
    }
}

后面會(huì)提到: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)系作者
  • 序言:七十年代末意敛,一起剝皮案震驚了整個(gè)濱河市馅巷,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌草姻,老刑警劉巖钓猬,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異撩独,居然都是意外死亡敞曹,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進(jìn)店門综膀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來澳迫,“玉大人,你說我怎么就攤上這事剧劝¢系牵” “怎么了?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長拢锹。 經(jīng)常有香客問我谣妻,道長,這世上最難降的妖魔是什么面褐? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮取胎,結(jié)果婚禮上展哭,老公的妹妹穿的比我還像新娘。我一直安慰自己闻蛀,他們只是感情好匪傍,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著觉痛,像睡著了一般役衡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上薪棒,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天手蝎,我揣著相機(jī)與錄音,去河邊找鬼俐芯。 笑死棵介,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的吧史。 我是一名探鬼主播邮辽,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼贸营!你這毒婦竟也來了吨述?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤钞脂,失蹤者是張志新(化名)和其女友劉穎揣云,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冰啃,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡灵再,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了亿笤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片翎迁。...
    茶點(diǎn)故事閱讀 38,626評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖净薛,靈堂內(nèi)的尸體忽然破棺而出汪榔,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布痴腌,位于F島的核電站雌团,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏士聪。R本人自食惡果不足惜锦援,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望剥悟。 院中可真熱鬧灵寺,春花似錦、人聲如沸区岗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽慈缔。三九已至叮称,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間藐鹤,已是汗流浹背瓤檐。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留娱节,地道東北人距帅。 一個(gè)月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像括堤,于是被迫代替她去往敵國和親碌秸。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評論 2 348

推薦閱讀更多精彩內(nèi)容