在深刻渲染函數以前,瞭解一些瀏覽器的工做原理很重要。如下面這段HTML爲例:html
<div> <h1>My title</h1> Some text content <!--TODO:添加標籤行--> </div>
當瀏覽器讀到這些代碼時,它會創建一個‘DOM節點樹’來保持追蹤,如同你會畫一張家譜樹來追蹤家庭成員的發展同樣。
HTML的DOM節點樹以下圖所示:
每一個元素都是一個節點。每片文字也是一個節點。甚至註釋也都是節點。一個節點就是頁面的一個部分。就像家譜樹同樣,每一個節點均可以有孩子節點(也就是說每一個部分能夠包含其它一些部分)。
高效的更新全部這些節點會是比較困難的,不過所幸你沒必要再手動完成這個工做了。你只須要告訴Vue你但願頁面上的HTML是什麼,這能夠是在一個模板裏:node
<h1>{{blogTitle}}</h1>
或則一個渲染函數裏:express
render:function(createElement){ return createElement('h1',this.blogTitle); }
在這兩種狀況下,Vue都會自動保持頁面的更新,即使blogTitle發生了改變。編程
Vue經過創建一個虛擬DOM對真實的DOM發生的變化保持追蹤。請仔細看這行代碼:數組
return createElement('h1',this.blogTitle)
createElement到底返回什麼呢?其實不是一個實際的Dom元素。它更準確的名字多是createNodeDescription,由於它所包含的信息會告訴Vue頁面上須要渲染什麼樣的節點,及其子節點。咱們把這樣的節點描述爲虛擬節點,也常簡寫爲VNode。虛擬DOM是咱們對Vue組件樹創建起來的整個VNode樹的稱呼。瀏覽器
createElement參數app
createElement( //{String|Object|Function} //一個HTML標籤字符串,組件選項對象,或則解析上述任何一種的一個async異步函數。必須參數。 'div', //{Object}一個包含模板相關屬性的數據對象,你能夠在template中使用這些特性。可選參數。 {}, //{String|Array}子虛擬節點,由createElement()構建而成,也可使用也可使用字符串來生成文本虛擬節點。可選參數。 [ '先寫一些文字', createElement('h1','一則頭條'), createElement(Mycomponent,{ props:{ someProp:'foobar' } }) ] )
有一點要注意:正如在模板語法中,v-bind:class和v-bind:style,會被特別對待同樣,在VNode數據對象中,下列屬性名是級別最高的字段。該對象也容許你綁定普通的HTML特性,就像DOM屬性同樣,好比innnerHTML(這會取代v-html指令)。dom
{ //和v-bind:class同樣的API,接收一個字符串、對象或字符串和對象組成的數組。 'class':{ foo:true, bar:false }, //和v-bind:style同樣的API,接收一個字符串、對象或對象數組組成的數組 style:{ color:'red', fontSize:'14px' }, //普通的HTML特性 attrs:{ id:'foo' }, //組件props props:{ myPro:'bar' }, //DOM屬性 domProps:{ innerHTML:'baz' }, //事件監聽器基於'on',因此再也不支持如v-on:keyup.enter修飾符,須要手動匹配keyCode. on:{ click:this.clickHandler }, //僅用於組件,用於監聽原生事件,而不是組件內部使用,vm.$emit觸發的事件。 nativeOn:{ click:this.nativeClickHandler }, //自定義指令。注意,你沒法對binding中的oldValue賦值,由於Vue已經自動爲你進行了同步。 directives:[ { name:'my-custom-directive', value:'2', expression:'1+1', arg:'foo', modifiers:{ bar:true } } ], //做用域插槽格式,{name:props=>createElement('span',props.text)} scopedSlots:{ default:props=>createElement('span',props.text) }, //若是組件是其它組件的子組件,須要爲插槽指定名稱。 slot:'name-of-slot', //其它特殊頂層屬性 key:'myKey', ref:'myRef', //若是你在渲染函數中向多個元素都應用了相同的ref名,那麼$refs.myRef會變成一個數組。 refInFor:true }
有了這些知識,咱們如今能夠完成咱們最開始想實現的組件:異步
var getChildrenTextContent = function(children){ return children.map(function(node){ return node.children ?getChildrenTextContent(node.children) :node.text }).join('') } Vue.component('anchord-heading',{ render:function(createElement){ //建立kebab-case風格的ID var headingId = getChildrenTextContent(this.$slots.default) .toLowerCase() .replace(/\W+/g,'-') .replace(/(^\-|\-$)/g,'') return createElement( 'h'+this.level, [ createElement('a',{ attrs:{ name:headingId, href:'#'+headingId } },this.$slots.default) ] ) }, props:{ level:{ type:Number, required:true } } })
VNodes必須惟一
組件樹中的全部VNodes必須是惟一的。這意味着,下面的render function是無效的:
render:function(createElement){async
var myParagraphVNode = createElement('div',[ //錯誤-重複的VNodes myParagraphVNode,myParagraphVNode ])
}
若是你真的須要重複不少次的元素/組件,你可使用工廠函數來實現。例如,下面這個例子render函數完美有效地渲染了20個相同的段落:
render:function(createElement){ return createElement('div', Array.apply(null,{length:20}).map(function(){ return createElement('p','hi') }) ) }