一、vue3的生命周期
vue3.0可以继续使用vue2.x中的生命周期钩子,但是有两个被更名
- beforeDestory改名为beforeUmount
- destoryed改名为unmounted
vue3.0也提供了Composition API形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
- berforeCreate ====> setup()
- created ========> setup()
- beforeMount =====> onBeforeMount()
- mounted ========> onMounted
- beforeUpdate =====> onBeforeUpdate
- updated =========> onUpdated
- beforeUnmount ====> onBeforeUnmount
- unmounted =======> onUnmounted
组合式API生命周期在前,配置项生命周期再后,但是一般要么用组合式的要么用配置式,不建议混合使用。
<template>
<div>
<h2>求和:{{ sum }}</h2>
<button @click="sum++" >增加数据</button>
</div>
</template>
<script>
import { ref,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted } from "vue";
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "Demo",
// emits:['hello'],
setup() {
console.log("setup");
let sum = ref(0)
//通过组合式API形式使用生命周期
onBeforeMount(() => {
console.log("onBeforeMount-----");
})
onMounted(() => {
console.log("onMounted-----");
})
onBeforeUpdate(() => {
console.log("onBeforeUpdate-----");
})
onUpdated(() => {
console.log("onUpdated-----");
})
onBeforeUnmount(() => {
console.log("onBeforeUnmount-----");
})
onUnmounted(() => {
console.log("onUnmounted-----");
})
return {
sum,
};
},
//#region
//通过配置项形式使用生命周期
beforeCreate() {
console.log("beforeCreate");
},
created() {
console.log("created");
},
beforeMount() {
console.log("beforeMount");
},
mounted() {
console.log("mounted");
},
beforeUpdate() {
console.log("beforeUpdate");
},
updated() {
console.log("updated");
},
beforeUnmount() {
console.log("beforeUnmount");
},
unmounted() {
console.log("unmounted");
},
//#endregion
};
</script>
二、自定义hook函数
什么式hook?本质是一个函数,把setup函数中使用的Composition API进行了封装。 类似于vue2.x中的mixin。自定义hook的优势:复用代码,让setup中的逻辑更清晰易懂。
import { reactive, onMounted, onBeforeUnmount } from "vue";
export default function usePoint() {
// 创建一个响应式对象来存储x和y坐标
let point = reactive({
x: 0,
y: 0,
});
// 当发生点击事件时,更新x和y坐标的函数
function onSavePoint(event) {
point.x = event.pageX;
point.y = event.pageY;
}
// 当组件挂载时,添加点击事件监听器
onMounted(() => {
window.addEventListener("click", onSavePoint);
});
// 当组件即将卸载时,移除点击事件监听器
onBeforeUnmount(() => {
window.removeEventListener("click", onSavePoint);
});
// 返回响应式的point对象
return point;
}
<template>
<div>
<h2>求和:{{ sum }}</h2>
<button @click="sum++" >增加数据</button>
<h2>点击的位置:x:{{ point.x }},y:{{ point.y }}</h2>
</div>
</template>
<script>
import usePoint from "../hooks/usePoint"
import { ref} from "vue";
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "Demo",
setup() {
console.log("setup");
let sum = ref(0)
let point = usePoint()
return {
sum,
point
};
},
};
</script>
三、toRef和toRefs
- 作用:创建一个ref对象,其value值指向另一个对象中的某个属性
- 语法:const name = toRef(person,'name')
- 应用:要将响应式对象中的某个属性单独提供给外部使用
- 扩展:toRefs与toRef功能一致,但可以批量创建多个ref对象,语法:toRefs(person)
<template>
<div>
<h2>求和:{{ sum }}</h2>
<button @click="sum++" >增加数据</button>
<h3>姓名:{{ name }} 年龄:{{ age }} 薪资:{{ salary }}</h3>
</div>
<button @click="name += '~';age++;salary++ " >修改person</button>
<hr>
{{ human }}
<h3>
代号:{{ idName }} 爱好:{{ hobby }} 工作类型:{{ job.type.jobName }}
</h3>
<button @click="idName += '~';hobby += '~';job.type.jobName += '~' " >修改human</button>
</template>
<script>
import { ref,reactive,toRef,toRefs } from "vue";
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "Demo",
setup() {
console.log("setup");
let sum = ref(0)
let person = reactive({
name:'张三',
age:18,
job:{
salary:20000
}
})
let human = reactive({
idName:'李四',
hobby:'篮球、足球',
job:{
type:{
jobName:'前端工程师',
}
}
})
return {
human,
sum,
name: toRef(person,'name'),
age: toRef(person,'age'),
salary:toRef(person.job,'salary'),
...toRefs(human)
};
},
};
</script>
四、其他Composition API
1、shallowReactive与shallowRef
- shallowReactive:只处理对象最外层属性的响应式(浅响应)
- shallowRef:只处理基本数据类型的响应式,不进行对象的响应式处理
- 什么时候用
如果有一个对象数据,结构比较深,但变化时张三外层属性变化===>shallowReactive
如果有一个对象数据,后续功能不会修改该对象中的属性,而是生成新的对象来替换===>shallowRef
<template>
<div>
<h2>求和:{{ sum }}</h2>
<h2>x:{{ x.y }}</h2>
<!-- 可以修改 -->
<button @click="sum++" >增加数据</button>
<!-- 无法修改 -->
<button @click="x.y++" >修改x</button>
</div>
<hr>
{{ human }}
<h3>
代号:{{ idName }} 爱好:{{ hobby }} 工作类型:{{ work.type.jobName }}
</h3>
<!-- 离谱,这里的jobName也会跟着改变 -->
<button @click="idName += '~';hobby += '~';work.type.jobName ++" >修改human</button>
<!-- 但是这里的时正常的jobName无法修改,符合reactiveRef的用法 -->
<button @click="work.type.jobName ++" > 修改work </button>
</template>
<script>
import { toRefs ,shallowReactive,shallowRef} from "vue";
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "Demo",
setup() {
let sum = shallowRef(0)
let x = shallowRef({
y:1
})
let human = shallowReactive({
idName:'李四',
hobby:'篮球、足球',
work:{
type:{
jobName: 300,
}
}
})
return {
x,
...toRefs(human),
sum,
};
},
};
</script>
2、readonly和shallowReadonly
- readonly:让一个响应式数据变为只读的(深只读,对象深度内容也无法修改)
- shallowReadonly:让一个响应式数据变为只读的(浅只读,对象深度内容可修改)
- 应用场景:不希望数据被修改时
<template>
<div>
<h2>求和:{{ sum }}</h2>
<button @click="sum++" >增加数据</button>
</div>
<hr>
<h3>
代号:{{ idName }} 爱好:{{ hobby }} 工作类型:{{ work.type.jobName }}
</h3>
<button @click="idName += '~';hobby += '~';work.type.jobName ++" >修改human</button>
<button @click="work.type.jobName ++" > 修改work </button>
</template>
<script>
import { toRefs ,ref,reactive,readonly,shallowReadonly} from "vue";
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "Demo",
setup() {
let sum = ref(0)
let human = reactive({
idName:'李四',
hobby:'篮球、足球',
work:{
type:{
jobName: 300,
}
}
})
sum = readonly(sum)//无法修改
// human = readonly(human) //无法修改
human = shallowReadonly(human)//jobName还是改变,不支持嵌套内容修改
return {
...toRefs(human),
sum,
};
},
};
</script>
3、toRaw和markRaw
toRaw:
作用:将一个由reactive生成的响应式对象转为普通对象。
使用场景:由于读取响应式对象对应的普通对象,对这个普遍对象的所有操作,不会引起页面更新。
markRaw:
作用:标记一个对象,使其永远不会再成为响应式对象。
应用场景:1、有些值不应该设置为响应式,例如复杂的第三方库等。
2、当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
<template>
<h3> 代号:{{ idName }} </h3>
<h3>爱好:{{ hobby }} </h3>
<h3>工作类型:{{ work.type.jobName }}</h3>
<h3>车数据{{ human.car }}</h3>
<button @click="idName += '~';hobby += '~';work.type.jobName ++" >修改human</button>
<button @click="getOriginHuman" > 获取最原始human </button>
<button @click="add" >添加车</button>
<button @click="changePrice" v-if="human.car" >改车钱</button>
</template>
<script>
import { toRefs ,reactive,toRaw,markRaw} from "vue";
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "Demo",
setup() {
let human = reactive({
idName:'李四',
hobby:'唱跳rap',
work:{
type:{
jobName: 300,
}
}
})
function getOriginHuman(){
human = toRaw(human)
console.log(human);
}
function add(){
let car = {
name:'奔驰',
price: 10000
}
human.car = markRaw(car)//添加之后car属性不再时响应式
}
function changePrice(){
human.car.price ++
console.log(human.car.price);//修改的只是普通值,不是响应式,也不参与页面更新
}
return {
human,
...toRefs(human),
getOriginHuman,
add,
changePrice
};
},
};
</script>
4、customRef,重点理解示例
作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显示控制。
<template>
<div>
<input type="text" v-model="content" >
{{ content }}
</div>
</template>
<script>
import { customRef } from 'vue';
export default {
name: 'App',
setup(){
function myRef(value,delay){
let timer
return customRef((track,trigger)=>{
return {
get(){
console.log(`有人读取了数据,${value}`);
track()//通知vue追踪value的变化(提前和get商量一下,让其认为这个value有用并执行get)
return value
},
set(newValue){
console.log(`有人修改了数据,${newValue}`);
clearTimeout(timer)
//每次修改数据都会清除上一次的定时器,对应一个变量timer防止快速添加数据时,显示内容会有bug,防抖动
timer = setTimeout(() => {
value = newValue
trigger() //通知vue重新解析模板
}, delay);
}
}
})
}
let content = myRef('hello',1000)//自定义Ref
return {
content
}
}
}
</script>
5、provide与inject
- 作用:实现祖与后代组件间通信,子组件也可以注入但是一般用props传递数据
- 套路:父组件有一个provide选项来提供数据,后代组件有一个inject选项来开始使用这些数据
- 具体写法:
1、祖组件中:
setup(){
.....
let car = reactive({name:'奔驰',price:'200000'})
provide('car',car)//两个参数,(命名,传入数据)
}
2、孙组件中:
setup(){
....
const car = inject('car')
return {car}
}
6、响应式数据的判断
- isRef:检查一个值是否为ref对象
- isReactive:检查一个对象是否由reactive创建的响应式代理
- isReadOnly:检查一个对象是否由readonly创建的只读代理
- isProxy:检查一个对象是否由reactive或者readonly方法创建的代理
<template>
<div class="app" >
<h1>测试判断</h1>
</div>
</template>
<script>
import { reactive,ref,readonly, isReactive,isReadonly,isRef, isProxy } from 'vue'
export default {
name: 'App',
setup(){
let sum = ref(0)
let person = reactive({
name:'张三',
age:18,
})
let car = readonly({
name:'奔驰',
price:40
})
let person2 = readonly(person)
console.log(isRef(sum)); //true
console.log(isReactive(person));//true
console.log(isReadonly(car));//true
console.log(isReadonly(person2));//true
console.log(isProxy(person));//true
console.log(isProxy(person2));//true
console.log(isProxy(sum));//false
return {person,sum,car}
}
}
</script>
<style>
</style>
7、Composition API的优势
- option API存在的问题:使用传统Option API中,新增或修改一个需求,就需要分别在data,method,computed里修改
- Composition API优势:我们可以更加优雅的组件我们的代码,函数。让相关功能的代码更加有序的组织在一起。
五、新的组件
1、Fragment
- 在Vue2中:组件必须有一个根标签
- 在Vue3中:组件可以没有根标签,内部会将多个标签包含在一个Fragment虚拟元素中
- 好处:减少标签层级,减少内存使用
2、Teleport(适用弹窗场景)
Teleport是一种能够将我们的组件html结构移动到指定位置的技术。
<teleport to='body(移动位置,只相对于body;不止是body,也可以使用选择器)' >
<div v-if="show" class="mask">
<div class="dialog" >
<h3>我是一个弹窗</h3>
<button @click="isShow=false" >关闭弹窗</button>
</div>
</div>
</teleport>
3、Suspense
等待异步组件渲染一些额外内容,让应用有更好的用户体验。(从用户角度想是有点像骨架屏,等待的组件可能数据量大,短时间内无法显示页面,但为了更好的用户体验,先默认显示出一个指定的加载状态,组件加载出来了,这个加载状态也就消失)
- 使用步骤:
异步引入组件
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child.Vue'))
使用Suspense包裹组件,并配置好default与fallback
父组件
<template>
<h1>父组件</h1>
<Suspense>
<template v-slot:default>
<Child/>
</template>
<template v-slot:fallback >
<h4>加载中...</h4>
</template>
</Suspense>
</template>
<script>
// import Child from "./components/Child.vue";//静态引入
import { defineAsyncComponent } from 'vue';
const Child = defineAsyncComponent(() => import('./components/Child.vue'))//动态引入即异步引入(Child这个组件比App出现的晚)
export default {
name: 'App',
components:{
Child
},
setup(){
}
}
</script>
子组件
<template>
<div>
<h1>我是孩子节点{{ sum }}</h1>
</div>
</template>
<script>
import { ref } from "vue";
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "Child",
//配合父组件Suspense
async setup() {
let sum = ref(111);
let p = new Promise((resolve) => {
setTimeout(() => {
resolve({ sum });
}, 500);
});
return await p;
},
};
</script>
六、其他改变
- 将全局的API,即Vue.XXX调整到应用实例(app)上
| 2.x全局API(Vue) | 3.x实例API(app) |
|---|---|
| Vue.config.xxx | app.config.xxx |
| Vue.config.productionTip(生产提示) | 移除 |
| Vue.component | app.component |
| Vue.directive | app.directive |
| Vue.mixin | app.mixin |
| Vue.use | app.use |
| Vue.prototype | app.config.globalProperties |
- data选项应始终声明为一个函数
- 过度类名的更改
- 移除keyCode作为v-on的修饰符,同时也不再支持config.keyCodes
- 移除v-on.native修饰符
- 移除过滤器(filter)
- .....剩下的找官方文档