摘要:寫在前面這篇文章講述了如何利用和實(shí)現(xiàn)雙向數(shù)據(jù)綁定,個(gè)人系早期玩家,寫這個(gè)小框架的時(shí)候也沒(méi)有參考等源代碼,之前了解過(guò)其他實(shí)現(xiàn),但沒(méi)有直接參考其他代碼,如有雷同,純屬巧合。我們同時(shí)也應(yīng)該支持事件機(jī)制,這里我們以最常用的方法作為例子實(shí)現(xiàn)。
寫在前面:這篇文章講述了如何利用Proxy和Reflect實(shí)現(xiàn)雙向數(shù)據(jù)綁定,個(gè)人系Vue早期玩家,寫這個(gè)小框架的時(shí)候也沒(méi)有參考Vue等源代碼,之前了解過(guò)其他實(shí)現(xiàn),但沒(méi)有直接參考其他代碼,如有雷同,純屬巧合。
代碼下載地址:這里下載
綜述關(guān)于Proxy和Reflect的資料推薦阮老師的教程:http://es6.ruanyifeng.com/ 這里不做過(guò)多介紹。
實(shí)現(xiàn)雙向數(shù)據(jù)綁定的方法有很多,也可以參考本專欄之前的其他實(shí)現(xiàn),我之所以選擇用Proxy和Reflect,一方面是因?yàn)榭梢源罅抗?jié)約代碼,并且簡(jiǎn)化邏輯,可以讓我把更多的經(jīng)歷放在其他內(nèi)容的構(gòu)建上面,另外一方面本項(xiàng)目直接基于ES6,用這些內(nèi)容也符合面向未來(lái)的JS編程規(guī)范,第三點(diǎn)最后說(shuō)。
由于這個(gè)小框架是自己在PolarBear這個(gè)咖啡館在一個(gè)安靜的午后開(kāi)始寫成,暫且起名Polar,日后希望我能繼續(xù)完善這個(gè)小框架,給添加上更多有趣的功能。
首先我們可以看整體功能演示:
一個(gè)gif動(dòng)圖,如果不能看,請(qǐng)點(diǎn)擊[這里的鏈接]
我們要做這樣一個(gè)小框架,核心是要監(jiān)聽(tīng)數(shù)據(jù)的改變,并且在數(shù)據(jù)的改變的時(shí)候進(jìn)行一些操作,從而維持?jǐn)?shù)據(jù)的一致。
我的思路是這樣的:
將所有的數(shù)據(jù)信息放在一個(gè)屬性對(duì)象中(this._data),之后給這個(gè)屬性對(duì)象用Proxy包裝set,在代理函數(shù)中我們更新屬性對(duì)象的具體內(nèi)容,同時(shí)通知所有監(jiān)聽(tīng)者,之后返回新的代理對(duì)象(this.data),我們之后操作的都是新的代理對(duì)象。
對(duì)于input等表單,我們需要監(jiān)聽(tīng)input事件,在回調(diào)函數(shù)中直接設(shè)置我們代理好的數(shù)據(jù)對(duì)象,從而觸發(fā)我們的代理函數(shù)。
我們同時(shí)也應(yīng)該支持事件機(jī)制,這里我們以最常用的click方法作為例子實(shí)現(xiàn)。
下面開(kāi)始第一部分,我們希望我們之后使用這個(gè)庫(kù)的時(shí)候可以這樣調(diào)用:
name:{{name}} age:{{age}}note:{{note}}
沒(méi)錯(cuò),和Vue神似吧,所以這種調(diào)用方式應(yīng)當(dāng)為我們所熟悉。
我們需要建立一個(gè)Polar類,這個(gè)類的構(gòu)造函數(shù)應(yīng)該進(jìn)行一些初始化操作:
constructor(configs){ this.root = this.el = document.querySelector(configs.el); this._data = configs.data; this._data.__bindings = {}; //創(chuàng)建代理對(duì)象 this.data = new Proxy(this._data, {set}); this.methods = configs.methods; this._compile(this.root); }
這里面的一部份內(nèi)容是直接將我們傳入的configs按照屬性分別賦值,另外就是我們創(chuàng)建代理對(duì)象的過(guò)程,最后的_compile方法可以理解為一個(gè)私有的初始化方法。
實(shí)際上我把剩下的內(nèi)容幾乎都放在_compile方法里面了,這樣理解起來(lái)方便,但是之后可能要改動(dòng)。
我們還是先不能看我們代理的set該怎么寫,因?yàn)檫@個(gè)時(shí)候我們還要先繼續(xù)梳理思路:
假設(shè)我們這樣
看上文的__bindings。這個(gè)對(duì)象用來(lái)存儲(chǔ)所有綁定的dom節(jié)點(diǎn)信息,__bindings本身是一個(gè)對(duì)象,每一個(gè)有對(duì)應(yīng)dom節(jié)點(diǎn)綁定的數(shù)據(jù)名稱都是它的屬性,對(duì)應(yīng)一個(gè)數(shù)組,數(shù)組中的每一個(gè)內(nèi)容都是一個(gè)綁定信息,這樣,我們?cè)谧约簩懙膕et代理函數(shù)中,我們一個(gè)個(gè)調(diào)用過(guò)去,就可以更新內(nèi)容了:
dataSet.__bindings[key].forEach(function(item){ //do something to update... });
我這里創(chuàng)建了一個(gè)用于構(gòu)造調(diào)用的函數(shù),這個(gè)函數(shù)用于創(chuàng)建存儲(chǔ)綁定信息的對(duì)象:
function Directive(el,polar,attr,elementValue){ this.el=el;//元素本身dom節(jié)點(diǎn) this.polar = polar;//對(duì)應(yīng)的polar實(shí)例 this.attr = attr;//元素的被綁定的屬性值,比如如果是文本節(jié)點(diǎn)就可以是nodeValue this.el[this.attr] = this.elementValue = elementValue;//初始化 }
這樣,我們的set可以這樣寫:
function set(target, key, value, receiver) { const result = Reflect.set(target, key, value, receiver); var dataSet = receiver || target; dataSet.__bindings[key].forEach(function(item){ item.el[item.attr] = item.elementValue = value; }); return result; }
接下來(lái)可能還有一個(gè)問(wèn)題:我們的{{name}}實(shí)際上只是節(jié)點(diǎn)的一部分,這并不是節(jié)點(diǎn)啊,另外我們是不是還可以這么寫:
關(guān)于這兩個(gè)問(wèn)題,前者的答案是我們將{{name}}替換成一個(gè)文本節(jié)點(diǎn),而為了應(yīng)對(duì)后者的情況,我們需要將兩個(gè)被綁定數(shù)據(jù)中間和前后的內(nèi)容,都變成新的文本節(jié)點(diǎn),然后這些文本節(jié)點(diǎn)組成文本節(jié)點(diǎn)串。(這里多說(shuō)一句,html5的normalize方法可以將多個(gè)文本節(jié)點(diǎn)合并成一個(gè),如果不小心調(diào)用了它,那我們的程序就要GG了)
所以我們?cè)?b>_compile函數(shù)首先:
var _this = this; var nodes = root.children; var bindDataTester = new RegExp("{{(.*?)}}","ig"); for(let i=0;i這樣,我們的數(shù)據(jù)綁定階段就寫好了,接下來(lái),我們處理這樣的情況。
這實(shí)際上是一個(gè)指令,我們只需要當(dāng)識(shí)別到這一個(gè)指令的時(shí)候,做一些處理,即可:
if(node.hasAttribute(("p-model")) && node.tagName.toLocaleUpperCase()=="INPUT" || node.tagName.toLocaleUpperCase()=="TEXTAREA"){ node.addEventListener("input", (function () { var attributeValue = node.getAttribute("p-model"); if(_this._data.__bindings[attributeValue]) _this._data.__bindings[attributeValue].push(new Directive(node,_this,"value",_this.data[attributeValue])) ; else _this._data.__bindings[attributeValue] = [new Directive(node,_this,"value",_this.data[attributeValue])]; return function (event) { _this.data[attributeValue]=event.target.value } })()); }請(qǐng)注意,上面調(diào)用了一個(gè)IIFE,實(shí)際綁定的函數(shù)只有返回的函數(shù)那一小部分。
最后我們處理事件的情況:
實(shí)際上這比處理p-model還簡(jiǎn)單,但是我們?yōu)榱酥С趾瘮?shù)參數(shù)的情況,處理了一下傳入?yún)?shù),另外我實(shí)際上將event始終作為一個(gè)參數(shù)傳遞,這也許并不是好的實(shí)踐,因?yàn)槭褂玫臅r(shí)候還要多注意。
if(node.hasAttribute("p-click")) { node.addEventListener("click",function(){ var attributeValue=node.getAttribute("p-click"); var args=/(.*)/.exec(attributeValue); //允許參數(shù) if(args) { args=args[0]; attributeValue=attributeValue.replace(args,""); args=args.replace(/[()""]/g,"").split(","); } else args=[]; return function (event) { _this.methods[attributeValue].apply(_this,[event,...args]); } }()); }現(xiàn)在我們已經(jīng)將所有的代碼分析完了,是不是很清爽?代碼除去注釋約100行,所有源代碼可以在這里下載。這當(dāng)然不能算作一個(gè)框架了,不過(guò)可以學(xué)習(xí)學(xué)習(xí),這學(xué)期有時(shí)間的話,還要繼續(xù)完善,也歡迎大家一起探討。
一起學(xué)習(xí),一起提高,做技術(shù)應(yīng)當(dāng)是直接的,有問(wèn)題歡迎指出~
最后說(shuō)的第三點(diǎn):是自己還是一個(gè)學(xué)生,做這些內(nèi)容也僅僅是出于興趣。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/88167.html
摘要:寫在前面這篇文章講述了如何利用和實(shí)現(xiàn)雙向數(shù)據(jù)綁定,個(gè)人系早期玩家,寫這個(gè)小框架的時(shí)候也沒(méi)有參考等源代碼,之前了解過(guò)其他實(shí)現(xiàn),但沒(méi)有直接參考其他代碼,如有雷同,純屬巧合。我們同時(shí)也應(yīng)該支持事件機(jī)制,這里我們以最常用的方法作為例子實(shí)現(xiàn)。 寫在前面:這篇文章講述了如何利用Proxy和Reflect實(shí)現(xiàn)雙向數(shù)據(jù)綁定,個(gè)人系Vue早期玩家,寫這個(gè)小框架的時(shí)候也沒(méi)有參考Vue等源代碼,之前了解過(guò)其...
摘要:前端進(jìn)階進(jìn)階構(gòu)建項(xiàng)目一配置最佳實(shí)踐狀態(tài)管理之痛點(diǎn)分析與改良開(kāi)發(fā)中所謂狀態(tài)淺析從時(shí)間旅行的烏托邦,看狀態(tài)管理的設(shè)計(jì)誤區(qū)使用更好地處理數(shù)據(jù)愛(ài)彼迎房源詳情頁(yè)中的性能優(yōu)化從零開(kāi)始,在中構(gòu)建時(shí)間旅行式調(diào)試用輕松管理復(fù)雜狀態(tài)如何把業(yè)務(wù)邏輯這個(gè)故事講好和 前端進(jìn)階 webpack webpack進(jìn)階構(gòu)建項(xiàng)目(一) Webpack 4 配置最佳實(shí)踐 react Redux狀態(tài)管理之痛點(diǎn)、分析與...
摘要:與大多數(shù)全局對(duì)象不同,沒(méi)有構(gòu)造函數(shù)。為什么要設(shè)計(jì)更加有用的返回值早期寫法寫法函數(shù)式操作早期寫法寫法可變參數(shù)形式的構(gòu)造函數(shù)一般寫法寫法當(dāng)然還有很多,大家可以自行到上查看什么是代理設(shè)計(jì)模式代理模式,為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn)。 這是專門探索 JavaScript 及其所構(gòu)建的組件的系列文章的第 19 篇。 如果你錯(cuò)過(guò)了前面的章節(jié),可以在這里找到它們: 想閱讀更多優(yōu)質(zhì)文章請(qǐng)...
摘要:只能劫持對(duì)象的屬性因此我們需要對(duì)每個(gè)對(duì)象的每個(gè)屬性進(jìn)行遍歷。屬性對(duì)于怎么拼接到和上面說(shuō)到了怎么使用做數(shù)據(jù)劫持,怎么結(jié)合訂閱發(fā)布,請(qǐng)結(jié)合數(shù)據(jù)雙向綁定探究對(duì)照著數(shù)據(jù)劫持的部分去替換看一下。 前言 2018年11月16日,關(guān)注vue的人都知道這個(gè)時(shí)間點(diǎn)發(fā)生了什么事兒吧。vue3.0更新內(nèi)容 研究數(shù)據(jù)雙向綁定的大佬們都在開(kāi)始猜測(cè)這個(gè)新機(jī)制了,用原生Proxy替換Object.definePro...
閱讀 3237·2021-11-02 14:44
閱讀 3737·2021-09-02 15:41
閱讀 1679·2019-08-29 16:57
閱讀 1799·2019-08-26 13:38
閱讀 3308·2019-08-23 18:13
閱讀 2119·2019-08-23 15:41
閱讀 1681·2019-08-23 14:24
閱讀 3039·2019-08-23 14:03