做这个对比的意义 对于Vue开发,绝大多数情况都是使用模版template
的方式来写渲染逻辑,但是通过官方文档我们知道有渲染函数render
的存在。对于初学者,使用template
能够快速上手,然而做一些高阶开发就用得上render
了。在render
中,又有两种实现的途径,分别是基于createElement
方法和JSX语法。
文档说render
更接近实际编译器。这个对比会让人对这句话有更直观的体会。另外对于模版的一些语法背后的实现也会有更深的理解。
本文会实现几项基本功能,每次都以template
开始,接着用后面两种方式实现同样的功能。
render函数的参数是:createElement
函数,为了简化也为了和bable工具统一,都写作了h
。
渲染基本数据 组件其它部分:
1 2 3 4 5 6 let vBindCom={ props:{ content:String, styleObj:Object }, }
template 1 2 template: `<p :style="styleObj">{{content}}</p>`
###render:createElement
1 2 3 4 5 6 7 8 9 render:function (h ) { return h( 'p' , { style:this .styleObj }, this .content ) }
render:jsx 1 2 3 render:function (h ) { return <p style ={this.styleObj} > {this.content}</p > }
v-html 组件其它部分:
1 2 3 props: { htmlcontent: String }
template 1 2 3 <template> <p><span v-html="htmlcontent"></span> by template</p> </template>
render:crateElement 1 2 3 4 5 6 7 8 9 10 render: function (h) { return h("p", [ h("span", { domProps: { innerHTML: this.htmlcontent, }, }), " by render createElement", ]); },
render:jsx 1 2 3 4 5 6 7 render: function (h) { return ( <p> <span domPropsInnerHTML={this.htmlcontent}></span> by render jsx </p> ); },
##v-if/v-else
组件其它部分:
1 2 3 props: { openFlag: Boolean }
template 1 2 3 4 5 6 7 <template> <div> <div v-if="openFlag">It's open</div> <div v-else>It's closed</div> <span>by template</span> </div> </template>
render:crateElement 1 2 3 4 5 6 7 8 9 10 render: function (h) { let children = []; if (this.openFlag) { children.push(h("div", "It's open")); } else { children.push(h("div", "It's closed")); } children.push(h("span", "by render createElement")); return h("div", children); },
render:jsx 1 2 3 4 5 6 7 8 9 10 render: function (h) { let children = []; if (this.openFlag) { children.push(<div>It's open</div>); } else { children.push(<div>It's closed</div>); } children.push(<span>by render jsx</span>); return <div>{children}</div>; },
v-model 组件其它部分:
1 2 3 props: { value: Number }
template 1 2 3 4 5 6 7 8 <template> <label> input a number: <input type="number" @input="clickHander" :value="value" /> <span>输入值:{{ value }}</span> <span> by template</span> </label> </template>
render:crateElement 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 render: function (h) { let children = []; children.push("input a number:"); children.push( h("input", { attrs: { type: "number", }, domProps: { value: this.value, }, on: { input: this.inputHander, }, }) ); children.push(h("span", "输入值:" + this.value)); children.push(h("span", " by render createElement")); return h("label", children); },
这里需要注意:使用crateElment函数给input元素绑定值,使用了domProps
属性,这和实现v-html
时类似。
render:jsx 1 2 3 4 5 6 7 8 9 10 render: function (h) { let children = []; children.push("input a number:"); children.push( <input value={this.value} onInput={this.inputHander} type="number" /> ); children.push(<span>输入值:{this.value}</span>); children.push(<span> by render jsx</span>); return h("label", children); },
v-for 这个组件用到了另外一个组件,它负责把一条goods的信息渲染在一行(采用了Bootstrap的样式文件):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <template> <div class="row"> <div class="col-3 border">{{ item.name }}</div> <div class="col-3 border text-end">{{ item.price }}</div> <div class="col-3 border text-end">{{ item.num }}</div> </div> </template> <script> import { Goods } from "./vforBase"; export default { props: { item: Goods,// Goods是定义的一个类,它有三个属性:name、price、num }, }; </script>
组件的其它部分:
1 2 3 4 5 6 7 8 import vforItem from "./vforItem"; ... props: { items: Array }, components: { "vfor-item": vforItem }
下面是循环渲染出所有goods的信息。
template 1 2 3 4 5 6 <template> <div class="container-fluid"> <vfor-item v-for="(item, index) in items" :key="index" :item="item" /> <div class="text-start">by template</div> </div> </template>
render:crateElement 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 render: function (h) { let childArr = []; console.log(this.items.length); for (let i = 0; i < this.items.length; i++) { let item = this.items[i]; childArr.push( h("vfor-item", { props: { item, }, key: i, }) ); } childArr.push( h( "div", { class: "text-start", }, "by render createElement" ) ); return h( "div", { class: "container-fluid", }, childArr ); },
render:jsx 1 2 3 4 5 6 7 8 9 render: function (h) { let children = []; for (let i = 0; i < this.items.length; i++) { let item = this.items[i]; children.push(<vfor-item item={item} key={i}></vfor-item>); } children.push(<div class="text-start">by render jsx</div>); return <div class="container-fluid">{children}</div>; },
##v-slot
template 1 2 3 4 5 6 7 8 9 <template> <div class="article"> This words is in component <slot name="title" /> <slot /> <div class="by">by template</div> </div> </template>
render:crateElement 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 render: function (h) { return h( "div", { class: "article", }, [ "This words is in component", this.$slots.title, this.$slots.default, h( "div", { class: "by", }, "by render createElement" ), ] ); },
render:jsx 1 2 3 4 5 6 7 8 9 10 render: function (h) { return ( <div class="article"> This words is in component {this.$slots.title} {this.$slots.default} <div class="by">by render jsx</div> </div> ); },
scoped slot 组件的其它部分,组件内部定义了title:
1 2 3 4 5 data: function () { return { title: "Title string in component" }; }
###template
1 2 3 4 5 6 7 <template> <div class="article"> This words is in component <slot name="title" v-bind:title="title">{{ title }}</slot> <div class="by">by template</div> </div> </template>
render:crateElement 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 render: function (h) { let titleContent = this.$scopedSlots.title ? this.$scopedSlots.title({ title: this.title }) : this.title; return h( "div", { class: "article", }, [ "This words is in component", titleContent, h( "div", { class: "by", }, "by render createElement" ), ] ); },
render:jsx 1 2 3 4 5 6 7 8 9 10 11 12 render: function (h) { let titleContent = this.$scopedSlots.title ? this.$scopedSlots.title({ title: this.title }) : this.title; return ( <div class="article"> This words is in component {titleContent} <div class="by">by render jsx</div> </div> ); },
filter 过滤器就是一个函数,大概定义成这样:
1 2 3 4 5 let myFilter = function (val) { // console.log(val); return val.toUpperCase(); }; export default myFilter;
组件的其它部分:
1 2 3 4 filters: { myFilter }, props: { name: String }
template 1 2 3 4 5 <template> <span> Welcom:<span>{{ name | myFilter }}</span> </span> </template>
render:createElement 1 2 3 4 5 6 render: function (h) { return h("span", [ "Welcom:", h("span", this.$options.filters.myFilter(this.name)), ]); },
render:jsx 1 2 3 4 5 6 7 render: function (h) { return ( <span> Welcom:<span>{this.$options.filters.myFilter(this.name)}</span> </span> ); },
可见,在模版中使用管道|
调用过滤器,在渲染函数中就变成了它本来的函数调用。
完整的实例 基本渲染
v-html
v-if
v-model
v-for
v-slot