vue3中没有了 beforeDestroy 和 destroyed 两个钩子函数,取而代之的是 beforeUnmount 和 unmounted ,卸载前和卸载后的两个钩子函数。
Vue3 配置项的钩子函数如下:
<template>
<h2>当前求和为:{{sum}}</h2>
<button @click="sum++">+1操作</button>
</template>
<script>
import {ref,reactive,watch,watchEffect} from 'vue'
export default {
name:'Demo',
setup(){
//数据
let sum = ref(0)
return {
sum
}
},
//通过配置项的形式使用生命周期钩子函数
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')
}
}
</script>
Vue3也提供了Composition API 组合式API形式的生命周期钩子:
同样开始要先引入对应钩子的api:
<template>
<h2>当前求和为:{{sum}}</h2>
<button @click="sum++">+1操作</button>
</template>
<script>
import {ref,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'
export default {
name:'Demo',
setup(){
//数据
let sum = ref(0)
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
}
},
}
</script>
如果以上的两种都配置了,组合式的钩子比配置项的钩子优先级高,同一个钩子都是先执行组合式的钩子在执行配置项的钩子。
什么是hook?
实现一个鼠标点击显示坐标的功能:
<template>
<h2>当前点击时鼠标的坐标为:x:{{point.x}}, y:{{point.y}}</h2>
</template>
<script>
import {ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, reactive} from 'vue'
export default {
name:'Demo',
setup(){
//数据
let sum = ref(0)
let point = reactive({
x:0,
y:0
})
//获取鼠标点击的xy坐标
function savePoint(event){
point.x = event.pageX
point.y = event.pageY
console.log(event.pageX,event.pageY)
}
//给window添加一个点击事件,获取鼠标当前的位置!
onMounted(()=>{
window.addEventListener('click',savePoint)
})
//组件被卸载之前,将window的事件移除
onUnmounted(()=>{
window.removeEventListener('click',savePoint)
})
return {
point
}
},
}
</script>
一般我们再src下面创建一个hooks目录,在里面创建useXxx.js的文件,作为钩子函数。
这样我们就可以将上面的那些功能全部拿出去,单独写一个js文件暴露出来,谁要用,谁引用,这就和钩子组合式API一样。
import {onMounted, onUnmounted, reactive} from "vue";
export default function (){
//实现鼠标打点相关的数据
let point = reactive({
x:0,
y:0
})
//实现鼠标打点相关的方法
function savePoint(event){
point.x = event.pageX
point.y = event.pageY
console.log(event.pageX,event.pageY)
}
//实现鼠标打点相关的钩子声明周期
onMounted(()=>{
window.addEventListener('click',savePoint)
})
//组件被卸载之前,将window的事件移除
onUnmounted(()=>{
window.removeEventListener('click',savePoint)
})
return point
}
其实hook就是为了复用代码。
同样使用toRef和toRefs前也需要导入对应api:
import {reactive,toRef,toRefs} from 'vue'
如下图中的name1 和 name2 :
toRef处理一个数据,而toRefs就能处理多个,写法如下:
let a = {
//在一个对象里面,通过...展开另外一个对象的写法
...toRefs(person),
}
//这种...写法让a对象扩展了toRefs(person)的属性
<template>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<h2>薪资:{{job.j1.salary}}</h2>
<button @click="name += '~'">修改姓名</button>
<button @click="age++">增长年龄</button>
<button @click="job.j1.salary++">涨薪</button>
</template>
<script>
import {reactive,toRef,toRefs} from 'vue'
export default {
name:'Demo',
setup(){
//数据
let person = reactive({
name:'张三',
age:18,
job:{
j1:{
salary: 20
}
}
})
//将person里面的所有属性全部代理!
const x = toRefs(person)
return {
//在一个对象里面,通过...展开另外一个对象的写法
...toRefs(person),
}
}
}
</script>
总结:
shallowReactive函数只是考虑第一层响应式:
//shallow是浅的的意思,就是仅第一层能够有响应式,而job下面的这些对象就没有响应式!
//shallowReactive只考虑也就是只考虑第一层
let person = shallowReactive({
name:'张三',
age:18,
job:{
j1:{
salary: 20
}
}
})
shallowRef函数,会去处理基本数据类型的响应式,不会去处理对象类型的响应式:
//shallowRef函数不会去处理对象类型的响应式!
//ref函数会去处理对象的响应式!
let x = shallowRef({
y:0
})
原因如下:
踩坑经历:
readonly函数就是让一个响应式数据变为只读(深只读)。
shallowReadonly函数让一个响应式数据变为只读的(浅只读)。
应用场景:就是不希望数据被修改时!
注意点 - 页面数据没有变化有两种情况:
而 readOnly 以及 shallowReadonly 函数是属于第二种情况,数据压根就不会变化的!
let person = reactive({
name:'张三',
age:18,
job:{
j1:{
salary: 20
}
}
})
//person里面所有的person都不能修改!
//person = readonly(person)
//浅可读
let person2 = shallowReadonly(person)
//person2就是浅可读的,这样的喊出就是别人没办法修改person2,但是却可以修改person,达到这样的一个效果。
raw英文直译:生的,未加工的。
toRaw作用:将一个reactive生成的响应式对象转为普通对象。
markRaw作用:标记一个对象,使其永远不会再成为响应式对象。
toRaw函数的使用:
markRaw函数 使用:
总结:
customRef 函数作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显式控制。
使用customRef函数,必须熟练使用customRef函数的两个参数track和trigger:
以下实现了一个定时器防抖效果的操作:
<template>
<input type="text" v-model="keyWord">
<h3>{{keyWord}}</h3>
</template>
<script>
import {ref,customRef} from 'vue'
export default {
name:'App',
setup(){
//自定义一个ref--名为:myRef
function myRef(value,delay){
let timer
//customref函数有两个参数:track , trigger
//trigger是个函数,一般再set中使用,通知vue重新解析模板
//track也是个函数,通知Vue追踪数据(return的值)的变化
return customRef((track, trigger)=>{
return {
//读,走get
get(){
console.log(`有人从myRef容器中读取了数据,${value}`)
track()//通知Vue追踪value的变化(相当于提前和get商量一下,让他任务这个value有用的!)
return value
},
//改,走set
set(newValue){
console.log(`有人改变了myRef容器中读取了数据,${newValue}`)
//先清除定时器,在设置定时器,这样就做成了防抖效果。
clearTimeout(timer)
timer = setTimeout(()=>{
//修改get用到的value
value = newValue
},delay)
trigger()//通知Vue重新解析模板
}
}
})
}
//使用自定义的ref
let keyWord = myRef('hello',500)
return {
keyWord
}
},
}
</script>
provide英文直译:提供。
inject英文直译:注入。
作用:实现祖孙组件间通信(祖和孙之间,隔了一个父)。
teleport组件作用:teleport是一种能够将我们的组件html结构移动到指定位置的计数。
使用teleport组件实现一个弹框效果:
<template>
<div>
<button @click="isShow=true">点我弹个窗</button>
<!--定位到id为itholmes的标签里面,也可以定位元素。-->
<teleport to="#itholmes">
<div v-if="isShow" class="mask">
<div class="dialog">
<h3>我是一个弹窗</h3>
<h4>一些内容</h4>
<h4>一些内容</h4>
<h4>一些内容</h4>
<button @click="isShow=false">关闭弹窗</button>
</div>
</div>
</teleport>
</div>
</template>
<script>
import {ref} from 'vue'
export default {
name: 'Dialog',
setup(){
let isShow = ref(false)
return {
isShow
}
}
}
</script>
<style>
.dialog{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
text-align: center;
width: 300px;
height: 300px;
background-color: green;
}
/*对话框后面的遮蔽框*/
.mask{
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0,0,0,0.5);
}
</style>
Suspense英文直译:悬念,悬疑。
Suspense组件作用就是等待异步组件渲染一些额外的环境,增加用户体验。
静态引入:
<template>
<div class="app">
<h3>我是App(祖)</h3>
<Child></Child>
</div>
</template>
<script>
//通过这种方式引入的Child组件,App会等child组件加载完成后,再加载
import Child from '@/components/Child'
export default {
name:'App',
components:{
Child
},
}
</script>
<style>
.app{
background-color: gray;
padding: 10px;
}
</style>
异步引入:
<template>
<div class="app">
<h3>我是App(祖)</h3>
<Child></Child>
</div>
</template>
<script>
import {defineAsyncComponent} from 'vue'
//这样
const Child = defineAsyncComponent(()=>import('@/components/Child')) //异步引入
export default {
name:'App',
components:{
Child
},
}
</script>
<style>
.app{
background-color: gray;
padding: 10px;
}
</style>
通过异步引入 和 Suspense组件的使用达到如下的效果图:
<template>
<div class="app">
<h3>我是App(祖)</h3>
<!-- Suspense是一个内置组件。 -->
<Suspense>
<!-- Suspense使用的是插槽技术 -->
<!-- default就是我们要放置的模块数据,因为是异步的所以app先加载。 -->
<template v-slot:default>
<Child></Child>
</template>
<!-- 在default定义的模块还没有加载出来之前,由fallback插槽模块进行替代。 -->
<template v-slot:fallback>
<h3>加载中...</h3>
</template>
</Suspense>
</div>
</template>
<script>
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('@/components/Child')) //异步引入
export default {
name:'App',
components:{
Child
},
}
</script>
<style>
.app{
background-color: gray;
padding: 10px;
}
</style>
在异步加载下,俩种情况下能产生组件之间延迟的情况。
补充一点:setup不能是一个async函数,因为返回值不再是return的对象,而是promise,模板看不到return对象中的属性。(后期也可以,返回一个Promise实例,但需要Suspense和异步组件的配合!)
Vue2 和 Vue3 全局API做出的一些调整:
Vue3的data配置项使用被声明为一个函数!(一般都用setup了。)
Vue3移除了keyCode作为v-on的修饰符,同时不再支持config.keyCodes了。
keyCode就是像 keyup.13 等等,也就是没有了@keyup.13的形式。
在Vue2中,native的使用:
而Vue3移除了v-on.native修饰符。