函数式编程

主要用到h函数,h接收三个参数

  • type:元素的类型
  • propsOrChildren:数据对象,主要表示props、attrs、dom props、class和style
  • children:子节点

优点:跳过了模板的编译过程

缺点:很难看,学习成本高

模板的编译过程:

  1. parser函数将模板转成抽象语法树ast
  2. transform函数将ast转成js api
  3. generate函数通过js api生成render函数

render函数会返回一个形如这样的数据,就是h函数:

1
2
3
return h("div",{},[
h("span",{},"1")
])

Vue3开发使用h函数较少,因为Vue3模板的编译性能也不差。

App.vue:

1
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<template>
<table border>
<tr>
<th>name</th>
<th>age</th>
<th>address</th>
<th>操作</th>
</tr>
<tr v-for="item in list" :key="item.id">
<td>{{ item.name }}</td>
<td>{{ item.age }}</td>
<td>{{ item.address }}</td>
<td>
<Btn type="success">编辑</Btn>
<Btn type="error">删除</Btn>
</td>
</tr>
</table>
</template>

<script setup lang="ts">
import { h, reactive } from 'vue'

const list = reactive([
{id:20,name:"yajue",age:20,address:"xbz"},
{id:21,name:"yajue",age:21,address:"xbz"},
{id:22,name:"yajue",age:22,address:"xbz"},
{id:23,name:"yajue",age:23,address:"xbz"},
{id:24,name:"yajue",age:24,address:"xbz"},
])

interface Props {
type: "success" | "error"
}

// 类似setup函数模式
// 可以用于封装一些小组件
const Btn = (props:Props,ctx:any,)=> {
// 参数1:节点名 参数2:节点属性 参数3:节点内容
return h("button",{
class:"btn",
style: {
color: props.type==="success"?"green":"red"
},
onClick:()=>{
if(props.type==="success") {
console.log("编辑")
} else {
console.log("删除")
}
}
},ctx.slots.default())
}
</script>

源码

在Vue源码(/package/runtime-core/src/h.ts)中可以看到h函数的源码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Actual implementation
// 根据h函数的参数创建虚拟节点
export function h(type: any, propsOrChildren?: any, children?: any): VNode {
const l = arguments.length
if (l === 2) {
if (isObject(propsOrChildren) && !isArray(propsOrChildren)) {
// single vnode without props
if (isVNode(propsOrChildren)) {
return createVNode(type, null, [propsOrChildren])
}
// props without children
return createVNode(type, propsOrChildren)
} else {
// omit props
return createVNode(type, null, propsOrChildren)
}
} else {
if (l > 3) {
children = Array.prototype.slice.call(arguments, 2)
} else if (l === 3 && isVNode(children)) {
children = [children]
}
return createVNode(type, propsOrChildren, children)
}
}