Vue3 生命周期
Vue3 与 Vue2 的生命周期没有很大的不同
Vue3 与 Vue2 对比:
总结:
其中 Composition API (通过组合式API的形式去使用生命周期钩子)即 on 打头的 都写在 setup() 函数中的
原来的不变的(通过配置项的形式使用生命周期钩子)可以写在 setup() 并列外面
这两种方式二选一,如果都存在,则 setup 中的(即 on 开头的) 会比写在 setup() 外面的先执行
CompositionAPI 的优势:
使用传统OptionsAPI(Vue2)中,新增或者修改一个需求,就需要分别在data,methods,computed里修改
Composition API (Vue3)的优势:我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起
通过 hook 来实现(把相关的数据,方法啥的放到一个 hook 中,要用的时候调用即可)
注意:使用组合式 API 记得要引入!
自定义 hook
hook:本质是一个函数,把 setup 函数中使用的 Composition API (比如 ref 函数、reactive 函数、计算属性与监视属性、生命周期等等)进行了封装,类似于 vue2.x 中的 mixin
自定义 hook 的优势:复用代码,让 setup 中的逻辑更清楚易懂
总结:
- 在 src 中创建一个 hook 文件夹,在此文件夹中创建 .js 文件,放要封装的函数
- 此函数记得要暴露出去,并且要有返回值(return)
- 哪个组件想用,引入就可以使用了
- 创建一个变量去接收此函数的返回值
- 模板中也可以直接使用该函数中的属性
toRef 与 toRefs
toRef:
作用:创建一个 ref 对象,其 value 指向另一个对象中的某个属性
语法:const name = toRef(person,'name')
应用:要将响应式对象中的某个属性单独提供给外部使用时
扩展:toRefs 与 toRef 功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)
例子:
<template>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<h2>薪资:{{job.j1.salary}}K</h2>
<button @click="name+='~'">修改姓名</button>
<button @click="age++">增长年龄</button>
<button @click="job.j1.salary++">涨薪</button>
</template>
<script>
import {reactive,toRefs} from 'vue'
export default {
name: 'Demo',
setup(){
// 数据
let person = reactive({
name:'张三',
age: 18,
job:{
j1:{
salary: 20
}
}
})
// 方法一 toRef(对象,'属性')
// return{
// name:toRef(person,'name'),
// age:toRef(person,'age'),
// salary:toRef(person.job.j1,'salary')
// }
//方法二 ...toRefs(对象)
return {
//因为对象中不能再跟一个对象,所以用解构,这样就把 person 中所有的属性都解析了
// 但是只能解析普通变量,对于对象类型的,还是要用 job.j1.salary
...toRefs(person)
}
}
}
</script>
其中 return name = toRef(person,'name'),相当于这里的 name 指向的就是 person 中的 name , 这样在模板字符中就只用写 name 而不是 person.name,用 toRef 可以把 person 对象中的对象再解析出来,比如 salary:toRef(person.job.j1,'salary')
toRefs 更方便, ...toRef(person),就可以把 person 中所有属性都解析出来,但是有一个问题,就是深层次的对象中的属性解析不出来,只解析第一层的属性,这样在模板字符中还是写job.j1.salary
shallowReactive与shallowRef
shallowReactive: 只处理对象最外层属性的响应式(浅响应式)
shallowRef: 只处理基本数据类型的响应式,不进行对象的响应式处理
什么时候用:
如果有一个对象数据,解构比较深,但变化时只是外层属性变化 ——> shallowReactive
如果有一个对象数据,后续功能不会修改该对象中的属性,而是生成新的对象来替换 ——> shallowRef
例子:
<template>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<h2>薪资:{{job.j1.salary}}K</h2>
<button @click="name+='~'">修改姓名</button>
<button @click="age++">增长年龄</button>
<button @click="job.j1.salary++">涨薪</button>
<hr>
<h2>{{sum.y}}</h2>
<button @click="sum.y++">点我加1</button>
<button @click="sum={y:888}">点我替换sum</button>
</template>
<script>
import {toRefs,shallowReactive,shallowRef} from 'vue'
export default {
name: 'Demo',
setup(){
// 数据
let person = shallowReactive({
name:'张三',
age: 18,
job:{
j1:{
salary: 20
}
}
})
let sum = shallowRef({
y:0
})
return {
...toRefs(person),
sum
}
}
}
</script>
其中生效的有修改 name 、age、替换 sum 的按钮
readonly与shallowReadonly
readonly: 让一个响应式数据变为只读的(深只读)
shallowReadonly: 让一个响应式数据变为只读的(浅只读)
应用场景:不希望数据被修改时
例子:
<template>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<h2>薪资:{{job.j1.salary}}K</h2>
<button @click="name+='~'">修改姓名</button>
<button @click="age++">增长年龄</button>
<button @click="job.j1.salary++">涨薪</button>
<hr>
<h2>{{sum.y}}</h2>
<button @click="sum.y++">点我加1</button>
<button @click="sum={y:888}">点我替换sum</button>
</template>
<script>
import {toRefs,reactive,shallowRef, shallowReadonly} from 'vue'
export default {
name: 'Demo',
setup(){
// 数据
let person = reactive({
name:'张三',
age: 18,
job:{
j1:{
salary: 20
}
}
})
// 深只读,就是全部都不能更改
// person = readonly(person)
// 浅只读,只有第一层不能修改,第二三层都可以修改
person = shallowReadonly(person)
let sum = shallowRef({
y:0
})
return {
...toRefs(person),
sum
}
}
}
</script>
toRaw与markRaw
toRaw:
作用:将一个由 reactive 生成的响应式对象转为普通对象
使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新
markRaw:
作用:标记一个对象,使其永远不会再成为响应式对象
应用场景:
1. 有些值不应被设置为响应式的,例如复杂的第三方类库等
2. 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能
例子:
<template>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<h2>薪资:{{job.j1.salary}}K</h2>
<h3>座驾信息:{{person.car}}</h3>
<button @click="name+='~'">修改姓名</button>
<button @click="age++">增长年龄</button>
<button @click="job.j1.salary++">涨薪</button>
<button @click="showPerson">输出最原始的person</button>
<button @click="addCar">给人添加一台车</button>
<button @click="person.car.name+='!'">换车名</button>
<button @click="changePrice">换价格</button>
<hr>
</template>
<script>
import {toRefs,reactive,toRaw, markRaw} from 'vue'
export default {
name: 'Demo',
setup(){
// 数据
let person = reactive({
name:'张三',
age: 18,
job:{
j1:{
salary: 20
}
}
})
function showPerson(){
const p = toRaw(person)
p.age++
console.log(p);
}
function addCar(){
let car = {name:'奔驰',price:40}
person.car = markRaw(car)
}
function changePrice(){
person.car.price++
console.log(person.car.price);
}
return {
person,
...toRefs(person),
showPerson,
addCar,
changePrice
}
}
}
</script>
customRef⭐
作用:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制
需要注意的点:创建自定义的 ref 必须要用到 customRef,来实现对数据的跟踪和触发
代码:
<template>
<input type="text" v-model="keyWord">
<h3>{{keyWord}}</h3>
</template>
<script>
import {customRef} from 'vue'
export default {
name: 'App',
setup(){
//自定义一个 ref——名为:myRef
function myRef(value,delay){
let timer
return customRef((track,trigger)=>{
return {
get(){
console.log(`有人从myRef这个容器中读取数据了,我把${value}`);
track() //通知Vue追踪value的变化(提前和get商量一下,让他认为这个value是有用的)
return value
},
set(newValue){
console.log(`有人把myRef这个容器中数据改为了:${newValue}`);
clearTimeout(timer)
timer = setTimeout(()=>{
value = newValue
trigger() //通知Vue去重新解析模板
},delay)
}
}
})
}
// let keyWord = ref('hello') //使用Vue提供的ref
let keyWord = myRef('hello',500) //使用程序员自定义的ref
return {keyWord}
}
}
</script>
其中,track 代表跟踪数据,用来告诉 get 输入的 value 是有用的,如果不写,修改后 get 不会重新执行,trigger 用来触发页面重新加载的,通知 Vue 去重新解析模板,在 set 里面放一个定时器可以实现防抖效果
provide与inject
作用:实现祖与后代组件间的通信
套路:父组件有一个 provide 选项来提供数据,后代组件有一个 inject 选项来开始使用这些数据
具体写法:
1.祖组件中:
setup(){
...
let car = reactive({name:'奔驰',price:'40w'})
provide('car',car)
...
}
2. 后代组件中:
setup(props,context){
...
const car = inject('car')
return {car}
...
}
一般用于祖孙间的传递,祖组件通过 provide('名字',数据) , 孙组件通过 inject('名字') 接收,就可以用了,父子间的传递用 props 就可以了
响应式数据的判断
isRef: 检查一个值是否为一个 ref 对象
isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理
Fragment 组件
在 Vue2 中:组件必须有一个根标签
在 Vue3 中:组件可以没有根标签,内部会将多个标签包含在一个 Fragment 虚拟元素中
好处:减少标签层级,减小内存占用
Teleport 组件 ⭐
Teleport 是一种能够将我们的组件 html 结构移动到指定位置的技术
<teleport to="移动位置">
<div v-if="isShow" class="mask">
<div class="dialog">
<h3>我是一个弹窗</h3>
<button @click="isShow = false">关闭弹窗</button>
</div>
</div>
<teleport>
比如在子子子组件中嵌套一个弹框,如果只是单单的放在子组件中,那么出现的时候就会把该弹框的所有父组件都撑大,不太好
如果用 Teleport 包裹,其中 to 代表以什么为父组件(比如以 body 为父组件),进行定位设置,那么弹框就会脱离它的父组件,可以直接跑到页面中间显示
Suspense
等待异步组件时渲染一些额外内容,让应用有更好的用户体验
使用步骤:
※ 异步引入组件
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child')) //异步引入
※ 使用 Suspense 包裹组件,并配置好 default 与 fallback
<template>
<div class="app">
<h3>我是App组件</h3>
<Suspense>
<!-- 真正要显示的内容放在 v-slot:default 中 -->
<template v-slot:default>
<Child/>
</template>
<!-- 页面还没加载完毕要显示的内容放在 fallback 中 -->
<template v-slot:fallback>
<h3>稍等,加载中...</h3>
</template>
</Suspense>
</div>
</template>
※ 要引入的组件中的代码
<template>
<div class="child">
<h3>我是Child组件</h3>
{{sum}}
</div>
</template>
<script>
import {ref} from 'vue'
export default {
name:'Child',
setup(){
let sum = ref(0)
return new Promise((resolve)=>{
setTimeout(()=>{
resolve({sum}) //延迟1秒才出现
},1000)
})
}
}
</script>
如果静态引入,那么就会导致最内层的元素没有加载完,那么所有的元素都不会显示,相当于就等待最慢的那一个
如果用异步引入的话,异步引入的组件会后出现,但是用户会不知道还有内容,出现的时候会有抖动,所以就要用到 Suspense, 把出现的组件放到 Suspense 标签中,相当于插槽
也可以手动调慢页面加载,写在Child 组件中,通过返回一个 Promise(只有异步才能用 Promise ,否则没效果)
以上就是全部内容啦~如果没看懂的同学,下去要多多复习噢