vue中的响应式原理3.x
回顾vue2.x的响应式
实现原理:
对象类型:通过object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。
数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)
0bject.defineProperty(data,'count', {
get(){},
set (){}
})
存在问题:
新增属性、删除属性,界面不会更新。
直接通过下标修改数组,界面不会自动更新。
App.vue
<template>
<div id="app">
<!--v-show="person.name" 如果名字不存在不展示-->
<h1 v-show="person.name">姓名:{{person.name}}</h1>
<h1 v-show="person.age">年龄:{{person.age}}</h1>
<h1>爱好:{{person.hobby}}</h1>
<button @click="deleteName">删除名称</button>
<button @click="updaeAge">修改年龄</button>
<button @click="updaeHobby">修改爱好</button>
</div>
</template>
<script>
import Vue from "vue"
export default {
name: 'App',
data(){
return{
person:{
name:"张三",
age:13,
hobby:["玩游戏","看电视"]
}
}
},
methods:{
updaeAge(){
//Vue.set(this.person,"age",100)
this.$set(this.person,"age",100)
},
deleteName(){
// this.$delete(this.person,"name")
Vue.delete(this.person,"name")
},
updaeHobby(){
//this.person.hobby.splice(0,1,"看电影")
//this.$set(this.person.hobby,0,"看电影")
Vue.set(this.person.hobby,0,"看电影")
}
}
}
</script>
<style>
</style>
vue3.x响应式
实现原理:
通过Proxy(代理)∶拦截对象中任意属性的变化,包括:属性值的读写、属性的添加、属性的删除等
通过Reflect(反射):对被代理对象的属性进行操作。
MDN文档中描述的Proxy与Reflect:
.Proxy: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
. Reflect: https://developer.mozilla.org/zh-CN/docs/WebJavaScript/Reference/Global
App.vue
<template>
<h1>信息</h1>
<h1 v-show="person.name">姓名{{person.name}}</h1>
<h1>年龄:{{person.age}}</h1>
<h1 v-show="person.sex">性别:{{person.sex}}</h1>
<h1>工作类型:{{person.job.type}}</h1>
<h1>工作薪水:{{person.job.salary}}</h1>
<h1>爱好:{{person.hobby}}</h1>
<button @click="geng()">更新数据</button>
<button @click="updateAge()">修改性别</button>
<button @click="deleteName()">删除名字</button>
</template>
<script>
//引用ref和reactive
import {reactive} from "vue"
export default {
name: 'App',
setup(){
//一个大的对象
let person=reactive({
name:"张三",
age:18,
sex:"男",
job:{
type:"前端工程师",
salary:"100k"
},
//数组类型通过reactive进行赋值
hobby:["抽烟","喝酒","吃槟榔"]
})
//点击时更新数据
function geng(){
person.name="李四"
person.age=20
//通过reactive进行更新值
person.job.type="UI设计师"
person.job.salary="200K"
//更新数组的值
person.hobby[0]="学习"
}
//修改年龄
function updateAge(){
person.sex="女"
}
//删除名字
function deleteName(){
delete person.name
}
return{
person,
geng,
updateAge,
deleteName
}
}
}
</script>
vue3.x reactive对比ref
从定义数据角度对比:
ref用来定义:基本类型数据。
reactive用来定义:对象(或数组)类型数据。
备注: ref也可以用来定义对象(或数组)类型数据,它内部会自动通过reactive转为代理对象。
从原理角度对比:
ref通过object.defineProperty()的get与set来实现响应式(数据劫持)。
reactive通过使用Proxy来实现响应式(数据劫持)﹐并通过Reflect操作源对象内部的数据。
从使用角度对比:
ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value .
reactive定义的数据:操作数据与读取数据:均不需要.value )。
vue3.x setup两个注意点
setup执行的时机
在beforeCreate之前执行一次,this是undefined
setup的参数
props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性
context: 上下文对象
attrs:值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性,相当于this.$attrs
slots:收到的插槽内容,相当于this.$slots
emit:分发自定义事件的函数,相当于this.$emit
App.vue
<template>
<Dome @hello="showHelloMsg" msg="你好" school="张三">
<template v-slot:que>
<span>张三</span>
</template>
</Dome>
</template>
<script>
import Dome from './components/Dome.vue'
export default {
name: 'App',
components: { Dome },
setup(){
function showHelloMsg(value){
alert('你出发了hello事件,接收到的参数是:'+value+'')
}
return{
showHelloMsg
}
}
}
</script>
Dome.vue
<template>
<h1>信息</h1>
<h1 v-show="person.name">姓名{{person.name}}</h1>
<h1>年龄:{{person.age}}</h1>
<button @click="test()">测试一下Demo组件的Hello事件</button>
</template>
<script>
import {reactive} from "vue"
export default {
name: 'Dome',
//在app配置的msg和school要在者配置
props:["msg","school"],
//在app配置的点击事件hello
emits:["hello"],
// beforeCreate(){
// console.log("-----beforeCreate-----")
// },
setup(props,context){
// console.log("--setup--",props)
//console.log("--context--",context.attrs)//详单与vue2中的$attrs
//console.log("--context--",emit)//触发自定义事件
console.log("--setup--",context.slots)//插槽
//数据
let person=reactive({
name:"张三",
age:18,
})
//方法
function test(){
//触发hello事件
context.emit("hello",666)
}
return{
person,
test
}
}
}
</script>