# 渲染函数
简单的渲染函数:
render: function (createElement) {
return createElement('h1', this.blogTitle)
}
//简化
render(){
return h('h1',{},this.blogTitle)//{}中为 prop 或 attribute
}
2
3
4
5
6
7
等价于
<h1>{{ blogTitle }}</h1>
createElement
:返回一个虚拟节点,来告诉页面需要如何渲染。
h
作为 createElement
的别名是 Vue 生态系统中的一个通用惯例。
如 view
中的 render
:
render:(h, params)=>{
return h('div', {style:{width:'100px',height:'100px',background:'#ccc'}}, '地方')
}
2
3
Vue3 的命名函数式组件
import { h } from 'vue'
const DynamicHeading = (props, context) => {
return h(`h${props.level}`, context.attrs, context.slots)
}
DynamicHeading.props = ['level']
export default DynamicHeading
2
3
4
5
6
7
8
9
export default oneComponent
render () {
return this.renderVN()
}
2
3
4
# createElement参数(h参数)
参数1 {String | Object | Function}
:一个 HTML 标签名、组件选项对象,或者 resolve
了上述任何一种的一个 async
函数。如:div
参数2 {Object}
:一个与模板中 attribute 对应的数据对象。可选。如:
{
'class': {/*class样式切换,foo类样式和bar类式样式的显示控制*/
foo: true,
bar: false
},
style: {/*样式*/
color: 'red',
fontSize: '14px'
},
attrs: {/*元素标签*/
name: headingId,
href: '#' + headingId
},
props: {/*组件prop*/
myProp: 'bar'
},
on: {//事件监听器
click: this.clickHandler
},
nativeOn: {/*`vm.$emit` 触发的事件*/
click: this.nativeClickHandler
},
scopedSlots: {/*作用域插槽 格式为{ name: props => VNode | Array<VNode> }*/
default: props => createElement('span', props.text)
},
// 如果组件是其它组件的子组件,需为插槽指定名称
slot: 'name-of-slot',
// 其它特殊顶层 property
key: 'myKey',
ref: 'myRef',
refInFor: true//如果在渲染函数中给多个元素都应用了相同的 ref 名,开启后`$refs.myRef` 会变成一个数组。
}
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
# vue2写法
更加的扁平化
{
class: ['button', { 'is-outlined': isOutlined }],
style: [{ color: '#34495E' }, { backgroundColor: buttonColor }],
id: 'submit',
innerHTML: '',
onClick: submitForm,
key: 'submit-button'
}
2
3
4
5
6
7
8
参数3 {String | Array}
:子级虚拟节点(VNodes)
,由 createElement()
构建而成,可选。如:
createElement(
// {String | Object | Function} //标签对象
'div',
// {Object} //标签的attr属性
[ //子级虚拟节点
createElement('h1', '一则头条'),
createElement(MyComponent, {
props: {
someProp: 'foobar'
}
})
]
2
3
4
5
6
7
8
9
10
11
12
# vue3写法
render
函数不再接收任何参数,相应的,其可以访问在作用域中声明的响应式状态和函数
() =>
h(
'div',
{
onClick: increment
},
state.count
)
2
3
4
5
6
7
8
# v-if 和 v-for
只要在原生的 JavaScript 中可以轻松完成的操作,Vue 的渲染函数就不会提供专有的替代方法。比如,在模板中使用的 v-if
和 v-for
:
<ul v-if="items.length">
<li v-for="item in items">{{ item.name }}</li>
</ul>
<p v-else>No items found.</p>
2
3
4
这些都可以在渲染函数中用 JavaScript 的 if
/else
和 map
来重写:
props: ['items'],
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.')
}
}
2
3
4
5
6
7
8
9
10
# v-model
v-model
指令扩展为 modelValue
和 onUpdate:modelValue
在模板编译过程中,我们必须自己提供这些 prop:
props: ['modelValue'],
emits: ['update:modelValue'],
render() {
return h(SomeComponent, {
modelValue: this.modelValue,
'onUpdate:modelValue': value => this.$emit('update:modelValue', value)
})
}
2
3
4
5
6
7
8
# v-on
我们必须为事件处理程序提供一个正确的 prop 名称,例如,要处理 click
事件,prop 名称应该是 onClick
。
render() {
return h('div', {
onClick: $event => console.log('clicked', $event.target)
})
}
2
3
4
5
# 事件修饰符
对于 .passive
、.capture
和 .once
事件修饰符,可以使用驼峰写法将他们拼接在事件名后面:
实例:
render() {
return h('input', {
onClickCapture: this.doThisInCapturingMode,
onKeyupOnce: this.doThisOnce,
onMouseoverOnceCapture: this.doThisOnceInCapturingMode
})
}
2
3
4
5
6
7
其他修饰符,可以直接通过 event
使用
这里是一个使用所有修饰符的例子:
render() {
return h('input', {
onKeyUp: event => {
// 如果触发事件的元素不是事件绑定的元素
// 则返回
if (event.target !== event.currentTarget) return
// 如果向上键不是回车键,则终止
// 没有同时按下按键 (13) 和 shift 键
if (!event.shiftKey || event.keyCode !== 13) return
// 停止事件传播
event.stopPropagation()
// 阻止该元素默认的 keyup 事件
event.preventDefault()
// ...
}
})
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 插槽
# 静态插槽(子组件)
你可以通过 this.$slots
访问静态插槽的内容,每个插槽都是一个 VNode
数组:
render() {
// `<div><slot></slot></div>`
return h('div', {}, this.$slots.default())
}
2
3
4
也可以通过 this.$scopedSlots
访问作用域插槽,每个作用域插槽都是一个返回若干 VNode
的函数:
props: ['message'],
render() {
// `<div><slot :text="message"></slot></div>`//默认插槽容器,具有text属性,text属性内容为message
return h('div', {}, this.$slots.default({
text: this.message
}))
}
2
3
4
5
6
7
# 作用域插槽(父组件)
如果要用渲染函数向子组件中传递作用域插槽:
其中 resolveComponent
是模板内部用来解析组件名称的同一个函数。
const { h, resolveComponent } = Vue
render() {
// `<div><child v-slot="props"><span>{{ props.text }}</span></child></div>`//默认插槽模板,props为当前插槽容器的属性集合
return h('div', [
h(
resolveComponent('child'),
{},
// 将 `slots` 以 { name: props => VNode | Array<VNode> } 的形式传递给子对象。
{//只有默认插槽才能直接运用到组件中,否则必须放在template标签上
default: (props) => h('span', props.text)
}
)
])
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# JSX语法
<anchored-heading :level="1">
<span>Hello</span> world!
</anchored-heading>
2
3
import AnchoredHeading from './AnchoredHeading.vue'
new Vue({
el: '#demo',
render: function (h) {
return (
<AnchoredHeading level={1}>
<span>Hello</span> world!
</AnchoredHeading>
)
}
2
3
4
5
6
7
8
9
10
11
h
作为createElement
的别名是Vue
生态系统中的一个通用惯例
极简化写法:
render() {
return <p>hello { this.message }</p>
}
2
3
# 函数式组件
# vue2
<script>
export default {
functional: true,
props: {
render: {
type: Function,
required: true,
},
scope: {
type: Object,
required: true,
},
},
render: (h, ctx) => {
const VNode = ctx.props.render(h, ctx.props.scope);
return VNode;
},
};
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# vue3
import { h } from 'vue'
import type { SetupContext } from 'vue'
type FComponentProps = {
render: (h:Function,scope:any)=>any
scope:any
}
export function FComponent(
props: FComponentProps,
context: SetupContext<any>
) {
const VNode = props.render(h, props.scope);
return return VNode;
}
2
3
4
5
6
7
8
9
10
11
12
13
14