成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專(zhuān)欄INFORMATION COLUMN

使用 Proxy 實(shí)現(xiàn)簡(jiǎn)單的 MVVM 模型

BetaRabbit / 463人閱讀

摘要:綁定實(shí)現(xiàn)的歷史綁定的基礎(chǔ)是事件。但臟檢查機(jī)制隨之帶來(lái)的就是性能問(wèn)題。是谷歌對(duì)于簡(jiǎn)化雙向綁定機(jī)制的嘗試,在中引入。掙扎了一段時(shí)間后谷歌團(tuán)隊(duì)宣布收回的提議,并在中完全刪除了實(shí)現(xiàn)。自然全軍覆沒(méi)其他各大瀏覽器實(shí)現(xiàn)的時(shí)間也較晚。

綁定實(shí)現(xiàn)的歷史

綁定的基礎(chǔ)是 propertyChange 事件。如何得知 viewModel 成員值的改變一直是開(kāi)發(fā) MVVM 框架的首要問(wèn)題。主流框架的處理有一下三大類(lèi):

另外開(kāi)發(fā)一套 API。典型框架:Backbone.js

Backbone 有自己的 模型類(lèi) 和 集合類(lèi)。這樣做雖然框架開(kāi)發(fā)簡(jiǎn)單運(yùn)行效率也高,但開(kāi)發(fā)者不得不使用這套 API 操作 viewModel,導(dǎo)致上手復(fù)雜、代碼繁瑣。

臟檢查機(jī)制。典型框架:angularjs

特點(diǎn)是直接使用 JS 原生操作對(duì)象的語(yǔ)法操作 viewModel,開(kāi)發(fā)者上手簡(jiǎn)單、代碼簡(jiǎn)單。但臟檢查機(jī)制隨之帶來(lái)的就是性能問(wèn)題。這點(diǎn)在我另外的一篇博文 《Angular 1 深度解析:臟數(shù)據(jù)檢查與 angular 性能優(yōu)化》 有詳細(xì)講解這里不另加贅述。

替換屬性。典型框架:vuejs
vuejs 把開(kāi)發(fā)者定義的 viewModel 對(duì)象(即 data 函數(shù)返回的對(duì)象)中所有的(除某些前綴開(kāi)頭的)成員替換為屬性。這樣既可以使用 JS 原生操作對(duì)象的語(yǔ)法,又是主動(dòng)觸發(fā) propertyChange 事件,效率也高。但這種方法也有一些限制,后文會(huì)分析。

Object.observe

Object.observe 是谷歌對(duì)于簡(jiǎn)化雙向綁定機(jī)制的嘗試,在 Chrome 49 中引入。然而由于性能等問(wèn)題,并沒(méi)有被其他各大瀏覽器及 ES 標(biāo)準(zhǔn)所接受。掙扎了一段時(shí)間后谷歌 Chrome 團(tuán)隊(duì)宣布收回 Object.observe 的提議,并在 Chrome 50 中完全刪除了 Object.observe 實(shí)現(xiàn)。

Proxy

Proxy(代理)是 ES2015 加入的新特性,用于對(duì)某些基本操作定義自定義行為,類(lèi)似于其他語(yǔ)言中的面向切面編程。它的其中一個(gè)作用就是用于(部分)替代 Object.observe 以實(shí)現(xiàn)雙向綁定。

例如有一個(gè)對(duì)象

let viewModel = {};

可以構(gòu)造對(duì)應(yīng)的代理類(lèi)實(shí)現(xiàn)對(duì) viewModel 的屬性賦值操作的監(jiān)聽(tīng):

viewModel = new Proxy(viewModel, {
  set(obj, prop, value) {
    if (obj[prop] !== value) {
      obj[prop] = value;
      console.log(`${prop} 屬性被改為 ${value}`);
    }
    return true;
  }
});

這時(shí)所有對(duì) viewModel 的屬性賦值的操作都不會(huì)直接生效,而是將這個(gè)操作轉(zhuǎn)發(fā)給 Proxy 中注冊(cè)的 set 方法,其中的參數(shù) obj 是原始對(duì)象(注意不能直接用 a,否則還會(huì)觸發(fā)代理函數(shù),造成無(wú)限遞歸),prop 是被賦值的屬性名,value 是待賦的值。
如果有:

viewModel.test = 1;

這時(shí)就會(huì)輸出 test 屬性被改為 1。

用 Proxy 實(shí)現(xiàn)簡(jiǎn)單的單向綁定。

有了 Proxy 就可以得知 viewModel 中屬性的變更了,還需要更新頁(yè)面上綁定此屬性的元素。

簡(jiǎn)單起見(jiàn),我們用 this 表示 viewModel 本身,使用 this.XXX 就表示依賴(lài) XXX 屬性。有 DOM 如下:

  

首先要獲得所有使用了單向綁定的元素:

const bindingElements = [...document.querySelectorAll("[my-bind]")];

獲取綁定表達(dá)式:

bindingElements.forEach(el => {
  const expression = el.getAttribute("my-bind");
});

由于獲得的表達(dá)式是個(gè)字符串,需要構(gòu)造一個(gè)函數(shù)去執(zhí)行它,得到表達(dá)式的結(jié)果:

const expression = el.getAttribute("my-bind");
const result = new Function(""use strict";
return " + expression).call(viewModel);

代碼中會(huì)動(dòng)態(tài)創(chuàng)建一個(gè)函數(shù),內(nèi)容就是將字符串解析執(zhí)行后將其結(jié)果返回(類(lèi)似 eval,但更安全)。將結(jié)果放到頁(yè)面上就可以了:

el.textContent = result;

與上文的 viewModel 結(jié)合起來(lái):

const bindingElements = [...document.querySelectorAll("[my-bind]")];

window.viewModel = new Proxy({}, { // 設(shè)置全局變量方便調(diào)試
  set(obj, prop, value) {
    if (obj[prop] !== value) {
      obj[prop] = value;

      bindingElements.forEach(el => {
        const expression = el.getAttribute("my-bind");
        const result = new Function(""use strict";
return " + expression)
          .call(obj);
        el.textContent = result;
      });
    }
    return true;
  }
});

如果實(shí)際放在瀏覽器中運(yùn)行的話(huà),改變 viewModel 中屬性的值就會(huì)觸發(fā)頁(yè)面的更新。

示例中寫(xiě)了循環(huán)會(huì)更新所有綁定元素,比較好的方式是只更新對(duì)當(dāng)前變更屬性有依賴(lài)的元素。這時(shí)就要分析綁定表達(dá)式的屬性依賴(lài)。
簡(jiǎn)單起見(jiàn)可以使用正則表達(dá)式解析屬性依賴(lài):

let match;
while (match = /this(?:.(w+))+/g.exec(expression)) {
  match[1] // 屬性依賴(lài)
}
添加事件綁定

事件綁定即綁定原生事件,在事件觸發(fā)時(shí)執(zhí)行綁定表達(dá)式,表達(dá)式調(diào)用 viewModel 中的某個(gè)回調(diào)函數(shù)。

click 事件為例。依然是獲取所有綁定了 click 事件的元素,并執(zhí)行表達(dá)式(表達(dá)式的值被丟棄)。與單項(xiàng)綁定不同的是:執(zhí)行表達(dá)式需要傳入事件的 event 參數(shù)。

[...document.querySelectorAll("[my-click]")].forEach(el => {
  const expression = el.getAttribute("my-click");
  const fn = new Function("$event", ""use strict";
" + expression);
  el.addEventListener("click", event => {
    fn.call(viewModel, event);
  });
});

Function 對(duì)象的構(gòu)造函數(shù),前 n-1 個(gè)參數(shù)是生成的函數(shù)對(duì)象的參數(shù)名,最后一個(gè)是函數(shù)體。代碼中構(gòu)造了包含一個(gè) $event 參數(shù)的函數(shù),函數(shù)體就是直接執(zhí)行綁定表達(dá)式。

雙向綁定

雙向綁定就是單項(xiàng)綁定和事件綁定的結(jié)合體。綁定元素的 input 事件來(lái)修改 viewModel 的屬性,然后再單項(xiàng)綁定元素的 value 屬性修改元素的值。

這里是一個(gè)較為完整的示例:http://sandbox.runjs.cn/show/...。完整的代碼放在我的 GitHub 倉(cāng)庫(kù)

使用 Proxy 實(shí)現(xiàn)雙向綁定的優(yōu)缺點(diǎn)

相較于 vuejs 的屬性替換,Proxy 實(shí)現(xiàn)的綁定至少有如下三個(gè)優(yōu)點(diǎn):

無(wú)需預(yù)先定義待綁定的屬性。

vuejs 要做屬性(getter, setter 方法)替換,首先需要知道有哪些屬性需要替換,這樣導(dǎo)致必須預(yù)先定義需要替換的屬性,也就是 vuejs 中的 data 方法。vuejs 中 data 方法必須定義完整所有綁定屬性,否則對(duì)應(yīng)綁定不能正常工作。
Vue 不能檢測(cè)到對(duì)象屬性的添加或刪除:Property or method "XXX" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
Proxy 不需要,因?yàn)樗O(jiān)聽(tīng)的是整個(gè)對(duì)象。

對(duì)數(shù)組相性良好。

雖說(shuō)數(shù)組里的方法可以替換(push、pop等),但是數(shù)組下標(biāo)卻不能替換為屬性,以致必須搞出一個(gè) set 方法用于對(duì)數(shù)組下標(biāo)賦值。

更容易調(diào)試的 viewModel 對(duì)象。

由于 vuejs 把對(duì)象中的所有成員全部替換成了屬性,如果想直接用 Chrome 的原生調(diào)試工具查看屬性值,你不得不挨個(gè)去點(diǎn)屬性后面的 (...):因?yàn)楂@取屬性的值其實(shí)是執(zhí)行了屬性的 get 方法,執(zhí)行一個(gè)方法可能會(huì)產(chǎn)生副作用,Chrome 把這個(gè)決定權(quán)留給開(kāi)發(fā)者。
Proxy 對(duì)象不需要。Proxyset 方法只是一層包裝,Proxy 對(duì)象自身維護(hù)原始對(duì)象的值,自然也可以直接拿出原始值給開(kāi)發(fā)者看。查看一個(gè) Proxy 對(duì)象,只需要展開(kāi)其內(nèi)置屬性 [[Target]] 即可看到原始對(duì)象的所有成員的值。你甚至還可以看到包裝原始對(duì)象的哪些 get、set 函數(shù)——如果你感興趣的話(huà)。

雖說(shuō)使用 Proxy 實(shí)現(xiàn)雙向綁定的優(yōu)點(diǎn)很明顯,但是缺點(diǎn)也很明顯:ProxyES2015 的特性,它無(wú)法被編譯為 ES5,也無(wú)法 Polyfill。IE 自然全軍覆沒(méi);其他各大瀏覽器實(shí)現(xiàn)的時(shí)間也較晚:Chrome 49、Safari 10。瀏覽器兼容性極大的限制了 Proxy 的使用。但是我相信,隨著時(shí)間的推移,基于 Proxy 的前端 MVVM 框架也會(huì)出現(xiàn)在開(kāi)發(fā)者眼前。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/51073.html

相關(guān)文章

  • 使用 Proxy 實(shí)現(xiàn)簡(jiǎn)單 MVVM 模型

    摘要:綁定實(shí)現(xiàn)的歷史綁定的基礎(chǔ)是事件。但臟檢查機(jī)制隨之帶來(lái)的就是性能問(wèn)題。是谷歌對(duì)于簡(jiǎn)化雙向綁定機(jī)制的嘗試,在中引入。掙扎了一段時(shí)間后谷歌團(tuán)隊(duì)宣布收回的提議,并在中完全刪除了實(shí)現(xiàn)。自然全軍覆沒(méi)其他各大瀏覽器實(shí)現(xiàn)的時(shí)間也較晚。 綁定實(shí)現(xiàn)的歷史 綁定的基礎(chǔ)是 propertyChange 事件。如何得知 viewModel 成員值的改變一直是開(kāi)發(fā) MVVM 框架的首要問(wèn)題。主流框架的處理有一下三...

    MarvinZhang 評(píng)論0 收藏0
  • 前端 MVVM 原理

    摘要:原來(lái)是在改變數(shù)據(jù)時(shí),還要手動(dòng)?,F(xiàn)在只需要直接改變數(shù)據(jù),會(huì)自動(dòng),更新元素。參考資料現(xiàn)代前端技術(shù)解析,張成文,年月第版,和的圖示,阮一峰,年月日, author: 陳家賓 email: 617822642@qq.com date: 2018/3/1 MVVM 背景 都說(shuō)懶惰使人進(jìn)步,MVVM 的進(jìn)化史,正印證了這句話(huà),是一步步讓開(kāi)發(fā)人員更懶惰更簡(jiǎn)單的歷史: 直接 DOM 操作 -> MVC...

    leiyi 評(píng)論0 收藏0
  • 學(xué)習(xí)MVVM及框架雙向綁定筆記

    摘要:的數(shù)據(jù)劫持版本內(nèi)部使用了來(lái)實(shí)現(xiàn)數(shù)據(jù)與視圖的雙向綁定,體現(xiàn)在對(duì)數(shù)據(jù)的讀寫(xiě)處理過(guò)程中。這樣就形成了數(shù)據(jù)的雙向綁定。 MVVM由以下三個(gè)內(nèi)容組成 View:視圖模板 Model:數(shù)據(jù)模型 ViewModel:作為橋梁負(fù)責(zé)溝通View和Model,自動(dòng)渲染模板 在JQuery時(shí)期,如果需要刷新UI時(shí),需要先取到對(duì)應(yīng)的DOM再更新UI,這樣數(shù)據(jù)和業(yè)務(wù)的邏輯就和頁(yè)面有強(qiáng)耦合。 在MVVM中,U...

    VioletJack 評(píng)論0 收藏0
  • 試著用Proxy 實(shí)現(xiàn)一個(gè)簡(jiǎn)單mvvm

    摘要:套數(shù)據(jù),實(shí)現(xiàn)界面先把計(jì)算屬性這個(gè)注釋掉,后面進(jìn)行實(shí)現(xiàn)計(jì)算屬性然后在函數(shù)中增加一個(gè)編譯函數(shù),號(hào)表示是添加的函數(shù)添加一個(gè)編譯函數(shù)上面我們添加了一個(gè)的構(gòu)造函數(shù)。 Proxy、Reflect的簡(jiǎn)單概述 Proxy 可以理解成,在目標(biāo)對(duì)象之前架設(shè)一層攔截,外界對(duì)該對(duì)象的訪(fǎng)問(wèn),都必須先通過(guò)這層攔截,因此提供了一種機(jī)制,可以對(duì)外界的訪(fǎng)問(wèn)進(jìn)行過(guò)濾和改寫(xiě)。Proxy 這個(gè)詞的原意是代理,用在這里表示由它...

    fnngj 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<