性能的提升
- 打包大小减少 按需加载 tree shake
- 初次渲染加快 重写虚拟 dom
- 内存使用减小 Proxy 代替 Object.defineProperty
解决的痛点
多行代码组件难维护
原来是一个功能在 data 在 computer 在 methods 中等等地方分开
现在是一个功能就是放在一个地方来开发
正所谓鸡蛋不能放在一个篮子里面
所有的 data, prop method 等数据都在一个篮子里面, 包死
Composition API
- ref, reactive
- computed, watch
- 新的生命周期函数
- 自定义 Hooks 像 React
- Teleport
- Suspense
- 全局 API 的优化和修改
- 更好的 TS 支持
API 的区别
原来是 options API 现在是 Composition API
// options API
data() {},
methods: {},
computed: {},
mounted() {},
xxx: {}
composition API
ref
使用
<div>
<h1>Count is :{{count}}</h1>
<h2>Double is {{double}}</h2>
<button @click="increase" >Click To Add Count</button>
<button @click="clearCount" >Click To Clear Count</button>
</div>
import { ref, computed } from 'vue'
export default {
name: "TestAdd",
setup() {
const count = ref(0)
const double = computed(() => count.value * 2)
const increase = () => {
count.value++
}
const clearCount = () => {
count.value = 0 // 在其他地方读取数据,需要在原数据中加一个 value属性
}
return {
count,
increase,
clearCount,
double
}
}
}
</script>
reactive
setup() {
const data = reactive({
count: 0,
increase: () => data.count++,
double: computed(() => data.count*2) // 这里double中computed回调使用了data的count,类型推论不了 在ts中加一个接口也可以
})
return {
data // 这里如果用 xx = data.xx 是不可以的。看下面的解释
// 在template中要 使用 data.count 等属性 不方便
}
}
toRefs
Vue3 使用 Proxy 之后,许多解构的东西都变得不响应式了,比如上面的 data 中,如果使用扩展运算符或者直接赋值,都是不可以用的
interface DataProps {
count: number;
increase: () => void;
double: number;
clearCount: () => void;
}
setup() {
const data: DataProps = reactive({
count: 0,
increase: ()=> data.count++,
double: computed(() => data.count * 2),
clearCount: () => {data.count = 0}
})
const refData = toRefs(data)
return {
...refData // 在template中可以不加data了
}
}
Object.defineProperty VS Proxy
Object,defineProperty(data, 'keyName', {
get() {},
ser() {}
})
new Proxy(data, {
get(keyName) {},
set(keyName, value) {}
})
生命周期差异
// Vue2 到 Vue3 的变化
beforeCreated -> use setup()
created -> use setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onbeforeUpdate
updated -> onUpdated
destroyed -> onUnmounted
activated -> onActivated
deactivated -> onDeactivated
errorCaptured -> onErrorCaptuere
// 新增
onRenderTracked
onRenderTriggered
用法:
setup() {
...
mounted(() => {
...actions
})
// 首先要导入,然后调用周期函数,传入回调,回调会被执行
// 这里有一点注意的是 ,不知道哪些是不在setup函数内的(仅我不知道)
}
onRenderTriggered
在渲染的时候 调试
Watch
Wacth和上面的生命周期 都是在setup函数中使用的
如果watch多个值,第一个参数传一个数组 [xxx, aaaa] 这样的
函数内有两个参数,第一个是监听的 ref或reactive函数生成的值 然后第二个是个回调 参数分别是新值和旧值
setup() {
const a = ref('')
watch(a, (newV,oldV) => {
// ...
})
}
在这里有一点 ,如果watch中的参数(不管是数组还是单个值) 都必须是响应式的
const daat = reactive({
count:0
})
上述data是响应式的,可以直接作为Watch的参数,但是data中的count是一个静态的数字类型,并不能响应式,如果想要响应式,可以这样
watch([a, () => data.count], (newV, oldV)=> {
// actions
})
// 可以做一个匿名函数来将它返回,这样,这个值也会变成响应式的了 (就是不知道为啥)
teleport
在template中使用 teleport标签
<teleport to="#model"></teleport>
然后在public中的index.html中 id为app的div下面,增加一个id为model的div
some 傻逼思考
// shared function
const isPromise = <T = any>(val: unknown): val is Promise<T> => {
return isObject(val) && isFunction(val.then) && isFunction(val.catch)
}
// resolveData function
if (__DEV__ && isPromise(data)) {
warn(
`data() returned a Promise - note data() cannot be async; If you ` +
`intend to perform data fetching before component renders, use ` +
`async setup() + <Suspense>.`
)
}
在Vue3源码中随便翻到的片段,让我不禁想到,如果在data函数中加入then和catch函数,是不是就会被判断成是一个Promise呢?
尝试了,不可以
因为data在ts中类型被定义好了,不可以有这些东西,
还挺牛逼
emit
首先 命名不能是驼峰,得是kebab-case
父节点的templa中 @emit-name 发布一个函数
然后子组件订阅的时候就会触发父组件中发布的函数
// parent.vue
html
button @emit-name="someFunc"
script
methods or setup function
setup() {
const someFunc = () {
// ... some action 这里的函数名字要和template中的名字一样
}
return {
someFunc
}
}
// child.vue
script
setup(props, context) {
ChildFunc() {
context.emit('emit-name') // 当触发了ChildFunc的时候,父组件的someFunc会被触发,执行someFunc的动作
}
}
与vue2的区别
快忘记了。。 先捋一捋