认识
2023年06月10日
一、认识
h
函数是一个辅助创建 虚拟 DOM
的工具函数。
二、细节
h
函数根据 type
的不同, 分别创建不同类型的 VNode
:
-
string
: 创建Element VNode
-
object
: 创建Component VNode
-
other
: 直接创建以type
为类型的节点, 比如Text VNode
、Comment VNode
、Fragment 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'))