真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

如何使用proxy實(shí)現(xiàn)一個(gè)MVVM庫(kù)的方法

這篇文章主要介紹如何使用proxy實(shí)現(xiàn)一個(gè)MVVM庫(kù)的方法,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!

在岳塘等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場(chǎng)前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供網(wǎng)站建設(shè)、網(wǎng)站制作 網(wǎng)站設(shè)計(jì)制作按需網(wǎng)站開發(fā),公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),品牌網(wǎng)站設(shè)計(jì),全網(wǎng)營(yíng)銷推廣,成都外貿(mào)網(wǎng)站建設(shè)公司,岳塘網(wǎng)站建設(shè)費(fèi)用合理。

前言

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)易的通過(guò) proxy 實(shí)現(xiàn)的 mvvm。

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

最終效果


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

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

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

如何使用proxy實(shí)現(xiàn)一個(gè)MVVM庫(kù)的方法

模塊詳解

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(),通過(guò)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ù)變化更新表單:跟其他指令更新視圖一樣,通過(guò)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 模型中的核心,一般通過(guò) 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ú)法通過(guò) 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

class Dep {
 constructor() {
  this.dependList = [];
 }
 addDep() {
  this.dependList.push(dep);
 }
 notfiy() {
  this.dependList.forEach(item => {
   item.update();
  });
 }
}

以上是“如何使用proxy實(shí)現(xiàn)一個(gè)MVVM庫(kù)的方法”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!


當(dāng)前標(biāo)題:如何使用proxy實(shí)現(xiàn)一個(gè)MVVM庫(kù)的方法
標(biāo)題路徑:http://weahome.cn/article/pghhdi.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部