一文詳解Vue3響應(yīng)式原理
回顧 vue2.x 的響應(yīng)式
實(shí)現(xiàn)原理:
- 對(duì)象類型:通過(guò)
object.defineProperty()
對(duì)屬性的讀取、修改進(jìn)行攔截(數(shù)據(jù)劫持) - 數(shù)組類型:通過(guò)重寫(xiě)更新數(shù)組的一系列方法來(lái)實(shí)現(xiàn)攔截(對(duì)數(shù)組的變更方法進(jìn)行了包裹)
Object.defineProperty(data,'count ",{ get(){}, set(){} })
存在問(wèn)題:
- 新增屬性、刪除屬性,界面不會(huì)更新
- 直接通過(guò)下標(biāo)修改數(shù)組,界面不會(huì)自動(dòng)更新
但是 vue2 給了解決方法,我們看以下代碼:
<template> <div> <h2>我是vue2寫(xiě)的效果</h2> <h4 v-show="person.name">姓名:{{person.name}}</h4> <h4>年齡:{{person.age}}</h4> <h4 v-show="person.sex">性別:{{person.sex}}</h4> <h4>愛(ài)好:{{person.hobby}}</h4> <button @click="addSex">添加sex屬性</button> <button @click="deleteName">刪除name屬性</button> <button @click="changeHobby">修改愛(ài)好</button> </div> </template> <script> import Vue from 'vue' export default { name: 'App', data(){ return{ person:{ name:'張三', age:18, hobby:['學(xué)習(xí)','吃飯'] } } }, methods:{ addSex(){ //這樣直接加是不行的 //this.person.sex = '男' this.$set(this.person,"sex",'男') //Vue.set(this.person,"sex",'男') }, deleteName(){ //這樣直接加是不行的 //delete this.person.name //this.$delete(this.person,'name') Vue.delete(this.person,"name") }, changeHobby(){ //這樣直接加是不行的 //this.person.hobby[0] = '逛街' //可以這樣 this.$set(this.person.hobby,0,'逛街') //或 //this.person.hobby.splice(0,1,"逛街") }, } } </script>
我們可以用 js 模擬 vue2 的響應(yīng)式:
<script> //源數(shù)據(jù) let person = { name:"張三", age:18 } let p = {} //模擬vue2實(shí)現(xiàn)響應(yīng)式 Object.defineProperty(p,"name",{ configurable:true, get() {//有人讀取name時(shí)調(diào)用 return person.name }, set(v) { person.name = v console.log("有人修改了name屬性,我發(fā)現(xiàn)了,我要去更新界面"); } }) Object.defineProperty(p,"age",{ get() {//有人讀取age時(shí)調(diào)用 return person.age }, set(v) { person.age = v console.log("有人修改了age屬性,我發(fā)現(xiàn)了,我要去更新界面"); } }) </script>
先輸出 person,然后看下 p,當(dāng)修改 name 或 age 時(shí)會(huì)檢測(cè)到
它的問(wèn)題是,如果增加一個(gè) sex 屬性,vue 不會(huì)檢測(cè)到,雖然增加了 sex 屬性,但它不像 name 和 age 有 getter 和 setter,不是響應(yīng)式的
同樣,當(dāng)刪除 name 屬性時(shí)也監(jiān)測(cè)不到
vue3的響應(yīng)式
<template> <h1>一個(gè)人的信息</h1> <h3 v-show="person.name">姓名:{{ person.name }}</h3> <h3>年齡:{{ person.age }}</h3> <h3 v-show="person.sex">性別:{{ person.sex }}</h3> ...... <button @click="changeInfo">修改人的信息</button> <button @click="addSex">添加一個(gè)sex屬性</button> <button @click="deleteName">刪除一個(gè)name屬性</button> </template> <script> import {reactive} from 'vue' export default { name: 'App', setup() { ...... function changeInfo() { ...... person.hobby[0] = '學(xué)習(xí)' } function addSex() { person.sex = "男" } function deleteName() { delete person.name } return { ...... addSex, deleteName } } } </script>
模擬 vue3 中的響應(yīng)式:
<script> //源數(shù)據(jù) let person = { name:"張三", age:18 } const p = new Proxy(person,{ //有人讀取p的某個(gè)屬性時(shí)調(diào)用 get(target, p, receiver) { console.log(`有人讀取了p身上的${p}屬性`); //return target[p] return Reflect.get(target,p) }, //有人修改、增加p的某個(gè)屬性時(shí)調(diào)用 set(target, p, value, receiver) { console.log(`有人修改了p身上的${p},我要去更新界面了`); //target[p] = value Reflect.set(target,p,value) }, //有人刪除p的某個(gè)屬性時(shí)調(diào)用 deleteProperty(target, p) { console.log(`有人刪除了p身上的${p},我要去更新界面了`); //return delete target[p] return Reflect.deleteProperty(target,p) } }) </script>
實(shí)現(xiàn)原理:
- 通過(guò)Proxy(代理)∶攔截對(duì)象中任意屬性的變化,包括:屬性值的讀寫(xiě)、屬性的添加、屬性的刪除等
- 通過(guò)Reflect(反射):對(duì)源對(duì)象的屬性進(jìn)行操作
MDN文檔中描述的 Proxy 與 Reflect
Reflect 是一個(gè)內(nèi)置的對(duì)象,它提供攔截 JavaScript 操作的方法。Reflect不是一個(gè)函數(shù)對(duì)象,因此它是不可構(gòu)造的。
new Proxy(data,{ //攔截讀取屬性值 get (target, prop){ return Reflect.get(target,prop) }, //攔截設(shè)置屬性值或添加新屬性 set (target,prop, value) { return Reflect.set(target,prop, value) }, //攔截刪除屬性 deleteProperty (target,prop) { return Reflect.deleteProperty(target,prop) } }) proxy.name = "tom"
到此這篇關(guān)于一文詳解Vue3響應(yīng)式原理的文章就介紹到這了,更多相關(guān)Vue3響應(yīng)式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue頁(yè)面切換項(xiàng)目實(shí)現(xiàn)轉(zhuǎn)場(chǎng)動(dòng)畫(huà)的方法
這篇文章主要介紹了vue頁(yè)面切換項(xiàng)目實(shí)現(xiàn)轉(zhuǎn)場(chǎng)動(dòng)畫(huà)的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11Vue組件通信入門(mén)之Provide和Inject機(jī)制
這篇文章主要給大家介紹了關(guān)于Vue組件通信入門(mén)之Provide和Inject機(jī)制的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Vue組件通信具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12vue scroll滾動(dòng)判斷的實(shí)現(xiàn)(是否滾動(dòng)到底部、滾動(dòng)方向、滾動(dòng)節(jié)流、獲取滾動(dòng)區(qū)域dom元素)
這篇文章主要介紹了vue scroll滾動(dòng)判斷的實(shí)現(xiàn)(是否滾動(dòng)到底部、滾動(dòng)方向、滾動(dòng)節(jié)流、獲取滾動(dòng)區(qū)域dom元素),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06關(guān)于vue-cli-service:command?not?found報(bào)錯(cuò)引發(fā)的實(shí)戰(zhàn)案例
這篇文章主要給大家介紹了關(guān)于vue-cli-service:command?not?found報(bào)錯(cuò)引發(fā)的相關(guān)資料,文中通過(guò)實(shí)例代碼將解決的過(guò)程介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2023-02-02vue項(xiàng)目開(kāi)發(fā)中setTimeout等定時(shí)器的管理問(wèn)題
這篇文章主要介紹了vue項(xiàng)目開(kāi)發(fā)中setTimeout等定時(shí)器的管理問(wèn)題,需要的朋友可以參考下2018-09-09詳解vue微信網(wǎng)頁(yè)授權(quán)最終解決方案
這篇文章主要介紹了 詳解vue微信網(wǎng)頁(yè)授權(quán)最終解決方案,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-06-06Ant design vue中的聯(lián)動(dòng)選擇取消操作
這篇文章主要介紹了Ant design vue中的聯(lián)動(dòng)選擇取消操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10VUE+Express+MongoDB前后端分離實(shí)現(xiàn)一個(gè)便簽墻
這篇文章主要介紹了VUE+Express+MongoDB前后端分離實(shí)現(xiàn)一個(gè)便簽墻,需要的朋友可以參考下2021-04-04