寫文章不容易,點個讚唄兄弟
專一 Vue 源碼分享,文章分爲白話版和 源碼版,白話版助於理解工做原理,源碼版助於瞭解內部詳情,讓咱們一塊兒學習吧
研究基於 Vue版本 【2.5.17】
若是你以爲排版難看,請點擊 下面連接 或者 拉到 下面關注公衆號也能夠吧html
【Vue原理】Render - 源碼版 之 主要 Render node
compile 咱們已經講了九篇的內容了,終於走到了 render,今天就來給本身記錄下渲染三部曲的第二部,render,咦,render 內容很少的,就兩篇文章哈哈哈數組
噔噔噔噔函數
render 的做用你們應該清楚學習
就是 執行 compile 生成的 render函數,而後獲得返回的 vnode 節點this
好比如今存在這個簡單的模板spa
通過 compile 以後,解析成了對應的 render 函數,以下prototype
function render() { with(this) { return _c('div', { attrs: { "data": 111 } }, [_v(111)]) } }
看着這個莫名其妙的 render 函數,裏面都是些什麼東西?3d
不怕,主要是出現了兩個函數,咱們要探索的就是這兩個東西code
_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 掛載