Render 函数及动态组件
By TommyShao
©️广州筷子信息科技

Render函数
- createElement
- JavaScript代替模板
- JSX模板
- 函数化组件
- 模板编译
- 案例分析
动态组件
- 循环子组件
- is 扩展组件
- 子组件实例化
- 案例分析
Render工厂🏭函数

v-dom组件的渲染
var CommentBox = React.createClass({
render: function() {
return (
<div className="commentBox">
Hello, world! I am a CommentBox.
</div>
);
}
});
ReactDOM.render(
<CommentBox />,
document.getElementById('content')
);v-dom组件编译
var CommentBox = React.createClass({displayName: 'CommentBox',
render: function() {
return (
React.createElement('div', {className: "commentBox"},
"Hello, world! I am a CommentBox."
)
);
}
});
ReactDOM.render(
React.createElement(CommentBox, null),
document.getElementById('content')
);Vue 2.x 的 Render 函数✈️
Vue.component('anchored-heading', {
render: function (createElement) {
return createElement(
'h' + this.level, // tag name 标签名称
this.$slots.default // 子组件中的阵列
)
},
props: {
level: {
type: Number,
required: true
}
}
})createElement参数📽
createElement(
// {String | Object | Function}
// 一个 HTML 标签字符串,组件选项对象,
// 或者一个返回值类型为String/Object的函数,必要参数
'div',
// {Object}
// 一个包含模板相关属性的数据对象
// 这样,您可以在 template 中使用这些属性.可选参数.
{
// data object
},
// {String | Array}
// 子节点(VNodes),可以是一个字符串或者一个数组. 可选参数.
[
createElement('h1', 'hello world'),
createElement(MyComponent, {
props: {
someProp: 'foo'
}
}),
'bar'
]
)data object参数
{
// 和`v-bind:class`一样的 API
'class': {
foo: true,
bar: false
},
// 和`v-bind:style`一样的 API
style: {
color: 'red',
fontSize: '14px'
},
// 正常的 HTML 特性
attrs: {
id: 'foo'
},
// 组件 props
props: {
myProp: 'bar'
},
// DOM 属性
domProps: {
innerHTML: 'baz'
},
// 事件监听器基于 "on"
// 所以不再支持如 v-on:keyup.enter 修饰器
// 需要手动匹配 keyCode。
on: {
click: this.clickHandler
},
// 仅对于组件,用于监听原生事件,而不是组件内部使用 vm.$emit 触发的事件。
nativeOn: {
click: this.nativeClickHandler
},
// 自定义指令. 注意事项:不能对绑定的旧值设值
// Vue 会为您持续追踪
directives: [
{
name: 'my-custom-directive',
value: '2'
expression: '1 + 1',
arg: 'foo',
modifiers: {
bar: true
}
}
],
// Scoped slots in the form of
// { name: props => VNode | Array<VNode> }
scopedSlots: {
default: props => h('span', props.text)
},
// 如果组件是其他组件的子组件,需为slot指定名称
slot: 'name-of-slot'
// 其他特殊顶层属性
key: 'myKey',
ref: 'myRef'
}JavaScript代替模板
render: function (createElement) {
if (this.items.length) {
return createElement('ul', this.items.map(function (item) {
return createElement('li', item.name)
}))
} else {
return createElement('p', 'No items found.')
}
}slots 内容
// slots
render: function (createElement) {
// <div><slot></slot></div>
return createElement('div', this.$slots.default)
}
// scopedSlots
render: function (createElement) {
// <div><slot :text="msg"></slot></div>
return createElement('div', [
this.$scopedSlots.default({
text: this.msg
})
])
}函数化组件
Vue.component('my-component', {
// 标记组件为 functional,
// 这意味它是无状态(没有 data),无实例(没有 this 上下文)
functional: true,
// 为了弥补缺少的实例
// 提供第二个参数作为上下文
render: function (createElement, context) {
// ...
},
// Props 可选
props: {
// ...
}
})模板编译
- Vue.compile
var res = Vue.compile('<div><span>{{ msg }}</span></div>')
new Vue({
data: {
msg: 'hello'
},
render: res.render,
staticRenderFns: res.staticRenderFns
})模板编译
-
*.vue单组件
- 当声明 template 标签,则由 vue-loader 的 Vue-compile 方法重写
- 当没有声明 template 标签,则组件内的 render 函数起作用
- 子组件声明可以使用 render 函数
模板编译
-
*.vue单组件
- 当声明 template 标签,则由 vue-loader 的 Vue-compile 方法重写
- 当没有声明 template 标签,则组件内的 render 函数起作用
- 子组件声明可以使用 render 函数
// Render.js
export default {
data () {
return {
text: 'Render'
}
},
render (createElement) {
const self = this._self
return createElement(
'div',
{
'class': 'class-render'
},
`${self.text}`
)
}
}模板编译
-
*.vue单组件
- 当声明 template 标签,则由 vue-loader 的 Vue-compile 方法重写
- 当没有声明 template 标签,则组件内的 render 函数起作用
- 子组件声明可以使用 render 函数
<template>
<div>
{{ title }}
<p>{{ now }}</p>
<button @click="handleClick">click</button>
<hr />
<child></child>
</div>
</template>
<script>
export default {
data () {
return {
title: 'Parent',
now: ''
}
},
methods: {
handleClick () {
this.title = 'Parent click Title'
}
},
components: {
child: {
data () {
return {
title: 'Child'
}
},
methods: {
handleClick () {
const now = new Date().getTime()
this.title = this.title.replace(/-.*$/, '') + '-' + now
this.$parent.now = now
}
},
created () {
this.$on('change', (val) => {
console.log('parent title change:', val)
})
},
render (createElement, context) {
const self = this._self
const h = self.$createElement
const parent = self.$parent
return h(
'div',
[
h('h2', parent.title),
h('h3', self.title),
h('button', {
on: {
click: self.handleClick
}
},
'child click'),
h('button', {
on: {
click: () => {
parent.title = 'child click title'
this.$emit('change', parent.title)
}
}
},
'click child title')
]
)
}
}
}
}
</script>模板编译
-
*.vue单组件
- 当声明 template 标签,则由 vue-loader 的 Vue-compile 方法重写
- 当没有声明 template 标签,则组件内的 render 函数起作用
- 子组件声明可以使用 render 函数
案例1
vue-JSX 代替createElement
render (h) {
return (
<div
// 自定义属性
id="foo"
// DOM 属性
domPropsInnerHTML="bar"
// 事件定义
onClick={this.clickHandler}
// 原生事件定义
nativeOnClick={this.nativeClickHandler}
// 特殊的顶级属性
class={{ foo: true, bar: false }}
style={{ color: 'red', fontSize: '14px' }}
key="key"
ref="ref"
// assign the `ref` is used on elements/components with v-for
refInFor
slot="slot">
</div>
)
}vue-jsx 与 React jsx的差异
- vNode 数据格式差异
- createElement 参数差异
vue-jsx 特征
- 可以使用不注册的组件
- 属性展开
- 指令的定义
vue-jsx 特征
- 可以使用不注册的组件
- 属性展开
- 指令的定义
import Todo from './Todo.js'
export default {
render (h) { // createElement函数自动注入
const data = {
class: ['b', 'c']
}
// 定义指令
const directives = [
{ name: 'my-dir', value: 123, modifiers: { abc: true } }
]
// data自动展开,class 合并到一起
// 以首写字母大写开头,不需注册 Todo 组件,自动注册
return <Todo class="a" {...data} {...{ directives }} />
}
}案例2
动态组件

循环子组件
- 顾名思义通过 v-for 循环对组件的增加和删减
is 组件
- 通过 component 组件标签绑定:is属性来决定生成属于哪个组件,比如router-view 的实现方式。
子组件实例化
- 通过子组件的实例化对象,通过 dom 操作 append 到父组件中,并使用实例化对象的函数进行父子组件之间的通信,不继续父组件特性,比如 vuex 的实例对象 store,vue-router 的$router等非全局注册对象
- 通过 $mount() 函数将子组件挂载到父组件中,继承父组件特性
实例 - 云创意库组件
Vue-Render-dynamic-component
By Tomi Eric
Vue-Render-dynamic-component
Vue-Render & dynamic-component
- 1,758