vue渲染函式&JSX
阿新 • • 發佈:2018-12-18
Vue推薦在絕大多數情況下使用template來建立你的HTML。然而在一些場景中,你真的需要JavaScript的完全程式設計能力,這時你可以使用render函式,它比template跟接近編譯器.
虛擬DOM
在深入渲染函式之前,瞭解一些瀏覽器的工作原理很重要。以下面這段HTML為例:
<div> <h1>My title</h1> Some text content <!--TODO:新增標籤行--> </div>
當瀏覽器讀到這些程式碼時,它會建立一個‘DOM節點樹’來保持追蹤,如同你會畫一張家譜樹來追蹤家庭成員的發展一樣。
HTML的DOM節點樹如下圖所示:
每個元素都是一個節點。每片文字也是一個節點。甚至註釋也都是節點。一個節點就是頁面的一個部分。就像家譜樹一樣,每個節點都可以有孩子節點(也就是說每個部分可以包含其它一些部分)。
高效的更新所有這些節點會是比較困難的,不過所幸你不必再手動完成這個工作了。你只需要告訴Vue你希望頁面上的HTML是什麼,這可以是在一個模板裡:
<h1>{{blogTitle}}</h1>
或則一個渲染函式裡:
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引數
createElement(
//{String|Object|Function}
//一個HTML標籤字串,元件選項物件,或則解析上述任何一種的一個async非同步函式。必須引數。
'div',
//{Object}一個包含模板相關屬性的資料物件,你可以在template中使用這些特性。可選引數。
{},
//{String|Array}子虛擬節點,由createElement()構建而成,也可以使用也可以使用字串來生成文字虛擬節點。可選引數。
[
'先寫一些文字',
createElement('h1','一則頭條'),
createElement(Mycomponent,{
props:{
someProp:'foobar'
}
})
]
)
深入data物件
有一點要注意:正如在模板語法中,v-bind:class和v-bind:style,會被特別對待一樣,在VNode資料物件中,下列屬性名是級別最高的欄位。該物件也允許你繫結普通的HTML特性,就像DOM屬性一樣,比如innnerHTML(這會取代v-html指令)。
{
//和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
}
}
})