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

一個簡化版Vue助你理解Vue原理(vue的原理是什么意思)

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

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

  • observe():用來劫持并監(jiān)聽數(shù)據(jù)變化的數(shù)據(jù)監(jiān)聽器,有變化就會通知下文中的訂閱器watcher
  • compile():節(jié)點(diǎn)DOM解析器,用來獲取和解析每一個節(jié)點(diǎn)及其指令,根據(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é)點(diǎn)DOM解析器compile,主要用來迭代遞歸獲取和解析每一個節(jié)點(diǎn)及其指令,根據(jù)初始化的模板數(shù)據(jù)來創(chuàng)建訂閱器watcher,實(shí)例化watcher就會接到數(shù)據(jù)變化的通知,進(jìn)而實(shí)現(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實(shí)例的構(gòu)造 用來接收實(shí)例的配置項(xiàng)

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ù)進(jìn)行劫持并監(jiān)聽

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

}

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)鍵方法 可以修改對象身上的默認(rè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é)點(diǎn)DOM解析器 用來獲取和解析每一個節(jié)點(diǎn)及其指令 根據(jù)初始化的模板數(shù)據(jù)來創(chuàng)建訂閱器watcher

let nodes = el.children;

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

let node = nodes[i];

if (node.children.length > 0) {

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

}

if (node.hasAttribute(‘yf-on:click’)) { // 節(jié)點(diǎn)中如存在該指令則執(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é)點(diǎn)的值扔給該指令的數(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ù)更新在視圖中從而實(shí)現(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

  • 戳我運(yùn)行代碼
  • Edit on GitHub
  • Try in JSFiddle

希望大家閱讀完本文可以有所收獲,因?yàn)槟芰τ邢?,掌握的知識也是不夠全面,歡迎大家提出來一起分享!謝謝O(∩_∩)O~

一個簡化版Vue助你理解Vue原理(vue的原理是什么意思)

相關(guān)新聞

聯(lián)系我們
聯(lián)系我們
公眾號
公眾號
在線咨詢
分享本頁
返回頂部
汪清县| 施秉县| 安达市| 金阳县| 郧西县| 桦甸市| 抚远县| 仁布县| 五寨县| 色达县| 于都县| 临颍县| 武川县| 通江县| 扶风县| 寻乌县| 和静县| 孟村| 美姑县| 增城市| 盖州市| 象州县| 盈江县| 思茅市| 刚察县| 仪征市| 潞城市| 历史| 固原市| 元阳县| 安丘市| 克山县| 桑日县| 香港 | 辽宁省| 武宣县| 南康市| 那坡县| 阿鲁科尔沁旗| 威宁| 林州市|