关于Vue Function-based API

对比

先看一下新的语法:

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
import { value, computed, watch, onMounted } from 'vue'

const App = {
template: `
<div>
<span>count is {{ count }}</span>
<span>plusOne is {{ plusOne }}</span>
<button @click="increment">count++</button>
</div>
`,
setup() {
// reactive state
const count = value(0)
// computed state
const plusOne = computed(() => count.value + 1)
// method
const increment = () => { count.value++ }
// watch
watch(() => count.value * 2, val => {
console.log(`count * 2 is ${val}`)
})
// lifecycle
onMounted(() => {
console.log(`mounted`)
})
// expose bindings on render context
return {
count,
plusOne,
increment
}
}
}

这个语法实际上是将原本的组件方法以函数的形式在 setup 中执行,这些函数通过 ES6 的模块语法导入,也就是说可以对这些模块进行静态分析,有效地进行 tree-shaking。

这种语法对应回 Vue 2.X 的写法是:

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
<template>
<div>
<span>count is {{ count }}</span>
<span>plusOne is {{ plusOne }}</span>
<button @click="increment">count++</button>
</div>
</template>

export default {
data () {
return {
count: 0
}
},
computed: {
plusOne () {
return this.count
}
},
methods: {
increment () {
this.count++
}
},
watch: {
count: {
handler (val) {
// 这里的写法存在问题,Vue 3 中会把 count * 2 作为数据源去检查变化
// 而不是 count
console.log(`count * 2 is ${val * 2}`)
},
immediate: true
}
},
mounted () {
console.log('mounted')
}
}

Props

在新语法的样例中没有将 props 的写法展示出来,实际上 props 并没有以函数的形式在 setup 中定义,他将作为一个属性与 templatesetup 同级,语法还是和 Vue 2.X 一样。不同的是,setup 函数将接受一个对象作为参数,这个对象就是先前定义的 props,也就是说和 React 类似,但不同点在于 Vue 3 需要主动声明 props 中的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const Comp = {
props: {
count: Number,
name: {
type: String,
required: true
}
},
setup (props) {
onMounted(() => {
console.log(props.count)
console.log(props.name)
})
}
}

Data

Vue 3 中的响应式状态需要通过 value 函数进行包装,根本原因在于基本数据类型只有值,对这类变量进行修改后是无法追踪变化的,但将他们包装成一个对象,就能通过 defineProperty 或者 Proxy 的方式在值变动时进行切面编程。

Watch

1
2
3
4
5
6
7
8
9
const stop = watch(
[valueA, () => valueB.value],
([a, b], [prevA, prevB], onCleanUp) => {
console.log(`a is: ${a}`)
console.log(`b is: ${b}`)
}
)

stop()

在新版本中,watch 默认是 immediate 的,也就是说在数据源初始化时就会触发一轮 watch,并且 watch 函数将会返回一个 stop 函数,执行后可以移除相对应的观察者,这是旧版中做不到的,旧版只能够加一个标志和观察条件来实现相似的功能,无法完全停止监听。

更正一下,Vue 2 中的 $watch 函数也是能返回对应的 unwatch 函数的。

新版本在数据源上也进行了改变,一个 watch 函数能同时对多个数据源进行观察,并且提供了 onCleanUp 函数,在数据源发生了改变观察者被清除的时候被调用,主要功能是做一些取消操作,比如 Axios 的取消请求。

生命周期函数

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
onBeforeCreated(() => {

})

onCreated(() => {

})

onBeforeMounted(() => {

})

onMounted(() => {

})

onBeforeUpdated(() => {

})

onUpdated(() => {

})

onBeforeDestroyed(() => {

})

onDestroyed(() => {

})

这部分没什么好说的,就是原本的 Class 写法变成了 Function 写法,函数名前面加了个 on。

Provide / Inject

1
2
3
4
5
6
7
8
9
10
11
12
const sym = Symbol()

const Ancestor = {
provide: {
parent: this,
[sym]: 123
}
}

const Descendent = {
inject: ['parent', sym]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const sym = Symbol()

const Ancestor = {
provide({
parent: this,
[sym]: 123
})
}

const Descendent = {
setup() {
const parent = inject('parent')
const value = inject(sym)
}
}

先说一下这个功能本身,他实际上是一个父级到子级的依赖注入,父级显式设置提供的变量,子级通过变量名或者 Symbol 确定要注入的变量。

这里可以看得出来,provide 的语法没什么变化,主要是变成了 Function,而 inject 的写法就比 Vue 2.X 要灵活得多,Vue 2.X 提供的是组件全局的注入,而在 Vue 3 中可以通过函数进行局部注入