成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

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

BetaRabbit / 424人閱讀

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

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

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

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

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

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

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

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

Object.observe

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

Proxy

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

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

let viewModel = {};

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

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ù),造成無限遞歸),prop 是被賦值的屬性名,value 是待賦的值。
如果有:

viewModel.test = 1;

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

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

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

簡(jiǎn)單起見,我們用 this 表示 viewModel 本身,使用 this.XXX 就表示依賴 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é)果返回(類似 eval,但更安全)。將結(jié)果放到頁面上就可以了:

el.textContent = result;

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

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)行的話,改變 viewModel 中屬性的值就會(huì)觸發(fā)頁面的更新。

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

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

事件綁定即綁定原生事件,在事件觸發(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 事件來修改 viewModel 的屬性,然后再單項(xiàng)綁定元素的 value 屬性修改元素的值。

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

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

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

無需預(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)聽的是整個(gè)對(duì)象。

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

雖說數(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)留給開發(fā)者。
Proxy 對(duì)象不需要。Proxyset 方法只是一層包裝,Proxy 對(duì)象自身維護(hù)原始對(duì)象的值,自然也可以直接拿出原始值給開發(fā)者看。查看一個(gè) Proxy 對(duì)象,只需要展開其內(nèi)置屬性 [[Target]] 即可看到原始對(duì)象的所有成員的值。你甚至還可以看到包裝原始對(duì)象的哪些 get、set 函數(shù)——如果你感興趣的話。

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

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

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

相關(guān)文章

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

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

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

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

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

    摘要:的數(shù)據(jù)劫持版本內(nèi)部使用了來實(shí)現(xiàn)數(shù)據(jù)與視圖的雙向綁定,體現(xiàn)在對(duì)數(shù)據(jù)的讀寫處理過程中。這樣就形成了數(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ù)的邏輯就和頁面有強(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ì)象的訪問,都必須先通過這層攔截,因此提供了一種機(jī)制,可以對(duì)外界的訪問進(jìn)行過濾和改寫。Proxy 這個(gè)詞的原意是代理,用在這里表示由它...

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

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

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<