1.render
字符串模板( template 写法)的代替方案,允许你发挥 JavaScript 最大的编程能力。该渲染函数接收一个 createElement 方法作为第一个参数用来创建 VNode。
如果组件是一个函数组件,渲染函数还会接收一个额外的 context 参数,为没有实例的函数组件提供上下文信息。
采用 render 函数创建一个标题标签
Vue.component('anchored-heading', {
render: function (createElement) {
return createElement(
'h' + this.level, // 标签名称
this.$slots.default // 子节点数组
)
},
props: {
level: {
type: Number,
required: true
}
}
})
1.1 createElement 参数
// @returns {VNode}
createElement(
// {String | Object | Function}
// 一个 HTML 标签名、组件选项对象,或者
// resolve 了上述任何一种的一个 async 函数。必填项。
'div',
// {Object}
// 一个与模板中 attribute 对应的数据对象。可选。
{
// (详情见下一节)
},
// {String | Array}
// 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,
// 也可以使用字符串来生成“文本虚拟节点”。可选。
[
'先写一些文字',
createElement('h1', '一则头条'),
createElement(MyComponent, {
props: {
someProp: 'foobar'
}
})
]
)
d2admin 中侧边栏的渲染
render (createElement) {
// 创建一个 1.1 div 标签, 后面是 div 的一些特性
return createElement('div', {
// 1.2 样式特性
attrs: { class: 'd2-layout-header-aside-menu-side' }
}, [
// 子标签 1.3.1 el-menu
createElement('el-menu', {
key: this.keyId,
props: {
// 子标签的折叠属性
collapse: this.asideCollapse,
'default-openeds': this.currentOpens,
uniqueOpened: false,
defaultActive: this.indexPath1 || this.$route.fullPath || this.active
},
ref: 'menu',
on: { select: this.handleMenuSelect }
// 1.3.3.1 子子标签(二级标签),通过子标签的 children 创建,递归调用 createElement 方法
}, this.aside.map(menu => (menu.children === undefined ? elMenuItem : elSubmenu).call(this, createElement, menu))),
...this.aside.length === 0 && !this.asideCollapse ? [
createElement('div', {
attrs: {
class: 'd2-layout-header-aside-menu-empty',
flex: 'dir:top main:center cross:center'
}
}, [
createElement('d2-icon', { props: { name: 'inbox'} }),
createElement('span', {}, '没有侧栏菜单')
])
] : []
])
},
1.2 Vue 中的 JSX 语法
createElement 有的时候写起来比较复杂
createElement(
'anchored-heading', {
props: {
level: 1
}
}, [
createElement('span', 'Hello'),
' world!'
]
)
表示的模板为
<anchored-heading :level="1">
<span>Hello</span> world!
</anchored-heading>
JSX 语法让写法又回到模板语法,AnchoredHeading 还是原先用 createElement 创建的组件
import AnchoredHeading from './AnchoredHeading.vue'
new Vue({
el: '#demo',
render: function (h) {
return (
// 因为 level={1} 是传入子组件中的数据,所以用 {} 区分
<AnchoredHeading level={1}>
<span>Hello</span> world!
</AnchoredHeading>
)
}
})
将
h作为createElement的别名是 Vue 生态系统中的一个通用惯例,实际上也是 JSX 所要求的。从 Vue 的 Babel 插件的 3.4.0 版本开始,我们会在以 ES2015 语法声明的含有 JSX 的任何方法和 getter 中 (不是函数或箭头函数中) 自动注入const h = this.$createElement,这样你就可以去掉(h)参数了。对于更早版本的插件,如果h在当前作用域中不可用,应用会抛错。
所以在新版 d2admin 里用了 JSX 的写法, {} 用来声明是引用值而不是字面量
render(h) {
// 返回 DOM 元素
return (
<div class="d2-layout-header-aside-menu-side">
<el-menu
// 折叠
collapse={this.asideCollapse}
collapseTransition={this.asideTransition}
uniqueOpened={true}
defaultActive={this.$route.fullPath}
ref="menu"
onSelect={this.handleMenuSelect}
>
{/* 根据aside数组创建菜单项目,递归调用生成菜单 */}
{this.aside.map((menu) => createMenu.call(this, h, menu))}
</el-menu>
{this.aside.length !== 0 && true ? (
// 如果侧边数组的长度为0,显示没有侧边栏
<div class="d2-layout-header-aside-menu-empty" flex="dir:top main:center cross:center">
<d2-icon name="inbox"></d2-icon>
<span>没有侧栏菜单</span>
</div>
) : null}
</div>
)
},