日本电影一区二区_日本va欧美va精品发布_日本黄h兄妹h动漫一区二区三区_日本欧美黄色

一個簡化版Vue助你理解Vue原理(vue 原理)

一個簡化版Vue助你理解Vue原理(vue 原理)

好多人看完我的這個文章對它的理解還是只是知道了大概原理,但是對具體的Vue雙向綁定的實現(xiàn)很模糊,因此就出了這篇文章,供大家參考希望可以得到收獲,以下是主要代碼邏輯,先陳述一下這一過程都需要什么:

需要有一個接收Vue實例配置項的構(gòu)造函數(shù)SimpleVue,給他加兩個原型方法分別是observe()和compile(),再構(gòu)造出一個訂閱器watcher,給他加一個更新視圖方法

  • observe():用來劫持并監(jiān)聽數(shù)據(jù)變化的數(shù)據(jù)監(jiān)聽器,有變化就會通知下文中的訂閱器watcher
  • compile():節(jié)點DOM解析器,用來獲取和解析每一個節(jié)點及其指令,根據(jù)初始化的模板數(shù)據(jù)來創(chuàng)建訂閱器watcher
  • watcher():訂閱器watcher,用來接收屬性值的相關(guān)數(shù)據(jù)的變化通知,調(diào)用自身原型方法update從而更新視圖

由于Vue就是一個MVVM的框架理念,所以就要通過Object.defineProperty()方法來劫持并監(jiān)聽所有屬性值相關(guān)的數(shù)據(jù),看看它是否變化,如有變化則通知訂閱器watcher看是否需要視圖更新,這一過程就是我們的數(shù)據(jù)監(jiān)聽器observe的工作任務(wù),由于數(shù)據(jù)和訂閱器是一對多的關(guān)系,所以通知訂閱器的時候需要把數(shù)據(jù)對應(yīng)的訂閱器的集合都放在一個oWatcherObj對象中,接下來需要一個節(jié)點DOM解析器compile,主要用來迭代遞歸獲取和解析每一個節(jié)點及其指令,根據(jù)初始化的模板數(shù)據(jù)來創(chuàng)建訂閱器watcher,實例化watcher就會接到數(shù)據(jù)變化的通知,進而實現(xiàn)VM更新視圖

template:

<div id=\”simpleVue\”>

<button yf-on:click=\”copy\”>戳我</button>

<div>

<textarea yf-model=\”name\”></textarea>

<div yf-text=\”name\”></div>

</div>

<hr>

<button yf-on:click=\”show\”>顯示/隱藏</button>

<div yf-if=\”isShow\”>

<input type=\”text\” yf-model=\”webSite\”>

<div yf-text=\”webSite\”></div>

</div>

</div>

SimpleVue構(gòu)造:

class SimpleVue { // 簡化版Vue實例的構(gòu)造 用來接收實例的配置項

constructor(options) {

this.$el = document.querySelector(options.el);

this.$data = options.data;

this.$methods = options.methods;

this.oWatcherObj = {}; // 所有屬性值相關(guān)的數(shù)據(jù)對應(yīng)的訂閱器的集合都放在該對象中

this.observe(); // 調(diào)用數(shù)據(jù)監(jiān)聽器對屬性值相關(guān)的數(shù)據(jù)進行劫持并監(jiān)聽

this.compile(this.$el); // 對該DOM節(jié)點進行解析

}

observe() { // 數(shù)據(jù)監(jiān)聽器 用來劫持并監(jiān)聽屬性值相關(guān)數(shù)據(jù)的變化 如有變化則通知訂閱器watcher

for (let key in this.$data) {

let value = this.$data[key];

this.oWatcherObj[key] = []; // 初始化該數(shù)據(jù)的訂閱器 數(shù)據(jù)和訂閱器的關(guān)系是一對多

let oWatcherObj = this.oWatcherObj[key];

Object.defineProperty(this.$data, key, { // 關(guān)鍵方法 可以修改對象身上的默認屬性值的ES5方法 下面用到的是ES中兩大屬性中的訪問器屬性,有以下四種描述符對象

configurable: false, // 該狀態(tài)下的屬性描述符不能被修改和刪除

enumerable: false, // 該狀態(tài)下的屬性描述符中的屬性不可被枚舉

get() { // 屬性值相關(guān)的數(shù)據(jù)讀取函數(shù)

return value;

},

set(newVal) { // 屬性值相關(guān)的數(shù)據(jù)寫入函數(shù)

if (newVal !== value) {

value = newVal;

oWatcherObj.forEach((obj) => {

obj.update(); // 通知和該數(shù)據(jù)相關(guān)的所有訂閱器

});

}

}

});

}

}

compile(el) { // 節(jié)點DOM解析器 用來獲取和解析每一個節(jié)點及其指令 根據(jù)初始化的模板數(shù)據(jù)來創(chuàng)建訂閱器watcher

let nodes = el.children;

for (let i = 0; i < nodes.length; i ) { // 迭代同級所有節(jié)點

let node = nodes[i];

if (node.children.length > 0) {

this.compile(node); // 遞歸所有子節(jié)點

}

if (node.hasAttribute(\’yf-on:click\’)) { // 節(jié)點中如存在該指令則執(zhí)行以下操作

let eventAttrVal = node.getAttribute(\’yf-on:click\’);

node.addEventListener(\’click\’, this.$methods[eventAttrVal].bind(this.$data)); // 綁定獲取到的指令對應(yīng)的數(shù)據(jù)所觸發(fā)的方法

}

if (node.hasAttribute(\’yf-if\’)) {

let ifAttrVal = node.getAttribute(\’yf-if\’);

this.oWatcherObj[ifAttrVal].push(new Watcher(this, node, \”\”, ifAttrVal)); // 給該指令對應(yīng)的數(shù)據(jù)創(chuàng)建訂閱器放在該數(shù)據(jù)對應(yīng)的訂閱器數(shù)組里

}

if (node.hasAttribute(\’yf-model\’)) {

let modelAttrVal = node.getAttribute(\’yf-model\’);

node.addEventListener(\’input\’, ((i) => { // 前方高能:此處有閉包請繞行!!! i的問題

this.oWatcherObj[modelAttrVal].push(new Watcher(this, node, \”value\”, modelAttrVal));

return () => {

this.$data[modelAttrVal] = nodes[i].value; // 將該指令所在節(jié)點的值扔給該指令的數(shù)據(jù)

}

})(i));

}

if (node.hasAttribute(\’yf-text\’)) {

let textAttrVal = node.getAttribute(\’yf-text\’);

this.oWatcherObj[textAttrVal].push(new Watcher(this, node, \”innerText\”, textAttrVal));

}

}

}

}

訂閱器構(gòu)造:

class Watcher { // 訂閱器構(gòu)造 用來接收屬性值的相關(guān)數(shù)據(jù)的變化通知 從而更新視圖

constructor(…arg) {

this.vm = arg[0];

this.el = arg[1];

this.attr = arg[2];

this.val = arg[3];

this.update(); // 初始化訂閱器時更新一下視圖

}

update() { // 將收到的新的數(shù)據(jù)更新在視圖中從而實現(xiàn)真正的VM

if (this.vm.$data[this.val] === true) {

this.el.style.display = \’block\’;

} else if (this.vm.$data[this.val] === false) {

this.el.style.display = \’none\’;

} else {

this.el[this.attr] = this.vm.$data[this.val];

}

}

}

Shortcuts

  • 戳我運行代碼
  • Edit on GitHub
  • Try in JSFiddle

希望大家閱讀完本文可以有所收獲,因為能力有限,掌握的知識也是不夠全面,歡迎大家提出來一起分享!謝謝O(∩_∩)O~

一個簡化版Vue助你理解Vue原理(vue 原理)

相關(guān)新聞

聯(lián)系我們
聯(lián)系我們
公眾號
公眾號
在線咨詢
分享本頁
返回頂部
元江| 蓝山县| 贡山| 宣汉县| 东安县| 中山市| 会宁县| 集安市| 聂荣县| 石家庄市| 巩义市| 罗山县| 德州市| 彭泽县| 驻马店市| 兰考县| 庆元县| 林西县| 罗江县| 开阳县| 增城市| 繁昌县| 乐都县| 万山特区| 富宁县| 临武县| 洮南市| 宁晋县| 凤庆县| 鞍山市| 且末县| 赤壁市| 子洲县| 平乐县| 文安县| 夏河县| 焦作市| 临西县| 弋阳县| 交口县| 宁明县|