vue3的学习
了解响应式的原理
vue2的响应式原理
实现的原理是通过 Object.defineProperty()
对属性的读取、修改进行拦截(数据劫持)
// 定义数据
let person = { name: '张三', age: 18 }
// 基于 person 创建响应式对象 data
const data = {};
Object.keys(person).forEach(key => {
Object.defineProperty(data, key, {
configurable:true,
get() {
// 此处依赖收集
console.log('访问了', key);
return person[key];
},
set(v) {
// 此处执行回调更新
console.log('访问了', key);
person[key] = v;
}
})
})
读取data.name的时候就会触发get函数,修改触发set函数
存在问题: 新增属性、删除属性, 界面不会更新。 直接通过下标修改数组, 界面不会自动更新,对于数组的一些方法检测不到,只能使用$set
来对数据继续操作,通过重写更新数组的一系列方法来实现拦截。
对象:
this.$set(需要修改的对象,需要修改的值,修改的值)
this.$delete(需要删除的对象,需要删除的值)
或者引入Vue
import Vue from 'vue'
Vue.set(需要修改的对象,需要修改的值,修改的值)
Vue.delete(需要删除的对象,需要删除的值)
数组:
this.Arr[0]= 'hello' Arr(这个是需要修改的数组名) 这种方法修改界面是不会刷新的
解决方法
this.$set(需要修改的数组,数组的索引,修改的值)
this.Arr.splice(索引,索引开始删除的数量,替换的值)
例如:this.Arr.splice(0,1,'hello')
Vue3的响应式
在vue2
存在的问题在vue3得到了解决
vue3
通过Proxy
(代理): 拦截对象中任意属性的变化, 内部基于 ES6
的 Proxy
实现,通过代理对象操作源对象内部数据进行操作,再通过Reflect
(反射): 对源对象的属性进行操作。
const initData = { value: 1 };
const p = new Proxy(initData, {
// 读取p的某个属性时调用
get(target, propName) {
return Reflect.get(target, propName)
},
// 修改p的某个属性、或给p追加某个属性时调用
set(target, propName, value) {
Reflect.set(target, propName, value)
},
// 删除p的某个属性时调用
deleteProperty(target, propName) {
return Reflect.deleteProperty(target, propName)
}
})
计算属性与监视
computed函数
写法:
// 引入
import {computed} from 'vue'
setup() {
// 定义数据
const data = reactive({
a: 'z',
b: 's'
})
// 计算属性——简写
const newData = computed(() => {
return <这里书写操作的数据>
})
// 计算属性——完整
const newData = computed({
get() {
return <这里书写操作的数据>
},
// 基于TS
set(value: string) {
// 这里书写获得了新的数据和操作数据的方法
}
})
return {
data,
newData
}
}
watch函数
监视reactive
定义的响应式数据时:oldValue
无法正确获取、强制开启了深度监视 (deep配置失效)。
监视reactive
定义的响应式数据中某个属性时:deep配置有效。
//情况一:监视ref定义的响应式数据
watch(<需要监视的数据>,(newValue,oldValue)=>{
console.log('sum变化了',newValue,oldValue) // newValue:新值;oldValue:旧值
},{immediate:true})
//情况二:监视多个ref定义的响应式数据
watch([a,b],(newValue,oldValue)=>{
console.log('a或b变化了',newValue,oldValue)
})
// 情况三:监视reactive定义的响应式数据
watch(<reactive定义的响应式数据>,(newValue,oldValue)=>{
console.log('数据变化了',newValue,oldValue)
},{immediate:true,deep:false}) //此处的deep配置不再奏效
//情况四:监视reactive定义的响应式数据中的某个属性
watch(()=>a.b,(newValue,oldValue)=>{
console.log('a的b变化了',newValue,oldValue)
},{immediate:true,deep:true})
//情况五:监视reactive定义的响应式数据中的某些属性
watch([()=>a.b,()=>a.c],(newValue,oldValue)=>{
console.log('a的属性变化了',newValue,oldValue)
},{immediate:true,deep:true})
//特殊情况
watch(()=>a.b,(newValue,oldValue)=>{
console.log('a的b变化了',newValue,oldValue)
},{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效
watchEffect函数
watchEffect
所指定的回调中用到的数据只要发生变化,则直接重新执行回调
watch
既要指明监视的属性,也要指明监视的回调
watchEffect
会监视函数内部使用的值
watchEffect(()=>{
const x1 = a.b
const x2 = c.d
console.log('watchEffect配置的回调执行了')
})
这里就会去监视a的b属性,c的d属性
provide 与 inject
主要用于父与自己后代的一种通信方式,有点像vue2
的消息订阅发布,全局事件总线
父组件有一个 provide
选项来提供数据,后代组件有一个 inject
选项来开始使用这 些数据,如下所示:
父组件:
setup(){
...
// 定义数据
let data = reactive({name:'zs',age:'18'})
provide('data',data)
...
}
后代组件:
setup(){
...
const data = inject('data')
return {data}
...
}
新的组件
Fragment
在vue2
中,文件的template
标签下必须要有一个根标签,也就是说必须要有一个div包裹,但在vue3
中可以不需要用div包裹,因为在vue3
的渲染的时候,会自动给我们添加Fragment
标签,使用vue
的调试工具可以查看
Teleport
这个组件常用于弹窗,能够将我们的 组件html
结构移动到指定位置的技术。
示例:
<teleport to="移动位置">
<div v-if="isShow" class="mask">
<div class="dialog">
<h3>我是一个弹窗</h3>
<button @click="isShow =false">关闭弹窗</button>
</div>
</div>
</teleport>
Suspense
该组件用于优化性能,在网络很慢的时候,不需要等待自己定义的组件完全加载完再去呈现内容,会提前加载静态的组件,可以理解为图片的懒加载,首先我们需要将自己定义的组件实现异步引入:
import { defineAsyncComponent } from 'vue'
const demo = defineAsyncComponent(()=>import('./components/demo.vue'))
接下来我们需要使用 Suspense
包裹组件,并配置好 default
与 fallback
,因为Suspense
的底层是使用插槽实现的,将需要展示的写在default
下,将懒加载的组件写在fallback
下:
<template>
<div class="app">
<Suspense>
<template v-slot:default>
<demo />
</template>
<template v-slot:fallback>
<h3>加载中.....</h3>
</template>
</Suspense>
</div>
</template>
全局API的转移
vue3
针对vue2
的这些API做出了调整:
2.x 全局 API( Vue ) 3.x 实例 API ( app )
Vue.config.xxxx app.config.xxxx
Vue.config.productionTip 移除
Vue.component app.component
Vue.directive app.directive
Vue.mixin app.mixin
Vue.use app.use
Vue.prototype app.config.globalProperties
同时vue3
移除keyCode
作为 v-on
的修饰符,同时也不再支持 config.keyCodes
移除 v-on.native
修饰符,需要指定的原生事件只需要说明即可,例如:
父组件中绑定事件:
<my-component
v-on:close="addList"
v-on:click="CheckList"
/>
子组件中声明自定义事件:
<script>
export default {
// 没有在emits注明,vue3会将click作为原生的点击事件
emits: ['close']
}
</script>
移除过滤器(filter
),官方建议把那些逻辑写在计算属性上
更多详细内容见vue3
官网:<Vue.js (vuejs.org)>