如果有問題歡迎指出,另外使用了一些常用的ES6語法。
首先看一下任務(wù)

這個任務(wù)沒要求實(shí)現(xiàn)動態(tài)數(shù)據(jù)綁定,所以之前的發(fā)布-訂閱模式、事件傳播機(jī)制就不用考慮了,只需要考慮渲染出數(shù)據(jù)就好。
從new一個Vue,可以看出Vue是一個構(gòu)造函數(shù)。根據(jù)使用Vue的經(jīng)驗(yàn),不需要手動bootstrap,所以new的過程應(yīng)該有個啟動。但是考慮到有些框架提供手動啟動(angular.bootstrap),并且這個方法是公用的,不用每個實(shí)例都創(chuàng)建一個,所以把初始化的方法放在原型里。
Vue接收兩個參數(shù)一個是Selector另一個是數(shù)據(jù)對象,根據(jù)Vue的使用經(jīng)驗(yàn)就是Selector是app的應(yīng)用范圍,其中的特定格式的({{}}中)字符串會被Vue實(shí)例中data中的數(shù)據(jù)同名屬性的值替換,這就是啟動的方法做的事情。所以任務(wù)分解成兩個小任務(wù),<b>一個是在dom中找到指定的格式,其實(shí)就是搜索文本和替換;</b><b>另一個是在遍歷data中的屬性,把這些可能需要替換的屬性保存起來;</b>
尋找dom中指定的字符串,通過正則去匹配,使用字符串的replace方法去替換。通過textContent這個HTML DOM Element對象的屬性,獲得dom節(jié)點(diǎn)內(nèi)的文本,但是這個有個問題就是設(shè)置 textContent 屬性,會刪除所有子節(jié)點(diǎn),并被替換為包含指定字符串的一個單獨(dú)的文本節(jié)點(diǎn)。所以要判斷到了dom樹的末端,才能使用,不然會破壞dom結(jié)構(gòu)。所以要用到遞歸和判斷。


子任務(wù)一:
// 判斷是否有textContent屬性
if (!('textContent' in document)) {
console.log('Please upgrade your browser!');
return
}
// 獲取app入口的dom節(jié)點(diǎn)
let appEntrance = document.getElementById(el.slice(1));
// 將入口元素內(nèi)部的符合的部分轉(zhuǎn)換成對應(yīng)的變量
function translate(entrance, reg, value) {
if (entrance instanceof HTMLElement) {
let children = entrance.childNodes;
let len = entrance.childNodes.length;
for (let i = 0; i < len; i++) {
if (children[i].childNodes.length > 0) {
translate(children[i], reg, value)
} else {
children[i].textContent = children[i].textContent.replace(reg, value);
}
}
} else {
console.log('Entrance is invalid!');
}
}
先在整個啟動程序前判斷一下有沒有textContent屬性,如果沒有,直接退出,因?yàn)楹诵木褪沁@個方法,避免后面一次次判斷了。
把dom中符合格式的內(nèi)容替換成指定的內(nèi)容的函數(shù)被我命名為translate,接收三個參數(shù)entrance、reg、value,分別是搜索入口、用來匹配的正則、替換成的值。先判斷一下入口是不是一個dom節(jié)點(diǎn),凡是dom節(jié)點(diǎn)都繼承于HTMLElement對象。使用兩個局部變量來保存一下子節(jié)點(diǎn)和長度,不然每次調(diào)用都會在dom中查找一遍。然后判斷如果有子節(jié)點(diǎn)則遞歸調(diào)用,直到末端,通過屬性獲得文本,通過正則替換符合格式的內(nèi)容,然后再賦予屬性。至此其中一個任務(wù)完成了。
子任務(wù)二:
// 遍歷屬性去替換
function walk(entrance, obj, name) {
if (entrance instanceof HTMLElement) {
if (obj !== null && typeof obj === 'object') {
let reg;
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
if (typeof obj[key] !== 'object') {
reg = name ? new RegExp(`{{${name}.${key}}}`, 'g') : new RegExp(`{{${key}}}`, 'g');
translate(entrance, reg, obj[key]);
} else {
walk(entrance, obj[key], name ? `${name}.${key}` : key);
}
}
}
}
} else {
console.log('Entrance is invalid!');
}
}
這個遍歷函數(shù)接收三個參數(shù),entrance、obj、name,其中entrance是入口的dom節(jié)點(diǎn)、obj是要查找屬性的對象、name是父級對象的名字,因?yàn)榛ɡㄌ柪锏氖菍ο竺Q加上屬性,所以需要記錄下祖先名稱。先判斷一下入口dom節(jié)點(diǎn)是不是合法,再判斷obj是否是對象,然后對obj遍歷。通過hasOwnProperty來排除繼承的屬性,如果屬性是對象還要遞歸調(diào)用,對每個非對象屬性使用tranlate屬性來查找替換dom中的內(nèi)容。``字符串中使用${}可以解析出中間變量的內(nèi)容。
感覺這樣實(shí)現(xiàn)復(fù)雜度有點(diǎn)高,應(yīng)該有更好的方法。
求贊,求star……
源碼地址:https://github.com/rennaiqian/baiduTask/blob/master/two-way-bind-4/two-way-bind-4.html