国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

Vue.js設計與實現之設計一個完善的響應系統

開發 前端
在本文中簡單實現了可以進行依賴收集的響應式系統,使用WeakMap配合Map構建了新的存儲結構,能夠在響應式數據和副作用函數之間建立更加精確的聯系。

1.寫在前面

響應系統是Vue.js的重要組成部分,我們要實現一個簡易的響應式系統,必須先要了解什么是響應式數據和副作用函數。在實現過程中,我們需要考慮如何避免無限遞歸,為什么需要嵌套副作用函數,以及多個副作用函數之間會產生什么影響?

2.副作用函數

所謂副作用函數,指的是會產生副作用的函數,而副作用指的是函數effect的執行會直接或間接影響到其它函數的執行,那么就說effect函數產生了副作用,effect就是副作用函數。

<div id="app"></div>

<script>
//全局變量
let state = {
name:"onechuan",
age:18,
address:"北京"
}
function effect(){
app.innerHTML = "hello pingping," + state.name + "," + state.age + "," + state.address;
}
effect();

setTimeout(()=>{
//修改全局變量,產生副作用
state.address = "廣州";
},1000)
</script>

在上面的代碼片段中,副作用函數effect會設置id為app的標簽innerHTML屬性 app.innerHTML = "hello pingping," + state.name + "," + state.age + "," + state.address;,其中state.address的值為"北京"。而當state.address發生變化時,希望副作用函數effect能夠重新執行,state.address的值變為"廣州"。

當前在setTimeout函數中代碼修改了state.address的值,除了對象的值本身發生變化外,沒有其他任何變化,達不到要求的效果。如果希望值變化后副作用函數立即更新,那么state對象數據就必須是響應式的,那么什么是響應式的,應該如何讓state實現響應式呢?

3.響應式數據

對上面的要求進行分析,要想讓state變成響應式數據,需要滿足兩個條件:

  • 在副作用函數effect執行時,從對象state中讀取address的值,觸發讀取操作
  • 當修改state.address的值時,把對象state中的address的值進行修改,觸發設置操作

再次思考,響應式數據的實現就變成了攔截對象進行取值和設值操作。當從state對象中讀取address時,就將副作用函數effect存儲到容器中,當設置state對象中的address值的時候,從容器中取出effect函數并執行。

取值操作

設置操作

那么,到底應該如何實現對一個對象屬性的讀取和設置操作呢?在Vue.js2中采用的是Object.defineProperty函數實現的,而在Vue.js3中則是采用Proxy代理對象的方法實現的。我們根據上面的思路和流程圖,先簡易實現個最low的攔截取值設置操作。

<div id="app"></div>
<script>
//全局變量
let state = {
name:"onechuan",
age:18,
address:"北京"
}

// 存儲副作用函數的桶
const bucket = new Set();

// 對原始數據的代理
const obj = new Proxy(state,{
// 攔截讀取操作
get(target, key){
// 將副作用函數effect添加到存儲副作用函數的桶中
bucket.add(effect)
// 返回屬性值
return target[key]
},
// 攔截設置操作
set(target, key, newVal){
// 設置屬性值
target[key] = newVal
// 把副作用函數從桶里取出并執行
bucket.forEach(fn=>fn())
// 返回true代表設置操作成功
return true
}
})

function effect(){
const app = document.querySelector("#app");
app.innerHTML = obj.name + "," + obj.age + "," + obj.address;
}

effect();

setTimeout(()=>{
//修改全局變量,產生副作用
obj.address = "廣州";
},1000)
</script>

在瀏覽器中渲染得到:

1s后頁面更新渲染為:

看到上面的代碼片段,不禁想問為什么要將存儲副作用函數的容器類型設置為Set類型,這是因為對于同一個對象屬性進行多次代理就會出現死循環的情況,對此使用Set可以用于去重。

state是被代理的原始數據,而obj是采用Proxy進行代理后的對象數據,在其中實現了攔截和取值設值操作,在取值和設置過程中實現了副作用函數effect的存儲和取出執行的操作。

4.尚且完善的響應式系統

為什么說是尚且完善的響應式系統,這是因為在本段中將循序漸進介紹,如何實現一個功能尚且完善的響應式系統。可以實現通用式的副作用函數,匿名函數也能夠被收集到副作用函數容器中,而非命名的effect函數。

注冊副作用函數

要實現這一點,只需要編寫一個通用函數,提供注冊副作用函數機制即可。

// 全局變量用于存儲當前被注冊的副作用函數
let activeEffect;
// effect用于注冊副作用函數
function effect(fn){
// 當調用effect注冊副作用函數時,將副作用函數fn賦值給activeEffect
activeEffect = fn;
// 執行副作用函數
fn();
}

effect(()=>{
app.innerHTML = state.name + "," + state.age + "," + state.address;
})

在上面代碼片段中,傳遞一個閉包即可實現注冊副作用函數的功能,當effect函數執行時,先將effect傳遞的閉包函數暫存到變量activeEffect,作為當前注冊的副作用函數。

//原始數據
let state = {
name:"onechuan",
age:18,
address:"北京"
}
// 存儲副作用函數的桶
const bucket = new Set();
// 對原始數據的代理
const obj = new Proxy(state,{
// 攔截讀取操作
get(target, key){
// 將activeEffect存儲的副作用函數收集到桶里
if(activeEffect){
bucket.add(activeEffect)
}
// 返回屬性值
return target[key]
},
// 攔截設置操作
set(target, key, newVal){
// 設置屬性值
target[key] = newVal
// 把副作用函數從桶里取出并執行
bucket.forEach(fn=>fn())
// 返回true代表設置操作成功
return true
}
})

effect(()=>{
app.innerHTML = state.name + "," + state.age + "," + state.address;
})

setTimeout(()=>{
//修改全局變量,產生副作用
obj.address = "廣州";
},1000)

當我們在響應式數據obj上設置一個不存在的屬性時,副作用函數并不會去對象上讀取這個屬性的值,也就是這個不存在的屬性并沒有與副作用函數建立響應聯系。原本不應該觸發副作用函數中的匿名函數,但是實際上卻觸發了effect函數的執行,這也印證了我們當前設計的系統還存在缺陷。

之所以出現上面的問題,這是因為在沒有副作用函數與被操作的目標字段之間建立明確的關系,這就是為什么在Vue.js3實際設計中沒有簡單使用Set類型的原因。為了解決這種問題,我們只需要在副作用函數與被操作字段間建立聯系即可,重新設計收集副作用函數的容器數據結構。

依賴收集的數據結構

要重新設計副作用函數的容器數據結構,需要我們分析effect函數的執行機制,這段代碼中存在三個重要部分:

  • 被操作(讀取)的代理對象obj (target對象)
  • 被操作(讀取)的屬性名稱address (target對象的鍵名)
  • 使用effect函數注冊的副作用函數effectFn

三者建立的關系是:

|-target
|- key
|- effectFn

對于上面的分析,我們得先重新設計存儲副作用函數的依賴收集容器的數據結構,創建WeakMap用于存儲對象,Set用于存儲副作用函數。

// 創建存儲副作用函數的桶
const bucket = new WeakMap();
// 全局變量用于存儲被注冊的副作用函數
let activeEffect;

// 響應式函數
const obj = new Proxy(state,{
// 攔截讀取操作
get(target, key){
// 沒有activeEffect
if(!activeEffect) return
// 根據目標對象從桶中獲得副作用函數
let depsMap = bucket.get(target);
// 判斷是否存在,不存在則創建一個Map
if(!depsMap) bucket.set(target, depsMap = new Map())
// 根據key從depsMap取的deps,存儲著與key相關的副作用函數
let deps = depsMap.get(key);
// 判斷key對應的副作用函數是否存在
if(!deps) depsMap.set(key, deps = new Set())
// 最后將激活的副作用函數添加到桶里
deps.add(activeEffect)
// 返回屬性值
return target[key]
},
// 攔截設值操作
set(target, key, newVal){
// 設置屬性值
target[key] = newVal;
// 根據target從桶中取的depMaps
const depMaps = bucket.get(target);
// 判斷是否存在
if(!depMaps) return
// 根據key值取得對應的副作用函數
const effects = depMaps.get(key);
// 執行副作用函數
effects && effects.forEach(fn=>fn())
}
})

在上面的代碼片段中,所寫WeakMap、Map和Set的數據結構關系如下圖所示。三者的具體作用:

  • WeakMap用于存儲代理對象target,用于存儲和判斷當前對象是否已經被Proxy進行代理過。如果被代理過則直接返回WeakMap中的代理對象,如果沒有被代理過則使用Proxy進行代理后存儲,從而避免同一個對象被代理多次。
  • Map用于存儲經過Proxy代理的對象的屬性名
  • Set用于存儲Map中對應的每個屬性的副作用函數,可以用于去重,避免多次調用

為什么使用WeakMap作為存儲對象的容器呢?

這是因為WeakMap是弱引用的Map,不會影響到垃圾回收機制的正常工作,WeakMap多引用的對象執行完畢后,會將對象從內存中移除,從而避免內存泄漏。所以WeakMap經常用于存儲那些只有當key所引用對象存在時(沒有被回收)才有價值的信息。

在前面代碼片段中,如果target對象沒有任何引用了,說明用戶沒有使用它,此時垃圾回收機制就可以將其進行清除,從而避免內存溢出。

整理抽取代碼

將前面的代碼片段進行抽取函數,封裝得到track和trigger函數,使得我們的代碼邏輯更加清晰明了,也能帶給我們更大的靈活性。

// 全局變量用于存儲被注冊的副作用函數
let activeEffect;
// 創建存儲副作用函數的桶
const bucket = new WeakMap();
// 原始數據
const state = {
name:"pingping",
age:18,
address:"北京"
}

// 響應式函數
const obj = new Proxy(state,{
// 攔截讀取操作
get(target, key){
// 將副作用函數activeEffect添加到存儲副作用函數的WeakMap中
track(target, key)
// 返回屬性值
return target[key]
},
// 攔截設值操作
set(target, key, newVal){
// 設置屬性值
target[key] = newVal;
// 將副作用函數從WeakMap中取出并執行
trigger(target, key)
}
})

// 在get攔截函數中調用追蹤取值函數的變化
function track(target, key){
// 沒有activeEffect
if(!activeEffect) return
// 根據目標對象從桶中獲得副作用函數
let depsMap = bucket.get(target);
// 判斷是否存在,不存在則創建一個Map
if(!depsMap) bucket.set(target, depsMap = new Map())
// 根據key從depsMap取的deps,存儲著與key相關的副作用函數
let deps = depsMap.get(key);
// 判斷key對應的副作用函數是否存在
if(!deps) depsMap.set(key, deps = new Set())
// 最后將激活的副作用函數添加到桶里
deps.add(activeEffect)
}

// 在set攔截函數中調用trigger函數觸發變化
function trigger(target, key){
// 根據target從桶中取的depMaps
const depMaps = bucket.get(target);
// 判斷是否存在
if(!depMaps) return
// 根據key值取得對應的副作用函數
const effects = depMaps.get(key);
// 執行副作用函數
effects && effects.forEach(fn=>fn())
}
// effect用于注冊副作用函數
function effect(fn){
// 當調用effect注冊副作用函數時,將副作用函數fn賦值給activeEffect
activeEffect = fn;
// 執行副作用函數
fn();
}


effect(()=>{
console.log("打印");
document.body.innerText = obj.name + "," + obj.age + "," + obj.address;
})

// 設置一個不存在的屬性時
setTimeout(()=>{
obj.address = "廣州"
},1000)

5.寫在后面

在本文中簡單實現了可以進行依賴收集的響應式系統,使用WeakMap配合Map構建了新的存儲結構,能夠在響應式數據和副作用函數之間建立更加精確的聯系。之所以采用WeakMap存儲引用對象,是因為其是弱引用的,當某個對象不再被使用時會被垃圾回收機制清除。此外,還對響應式系統的代碼進行了功能抽取,對應封裝成調用函數track和trigger。

責任編輯:武曉燕 來源: 前端一碼平川
相關推薦

2022-04-09 17:53:56

Vue.js分支切換嵌套的effect

2022-04-04 16:53:56

Vue.js設計框架

2022-04-01 08:08:27

Vue.js框架命令式

2022-04-17 09:18:11

響應式數據Vue.js

2022-04-25 07:36:21

組件數據函數

2022-04-12 08:08:57

watch函數options封裝

2022-04-18 08:09:44

渲染器DOM掛載Vue.js

2022-04-16 13:59:34

Vue.jsJavascript

2022-04-11 08:03:30

Vue.jscomputed計算屬性

2022-04-14 09:35:03

Vue.js設計Reflect

2022-04-03 15:44:55

Vue.js框架設計設計與實現

2022-05-03 21:18:38

Vue.js組件KeepAlive

2025-09-03 02:46:00

Vue.js響應式變量

2022-04-26 05:55:06

Vue.js異步組件

2022-04-20 09:07:04

Vue.js的事件處理

2022-04-19 23:01:54

Vue.jsDOM節點DOM樹

2017-08-30 17:10:43

前端JavascriptVue.js

2021-01-22 11:47:27

Vue.js響應式代碼

2019-10-15 09:05:07

域插槽組件前端

2018-01-31 15:45:07

前端Vue.js組件
點贊
收藏

51CTO技術棧公眾號

欧美一区二区精品久久911| 最近2019中文字幕第三页视频 | 国产精品vip| 激情小说综合网| 日本视频一区二区三区| av动漫在线播放| 国产欧美日韩三区| 中文字幕一二三区在线观看| 日韩欧美中文第一页| 中文字幕伦理免费在线视频| 亚洲老头老太hd| 亚洲精品一区二区三区中文字幕| 国产91在线播放| 亚洲区国产区| 农民人伦一区二区三区| 亚洲色图欧洲色图婷婷| 成人精品一区二区三区校园激情| 亚洲黄一区二区| 加勒比中文字幕精品| 亚洲最大av在线| 精一区二区三区| 中文久久久久久| 色综合视频一区二区三区高清| 日韩伦理电影网站| 欧美日韩国产成人在线| 中文在线日韩| 丰满少妇大力进入| 黄色成人在线播放| 在线天堂资源| 国产在线高清精品| 国产一区二三区| 免费在线观看麻豆视频 | 欧美人体视频| 久久精品一二三区| 国产亚洲污的网站| 国产三级视频在线| 日韩在线欧美在线| 欧美日韩1区| 亚洲熟妇av一区二区三区漫画| 欧美日韩亚洲国产一区| 欧美日一区二区三区| 99精品国产高清一区二区| 国产精品亚洲一区二区三区妖精| 91欧洲在线视精品在亚洲| 精品国产一区二区三区久久影院 | 欧美极品少妇videossex| 欧美在线免费视频| 国产专区综合网| 女人偷人在线视频| 美女撒尿一区二区三区| 一本色道精品久久一区二区三区| 熟女人妇 成熟妇女系列视频| 337p亚洲精品色噜噜噜| 日韩影视高清在线观看| 一级性生活视频| 欧美性视频一区二区三区| 超碰成人免费| 三年中国中文在线观看免费播放| 一本久久a久久免费精品不卡| 国产精品亚洲一区二区在线观看| 欧美精品一区二区视频| 亚洲高清一区二区三区| 国产精品久久久久久久久免费高清| 91在线网站视频| 国产精品国产三级国产普通话三级| 亚洲国产成人二区| 免费不卡亚洲欧美| 午夜精品免费在线| 久久夜色电影| 国产精品999视频| 亚洲精品aⅴ中文字幕乱码| 午夜精品婷婷| 日韩男人天堂| 欧美精品videos另类日本| 国产精品1024久久| 成年人视频免费在线播放| 国产99在线免费| 午夜欧美在线一二页| 亚洲v天堂v手机在线| 国产精品视频一区二区三区四区五区| 日韩精品一区二区三区中文不卡 | 国产精品免费电影| 91偷拍与自偷拍精品| 亚洲欧美韩国| 婷婷四月色综合| 在线不卡中文字幕播放| 欧美jjzz| 久草在线网址| 99久久精品免费看国产一区二区三区| 亚洲一卡二卡三卡四卡五卡| 日韩大片在线免费观看| 一级黄色特级片| 欧美激情二区三区| 久久精品免视看| 国产免费区一区二区三视频免费| 成人av在线不卡| 少妇高潮久久久久久潘金莲| 成人黄色777网| www欧美在线观看| www日韩视频| 久久久亚洲精选| 国产精品久久久久久久久久久免费看| 99综合99| av激情网站| 国产人妖伪娘一区91| 欧美午夜无遮挡| 激情国产一区| 在线免费av导航| 伊人久久青草| 日韩久久免费电影| 丁香激情综合五月| 亚洲国产中文在线| 含羞草激情视频| 91在线观看免费网站| 欧美在线你懂得| 日韩av网站免费在线| 神马久久资源| 欧美亚洲日本在线观看| 热99在线视频| 在线观看一区不卡| 奇米在线7777在线精品| 精品欧美日韩精品| 激情校园亚洲图片| 91精品久久久久久蜜桃| 日韩欧美激情在线| 成人网页在线观看| 成人看片爽爽爽| 小明精品国产一区二区三区| 精品999在线观看| 亚洲日本中文字幕免费在线不卡| 久久影院视频免费| 日韩欧美精品一区| 日本高清中文字幕在线| 自拍偷拍99| 久久人人爽人人爽人人片av高清| 精品国产福利视频| 奇米888四色在线精品| 图片一区二区| 亚洲最大黄色| 性欧美18一19内谢| 欧美性一区二区三区| 色国产精品一区在线观看| 激情国产一区二区| 国产日产精品一区二区三区四区的观看方式| 国产精品一区二区三区四区色| 亚洲欧美99| 91禁国产网站| 欧美日韩一区成人| 久久综合给合久久狠狠狠97色69| 91综合久久一区二区| 桃花岛tv亚洲品质| 偷拍自拍在线视频| 日韩av新片网| 动漫精品视频| 久久99久久99精品免观看粉嫩| 在线欧美小视频| www国产精品av| 一本综合久久| 欧美天堂社区| 手机在线理论片| 一级片免费在线观看| 欧美视频在线观看视频| 91精品国产综合久久久久久丝袜 | 欧美男男tv网站在线播放| 亚洲成人天堂网| 日本免费高清不卡| 97碰在线观看| 精品999在线播放| 亚洲一区二区精品久久av| 老司机精品视频导航| 九九在线精品| 日韩高清不卡| 毛片在线看片| 天堂资源av| 91视频最新入口| 日本一区二区三区四区在线观看 | 日韩国产小视频| 91久久国产综合久久蜜月精品 | 在线播放亚洲一区| 亚洲图片激情小说| 国产风韵犹存在线视精品| 欧美fxxxxxx另类| 卡通动漫精品一区二区三区| 超碰激情在线| www日韩tube| heyzo视频在线播放| 99久久久精品视频| 久久久99国产精品免费| 一区久久精品| 999久久久精品一区二区| 伊人网在线播放| 免费黄色网址在线观看| 亚洲啪啪aⅴ一区二区三区9色| 18岁视频在线观看| 综合久久国产| 色女人综合av| 欧美久久久久久久| 国产日韩欧美亚洲一区| 亚洲va欧美va国产综合久久| 国产97在线|日韩|