先問大家一個(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
]