寫文章不容易,點個讚唄兄弟
專一 Vue 源碼分享,文章分爲白話版和 源碼版,白話版助於理解工做原理,源碼版助於瞭解內部詳情,讓咱們一塊兒學習吧 研究基於 Vue版本 【2.5.17】html
若是你以爲排版難看,請點擊 下面連接 或者 拉到 下面關注公衆號也能夠吧node
【Vue原理】Render - 源碼版 之 主要 Render 數組
compile 咱們已經講了九篇的內容了,終於走到了 render,今天就來給本身記錄下渲染三部曲的第二部,render,咦,render 內容很少的,就兩篇文章哈哈哈bash
噔噔噔噔函數
render 的做用你們應該清楚學習
就是 執行 compile 生成的 render函數,而後獲得返回的 vnode 節點ui
好比如今存在這個簡單的模板this
通過 compile 以後,解析成了對應的 render 函數,以下spa
function render() {
with(this) {
return _c('div', {
attrs: {
"data": 111
}
},
[_v(111)])
}
}
複製代碼
看着這個莫名其妙的 render 函數,裏面都是些什麼東西?prototype
不怕,主要是出現了兩個函數,咱們要探索的就是這兩個東西
_c , _v
這個兩個函數的做用,都是建立 Vnode,可是建立的過程不同
而且 render 函數執行的時候,會綁定上 模板對應的實例 爲上下文對象
模板是屬於哪一個實例的,就綁定哪一個實例
render.call(實例)
複製代碼
再經過 with 的做用
調用 _c 和 _v 就至關於 vm._c 和 vm._v
如今就來看看 vm._v 是哪裏來的
function installRenderHelpers(target) {
target._v = createTextVNode;
}
installRenderHelpers(Vue.prototype);
複製代碼
由上面可知,每一個Vue 實例都會繼承有 _v 這個方法,因此能夠經過 vm._v 直接調用
再來看看 _v 對應的 createTextVNode 的做用是什麼
建立文本節點!!
看下源碼
function createTextVNode(val) {
return new VNode(
undefined, undefined,
undefined, String(val)
)
}
複製代碼
好比這個模板
{{data}} 雖然是字符串,可是也要做爲一個子節點存在,因此就當作是 文本節點
而 data 的值是 111
而後 上面的模板就會獲得這樣的 Vnode 結構以下
_c 是一個大頭,render 的重中之重,先來看看他是怎麼來的
function initRender(vm) {
vm._c = function(a, b, c, d) {
return createElement(vm, a, b, c, d);
};
}
Vue.prototype._init = function(options) {
initRender(this)
}
複製代碼
在實例初始化的時候,就會給實例綁定上 _c 方法
因此,vm 能夠直接調用到 _c
看了上面的源碼,看到 _c 內部調用了 createElement
那就來看看createElement 的源碼吧
我的已經簡化得很是簡單,以爲不偏離咱們的主題就能夠
function createElement(
context, tag, data, children
) {
return _createElement(
context, tag, data, children
)
}
function _createElement(
context, tag, data, children
) {
var vnode;
if (若是tag是正常html標籤) {
vnode = new VNode(
tag, data, children,
undefined, undefined,
context
);
}
.....若是tag是組件名,就特殊處理 ,處理流程已經省略
if (Array.isArray(vnode))
return vnode
else {
// ...動態綁定 style ,class,代碼已經省略
return vnode
}
}
複製代碼
你一看就能夠看到,createElement 主要就是調用了 new VNode,固然了,render 就是爲了建立 vnode 的嘛
你在前面也看到了 render 函數,有傳了不少參數給 _c,以下,_c 再把這些參數傳給構造函數 VNode
_c('div',
{
attrs: {"data": 111}
},
[_v(111)]
)
複製代碼
上面這些參數都會傳給 Vnode,並保存在建立的 Vnode 中
function VNode(
tag, data, children, text
) {
this.tag = tag;
this.data = data;
this.children = children;
this.text = text;
}
複製代碼
而後獲得這麼一個 Vnode
{
tag:"div",
data:{
attrs: {"data": 111}
},
children:[{
tag:undefined,
data:undefined,
text:111
}]
}
複製代碼
說到這裏,已經能很清楚 render 內部是如何建立Vnode 了
可是這裏只是其中一種小小的簡單 render
要是項目中的render,數據是不少,很複雜的
而咱們主要要把握的是主要流程就能夠了
不過,還有必要記錄其餘 render,那就是遍歷
看下面這個 template
解析成下面的render
function render() {
with(this) {
return _c('div',
_l(2,function(item, index) {
return _c('span')
})
)
}
}
複製代碼
看到一個 _l, 他一定就是遍歷生成 Vnode 的幕後黑手了
一樣的,_l 和 _v 在同一個地方 installRenderHelpers 註冊的
function installRenderHelpers(target) {
target._l = renderList;
}
複製代碼
不客氣地搜出 renderList 源碼出來
先跳到後面的分析啊,源碼有點長了,雖然很簡單
function renderList(val, _render) {
var ret, i, l, keys, key;
// 遍歷數組
if ( Array.isArray(val) ) {
ret = new Array(val.length);
// 調用傳入的函數,把值傳入,數組保存結果
for (i = 0, l = val.length; i < l; i++) {
ret[i] = _render(val[i], i);
}
}
// 遍歷數字
else if (typeof val === 'number') {
ret = new Array(val);
// 調用傳入的函數,把值傳入,數組保存結果
for (i = 0; i < val; i++) {
ret[i] = _render(i + 1, i);
}
}
// 遍歷對象
else if (typeof val =="object") {
keys = Object.keys(val);
ret = new Array(keys.length);
// 調用傳入的函數,把值傳入,數組保存結果
for (i = 0, l = keys.length; i < l; i++) {
key = keys[i];
ret[i] = _render(val[key], key, i);
}
}
// 返回 vnode 數組
return ret
}
複製代碼
看到 renderList 接收兩個參數,val 和 render,而 _l 調用的時候,也就是傳入的這兩個參數,好比下面
_l(2,function(item, index) {
return _c('span')
})
複製代碼
val 就是 2,_render 就是上面的函數
遍歷的數據分爲三種類型,一種是對象,一種是數字,一種是數組
重要是這個回調
一、renderList 每次遍歷都會執行回調,並把的每一項 item 和 index 都傳入 回調中
二、回調執行完畢,會返回 vnode
三、使用數組保存 vnode,而後 遍歷完畢就返回 數組
因而能夠看上面的 render 函數 ,傳入了 數字2,和 建立 span 的回調
_l(2,function(item, index) {
return _c('span')
})
複製代碼
_l 執行完畢,內部遍歷兩次,最後返回 兩個 span vnode 的數組,而後傳給外層的 _c ,做爲 vnode.children 保存
render 執行完畢,獲得這樣的 vnode
{
tag:"div",
data:undefined,
children:[{
tag:"span",
data:undefined
},{
tag:"span",
data:undefined
}]
}
複製代碼
都灰常簡單啊,沒寫以前,我還以爲內容應該挺多的,寫完發現還能夠
固然還有其餘的 render ,可是我都已經在其餘文章中有詳細的記錄了,能夠直接點鏈接觀看
好比要模板含有 filter
好比要模板含有 普通 slot Slot - 源碼版之普通插槽
好比要模板含有 做用域 slot
如今咱們來解決一個問題
render 何時開始執行?
能夠參考另外一篇文章: 從模板到DOM的簡要流程
每一個模板通過 compile 都會生成一個 render 函數
render 做爲 渲染三部曲的第二部,主要做用就是 執行 render,生成 Vnode
把 template 上綁定的數據,都保存到 vnode 中
而後,生成 Vnode,就是爲了給 渲染三部曲的 第三部 Diff 提供源動力
從而完成 DOM 掛載