h
2023年06月10日
一、认识
h()
创建虚拟 DOM
节点 (vnode
)。这个名字来源于许多虚拟 DOM
实现默认形成的约定。一个更准确的名称应该是 createVnode()
,但当你需要多次使用渲染函数时,一个简短的名字会更省力。
二、语法
h(type: string | Component , props?: object | null ,children ?: Children | Slot | Slots);
-
type
: 既可以是一个字符串 (用于原生元素) 也可以是一个Vue
组件定义 -
props
: 要传递的prop
-
children
: 子节点。当创建一个组件的vnode
时,子节点必须以插槽函数进行传递。如果组件只有默认槽,可以使用单个插槽函数进行传递。否则,必须以插槽函数的对象形式来传递。
三、h element
3.1 两个参数
没有 props
时可以省略不写
const vnode = h('div', [h('div', '嘻嘻,哈哈'), h('div', '呵呵')])
render(vnode, document.querySelector('#app'))
3.2 三个参数
const vnode = h(
'div',
{
id: 'div-id',
class: 'div-class',
onClick: () => {
console.log('事件')
}
},
[h('div', '嘻嘻,哈哈'), h('div', '呵呵')]
)
console.log('vnode', vnode)
render(vnode, document.querySelector('#app'))
四、h component
4.1 对象
const Component = {
name: '',
setup() {},
render() {
return h('div', {}, 'Component VNode')
}
}
const vnode = h(Component)
console.log('vnode', vnode)
render(vnode, document.querySelector('#app'))
4.2 文件
在给组件创建 vnode
时,传递给 h()
函数的第一个参数应当是组件的定义。这意味着使用渲染函数时不再需要注册组件了 —— 可以直接使用导入的组件:
import Foo from "./Foo.vue";
const vnode = h(Foo);
render(vnode, document.querySelector('#app'))
不管是什么类型的文件,只要从中导入的是有效的 Vue
组件,h
就能正常运作。
五、h slot
const ChildComponent = {
name: 'ChildComponent',
setup(props, setupContext) {
const a = reactive({
b: 1,
c: 2
})
const { slots } = setupContext
return () => {
return h('div', null, [
h('div', null, 'Child Component'),
// 默认插槽 <div> <slot></slot> </div>
h('div', null, slots.default()),
// 具名插槽 <div> <slot name="header" ></slot> </div>
h('div', null, slots.header()),
// 作用域插槽 <div> <slot name="header" :a="a"></slot> </div>
h(
'div',
null,
slots.body({
a: a
})
)
])
}
}
}
const AppComponent = {
name: 'AppComponent',
setup() {},
render() {
return h('div', {}, [
h('div', null, 'App Component'),
h(ChildComponent, null, {
default() {
return h('div', null, 'default 插槽渲染内容')
},
header() {
return h('div', null, 'header 具名插槽渲染内容')
},
body(slotProps) {
const { a } = slotProps
return h('div', null, [
h('div', null, 'body 作用域插槽渲染内容'),
h(
'div',
null,
`body 作用域插槽中 子组件传递给父组件 的数据 ${a.b}`
)
])
}
})
])
}
}
const vnode = h(AppComponent)
console.log('vnode', vnode)
render(vnode, document.querySelector('#app'))
六、h KeepAlive
<KeepAlive>
内置组件在渲染函数中必须导入才能使用
const ChildComponent = {
name: 'ChildComponent',
setup() {},
render() {
return h('div', null, [h('div', null, 'ChildComponent')])
}
}
const AppComponent = {
name: 'AppComponent',
setup() {},
render() {
return h('div', null, [
h('div', null, 'AppComponent'),
h(KeepAlive, { mode: 'out-in' }, [h(ChildComponent)])
])
}
}
const vnode = h(AppComponent)
render(vnode, document.querySelector('#app'))
七、h Text
const vnode = h(Text, 'Text VNode')
console.log('vnode', vnode)
render(vnode, document.querySelector('#app'))
八、h Comment
const vnode = h(Comment, 'Comment VNoe')
console.log('vnode', vnode)
render(vnode, document.querySelector('#app'))
九、h Fragment
const vnode = h(Fragment, null, [h('div', null, 'Fragment VNode')])
console.log('vnode', vnode)
render(vnode, document.querySelector('#app'))
十、h v-model
v-model
指令扩展为 modelValue
和 onUpdate:modelValue
在模板编译过程中,我们必须自己提供这些 props
:
const AppComponent = {
name: 'AppComponent',
setup(props, setupContext) {
const a = ref('')
const { emit } = setupContext
return () =>
h('div', null, [
h('div', null, 'AppComponent'),
h('input', {
modelValue: a.value,
'onUpdate:modelValue': value => emit('update:modelValue', value)
})
])
}
}
const vnode = h(AppComponent)
render(vnode, document.querySelector('#app'))
十一、h v-if
const flag = false
const vnode = h('div', null, flag ? h('div', '嘻嘻') : h('div', '哈哈'))
console.log('vnode', vnode)
render(vnode, document.querySelector('#app'))
十二、h v-for
const list = [1, 2, 3]
const vnode = h(
'div',
null,
list.map(item => h('div', null, item))
)
console.log('vnode', vnode)
render(vnode, document.querySelector('#app'))
十三、h v-on
13.1 驼峰拼接
对于 .passive
、.capture
和 .once
事件修饰符,可以使用驼峰写法将他们拼接在事件名后面:
const vnode = h(
'div',
{
onClickOnce: () => {
console.log('哈哈')
}
},
'嘻嘻'
)
render(vnode, document.querySelector('#app'))
13.2 withModifiers
对于事件和按键修饰符,可以使用 withModifiers
函数:
const vnode = h(
'div',
{
onClick: withModifiers(() => {
console.log('哈哈')
}, ['self'])
},
'嘻嘻'
)
render(vnode, document.querySelector('#app'))
十四、h 动态组件
14.1 直接导入
import A from './a.vue'
import B from './b.vue'
import { h, render, withModifiers } from 'vue'
const flag = true
const vnode = h('div', null, flag ? A : B)
render(vnode, document.querySelector('#app'))
14.2 动态导入
如果一个组件是用名字注册的,不能直接导入 (例如,由一个库全局注册),可以使用 resolveComponent()
来解决这个问题。
const AppComponent = {
name: 'AppComponent',
setup() {
const AComponent = resolveComponent('A')
return () => {
return h('div', null, AComponent)
}
}
}
const vnode = h(AppComponent)
render(vnode, document.querySelector('#app'))