# Composition API 初探
# 好处
Vue3 的 break changing 就是 composition api, 它打破了以往用 options 构建 Vue 实例的方式. 使用函数来代替. 这次 Vue3 升级的目的是什么呢?官方给出的解释是:
# 1. 代码组合更好
过去使用 options(data
, methods
, computed
)来组织代码, 那么关于一个逻辑的代码就要被分散到各个 options 里面. 随着项目变得越来越大,
整个结构就会变得不可维护, 可读性也很差.
# 2. 便于逻辑分解和复用
Vue 2.x 里的 Mixins 可以为我们提供抽离逻辑并复用的功能. 可是一旦 Mixins 的数量太多, 就会导致一些问题:
- 命名空间冲突
- 模板数据来源不清晰
Vue3 将复用的逻辑以函数的形式组织起来, 在函数体里同样可以使用响应式, 生命周期钩子. 然后用 ES module 来 import 和 export(因此可以 tree shaking). 这样的写法就解决了上面 Mixins 的两个缺点, 也不会带来像高阶组件附带的额外性能开销.
# 3. 全面支持 TypeScript, 增强类型推断
Vue2.x 里面对 TS 的支持是很不友好的. Vue2.x 使用this
作为上下文来暴露属性, 而this
就是一个黑魔法一样的存在: methods
里的this
指向的不是
methods
而是 Vue 实例, data
里的数据不是用vm.data.xxx
来访问而是直接暴露为实例的属性(vm.xxx
).这些给类型推断会带来极大的麻烦. Vue2.x 的 API 也
根本不是为了类型推断而设计的. 但是从现在的前端趋势看, 去面向对象化的潮流正在兴起, 函数式编程开始崛起(React Hooks, Vue 3 Composition API). TypeScript 对函数式的支持是天然友好的. 我不知道是谁带动了谁, 但是在 2020 年, TS 和函数式是前端必学的技能.
# 起步
# 代码
<template>
<div class="hello">
<h1>My Event</h1>
<p>Capacity: {{ capacity }}</p>
<button @click="increaseCapacity()">Increase Capacity</button>
<button @click="increment">
Count is {{ state.count }}, double is: {{ state.double }}
</button>
</div>
</template>
<script>
import { ref } from "@vue/composition-api"; //tree shaking
export default {
setup() {
// run after beforeCreated and before created
const capacity = ref(3); // refer to reactive reference, return a reactive object
console.log(capacity);
const state = reactive({
count: 0,
double: computed(() => state.count * 2) // can't be primitive type
});
console.log("state", state);
function increaseCapacity() {
capacity.value++;
}
function increment() {
state.count++;
}
return { capacity, state, increment, increaseCapacity }; // return objects & methods that template needs to access
}
};
</script>
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
# 引入 composition api
import {ref, reactive, computed} from "@vue/composition-api"
为了使用 tree shaking, 这里使用 import 方式引入
# setup()
setup
函数是 Vue3 新增的 hook, 它在beforeCreated
之后又在created
之前的阶段执行.
# ref()
调用ref()
后, 传入的参数会被修改为一个 reactive object,并且添加getter
,setter
, 使其在改变时会被监听到变动进而更新视图. 参数也被放入到 reactive object 的value
属性里. 我们在控制台可以看到capacity
是什么:
# reactive()&computed()
reactive
接受一个对象, 返回一个 reactive object. 它在内部可以使用computed(fn)
, 但是内部不能使用值类型. computed
内部伪代码如下:
function computed(getter) {
let value;
watch(() => {
value = getter();
});
return value;
}
2
3
4
5
6
7
如果是值类型, 那么computed
赋值的对象就会重新使用一个内存空间存放computed
的返回值. 因此, 对赋值对象的修改不会被依赖追踪发现. 而对象是引用类型, 对它的修改也会导致引用它的对象也发生变化.
实际上, ref()
是reactive()
的 fallback, 由于值类型不能在reactive()
里使用, 所以 Vue 提供了ref()
给我们处理值类型的数据
# return
最后我们要将模板里需要的对象和方法返回, 使模板能够访问到.
# 生命周期
Vue3 里的生命周期钩子只能在setup()
里使用, 使用方式是接受一个回调:
import {onMounted} from "vue"
setup(){
onMounted(()=>{
console.log('component is mounted')
})
}
2
3
4
5
6
← 数据响应式 Virtual DOM的优化 →