一、vue3给的几颗糖
1. suspense
一个具有插槽的内置组件
<suspense>
<template #default>
<main-model></main-model>
</template>
<template #fallback>
<div>loading</div>
</template>
</suspense>
2.Fragment
在vue2中一个组件只能有一个根节点,但vue3不再受此限制,会自动将多个元素保存在一个虚拟元素fragment中
<template>
<h1>一级标题</h1>
<h2>二级标题</h2>
<h2>二级标题</h2>
<h2>二级标题</h2>
</template>
3.teleport
传送门——类似于ElementUI里dialog组件的appendTo属性,能将组件的html结构转移到指定的容器元素中,通常用于dialog的情况。
<template>
<div @click="showDialog = true">打开弹窗</div>
<teleport to="body">
<div class="mask" v-show="showDialog">
<div class="dialog">
<h3>title</h3>
<button @click="showDialog = false">关闭弹窗</button>
</div>
</div>
</teleport>
</template>
二、Composition API
如果按照vue2的option配置方法写组件,当业务复杂度越来越高,代码量会不断加大;由于相关业务的代码需要遵循option的配置写到特定的区域,导致后续维护非常的复杂,同时代码可复用性不高,而composition-api就是为了解决这个问题而生的
可以先来看看几个基础的API:
1. setup
- 返回对象时,对象中的属性方法都可以在模版中直接使用
- 返回渲染函数,则可以自定义渲染内容
setup(){
// 数据
let name = "suosuojiang"
let age = "18"
// 方法
function sayHello(){
alert(`我叫${name},我${age}岁了,你好啊`)
}
function test2(){
console.log(this.sex)
console.log(this.sayWelcom)
}
// 返回一个对象
return {
name,
age,
sayHello,
test2
}
//返回一个函数(渲染函数)
// return () => h('h1','ssj')
}
2. ref:定义响应式数据
- 接收的数据可以是基本类型,也可以是对象类型
- 基本类型:响应式仍然是靠Object.defineProperty()的get和set完成
- 对象类型:内部求助了Vue3.0中的一个新函数————reactive函数
import {ref} from 'vue'
<script>
setup(){
let name = ref("suosuojiang") //
function changeInfo(){
name.value = "ssj"
}
return {
name,
changeInfo
}
}
</script>
3.reactive
- 作用是定义对象类型的响应式数据(基本类型不要用它,要用ref)
- 返回一个代理对象proxy
- reactive定义的响应式数据是深层次的
4.watch与watchEffect
- watch与vue2的配置基本相同,但有坑:
- 监听reactive定义的响应式数据时,deep配置无效,无法正确获取oldValue的值
- 监视的是reactive所定义的对象中的某个属性时,deep配置有效
- watchEffect不用指定要监听的对象,回调里用到谁就监听谁
// 情况一:监视ref所定义的响应式数据
watch(sum, (newV, oldV)=>{
console.log('sum值变化了',newV, oldV)
},{immediate: true})
//情况二:监视ref所定义的多个响应式数据
watch([sum,msg], (newV, oldV)=>{
//此时newV和oldV也变成了数组
console.log('msg值变化了',newV, oldV)
})
/*
情况三:监视reactive定义的响应式数据的全部属性
1.注意:此处无法正确获得oldV
2.注意:强制开启深度监视,deep配置无效
*/
watch(person,(newV, oldV)=>{
console.log('person值变化了',newV, oldV)
},{deep:false})//deep配置无效
//情况四:监视reactive定义的响应式数据中的某个属性
watch(()=> person.age,(newV, oldV)=>{
console.log('person的age变化了',newV, oldV)
})
// 情况五:监视reactive定义的响应式数据中的多个属性
watch([()=> person.name,()=> person.age],(newV, oldV)=>{
console.log('person的name和age变化了',newV, oldV)
})
// 特殊情况
watch(()=> person.jobs,(newV, oldV)=>{
console.log('person的job变化了',newV, oldV)
},{deep:true})//此处由于监视的是reactive所定义的对象中的某个属性,deep配置有效
watchEffect(()=>{
const x1 = sum.value
console.log('是谁的大眼睛,还没看到我堂堂watchEffect的会调?')
})
5.computed
两种写法:
// 计算属性--简写 没有考虑计算属性被修改的情况
person.fullName = computed(()=>{
return person.firstName + '-' + person.lastName
})
// 计算属性--完整写法
let fullName = computed({
get(){
return person.firstName + '-' + person.lastName
},
set(value){
const nameArr = value.split('-')
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
})
6.生命周期
三、性能优化:又快又小
vue3在性能方面比vue2快了近2倍,打包后的体积小1/3到1/2.
1. 优化diff算法:
vue2中的虚拟dom是进行的全量对比
<template>
<h1>一级标题</h1>
<h2>{{title}}</h2>
<h2>二级标题</h2>
<h2>二级标题</h2>
</template>
vue3中则新增了PatchFlag,只对比带有PatchFlag的节点,通过当前PatchFlag标记的信息来确定当前节点要比对的具体内容。
补充:PatchFlag核心思想:会根据vnode
的patchFlag
上具有的属性来执行不同的patch
方法,如果没有patchFlag
那么就执行full diff
,也就是这里的patchProps
,那么猜也知道,patchProps
应该包含了下面大部分的单个patch
,即:
patchClass
patchStyle
patchEvent
- 等等
举例:
<template>
<div :class="classNames" :id="id">{{name}}</div>
</template>
编译后:
createElement(
"div",
{ class: classNames, id: id },
[name],
PatchFlags.TEXT | PatchFlags.CLASS | PatchFlags.PROPS,
["id"] // 标明具体那几个props是动态的
)
2. vue2响应式原理依靠Object.defineProperty
对数据的劫持;vue3则通过Proxy
拦截对象中任意属性的变化,包括属性的读写、增删,另外还通过Reflect
对被代理对象(源对象)的属性进行操作
3. tree-shaking
核心api都支持tree-shaking,通过包引入的方式而不是直接在实例化时就注入,只会对使用到的功能或特性进行打包(按需打包),故而体积更小。
四、其他
1. 自定义hooks
import { onMounted, onUnmounted, ref } from "vue";
function useWindowResize() {
const width = ref(0);
const height = ref(0);
function onResize() {
width.value = window.innerWidth;
height.value = window.innerHeight;
}
onMounted(() => {
window.addEventListener("resize", onResize);
onResize();
});
onUnmounted(() => {
window.removeEventListener("resize", onResize);
});
return {
width,
height
};
}
export default useWindowResize;
<template>
<div id="app">
<h1>{{ count }}</h1>
<button @click="plus">plus</button>
<h1>屏幕尺寸:</h1>
<div>宽度:{{ width }}</div>
<div>高度:{{ height }}</div>
</div>
</template>
<script lang="ts">
import { ref, watch } from "vue";
import useWindowResize from "./hooks/useWindowResize";
export default {
name: "App",
setup() {
const count = ref(0);
function plus() {
count.value++;
}
watch(count, () => {
document.title = "update: " + count.value;
});
const { width, height } = useWindowResize();
return {
count,
plus,
width,
height
};
}
};
</script>