vue render函数解析
1、使用场景:Vue 推荐在绝大多数情况下使用模板来创建你的 HTML。然而在一些场景中,你真的需要 JavaScript 的完全编程的能力。这时你可以用渲染函数,它比模板更接近编译器。 render 函数的使用,重点:样式、事件、插槽、指令、props、v-model、函数组件的处理。
2、参数说明:
// @returns {VNode}
createElement(
// {String | Object | Function}
// 一个 HTML 标签名、组件选项对象,或者
// resolve 了上述任何一种的一个 async 函数。必填项。
'div',
// {Object}
// 一个与模板中 attribute 对应的数据对象。可选。
{
// 与 `v-bind:class` 的 API 相同,
// 接受一个字符串、对象或字符串和对象组成的数组
'class': {
foo: true,
bar: false
},
// 与 `v-bind:style` 的 API 相同,
// 接受一个字符串、对象,或对象组成的数组
style: {
color: 'red',
fontSize: '14px'
},
// 普通的 HTML attribute
attrs: {
id: 'foo'
},
// 组件 prop
props: {
myProp: 'bar'
},
// DOM property
domProps: {
textContent: 'div 文本',// 优先级高于 v-text
innerHTML: 'BAR' // 优先级高于 v-html
},
// 事件监听器在 `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 => VNode | Array<VNode> }
scopedSlots: {
default: props => createElement('span', props.text)
},
// 如果组件是其它组件的子组件,需为插槽指定名称
slot: 'name-of-slot',
// 其它特殊顶层 property
key: 'myKey',
ref: 'myRef',
// 如果你在渲染函数中给多个元素都应用了相同的 ref 名,
// 那么 `$refs.myRef` 会变成一个数组。
refInFor: true
},
// {String | Array}
// 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,
// 也可以使用字符串来生成“文本虚拟节点”。可选。
[
'先写一些文字',
createElement('h1', '一则头条'),
createElement(MyComponent, {
props: {
someProp: 'foobar'
}
})
]
)
3、使用例子:render函数在vue中非常重要,但其实本质上执行渲染工作的是h函数,本质上也就是createElement函数!
{
title: 'Name',
key: 'name',
render: (h, params) => {
return h('div', [
h('a',{
class: {
'a-font-size': true
}
}, params.row.name),
h('p',{
class: {
'p-margintb': true
}
}, '从 ' + params.row.gmtModified + ' 到 ' + params.row.endDate ),
h('p',{
class: {
'p-marginb': true
}
},'负责人:'+ params.row.users.name)
])
}
}
// 渲染结果
<div>
<a class="a-font-size">Name</a>
<p class="p-margintb">从2016年7月1日到2018年3月19日</p>
<p class="p-marginb">负责人:Frank</p>
</div>
3-1、使用插槽:
//button组件
<template>
<div>
<slot name="left"></slot>
<button>
<slot v-bind:person="person">
<span>按钮</span>
</slot>
</button>
<slot name="right" v-bind:age="person.age"></slot>
</div>
</template>
<script>
export default {
name: "MyButton",
data() {
return {
person: {
name: 'jack',
age: 23,
},
}
},
}
</script>
普通命名插槽,使用h('template',{slot:'slotName'},children)
编写,然后放渲染组件的第三个参数
里。
<template>
<div>
<comp></comp>
</div>
</template>
<script>
const comp = {
render(h) {
//NOTE h 第一个参数为 template 第二个参数里的 slot 属性指定插槽名称
const slotLeft = h('template', { slot: 'left' }, '按钮左边')
const slotRight = h('template', { slot: 'right' }, '按钮右边')
const slotDefault = h('template', { slot: 'default' }, '默认插槽')
const children = [slotLeft, slotDefault, slotRight]
console.log(h(button, {}, children))
return h(button, {}, children)
},
}
import button from './button.vue'
export default {
name: 'UseButton',
components: {
comp,
},
}
</script>
作用域插槽在第二个参数的 scopedSlots
对象里,该对象的每个属性名是组件的插槽名
,值是一个函数,参数为插槽绑定的数据。
<template>
<div>
<comp></comp>
</div>
</template>
<script>
const comp = {
render(h) {
//NOTE h 第一个参数为 template 第二个参数里的 slot 属性指定插槽名称
const slotLeft = h('template', { slot: 'left' }, '按钮左边')
const children = [slotLeft]
return h(button, {
scopedSlots: {
default: props => {
console.log(props)
const { person } = props
const text = `作用域插槽,${JSON.stringify(person)}`
// 返回 h 创建的 VNode
return h('span', {}, text)
},
right: props => {
console.log(props)
const { age } = props
// 返回 jsx
return <span>按钮右边 {age} 岁</span>
},
},
}, children)
},
}
import button from './button.vue'
export default {
name: 'UseButton',
components: {
comp,
},
}
</script>
3-2、v-model
//1. 在数据对象中使用 model 属性:
{
model: {
value: this.value,// value 是 data 里的属性
callback: value => {
// 可以再赋值之前做其他逻辑
// 验证数据
// 触发事件
this.value = value
}
}
}
//2. 传递 value + 监听 input 事件
{
props: {
// value 是 data 中的属性
value: this.value
},
on: {
input: value => {
// 可做其他事情
// 触发事件
this.value = value
}
}
}
//3. 在 jsx 中使用 vModel 属性
// input 是 data 中的属性
<MyInput vModel={this.input} />
4、render 封装一个输入框
export default {
name: 'MyInput',
props: {
// 需要实现 v-model 指令
value: {
type: [String, Number],
default: '',
},
},
render(h) {
return h('input', {
class: {
'my-input': true,
},
style: {
backgroundColor: '#ccc',
},
attrs: {
id: 'my-input',
class: 'a-my-input',
'data-key': 'key',
},
domProps: {
value: this.value,
},
// 监听 input 的 input 事件
on: {
input: ({ target }) => {
this.$emit('input', target.value)
},
},
})
},
}
使用该组件
<MyInput v-model="myInput" />
在 render 函数中使用
export default {
name: 'UseInput',
data() {
return {
input: '',
}
},
render(h) {
return h('div', {}, [
h(MyInput, {
model: {
value: this.input,
callback: value => {
// 可在此做其他事件
this.input = value
},
},
}),
h('h3', {}, this.input),
])
},
}