在深刻渲染函数以前,了解一些浏览器的工做原理很重要。如下面这段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') }) ) }