Vue3
1.diff算法,分两类:
- 有key的算法分五部:
- 前序对比算法
- 尾序对比算法(Vue2还会进行头尾和尾头对比)
- 新节点多出来就挂载
- 旧节点多出来就是卸载
- 特殊情况乱序(dom有移动,需要求新旧子树的最长递增子序列)
- 给结点的key值重新排序,比如0:1,1:2,2:3;重新排序后就是2:0,1:1,0:2
- 如果有多余的旧节点或新结点不包含旧节点;删除
- 如果结点出现交叉,就需要求最长递增子序列,做最小的移动步骤
- 无key的算法:
- patch(渲染)的时候会逐个替换救VNode
- 新增
- 删除
ps:无key的算法在第一步时多替换了很多重复的结点,有可能只是删除或新增了dom,但要替换很多次。
2.ref响应式
vue3包含ref,Ref,isRef以及,其中Ref是ref的接口,ref()方法返回一个class,修改其值必须使用ref.value的方式修改。
shadowRef和ref不能写在同一个组合式API里面,因为ref会走triggerRefValue方法,导致依赖更新,即更新视图。
- shallowRef是浅层响应式
- triggerRef是强制更新收集的依赖。
- customRef自定义响应式,需要通过track收集依赖,通过 trigger()触发视图 更新
3. Reactive响应式
reactive底层使用了泛型约束,只支持引用类型,比如Array、Object、Map、Set
- reactive不能直接赋值,否则会覆盖其本身(proxy对象)
// const list = reactive<string[]>([])
// 上述list只能使用push的方式添加值,如果直接赋值[],则会改变其响应式
4.readonly
给响应式reactive对象套一层readonly,则该readonly对象的属性不可以修改,但可以修改原本reactice对象的值;
let obj = reactive({name:'小满'})
const read = readonly(obj)
obj.name = “xxx” //不报错
read.name = "xxx" //报错
同样的shadowReactive和reactive一起使用,reactive会触发视图更新,导致shadowReactive非浅层响应。
5. toRef,toRefs,toRaw
toRef(object,key),用于将对象中的某个属性变为响应式解构出来;
- 底层是调用了ObjectRefImpl,用于将响应式对象的属性值直接返回出来,修改属性值会导致reactive底层更新视图,因此该方法不需要再做一遍。而非响应式对象的属性值返回回来,也不会触发视图收集和更新,因为该方法没有track和trigger方法。
toRefs(objcet) 用于将对象中全部属性转为响应式并解构出来。
toRaw(object)用于将一个响应式对象(proxy)变为非响应式。
6.Vue3响应式原理
7. computed
- 选项式写法:传入get和set方法,可以去修改计算属性的值
- 函数式写法:传入一个回调函数,不允许修改值
8.Watch
- ref属性需要手动开启deep监听,reactive不需要
- 监听对象的单一值,需要使用回调函数,否则使用数组
watch(()=>message.bar.foo.name,(newVal,oldVal)=>{
console.log(newVal.oldVal)
},{
//deep:true;
//immediate:true //立即执行callback
//flush:"pre" //pre组件更新之前执行,sync组件更新同步执行,post组件更新之后## 执行
})
9.WatchEffect
该方法不需要显示声明需要监听的值,
//返回一个终止函数
const stop = watchEffect((oninvaildate)=>{
console.log(message.value)
oninvalidate(()=>{
console.log("before") //该方法会首先执行,一般做防抖节流
})
},{
flush:"post" //在渲染Dom之后获取值
onTrigger(e){
debugger
}
})
const stopWatch = ()=> stop()
10. 生命周期钩子
- setup(相当于Vue2的beforeCreated和created函数)
- onBeforeMounted(获取不到dom元素)
- onMounted(可以获取dom)
- onBeforeUpdate
- onUpdated
- onBeforeUnmount
- onUnmounted
(8. onRenderTracked((e)=>{})和onRenderTriggered((e)=>{}),这两个钩子可以用来调试,前者执行依赖收集get,后者执行依赖更新set)
11.bem架构
- block-element-modify:比如element-ui:块级元素用短横杠-连接,元素里面的内容用下划线__连接,修饰符用两个短横杠–连接
- sass语法:
- 定义变量: 定义变量: 定义变量:width:5em(less使用@定义变量)
- 插值语句:#{$name},可以使用定义的变量
- @mixin混入,@include引入
12 BFC
块级格式化上下文,页面中一个隔离的独立容器,容器内的标签不会影响到容器外的标签;使用BFC可以避免margin塌陷;
触发BFC的方式有:
- body根元素
- 浮动元素:float除None以外的值
- 绝对定位元素:position(absolute、fixed)
- display:inline-block 、table-cells、flex
- overflow 除 visible以外的值(hidden、auto、scroll)
计算BFC高度时,浮动元素也会参与计算:
当container设置为inlined-block时,内部元素设置为float,此时该container才会显示出来。
13.父子组件传参
父组件给子组件传参:父组件通过:绑定参数,子组件
- 通过props接受传参,并通过.的方式取值。
const props = defineProps({title:{type:String,default:"默认值"}})
console.log(props.title) //js中需要通过Props取值,模板中不需要
- ts泛型字面量(ts特有默认值)
const props = defineProps<{
title:string,
arr:number[]
}>()
withDefaults(defineProps<{
title:string,
arr:number[]
}>(),{
arr:{}=>[666] //需要通过函数返回,防止引用?
})
子组件给父组件传参:
<template>
<button @click="send">父组件传值</button>
</template>
<script setup lang='ts'>
const emit = defineEmits(['on-click'])
const send = () => {
emit('on-click','小满')
}
</script>
ts特有方式:
const emit = defineEmits<{
(e:"on-click",name:string):void
}>()
const send = ()=>{
emit('on-click','小满')
}
父组件主动读取子组件的值
// 子组件
defineExpose({
name:"小满",
open:()=>console.log("1")
})
// 父组件
<son ref="Son"></son>
<script setup lang='ts'>
import son from './componments/son.vue'
const son = ref<InstanceType<type of son>()
console.log(son.value?.name) //或者son.value.open()调用方法
</script>
14. 组件
局部组件:
通过在页面上import的方式引入局部组件;
全局组件:
在main.ts中引入的组件是全局组件,import之后需要在app.component(‘temp’,temp.vue)注册
递归组件:
在App.vue文件内引入递归组件,并定义递归数组data,写上递归组件的组件名;同时在递归组件内部引用自身(Vue3.3+可以使用defineOption({name:“”})的方式定义别名,或者export default{name:“”}的方式定义别名),并传入子数组的值;
举例:
App.vue:
Tree.vue
15.可选链操作符,双问号表达式
a.children?.length?.xxx //不管后面带多少属性,没有的话就返回undefined;
a.children?.length?.xxx ?? [] //在undefined或者null的时候会返回双问号后面的值,但0和false不做处理
16. 动态组件
<component :is="comId"></component> //该方式比v-if来说,会保留之前的渲染状态,而v-if会销毁后重新创建
- 使用import引入对象的方式(性能较优)
import Avue from 'xxx'
const comId = shallowRef(AVue) //只需要跟踪到.vue,不需要劫持组件的其他属性,性能优化
- 使用选项式API的方式,此时上述AVue使用字符串的形式即可。
components:{
AVue,BVue,CVue
}
17. 插槽
父组件使用
<template v-slot> //匿名插槽
<div>xxx<div>
</template>
<template v-slot:header> //具名插槽,子组件需要定义name
<div>header<div>
</template>
<template #default> //简写形式
<div>xxx<div>
</template>
<template #header> //具名插槽简写
<div>header<div>
</template>
<template v-slot=“{data}”> //接受子组件传过来的值
<div>xxx<div>
</template>
--------------------------------------------------------------------
<template #[name]> //动态插槽
<div>xxx<div>
</template>
<script>
let name = ref('header')
</script>
子组件
<slot name="header" :data="data"></slot>
18.顶层await技术
ES7之后可以在script的顶层写await,此时下方所有的请求都会变成异步的。
19. 异步组件
异步组件必须在App.vue中使用<Suspense></Suspense>标签去引入,包含default和fallback插槽,后者是加载的时候显示的内容,前者是加载完成后显示的内容。使用该方式的话,异步组件不会被打包进主包里,而是单独一个js和css文件。
异步组件在script标签里使用defineAsyncCompomponent(()=>import(url))的方式去引入,必须使用import函数式的方式去引入。
20.Teleport传送组件
可以把包裹的内容传送到指定标签的位置,比如body,可以让absolute属性相对于Body定位而不是父relative标签
<Teleport to="body">
<A></A>
</Teleport>
21. keep-alive
包裹在keep-alive中的组件不会被销毁,而是会记录其状态。比如在表单中需要保留用户输入的内容。
-
include属性,接受一个数组,可以只保留某个组件的状态
-
exclude属性,接受一个数组,不保留某个组件的状态
-
max属性,接受一个number,会使用LRU算法剔除不常用的组件
-
新增两个生命周期onActivated和onDeactivated,分别是keep-alive组件的初始化和卸载
22. transition组件
Vue3内置组件,需写上name属性
- .name-enter-from:元素开始的状态
- .name-enter-active:元素变换时的状态,比如transition:all 1.5s ease;
- .name-enter-to:元素结束的状态
- .name-leave-from:离开时的状态
- .name-leave-active
- .name-leave-to: 元素离开到达的位置
某个组件的状态
-
max属性,接受一个number,会使用LRU算法剔除不常用的组件
-
新增两个生命周期onActivated和onDeactivated,分别是keep-alive组件的初始化和卸载
22. transition组件
Vue3内置组件,需写上name属性
- .name-enter-from:元素开始的状态
- .name-enter-active:元素变换时的状态,比如transition:all 1.5s ease;
- .name-enter-to:元素结束的状态
- .name-leave-from:离开时的状态
- .name-leave-active
- .name-leave-to: 元素离开到达的位置