Vue3.0
# Vue3.0
# Vue3.0是如何变快的
# diff方法优化
- Vue2中的虚拟dom是进行全量的对比
- Vue3新增了静态标记(PatchFlag)
- 在创建虚拟DOM的时候,会根据DOM中的内容会不会发生变化,添加静态标记
# hoistStatic静态提升
- Vue2中无论元素是否参与更新,每次都会重新创建,然后再渲染
- Vue3中对于不参与更新的元素,会做静态提升,只被创建一次,在渲染时直接复用即可
# cacheHandlers事件侦听器缓存
- 默认情况下onClick会被视为动态绑定,每次都会去追踪它的变化
- 但是因为同一个函数,所以没有追踪变化,直接缓存起来复用即可
# 创建Vue3项目
# 什么是Vite
- Vite是Vue作者开发的一款意图取代webpack的工具
- 其实现原理是利用ES6的import会发送请求去加载文件的特性,拦截这些请求,做一些编译,省去webpack冗余的打包时间
# 安装Vite
npm i -g create-vite-app
# 利用Vite创建Vue3项目
create-vite-app projectName
# 安装依赖运行项目
- cd projectName
- npm install
- npm run dev
# setup
setup函数是组合API(Composition API)的入口函数,setup只能是同步的函数
两个参数
- props :接受父组件传递过来的值
- context
- context.emit :向父组件传递数据,并类似于接受数据props一样要声明方法,如emits:['hello']
- context.attrs :与vue2一样拿props不要的数据
- context.slots :插槽语法 (vue2使用v-slot="名字",vue3使用v-slot:名字)
要想让变量为动态的则需要导入ref
在组合API中定义的变量/方法,要想在外界使用,必须通过return {xxx,xxx}暴露出去
import {ref} from 'vue'
setup(){
let count = ref(0)
}
return {count}
2
3
4
5
注意:ref函数只能监听简单类型的变化,不能监听复杂类型的变化
- reactive函数监听复杂类型的变化(对象/数组)
import {reactive} from 'vue'
setup(){
let state = reactive({
stus:[]
})
return {state}
}
2
3
4
5
6
7
# setup本质
- Composition API注入到Option API中(data,methods)
# setup执行时机
顺序:执行在beforeCreate之前
- beforeCreate:表示组件刚刚被创建出来,组件的data和methods还没有初始化好
- setup:不能够使用data和methods的,只能是同步的,不能是异步的
- Created:表示组件刚刚被创建出来,并且组件的data和methods已经初始化好
# reactive
本质:就是将传入的数据包装成一个Proxy对象
- reactive是Vue3中提供的实现响应式数据的方法
- 在Vue2中响应式数据是通过defineProperty来实现的
- 在Vue3中响应式数据是通过ES6的Proxy来实现的
注意:
- reactive参数必须是对象(json/arr)
- 如果reactive传递了其它对象(内置对象)
- 默认情况下修改对象,界面不会自动更新
- 如果想更新,可以通过重新赋值的方式
# ref
本质:ref底层其实还是reactive
- ref和reactive一样,也是实现响应式数据的方法
- 系统会根据我们给ref传入的值将它转换成ref(xxx) -> reactive({value:xxx})
注意:
- 在Vue中使用ref的值不用通过value获取,需要通过变量.value来更改值
- 在JS中使用ref的值必须通过value获取
- 如果是通过ref创建的数据,那么在template中使用的时候不用通过.value来获取,因为Vue会自动给我们添加.value
# ref和reactive的区别
- 如果在template里使用的是ref类型,那么Vue会自动帮我们添加.value
- 如果在template里使用的是reactive类型,那么Vue不会自动帮我们添加.value
Vue是如何决定是否需要添加.value的
- Vue在解析数据之前,会自动判断这个数据是否是ref类型的
- 如果是就自动添加.value,如果不是就不自动添加.value
Vue是如何判断当前的数据是否是ref类型的
- 通过当前数据的
__v_ref
来判断的 - 如果有这个私有的属性,并且取值为true,那么就代表是一个ref类型的数据
- 可以通过isRef,isReactive来判断,import {isRef,isReactive} from 'vue'
# 递归监听
默认情况下,无论是通过ref还是reactive都是递归监听
注意:如果数据量较大,非常消耗性能
# 非递归监听
通过shallowRef和sallowReactive只能监听第一层数据
- 只监听第一层(对象)数据,只有第一层更改为了Proxy对象
- 只要第一层的数据改变,后面的也会改变
- 如果是通过shallowRef创建的数据,那么Vue监听的是.value的变化,并不是第一层的变化
- triggerRef能够主动更新数据,Vue3没有triggerReactive
注意:只有在需要监听的数据量比较大的时候,我们才使用shallowRef/shallowReactive
# toRaw
将响应式数据改为原始数据,只能处理reactive的响应式数据
- 从Reactive或Ref中得到原始数据
- 做一些不想被监听的事情(提高性能)
- 获取reactive的原始数据let obj2 = toRaw(state)
- 获取ref的原始数据let obj2 = toRaw(state.value)
# markRaw
不想有响应式数据,不想被追踪
# toRef
- 如果利用ref将某一个对象中的属性变成响应式的数据,我们修改响应式的数据是不会影响到原始数据的
- 如果利用toRef将某个对象中的属性变成响应式的数据,我们修改响应式的数据是会影响到原始数据的,但是如果响应式的数据是通过toRef创建的,那么修改了数据并不会触发UI界面的更新
使用:如果想让响应式数据和以前的数据关联起来,并且更新响应式数据之后还不想更新UI,那么就可以用toRef
# toRefs
多个变量变成响应式数据
# customRef
- 返回一个ref对象,可以显示地控制依赖追踪和触发响应
- 自定义Ref
# 通过Ref获取界面是上的元素
- 因为setup是在beforeCreate和Created之间执行的,所以获取不到元素,要用到生命周期函数onMounted获取
# readonly
readonly用于创建只读的数据,并且是递归只读(每一层都是只读的)
shallowReadonly用于创建第一层只读的数据
isReadonly用于判断是否为readonly
# const和readonly的区别
- const:赋值保护,不能给变量重新赋值
- readonly:属性保护,不能给属性重新赋值
const value = {name:'zs',age:123}
value.name = 'ls'
value.age = 666 //value的属性值都会改变
2
3
# 手写Proxy
传递对象
传递数组
let arr = [1,3,5]
注意:修改完数组后必须要return true,因为数组长度会改变,否则会报错
# shallowReactive
只处理对象最外层属性的响应式(浅响应式)
# shallowRef
只处理基本数据类型的响应式,不进行对象的响应式处理
# 计算属性
import {computed} from 'vue'
setup(){
//计算属性简写
let fullName = computed(() => {
return person.firstName + '-' + person.lastName
})
//计算属性完整写法
let fullName = computed({
get(){
return person.firstName + '-' + person.lastName
}
set(value){
const nameArr = value.spilt('-')
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
})
return {person}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# watch
- ref定义的数据可以获取newValue、oldValue
- reactive定义的数据只能获取newValue,无法获取oldValue
- reactive强制开启了深度监视=>deep:true(多层嵌套,即多个对象,深度监视),配置了deep也无效
import {ref,watch} from 'vue'
setup(){
let sum = ref(0)
let msg = ref('你好啊')
//情况一:监听ref所定义的一个响应式数据(页面刚进去immediate参数为直接监听)
watch(sum,(newValue,oldValue) => {
console.log('sum变了',newValue,oldValue)
},{immediate:true,deep:true})
//情况二:监听ref所定义的多个响应式数据
watch([sum,msg],(newValue,oldValue) => {
console.log('sum或msg变了',newValue,oldValue)
},{immediate:true})
//情况三:监视reactive所定义的一个响应式数据的全部属性
watch(person,(newValue,oldValue) => {
console.log('person变了',newValue,oldValue)
},{deep:true}) //此处的deep配置无效
//情况四:监视reactive所定义的一个响应式数据的某个属性
watch(()=>person.age,(newValue,oldValue) => {
console.log('age变了',newValue,oldValue)
})
//情况五:监视reactive所定义的一个响应式数据的某些属性
watch([()=>person.name,()=>person.age],(newValue,oldValue) => {
console.log('person的name和age变了',newValue,oldValue)
})
//特殊情况,job是一个对象
watch(()=>person.job,(newValue,oldValue) => {
console.log('person的job变了',newValue,oldValue)
},{deep:true}) //此处必须加deep深度监视
return {sum,msg}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# watchEffect
- 不用指明监视哪个属性,监视回调用到哪个属性,那就监视哪个属性
- watchEffect有点像computed:
- computed注重计算出来的值,所以必须要写返回值
- watchEffect更注重的是过程,所以不用写返回值
import {watchEffect} from 'vue'
setup(){
watchEffect(() => {
const x1 = sum.value //监视了
const x2 = person.job.j1.salary //监视了
console.log('watchEffect所指定的回调执行了')
})
}
2
3
4
5
6
7
8
# 生命周期
setup(){
//组合式API的形式去使用生命周期钩子
onBeforeMount(()=>{})
onMounted(()=>{})
onBeforeUpdate(()=>{})
onUpdate(()=>{})
onBeforeUnmount(()=>{})
onUnmounted(()=>{})
}
//通过配置项形式使用生命周期钩子
beforeCreate(){},
created(){},
beforeMount(){},
mounted(){},
beforeUpdate(){},
updated(){},
beforeUnmount(){},
Unmounted(){}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# provide/inject
祖与后代组件间的通信
1、祖组件中
setup(){
let car = reactive({name:'奔驰',price:'40万'})
provide('car',car)
}
2
3
4
2、后代组件中
setup(){
const car = inject('car')
return {car}
}
//调用
car.name
car.price
2
3
4
5
6
7
# 响应式数据的判断
- isRef
- isReactive
- isReadonly
- isProxy:检查一个对象是否是由reactive或者rendonly方法创建的代理
# Fragment
- 在Vue3中,组件可以没用根标签,内部会将多个标签包在一个Fragmenet虚拟元素中
- 好处:减小标签层级,减小内存占用
# Teleport
将teleport里面的内容移动到body下
<teleport to="body">
//内容
</teleport>
2
3
# Suspense
等待异步组件时渲染一些额外内容,让应用有更好的用户体验
使用步骤
- 异步引入组件
import {defineAsyncComponent} from 'vue' const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
1
2- Suspense引用
<Suspense> <template v-slot:default> <Child/> </template> <template v-slot:fallback> <h3>加载中</h3> </template> </Suspense>
1
2
3
4
5
6
7
8
# Vue.prototype迁移
Vue.prototype=>app.config.globalProperties