一文帶你深入理解Vue3響應式原理
響應式原理
Vue2 使用的是 Object.defineProperty Vue3 使用的是 Proxy
2.0的不足
對象只能劫持 設置好的數據,新增的數據需要Vue.Set(xxx) 數組只能操作七種方法,修改某一項值無法劫持。
reactive和effect的實現
export const reactive = <T extends object>(target:T) => {
return new Proxy(target,{
get (target,key,receiver) {
const res = Reflect.get(target,key,receiver) as object
return res
},
set (target,key,value,receiver) {
const res = Reflect.set(target,key,value,receiver)
return res
}
})
}Vue3 的響應式原理依賴了 Proxy 這個核心 API,通過 Proxy 可以劫持對象的某些操作。
effect track trigger
實現effect 副作用函數
let activeEffect;
export const effect = (fn:Function) => {
const _effect = function () {
activeEffect = _effect;
fn()
}
_effect()
}使用一個全局變量 active 收集當前副作用函數,并且初始化的時候調用一下
實現track
const targetMap = new WeakMap()
export const track = (target,key) =>{
let depsMap = targetMap.get(target)
if(!depsMap){
depsMap = new Map()
targetMap.set(target,depsMap)
}
let deps = depsMap.get(key)
if(!deps){
deps = new Set()
depsMap.set(key,deps)
}
deps.add(activeEffect)
}執(zhí)行完成成后我們得到一個如下的數據結構

實現trigger
export const trigger = (target,key) => {
const depsMap = targetMap.get(target)
const deps = depsMap.get(key)
deps.forEach(effect=>effect())
}當我們進行賦值的時候會調用 set 然后 觸發(fā)收集的副作用函數
import {track,trigger} from './effect'
export const reactive = <T extends object>(target:T) => {
return new Proxy(target,{
get (target,key,receiver) {
const res = Reflect.get(target,key,receiver) as object
track(target,key)
return res
},
set (target,key,value,receiver) {
const res = Reflect.set(target,key,value,receiver)
trigger(target,key)
return res
}
})
}給 reactive 添加這兩個方法
測試代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
</div>
<script type="module">
import { reactive } from './reactive.js'
import { effect } from './effect.js'
const user = reactive({
name: "小滿",
age: 18
})
effect(() => {
document.querySelector('#app').innerText = `${user.name} - ${user.age}`
})
setTimeout(()=>{
user.name = '大滿很厲害'
setTimeout(()=>{
user.age = '23'
},1000)
},2000)
</script>
</body>
</html>遞歸實現reactive
import { track, trigger } from './effect'
const isObject = (target) => target != null && typeof target == 'object'
export const reactive = <T extends object>(target: T) => {
return new Proxy(target, {
get(target, key, receiver) {
const res = Reflect.get(target, key, receiver) as object
track(target, key)
if (isObject(res)) {
return reactive(res)
}
return res
},
set(target, key, value, receiver) {
const res = Reflect.set(target, key, value, receiver)
trigger(target, key)
return res
}
})
}<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
</div>
<script type="module">
import { reactive } from './reactive.js'
import { effect } from './effect.js'
const user = reactive({
name: "小滿",
age: 18,
foo:{
bar:{
sss:123
}
}
})
effect(() => {
document.querySelector('#app').innerText = `${user.name} - ${user.age}-${user.foo.bar.sss}`
})
setTimeout(()=>{
user.name = '大滿很厲害'
setTimeout(()=>{
user.age = '23'
setTimeout(()=>{
user.foo.bar.sss = 66666666
},1000)
},1000)
},2000)
</script>
</body>
</html>
總結
到此這篇關于Vue3響應式原理的文章就介紹到這了,更多相關Vue3響應式原理內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
vue-cli3.0如何使用prerender-spa-plugin插件預渲染
這篇文章主要介紹了vue-cli3.0如何使用prerender-spa-plugin插件預渲染,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-05-05

