一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第 10 天,点击查看活动详情。
渲染函数 -- render
1、使用
通常我们都是使用模板创建html的,这也是vue推荐的方式,但也可以使用js完全去控制生成html。
渲染函数:
字符串模板的代替方案,允许你发挥 JavaScript 最大的编程能力。该渲染函数接收一个 createElement 方法作为第一个参数用来创建 VNode。
渲染函数接收一个函数参数,利用这个函数创建虚拟DOM, 最终返回一个虚拟DOM。
写法:
render: function (createElement) {
// createElement函数返回结果是VNode
return createElement(
tag, // 标签名称
data, // 传递数据
children // 子节点(VNode)数组
)
}
复制代码
使用demo
<heading level="1" title="123">哈哈哈</heading>
复制代码
Vue.component('heading', {
// 考虑用户是否会传递level,可使用对象形式校验数据类型或设置为必填
props: {
level: {
type: String,
required: true
}
},
// render接收的这个参数通常写为h,因为VNode在底层使用的算法叫snabbdom, 在这个算法里生成虚拟DOM的函数就叫做h
render(h) {
let vnode = h(
'h' + this.level, // 标签名称
{
attrs: {
id: 'foo'
}
}, // 传递数据;没有被定义为 prop 的 attribute ,它会自动添加到组件的根元素上
this.$slots.default, // 子节点VNode数组
)
console.log(vnode)
return vnode
}
})
复制代码
3、createElement参数
// @returns {VNode}
createElement(
// {String | Object | Function}
// 一个 HTML 标签名、组件选项对象,或者
// resolve 了上述任何一种的一个 async 函数。必填项。
'div',
// {Object}
// 一个与模板中 attribute 对应的数据对象。可选。
{
// (详情见链接:深入数据对象)
},
// {String | Array}
// 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,
// 也可以使用字符串来生成“文本虚拟节点”。可选。
[
'先写一些文字',
createElement('h1', '一则头条'),
createElement(MyComponent, {
props: {
someProp: 'foobar'
}
})
]
)
复制代码
// 使用
// <heading level="1" title="fdsafsd" icon="zhongguoditu">render标题1</heading>
// 丰富组件
Vue.component('heading', {
props: {
level: {
type: String,
required: true
},
title: {
type: String,
default: ''
},
icon: {
type: String,
default: ''
}
},
render(h) {
// 子节点数组
let children = [];
// icon属性处理逻辑
// <svg class="icon ">
// <use xlink:href="#icon-zhongguoditu"/>
// </svg>
if(this.icon){
children.push(h(
'svg',
{
class: 'icon',
},
[h(
'use',
{
attrs: {
'xlink:href': '#icon-'+ this.icon
}
}
)]
))
}
// 再添加一个子节点:my-component组件,给其传递参数
children.push(h(
'my-component',
{
props: {
message: '组件啊'
}
}
))
// 拼接子节点
children = children.concat(this.$slots.default)
let vnode = h(
'h' + this.level, // 标签名称
{
attrs: {
title: this.title
},
}, // 传递数据;给组件绑定的数据可以不用手动处理,它会自动移到这个根节点上
children, // 子节点VNode数组
)
console.log(vnode)
return vnode
}
})
Vue.component('my-component', {
props: ['message'],
template: `
<div>{{message}}</div>
`
})
复制代码
函数式组件
1、渲染函数
没有管理任何状态,也没有监听任何传递给它的状态,也没有生命周期方法。实际上,它只是一个接受一些 prop 的函数。在这样的场景下,我们可以将组件标记为 functional,这意味它无状态 (没有响应式数据),也没有实例 (没有 this 上下文)。
画重点:
- 没有状态,也就是函数内部没有定义data属性等
functional: true
将组件标记为函数式组件- 函数式组件没有实例,不能直接使用this访问上下文
将上面代码改造为函数式组件
// <heading level="1" title="fdsafsd" icon="zhongguoditu">render标题1</heading>
Vue.component('heading', {
functional: true, // 标记为函数式组件
// 在 2.3.0 之前的版本中,props 选项是必须的
// 在 2.3.0 或以上的版本中, 可以省略 props 选项,所有组件上的 attribute 都会被自动隐式解析为 prop。
props: {
level: {
type: String,
required: true
},
title: {
type: String,
default: ''
},
icon: {
type: String,
default: ''
}
},
// 不能用template, 使用render; 没有实例,不存在this, render通过context传递上下文;
render(h, context) {
// 属性获取的变化
const {icon, level, title} = context.props;
// 子节点数组
let children = [];
// icon属性处理逻辑
// <svg class="icon ">
// <use xlink:href="#icon-zhongguoditu"/>
// </svg>
if(icon){
children.push(h(
'svg',
{
class: 'icon',
},
[h(
'use',
{
attrs: {
'xlink:href': '#icon-'+ icon
}
}
)]
))
}
// 拼接子节点 将this.$slots.default更新成context.children
children = children.concat(context.children)
let vnode = h(
'h' + level, // 标签名称
{
attrs: {
title: title
},
}, // 传递数据;给组件绑定的数据可以不用手动处理,它会自动移到这个根节点上
children, // 子节点VNode数组
)
return vnode
}
})
复制代码
- 小结
-
如果组件是一个函数组件,渲染函数还会接收一个额外的 context 参数,为没有实例的函数组件提供上下文信息。
-
props选项:
在 2.3.0 之前的版本中,props 选项是必须的
在 2.3.0 或以上的版本中, 可以省略 props 选项,所有组件上的 attribute 都会被自动隐式解析为 prop。 -
函数式组件只是函数,渲染开销低很多
-
slots()
与children
区别:
children就是指所有子节点;
而slots()
可以让组件感知某个插槽机制, 如slots().default
默认匿名内容
2、基于模板的函数式组件
基于模板的函数式组件可以这样声明:
<template functional>
</template>
复制代码
<!--函数式组件-->
<template functional>
<div :title='data.attrs.title'>
{{data.attrs}}
<slot name='header'/>
<slot/>
</div>
</template>
复制代码
<!--使用函数式组件-->
<functional-temp :title='title'>
<template v-slot:header>
头部
</template>
内容
</functional-temp>
复制代码