欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

如何使用proxy實(shí)現(xiàn)一個(gè)簡(jiǎn)單完整的MVVM庫(kù)的示例代碼

 更新時(shí)間:2019年09月17日 14:36:41   作者:可魚不是魚  
這篇文章主要介紹了如何使用proxy實(shí)現(xiàn)一個(gè)簡(jiǎn)單完整的MVVM庫(kù)的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

前言

MVVM 是當(dāng)前時(shí)代前端日常業(yè)務(wù)開發(fā)中的必備模式(相關(guān)框架如reactvue,angular 等), 使用 MVVM 可以將開發(fā)者的精力更專注于業(yè)務(wù)上的邏輯,而不需要關(guān)心如何操作 dom。雖然現(xiàn)在都 9012 年了,mvvm 相關(guān)原理的介紹已經(jīng)爛大街了,但出于學(xué)習(xí)基礎(chǔ)知識(shí)的目的(使用 proxy 實(shí)現(xiàn)的 vue3.0 還在開發(fā)中), 在參考了之前 vue.js 的整體思路之后,自己動(dòng)手實(shí)現(xiàn)了一個(gè)簡(jiǎn)易的通過 proxy 實(shí)現(xiàn)的 mvvm

本項(xiàng)目代碼已經(jīng)開源在github,項(xiàng)目正在持續(xù)完善中,歡迎交流學(xué)習(xí),喜歡請(qǐng)點(diǎn)個(gè) star 吧!

最終效果

<html>
 <body>
  <div id="app">
   <div>{{title}}</div>
  </div>
 </body>
</html>
import MVVM from '@fe_korey/mvvm';
new MVVM({
 view: document.getElementById('app'),
 model: {
  title: 'hello mvvm!'
 },
 mounted() {
  console.log('主程編譯完成,歡迎使用MVVM!');
 }
});

結(jié)構(gòu)概覽

  • Complier 模塊實(shí)現(xiàn)解析、收集指令,并初始化視圖
  • Observer 模塊實(shí)現(xiàn)了數(shù)據(jù)的監(jiān)聽,包括添加訂閱者和通知訂閱者
  • Parser 模塊實(shí)現(xiàn)解析指令,提供該指令的更新視圖的更新方法
  • Watcher 模塊實(shí)現(xiàn)建立指令與數(shù)據(jù)的關(guān)聯(lián)
  • Dep 模塊實(shí)現(xiàn)一個(gè)訂閱中心,負(fù)責(zé)收集,觸發(fā)數(shù)據(jù)模型各值的訂閱列表

流程為:Complier收集編譯好指令后,根據(jù)指令不同選擇不同的Parser,根據(jù)ParserWatcher中訂閱數(shù)據(jù)的變化并更新初始視圖。Observer監(jiān)聽數(shù)據(jù)變化然后通知給 WatcherWatcher 再將變化結(jié)果通知給對(duì)應(yīng)Parser里的 update 刷新函數(shù)進(jìn)行視圖的刷新。

模塊詳解

Complier

將整個(gè)數(shù)據(jù)模型 data 傳入Observer模塊進(jìn)行數(shù)據(jù)監(jiān)聽

this.$data = new Observer(option.model).getData();

循環(huán)遍歷整個(gè) dom,對(duì)每個(gè) dom 元素的所有指令進(jìn)行掃描提取

function collectDir(element) {
 const children = element.childNodes;
 const childrenLen = children.length;

 for (let i = 0; i < childrenLen; i++) {
  const node = children[i];
  const nodeType = node.nodeType;

  if (nodeType !== 1 && nodeType !== 3) {
   continue;
  }
  if (hasDirective(node)) {
   this.$queue.push(node);
  }
  if (node.hasChildNodes() && !hasLateCompileChilds(node)) {
   collectDir(element);
  }
 }
}

對(duì)每個(gè)指令進(jìn)行編譯,選擇對(duì)應(yīng)的解析器Parser

const parser = this.selectParsers({ node, dirName, dirValue, cs: this });

將得到的解析器Parser傳入Watcher,并初始化該 dom 節(jié)點(diǎn)的視圖

const watcher = new Watcher(parser);
parser.update({ newVal: watcher.value });

所有指令解析完畢后,觸發(fā) MVVM 編譯完成回調(diào)$mounted()

this.$mounted();

使用文檔碎片document.createDocumentFragment()來(lái)代替真實(shí) dom 節(jié)點(diǎn)片段,待所有指令編譯完成后,再將文檔碎片追加回真實(shí) dom 節(jié)點(diǎn)

let child;
const fragment = document.createDocumentFragment();
while ((child = this.$element.firstChild)) {
 fragment.appendChild(child);
}
//解析完后
this.$element.appendChild(fragment);
delete $fragment;

Parser

Complier模塊編譯后的指令,選擇不同聽解析器解析,目前包括ClassParser,DisplayParser,ForParser,IfParser,StyleParser,TextParser,ModelParser,OnParser,OtherParser等解析模塊。

switch (name) {
 case 'text':
  parser = new TextParser({ node, dirValue, cs });
  break;
 case 'style':
  parser = new StyleParser({ node, dirValue, cs });
  break;
 case 'class':
  parser = new ClassParser({ node, dirValue, cs });
  break;
 case 'for':
  parser = new ForParser({ node, dirValue, cs });
  break;
 case 'on':
  parser = new OnParser({ node, dirName, dirValue, cs });
  break;
 case 'display':
  parser = new DisplayParser({ node, dirName, dirValue, cs });
  break;
 case 'if':
  parser = new IfParser({ node, dirValue, cs });
  break;
 case 'model':
  parser = new ModelParser({ node, dirValue, cs });
  break;
 default:
  parser = new OtherParser({ node, dirName, dirValue, cs });
}

不同的解析器提供不同的視圖刷新函數(shù)update(),通過update更新dom視圖

//text.js
function update(newVal) {
 this.el.textContent = _toString(newVal);
}

OnParser 解析事件綁定,與數(shù)據(jù)模型中的 methods字段對(duì)應(yīng)

//詳見 https://github.com/zhaoky/mvvm/blob/master/src/core/parser/on.ts
el.addEventListener(handlerType, e => {
 handlerFn(scope, e);
});

ForParser 解析數(shù)組

詳見 https://github.com/zhaoky/mvvm/blob/master/src/core/parser/for.ts

ModelParser 解析雙向綁定,目前支持input[text/password] & textarea,input[radio],input[checkbox],select四種情況的雙向綁定,雙綁原理:

數(shù)據(jù)變化更新表單:跟其他指令更新視圖一樣,通過update方法觸發(fā)更新表單的value

function update({ newVal }) {
 this.model.el.value = _toString(newVal);
}

表單變化更新數(shù)據(jù):監(jiān)聽表單變化事件如input,change,在回調(diào)里set數(shù)據(jù)模型

this.model.el.addEventListener('input', e => {
 model.watcher.set(e.target.value);
});

Observer

MVVM 模型中的核心,一般通過 Object.definePropertyget,set 方法進(jìn)行數(shù)據(jù)的監(jiān)聽,在 get 里添加訂閱者,set 里通知訂閱者更新視圖。在本項(xiàng)目采用 Proxy 來(lái)實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽,好處有三:

Proxy 可以直接監(jiān)聽對(duì)象而非屬性

Proxy 可以直接監(jiān)聽數(shù)組的變化

Proxy 有多達(dá) 13 種攔截方法,查閱

而劣勢(shì)是兼容性問題,且無(wú)法通過 polyfill 磨平。查閱兼容性

注意 Proxy 只會(huì)監(jiān)聽自身的每一個(gè)屬性,如果屬性是對(duì)象,則該對(duì)象不會(huì)被監(jiān)聽,所以需要遞歸監(jiān)聽

設(shè)置監(jiān)聽后,返回一個(gè) Proxy 替代原數(shù)據(jù)對(duì)象

var proxy = new Proxy(data, {
 get: function(target, key, receiver) {
  //如果滿足條件則添加訂閱者
  dep.addDep(curWatcher);
  return Reflect.get(target, key, receiver);
 },
 set: function(target, key, value, receiver) {
  //如果滿足條件則通知訂閱者
  dep.notfiy();
  return Reflect.set(target, key, value, receiver);
 }
});

Watcher

Complier 模塊里對(duì)每一個(gè)解析后的 Parser 進(jìn)行指令與數(shù)據(jù)模型直接的綁定,并觸發(fā) Observerget 監(jiān)聽,添加訂閱者(Watcher

this._getter(this.parser.dirValue)(this.scope || this.parser.cs.$data);

當(dāng)數(shù)據(jù)模型變化時(shí),就會(huì)觸發(fā) -> Observerset 監(jiān)聽 -> Depnotfiy 方法(通知訂閱者的所有訂閱列表) -> 執(zhí)行訂閱列表所有 Watcherupdate 方法 -> 執(zhí)行對(duì)應(yīng) Parserupdate -> 完成更新視圖

Watcher 里的 set 方法用于設(shè)置雙向綁定值,注意訪問層級(jí)

Dep

  • MVVM 的訂閱中心,在這里收集數(shù)據(jù)模型的每個(gè)屬性的訂閱列表
  • 包含添加訂閱者,通知訂閱者等方法
  • 本質(zhì)是一種發(fā)布/訂閱模式
class Dep {
 constructor() {
  this.dependList = [];
 }
 addDep() {
  this.dependList.push(dep);
 }
 notfiy() {
  this.dependList.forEach(item => {
   item.update();
  });
 }
}

后記

目前該 mvvm 項(xiàng)目只實(shí)現(xiàn)了數(shù)據(jù)綁定視圖更新的功能,通過這個(gè)簡(jiǎn)易輪子的實(shí)現(xiàn),對(duì) dom 操作,proxy,發(fā)布訂閱模式等若干基礎(chǔ)知識(shí)都進(jìn)行了再次理解,查漏補(bǔ)缺。同時(shí)歡迎大家一起探討交流,后面會(huì)繼續(xù)完善!

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論