前言
今天是更文挑战的最后一天,想了许久,最后决定梳理一篇关于Vue3
的个人小结,一来作为完美收官6
月份,二来为7
月份做个铺垫!
内容目录预览如下:
Vue2和Vue3的区别
- 重构响应式系统,使用
Proxy
替换Object.defineProperty
,Proxy
优势:
- 可直接监听数组类型的数据变化
- 监听的目标为对象本身,不需要像
Object.defineProperty
一样遍历每个属性,有一定的性能提升 - 可拦截的东西更多,提供了
apply
、ownKeys
、has
等13种拦截器方法
- 新增
Composition API
,更好的逻辑复用和代码组织 - 重构
Virtual DOM
- 模板编译时的优化,将一些静态节点编译成常量
slot
优化,将slot
编译为lazy
函数,将slot
的渲染的决定权交给子组件- 模板中内联事件的提取并重用(原本每次渲染都重新生成内联函数)
- 代码结构调整,更便于
Tree shaking
,使得体积更小 - 使用
Typescript
替换Flow
Vue3为什么引入composition API
首先我们使用Vue2.x
的形式来实现一个简单功能
需求:
1.当点击商品列表项时删除该项
2.当点击添加按钮时,会根据表单中数据添加一行到商品列表中
代码如下:
<template>
<div class="wrap">
<section>
<h6>商品列表</h6>
<table>
<thead>
<td>序号</td>
<td>名称</td>
<td>单价</td>
<td>折扣</td>
<td>折后价</td>
</thead>
<tbody>
<tr
v-for="(fruit, index) in fruits"
:key="fruit.id"
@click="remove_item(index)"
>
<td>{{ fruit.id }}</td>
<td>{{ fruit.fruit_name }}</td>
<td>{{ fruit.price }}</td>
<td>{{ fruit.discount }}</td>
<td>{{ (fruit.price * fruit.discount).toFixed(2) }}元/斤</td>
</tr>
</tbody>
</table>
<br />
</section>
<section>
<h6>如果想添加一个商品,请输入:</h6>
<form>
商品序号:<input type="text" v-model="f.id" /><br />
商品名称:<input type="text" v-model="f.fruit_name" /><br />
单价价格:<input type="text" v-model="f.price" /><br />
打折折扣:<input type="text" v-model="f.discount" /><br />
<button @click="add_item">添加</button>
</form>
</section>
</div>
</template>
<script>
export default {
name: "App",
data: function () {
return {
fruits: [
{ id: 1, fruit_name: "apple", price: 10, discount: 0.8 },
{ id: 2, fruit_name: "banana", price: 3, discount: 0.7 },
{ id: 3, fruit_name: "orange", price: 5, discount: 0.5 },
{ id: 4, fruit_name: "durain", price: 50, discount: 0.8 },
],
f: {
id: 5,
fruit_name: "",
price: "",
discount: "",
},
};
},
methods: {
remove_item(index) {
this.fruits = this.fruits.filter((item, key) => index !== key);
},
add_item(e) {
e.preventDefault();
let temp = Object.assign({}, this.f);
this.fruits.push(temp);
this.f.id = this.fruits.length + 1;
this.f.fruit_name = "";
this.f.price = "";
this.f.discount = "";
},
},
};
</script>
复制代码
简单加点样式
.wrap{
width: 600px;
margin: 10px auto;
display: flex;
justify-content:space-around;
background-color:rgb(253, 247, 247);
border-radius:4px;
}
.wrap table thead{
background-color: deepskyblue;
font-weight: bold;
font-size: 0.9em;
}
.wrap table tr:nth-of-type(2n+1){
background-color:pink;
}
.wrap form{
font-size: 0.9em;
}
.wrap button{
margin-top: 10px;
width: 100%;
color: rgb(224, 43, 31);
font-weight: 700;
}
复制代码
显示结果如下
上述例子中,我们可以看到,使用2.x
的option API
,每当实现一个功能,都会在data
中添加一些数据,同时在methods
中添加业务逻辑。这里还好只有两个简单的功能,但实际工作中,当添加很多很多功能时,要找到某个功能对应数据和业务逻辑就变得非常困难,并且业务逻辑不一定就在methods
中,还可能分散在computed
、watch
等选配项中。所以vue3.0
中引入了composition API
,专门用于解决功能、数据和业务逻辑分散的问题,使项目更益于模块化开发以及后期维护
Vue3更优雅的使用v-model
我们知道,在Vue2.0
中如何实现双向数据绑定一种是v-model
,另一种是.sync
。通常一个组件只能用于一个v-model
,但是有的组件需要有多个可以双向响应的数据,所以就出现了.sync
在Vue3.0
中为了实现统一,实现了让一个组件可以拥有多个v-model
,同时删除掉了.sync
。在vue3.0
中,v-model
后面需要跟一个modelValue
,即要双向绑定的属性名,Vue3.0
就是通过给不同的v-model
指定不同的modelValue
来实现多个v-model
Composition API和React Hook的区别
从React Hook
的实现角度看,React Hook
是根据useState
调用的顺序来确定下一次重渲染时的state
是来源于哪个useState
,所以出现了以下限制
- 不能在循环、条件、嵌套函数中调用
Hook
- 必须确保总是在你的
React
函数的顶层调用Hook
useEffect
、useMemo
等函数必须手动确定依赖关系
而Composition API
是基于Vue
的响应式系统实现的,与React Hook
的相比
Composition API
声明在setup
函数内,一次组件实例化只调用一次setup
,而React Hook
每次重渲染都需要调用Hook
,使得React
的GC
比Vue
更有压力,性能也相对于Vue
来说也较慢Compositon API
的调用不需要顾虑调用顺序,也可以在循环、条件、嵌套函数中使用- 响应式系统自动实现了依赖收集,进而组件的部分的性能优化由
Vue
内部自己完成,而React Hook
需要手动传入依赖,而且必须必须保证依赖的顺序,让useEffect
、useMemo
等函数正确的捕获依赖变量,否则会由于依赖不正确使得组件性能下降
虽然Compositon API
看起来比React Hook
好用,但是其设计思想也是借鉴了React Hook
ref函数
我们知道,在vue3.0
引入了composition API
,setup
函数是其核心函数
在setup
函数中,可以使用ref
函数,用于创建一个响应式数据,当数据发生改变时,Vue
会自动更新UI
例如:使用ref
函数定义一个变量count
import { ref } from 'vue';
function useChangeCount() {
let count = ref(0);
function change_count() {
count.value += 1;
}
return { count, change_count }
}
export default useChangeCount;
复制代码
然后在组件中引入该模块:import useChangeCount from "./composition_tiny_js/count"
并且在组件的setup
中使用,并通过return
的形式将外界需要用到的变量和函数暴露出去
setup() {
let { count, change_count } = useChangeCount();
return { count, change_count };
}
复制代码
这样上面暴露的count
变量,change_count
方法就可以在外界使用了
<template>
<div>
<h1>{{ count }}</h1>
<button @click="change_count">点我</button>
</div>
</template>
复制代码
需要注意的是:
- 在
setup
中定义的变量或方法,都必须通过return {xxx,xxx}
暴露出去,外界才能使用 ref
函数仅能监听基本类型的变化,不能监听复杂类型的变化(比如对象、数组)
reactive函数
reactive
的用法与ref
的用法相似,也是将数据变成响应式数据,当数据发生变化时UI
也会自动更新。不同的是ref
用于基本数据类型,而reactive
是用于复杂数据类型,比如对象和数组
例如:定义一个对象类型的变量user
<template>
<div>
<p>{{ user }}</p>
<button @click="increase">click me! one year later</button>
</div>
</template>
<script>
import { reactive } from "vue";
export default {
name: "reactive",
setup() {
const user = reactive({ name: "Alice", age: 12 });
function increase() {
++user.age
}
return { user, increase };
},
};
</script>
复制代码
如上,当点击按钮时,让数据user.age
加1,当Vue发现数据发生变化,UI
会自动更新
那我们验证了,确实reactive
函数可以将一个复杂数据类型变成响应式数据。我们不妨将reactive
函数执行的结果打印出来看下,看看它返回值是什么
我们发现,
reactive
执行结果是将传递的对象包装成了proxy
对象
接下来我们测试一下,如果传递基本数据类型呢?
<template>
<div>
<p>{{ userAge }}</p>
<button @click="increase">click me! one year later</button>
</div>
</template>
<script>
import { reactive } from "vue";
export default {
name: "reactive",
setup() {
let userAge = reactive(12);
function increase() {
console.log(userAge);
++userAge;
}
return { userAge, increase };
},
};
</script>
复制代码
运行发现,基本数据传递给reactive
,reactive
并不会将它包装成porxy对象,并且当数据变化时,界面也不会变化
需要注意的是,reactive
中传递的参数必须是json
对象或者数组,如果传递了其他对象(比如new Date()
),在默认情况下修改对象,界面不会自动更新,如果也需要具有响应式,可以通过重新赋值的方式实现
setup函数的注意点
我们知道setup
函数是组合API
的核心入口函数,使用时需要注意几点:
setup
函数的执行时机是在beforeCreate
和created
之间- 由于
setup
执行时机是在created
之间,所以组件才刚刚被创建,而data
和methods
还没初始化好,所以无法在setup
中使用data
和methods
setup
中this
指向undefined
setup
只能是同步的,不能是异步的
递归监听和非递归监听
我们知道ref
函数和reactive
函数用于将一个普通数据变成一个响应式的数据。当数据发生改变时,界面也会立即更新。
其实还有一个规律,就是是深度监听数据的每一层,我们称之为递归监听
import { reactive } from "vue";
export default {
setup() {
const alice = {
name: "Alice",
age: 80,
sex: 'female',
child:{
name:'Tom',
sex: 'male',
age:59,
child:{
name:'Frank',
sex: 'male',
age:30,
child:{
name:'Blue',
sex: 'male',
age:3
}
}
}}
const AliceFamily = reactive(alice );
return { AliceFamily };
},
};
复制代码
如上例子,vue
会通过reactive
函数将我们传递的对象alice
的每一层打包成一个proxy
对象,深度监听对象的每一层的每一个数据,当任意一层的任意一个数据发生改变,vue
都会检测到,并更新对应的UI
ref
也是类似,因为ref
的本质也是reactive
ref(12) 相当于 reactive({value:12})
复制代码
递归监听的好处显而易见,可以监听到每一个数据的变化,但正因为要监听每一个数据,当数据非常复杂时,vue
要讲每个数据都通过Proxy
包装一次,数据量大的话这个过程是非常消耗性能的。所以为了解决这个问题,vue3
提供了两个函数用于创建浅度监听数据,即非递归监听。这两个函数是:
shallowRef
shallowReactive
使用过程中需要注意:
- 如果是通过
shallowRef
创建数据,那么vue
监听的是.value
的变化,而不是第一层的变化 - 另外
vue3
还提供了triggerRef
函数,用于手动更新界面UI
。但是没有提供triggerReactive,所以如果是reactive的数据,那是无法手动触发界面更新的。
那有人会想:在什么情况下用递归监听,什么情况下用非递归监听呢?
其实只要记住,非递归监听是为了解决监听数据量大的问题而出现的,所以只有当数据量非常大时,我们才考虑用shallowRef
和shallowReactive
,一般情况下都使用ref
和reactive
toRaw函数和markRaw函数
在setup
函数中,我们通过ref
和reactive
函数创建响应式数据,其特点是,每次修改数据都会更新UI
界面,这样的问题是非常消耗性能
所以,如果我们有些时候,有些操作可以不需要每次修改数据都去更新UI
界面,那么我们可以通过vue3
提供的toRaw
方法来获取该数据被Proxy
包装前的原始数据,然后通过对原始数据进行修改,进而可以修改对应的代理对象内部数据。这是通过原始数据修改改变的,并不会触发 UI
界面更新
<script>
import { reactive, toRaw } from "vue";
export default {
setup() {
const obj1= {name:'alice'};
const user = reactive(obj1);
const obj2 = toRaw(user);
console.log(obj1 === obj2);//true
function change() {
obj2.name = 'Frank';
}
return { user, change};
},
};
</script>
复制代码
上面例子中,通过包装后的响应式对象user
来修改,界面会立即更新。但是如果通过修改原始数据obj2
来修改数据,界面是不会跟新的。另外我们可以看见obj2
===obj1
,由此证明:
toRow
就是用于获取一个Proxy对象的原始数据
以上是获取reactive
的原始数据(创建时响应式数据时传入的原始数据),我们再来看下如何获取ref
的原始数据
我们知道ref
的本质是reactive
,即ref(obj)相当于reactive({value : obj})
所以如果想通过toRaw
方法获取ref
类型的原始数据,就必须明确告诉toRaw
,需要获取的是.value
的值。因为.value中保存的才是当初创建时传入的原始数据
import { ref, toRaw } from "vue";
export default {
setup() {
const obj1= {name:'alice'};
const user = ref(obj1);
const obj2 = toRaw(user.value);
console.log(obj1,user,obj2);
return { user };
},
};
复制代码
有的情况下,我们希望某个数据在以后的操作中永远都不会被追踪,vue3
提供了一个方法:markRaw
setup() {
const obj= {name:'alice',age:18};
obj= markRaw(obj);
const user= reactive(obj);
function change(){
user.age=19
}
return { user , change};
}
复制代码
上述代码中,obj被markRaw标记后,当后面将obj变成proxy对象时,发现调用change方法改变数据时,界面并不会再跟新了!
toRef函数和toRefs函数
我们知道ref
可以用于创建一个响应式数据,而toRef
也可以创建一个响应式数据,那他们之间有什么区别呢?
事实上,如果利用ref
函数将某个对象中的属性变成响应式数据,修改响应式数据是不会影响到原始数据。
import {ref} from 'vue';
export default {
name:'App'
setup(){
let obj = {name : 'alice', age : 12};
let newObj= ref(obj.name);
function change(){
newObj.value = 'Tom';
console.log(obj,newObj)
}
return {newObj,change}
}
}
复制代码
上述代码,当change执行的时候,响应式数据发生改变,而原始数据obj并不会改变。
原因在于,ref
的本质是拷贝,与原始数据没有引用关系
需要注意ref(obj.name)相当于ref('alice')相当于reactive({value:'alice'}) 所以在修改数据时,是修改newObj.value=xxx
而如果使用toRef
将某个对象中的属性变成响应式数据,修改响应式数据是会影响到原始数据的。但是需要注意,如果修改通过toRef
创建的响应式数据,并不会触发UI
界面的更新。
所以,toRef的本质是引用,与原始数据有关联
import {toRef} from 'vue';
export default {
name:'App'
setup(){
let obj = {name : 'alice', age : 12};
let newObj= toRef(obj, 'name');
function change(){
newObj.value = 'Tom';
console.log(obj,newObj)
}
return {newObj,change}
}
}
复制代码
上述代码,当change执行的时候,响应式数据发生改变,原始数据obj并不会改变,但是UI界面不会更新
小结:
ref和toRef的区别
(1). ref本质是拷贝,修改响应式数据不会影响原始数据;toRef的本质是引用关系,修改响应式数据会影响原始数据
(2). ref数据发生改变,界面会自动更新;toRef当数据发生改变是,界面不会自动更新
(3). toRef传参与ref不同;toRef接收两个参数,第一个参数是哪个对象,第二个参数是对象的哪个属性
所以如果想让响应式数据和以前的数据关联起来,并且想在更新响应式数据的时候不更新UI
,那么就使用toRef
有的时候,我们希望将对象的多个属性都变成响应式数据,并且要求响应式数据和原始数据关联,并且更新响应式数据的时候不更新界面,就可以使用toRefs
,用于批量设置多个数据为响应式数据。(toRef一次仅能设置一个数据)
toRefs
接收一个对象作为参数,它会遍历对象身上的所有属性,然后挨个调用toRef
执行
例如
import {toRefs} from 'vue';
export default {
name:'App'
setup(){
let obj = {name : 'alice', age : 12};
let newObj= toRefs(obj);
function change(){
newObj.name.value = 'Tom';
newObj.age.value = 18;
console.log(obj,newObj)
}
return {newObj,change}
}
}
复制代码
使用customRef函数自定义一个ref
我们知道,ref
函数可以创建一个响应式数据,在数据更新时同时更新UI
界面
有的时候,我们希望可以显示的控制依赖追踪和触发响应,那就可以使用customRef
函数自定义一个ref
自定义ref
本质其实就是return customRef()
需要注意:
customRef
函数接受一个工厂函数,该工厂函数有两个参数,分别是用于追踪的track
与用于触发响应的trigger
,并且返回一个的对象,该对象需要有get
和set
方法
官方例子:使用自定义 ref 实现带防抖功能的 v-model :
<input v-model="text" />
复制代码
function useDebouncedRef(value, delay = 200) {
let timeout
return customRef((track, trigger) => {
return {
get() {
track()
return value
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger()
}, delay)
},
}
})
}
export default {
setup() {
return {
text: useDebouncedRef('hello'),
}
},
}
复制代码
如上代码,在get中显示调用track()表示该数据需要被追踪,在set中显示的调用trigger()表示当数据被修改时,需要更新UI界面。
使用customRef函数自定义一个ref函数的步骤:
1.函数返回customRef()
2.customRef接受两个参数track和trigger
3.customRef返回一个对象
4.customRef返回的对象必须实现set和get方法
5.在get中显示调用track()表示该数据需要被追踪,在set中显示的调用trigger()表示当数据被修改时,需要更新UI界面
如何通过ref属性获取元素
在vue2.x
中,可以通过给元素添加ref='xxx'
属性,然后在代码中通过this.$refs.xxx
获取到对应的元素
然而在vue3
中时没有$refs
这个东西的,因此vue3
中通过ref
属性获取元素就不能按照vue2
的方式来获取
vue3
需要借助生命周期方法,原因很简单,在setup
执行时,template
中的元素还没挂载到页面上,所以必须在mounted
之后才能获取到元素。
<template>
<div ref='box'>I am DIV</div>
</template>
<script>
import {ref,onMounted}
export default{
setup(){
let box = ref(null);
onMounted(()=>{
console.log(box.value)
});
return {box}
}
}
</script>
复制代码
如上代码,vue3
中,所有生命周期方法都抽离出去了,需要用时直接import
。这里导入了一个onMounted
当界面挂载出来的时候,就会自动执行onMounted
的回调函数,里头就可以获取到dom元素
小结
1.在compositionAPI中如何使用生命周期函数?
需要用到哪个生命周期函数,就将对应函数的import进来,接着在setup中调用即可
2.vue3如何通过ref属性获取界面上的元素?
在template中的写法跟vue2一样,给元素添加个ref='xxx'
在setup中,先创建一个响应式数据,并且要把响应式数据暴露出去
当元素被创建出来的适合,就会给对应的响应数据赋值
当响应式数据被赋值之后,就可以利用生命周期方法,在生命周期方法中获取对应的响应式数据,即DOM元素
readonly和shallowReadonly函数
vue3
中提供了只读相关的函数:readonly
、shallowReadonly
和isReadonly
三者用途和区别显而易见:
readonly
:用于创建一个只读数据,并且是深度只读或者叫递归只读
shallowReadonly
:也是用于创建一个只读数据,但是这个只读只是第一层只读,非深度只读
isReadonly
:判断一个数据是不是只读数据
我们知道const定义的变量也是不能改的,那readonly和const有什么区别?
-
const是赋值保护,使用const定义的变量,该变量不能重新赋值。但如果const赋值的是对象,那么对象里面的东西是可以改的。原因是const定义的变量不能改说的是,对象对应的那个地址不能改变
-
而readonly是属性保护,不能给属性重新赋值
Teleport组件
Teleport
,被称为传送门组件,用于提供一种简洁的方式,指定其里面内容的父元素
例如,定义一个模态框,该模态框原本是在一个组件中的,但我希望当弹出的时候,它的结构是body
下,与#app
平级。如下代码
<template>
<button @click="modalsOpen = true">弹出框按钮</button>
<teleport to='body'>
<div v-if="modalsOpen" class="modals">
<div>
<p>这是一个弹出框,并且是被传送到body上</p>
<button @click="modalsOpen = false">关闭</button>
</div>
</div>
</teleport>
</template>
<script>
import { ref } from "vue";
export default {
name: "Modals",
setup() {
let modalsOpen = ref(false);
return { modalsOpen };
},
};
</script>
<style scoped>
.modals{
background-color: rgba(0, 0, 0, .5);
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.modals div{
border-radius: 4px;
width: 300px;
height: 100px;
background-color:#fff;
padding: 20px;
display: flex;
flex-direction: column;
justify-content: center;
font-size: .9em;
color: rgba(0, 0, 0, .9);
}
button{
padding: 5px;
}
</style>
复制代码
显示结果如下:
我们发现,teleport
组件使用也比较简单,就是通过to
属性能将其里面的内容被传送到指定的哪个元素下,属性值为元素选择器
Fragments和emits选项
Vue3中组件可以拥有多根
我们知道,在vue2.x
中,每个组件只能有一个根,这意味着我们写每个组件模板时都要套一个父元素。vue3
为了更方便的书写组件模板,引入Fragments
,说白了就是vue3
中组件可以拥有多个根了
例如:创建一个Fragments.vue
,组件模板可以写多个根
<template>
<div>
<h1>Fragments</h1>
</div>
<div>
<p>vue3引入Fragments,本意是碎片</p>
<p>说白了就是vue3的组件可以拥有多个根了</p>
</div>
<!--还可以拥有其他更多根-->
</template>
复制代码
如上,由以前的单根变成多根的情况,是不是有点零零碎碎的感觉,这就是Fragmens
意思的来源----碎片
vue3针对自定义事件,添加了emits选项
vue3
新增emits
选项,需要将组件发送的自定义事件定义在emits
选项中。一来可以避免当自定义事件与原生事件重名时,事件会触发多次的情况,比如:click事件;二来可以更好的指示组件的工作方式
例如:创建一个Emits.vue,我们自定义了一个click事件
<template>
<button @click="$emit('click')">点我</button>
</template>
<script>
export default {
//emits:['click']
};
</script>
复制代码
如果自定义事件没有写入到emits
选项中,当自定义事件名与原生事件名冲突时,事件会被多次触发
<template>
<Emits @click="onClick"></Emits>
</template>
<script>
import Emits from './components/Emits.vue'
export default {
name: 'App',
components: {
Emits
},
methods:{
onClick(){console.log('clicked!!!!!')}
}
}
</script>
复制代码
因此,在vue3
书写自定义事件的时候,都建议将自定义事件书写到emits
选项中
Vue3中全局API改为应用程序实例调用
vue3
中存在一些具有破坏性的变更,比如Global API
改为应用程序实例调用
Vue2
中很多全局API
会改变vue
的行为,全局的API
会导致一些问题:
Vue2
没有app
的概念,new Vue()
得到的根实例作为app
,这样所有创建的根实例app
都是共享相同的全局配置,这在测试时会污染其他测试用例,导致测试困难- 全局配置也导致没有办法在单页面创建不同全局配置的多个
app
实例
所以,Vue3
使用createApp
函数返回应用程序实例app
,并由这个app
实例暴露一系列的全局API
比如,Vue2
中Vue.component
变更为如下形式
import { createApp, h } from 'vue'
import App from './App.vue'
import './index.css'
const app = createApp(App)
.component('comp', { render: () => h('div', 'i am comp!!!') })
.mount('#app')
复制代码
可以看到,Vue2
中通过Vue
的构造函数调用component
创建全局组件,现在变成有应用实例app
调用的形式
类似的变更(Vue.component
变更为app.component
)还有:
1. Vue.directive变更为app.directive
2. Vue.mixin变更为app.mixin
3. Vue.use变更为app.use
4. Vue.config变更为app.config
5. Vue.config.ignoredElements变更为app.config.ignoredElements
注意:Vue.config.productionTip
和Vue.filter
在Vue3
中被移除
关于Tree-shaking
我们知道,Vue3
中提到一个叫Tree-shaking
的东西,其实也并不是一个新的东西,有人称之为"摇树优化
",什么意思?
按照尤大的原话解释,Tree-shaking
其实就是:把无用的模块进行“剪枝”,很多没有用到的API就不会打包到最后的包里
其实说白了,Tree-shaking
本质并不是Vue3
的东西,而是那些打包工具的功能。只是Vue3
代码结构调整,当用webpack
等打包工具打包项目时,webpack
会将那些没用用到的代码不打包到最后的项目中,这样使得项目体积更小
主要原理:依赖es6
的模块化的语法,将无用的代码(dead-code
)进行剔除!
Vue3中生命周期函数的变化
我们知道,在每个组件在被创建时,要经过一系列的初始化过程,比如,需要设置数据监听、编译模板、实例挂载并在数据变化时触发DOM
更新等。
在Vue2
中提供了生命周期钩子函数,如 beforeCreate
、created
、beforeMount
、mounted
、beforeUpdate
、updated
、beforeDestory
、destoryed
等,用于在组件不同阶段执行一些我们想要的执行的操作
到了Vue3
,有所变化
beforeCreate ---->setup
created ---->setup
beforeMount ---->onBeforeMount
mounted ---->onMounted
beforeUpdate ---->onBeforeUpdate
updated ---->onUpdated
beforeDestory ---->onBeforeUnmount
destoryed ---->onUnmounted
可以看到,setup
函数代替了 beforeCreate
和created
两个生命周期函数,因此我们认为setup的执行时间在beforeCreate
和 created
之间
为了更好的Tree-shaking
,Vue3
的生命周期函数都是从 vue
中导入,再进行直接调用
//从 vue 中引入 多个生命周期函数
import {onBeforeMount, onMounted, onBeforeUpdate, onUpdated,
onBeforeUnmount, unMounted} from 'vue'
export default {
name: 'App',
setup() {
onBeforeMount(() => {
// 在挂载前执行
})
onMounted(() => {
// 在挂载后执行
})
onBeforeUpdate(() => {
// 在更新前前执行
})
onUpdated(() => {
// 在更新后执行
})
onBeforeUnmount(() => {
// 在组件销毁前执行
})
unMounted(() => {
// 在组件销毁后执行
})
return {}
}
}
复制代码
END~
以上就个人的一些学习笔记,不一定都正确!希望下个月好好学好Vue
吧~
6月,完美收官!!撒花~