1. vue3的生命周期
vue3
的生命周期一般有2种形式写法,一种是基于vue2
的options API
的写法,一种是vue3
特有的Composition API
options API的生命周期
基本同vue2
的生命周期基础,只是为了与生命周期beforeCreate
和created
对应,将beforeDestroy
和destroyed
更名为beforeUnmount
和unmounted
,使用方法同vue2
生命周期
{{msg}}
composition API的生命周期
composition API
的生命周期钩子函数是写在setup
函数中的,它所有生命周期是在vue2生命周期名字前加on
,且必须先导入才可使用
在这种写法中,是没有onBeforeCreate
和onCreated
周期的,setup
等同于(或者说是介于)这两个生命周期
composition API生命周期
{{msg}}
2. 如何理解 Composition API 和 options API ?
Composition API
带来了什么:
- 更好的代码组织
- 更好的逻辑复用,避免
mixins
混入时带来的命名冲突和维护困难问题 - 更好的类型推导
options API
使用options API
,当代码很多时,即当data, watch, methods
等有很多内容时,业务逻辑比较复杂,我们需要修改一部分时,可能要分别到data/methods/模板
中对应修改,可能需要我们在页面反复横跳来修改,逻辑块会比较散乱。
Composition API
Composition API
则会将业务相关的部分整合到一起,作为一个模块,当要修改,统一到一处修改,代码看起来会更有条理
它包含的内容包括:
- reactive
- ref相关(ref, toRef, toRefs,后面会具体介绍)
- readonly
- watch和watchEffect
- setup
- 生命周期钩子函数
两者的选择:
- 不建议共用,否则容易引起混乱(思路、组织方式、写法都不太一样)
- 小型项目,业务逻辑简单的,建议用
options API
,对新手也比较友好 - 中大型项目、逻辑复杂,建议使用
Composition API
Composition API
它属于高阶技巧,不是基础必会的,有一定的学习成本,是为了解决复杂业务逻辑而设计,就像hooks
在React
中的地位一样
3. 如何理解ref,toRef,toRefs
ref
- 通过
ref
方式创建响应式的值类型,并赋予初始值,并通过.value
的方式获取值或修改值 - 通过
reactive
方式创建响应式的引用类型,并赋予初始值,修改和获取方式同普通对象一样 - 除了以上两种用法,还可以使用
ref
来声明dom
元素,也就是类似vue2
中的用法
ref demo
{{nameRef}}今年{{ageRef}}岁了
他喜欢{{hobbies.type}}
PS: 小建议,
ref
定义的数据可以加个Ref
后缀,这样就能区分ref
和reactive
定义的变量了
toRef
看定义有点绕
- 针对一个响应式对象(
reactive
封装)的属性prop
- 通过
toRef
创建一个ref
对象,这个ref
对象和reactive
对象的某属性两者保持引用关系
注意,如果toRef是通过普通对象来生成ref
对象,那么普通对象和ref
对象都将不是响应式的
toRef demo
小花今年 - {{ageRef}}岁 - {{state.age}}岁
toRefs
- 将响应式对象(
reactive
)的所有属性prop
,转换为对应prop
名字的ref
对象 - 两者保持引用关系
toRef demo
{{name}}今年 - {{age}}岁
应用:
当使用composition API时,抽象出一个模块,使用toRefs
返回响应式对象,这样,在接收的时候,我们就可以使用解构的方式获取到对象里面的内容,这也是比较符合我们常用的方式
// 封装一个模块,使用toRefs导出对象
export function useFeature() {
const state = reactive({
x: 1,
y: 2
})
// ...
return toRefs(state)
}
// 导入时,可以使用解构方式
import { useFeature } from './features'
export default {
setup() {
// 可以在不丢失响应式的情况下解构
const { x, y } = useFeature()
return { x, y }
}
}
ref, toRef, toRefs 使用小结:
- 用
reactive
做对象的响应式,用ref
做值类型的响应式 - setup中返回
toRefs(state)
,或toRef(state, prop)
-
ref
变量命名建议用xxxRef
- 合成函数返回响应式对象时,使用
toRefs
为什么需要 ref ?
- 如果没有
ref
,普通的值类型定义,没法做响应式 -
computed,setup,合成函数
,都有可能返回值类型,要保证其返回是响应式的 - 如果vue不定义
ref
,用户可能会自己造ref
,反而更加混乱
为什么需要.value ?
-
ref
是一个对象(保证响应式),value
用来存储值 - 通过
.value
属性的get
和set
实现响应式 - 用于模板、
reactive
时,不需要.value
,这是因为vue
编译会自动识别,其他情况则需要使用
为什么需要 toRef 和 toRefs ?
- 目的:为了不丢失响应式的情况下,把对象数据分散、扩散(或者说是解构)
- 前提:针对的是响应式对象(reactive封装的对象)
- 本质:不创建响应式(创建是ref和reactive的事),而是延续响应式
4. watch和watchEffect的区别
-
watch
和watchEffect
都可以监听data
的变化 -
watch
需要指定监听的属性,默认初始时不会触发,如果初始要触发,需要配置immediate: true
-
watchEffect
是不需要指定监听的属性,而是自动监听其用到的属性,它初始化时,一定会执行一次,这是为了收集要监听的属性
watch 的使用
numberRef: {{numberRef}}
{{name}}-{{age}}
// watchEffect监听
watchEffect(() => {
console.log('watchEffect');
console.log(numberRef.value);
console.log(state.age);
})
5. 在setup中怎么获取组件实例
- 在
setup
和其它compostion API
中没有this
- 如果一定要获取,要使用
getCurrentInstance
获取,并且在挂载后才可获取数据 - 如果是
options API
,则可以像vue2
一样正常使用this
get instance
6. vue3升级了哪些重要的功能
参考官网升迁指南
createApp
// vue2
const app = new Vue({/*options*/})
Vue.use(/*...*/)
Vue.mixin(/*...*/)
Vue.component(/*...*/)
Vue.directive(/*...*/)
// vue3
const app = Vue.createApp({/*options*/})
app.use(/*...*/)
app.mixin(/*...*/)
app.component(/*...*/)
app.directive(/*...*/)
emits属性
- 在
setup
中可以使用emit
向父组件发出事件 - 在子组件中,需要
emits
声明向父组件发出的事件集合
parent
多事件处理
fragment
vue2
只允许template
中只有一个元素,如果多个元素,必须用一个元素包裹
vue3
则允许template
中可以直接有多个元素,这样就可以减少dom
层级
移除.sync
vue2中的.sync
vue2
中使用.sync
是对以下语句的语法糖,父组件通过v-bind:xxx.sync='xxx'
来向子组件说明这个属性是双向绑定的,子组件中通过$emit('update:xxx', newVal)
来更新这个值
在vue3
中,废除了.sync
的写法,换成一种更具有语义的写法v-model:xxx
,在父组件中使用v-model:xxx
方式说明这个属性是双向绑定的,子组件中通过$emit('update:xxx', newVal)
来更新这个值
{{name}}-{{age}}
异步组件的导入方式不一样
vue2
: child: () => import('child.vue')
vue3
:需要defineAsyncComponent
导入,child: defineAsyncComponent(() => import('child.vue'))
teleport
teleport
将我们的模板移动到DOM
中 Vue app
之外的其他位置,比如可以使用teleport
标签将组件在body
层
这是放在当前组件下的内容
假设这是个弹窗,直接放到body下