摘要:的其中一個賣點(diǎn),就是數(shù)據(jù)綁定。為了解決這個問題,再來一個函數(shù)最終的函數(shù)應(yīng)該是下面這樣子的一個數(shù)據(jù)綁定的實(shí)例,叫做不為什么,就叫做。后記僅僅是一個用于學(xué)習(xí)數(shù)據(jù)綁定的實(shí)驗(yàn)性質(zhì)的項(xiàng)目,代碼仍然不夠優(yōu)雅,功能也不夠豐富。
項(xiàng)目地址:https://github.com/jrainlau/mog
在線體驗(yàn):http://codepen.io/jrainlau/pe...
作為一個前端開發(fā)者,曾踩了太多的“數(shù)據(jù)綁定”的坑。在早些時候,都是通過jQuery之類的工具手動完成這些功能,但是當(dāng)數(shù)據(jù)量非常大的時候,這些手動的工作讓我非常痛苦。直到使用了VueJS,這些痛苦才得以終結(jié)。
VueJS的其中一個賣點(diǎn),就是“數(shù)據(jù)綁定”。使用者無需關(guān)心數(shù)據(jù)是怎么綁定到dom上面的,只需要關(guān)注數(shù)據(jù)就好,因?yàn)?b>VueJS已經(jīng)自動幫我們完成了這些工作。
這真的非常神奇,我不可救藥地愛上了VueJS,并且把它用到我自己的項(xiàng)目當(dāng)中。隨著使用的深入,我更加想知道它深入的原理是什么。
VueJS是如何進(jìn)行數(shù)據(jù)綁定的?通過閱讀官方文檔,我看到了下面這段話:
把一個普通 Javascript 對象傳給 Vue 實(shí)例來作為它的 data 選項(xiàng),Vue 將遍歷它的屬性,用 Object.defineProperty 將它們轉(zhuǎn)為 getter/setter。
關(guān)鍵詞是Object.definProperty,在MDN文檔里面是這么說的:
Object.defineProperty()方法直接定義一個對象的屬性,或者修改對象當(dāng)中一個已經(jīng)存在的屬性,并返回這個對象。
讓我們寫個例子來測試一下它。
首先,建立一個鋼鐵俠對象并賦予他一些屬性:
let ironman = { name: "Tony Stark", sex: "male", age: "35" }
現(xiàn)在我們使用Object.defineProperty()方法來對他的一些屬性進(jìn)行修改,并且在控制臺把所修改的內(nèi)容輸出:
Object.defineProperty(ironman, "age", { set (val) { console.log(`Set age to ${val}`) return val } }) ironman.age = "48" // --> Set age to 48
看起來挺完美的。如果把console.log("Set age to ${val}")改為element.innerHTML = val,是不是就意味著數(shù)據(jù)綁定已經(jīng)完成了呢?
讓我們再修改一下鋼鐵俠的屬性:
let ironman = { name: "Tony Stark", sex: "male", age: "35", hobbies: ["girl", "money", "game"] }
嗯……他就是一個花花公子?,F(xiàn)在我想把一些“愛好”添加到他身上,并且在控制臺看到對應(yīng)的輸出:
Object.defineProperty(ironman.hobbies, "push", { value () { console.log(`Push ${arguments[0]} to ${this}`) this[this.length] = arguments[0] } }) ironman.hobbies.push("wine") console.log(ironman.hobbies) // --> Push wine to girl,money,game // --> [ "girl", "money", "game", "wine" ]
在此之前,我是使用get()方法去追蹤對象的屬性變化,但是對于一個數(shù)組,我們不能使用這個方法,而是使用value()方法來代替。雖然這招也靈,但是并非最好的辦法。有沒有更好的方法可以簡化這些追蹤對象或數(shù)組屬性變化的方法呢?
在ECMA2015,Proxy是一個不錯的選擇什么是Proxy?在MDN文檔中是這么說的(誤):
Proxy可以理解成,在目標(biāo)對象之前架設(shè)一層“攔截”,外界對該對象的訪問,都必須先通過這層攔截,因此提供了一種機(jī)制,可以對外界的訪問進(jìn)行過濾和改寫。
Proxy是ECMA2015的一個新特性,它非常強(qiáng)大,但我并不會討論太多關(guān)于它的東西,除了我們現(xiàn)在需要的一個?,F(xiàn)在讓我們一起來新建一個Proxy實(shí)例:
let ironmanProxy = new Proxy(ironman, { set (target, property, value) { target[property] = value console.log("change....") return true } }) ironmanProxy.age = "48" console.log(ironman.age) // --> change.... // --> 48
符合預(yù)期。那么對于數(shù)組呢?
let ironmanProxy = new Proxy(ironman.hobbies, { set (target, property, value) { target[property] = value console.log("change....") return true } }) ironmanProxy.push("wine") console.log(ironman.hobbies) // --> change... // --> change... // --> [ "girl", "money", "game", "wine" ]
仍然符合預(yù)期!但是為什么輸出了兩次change...呢?因?yàn)槊慨?dāng)我觸發(fā)push()方法的時候,這個數(shù)組的length屬性和body內(nèi)容都被修改了,所以會引起兩次變化。
實(shí)時數(shù)據(jù)綁定解決了最核心的問題,可以考慮其他的問題了。
想象一下,我們有一個模板和數(shù)據(jù)對象:
Hello, my name is {{name}}, I enjoy eatting {{hobbies.food}}
let ironman = { name: "Tony Stark", sex: "male", age: "35", hobbies: { food: "banana", drink: "wine" } }
通過前面的代碼,我們知道如果想要追蹤一個對象的屬性變化,我們應(yīng)該把這個屬性作為第一個參數(shù)傳入Proxy實(shí)例。讓我們一起來創(chuàng)建一個返回新的Proxy實(shí)例的函數(shù)吧!
function $setData (dataObj, fn) { let self = this let once = false let $d = new Proxy(dataObj, { set (target, property, value) { if (!once) { target[property] = value once = true /* Do something here */ } return true } }) fn($d) }
它可以通過以下的方式被使用:
$setData(dataObj, ($d) => { /* * dataObj.someProps = something */ }) // 或者 $setData(dataObj.arrayProps, ($d) => { /* * dataObj.push(something) */ })
除此之外,我們應(yīng)該實(shí)現(xiàn)模板對數(shù)據(jù)對象的映射,這樣才能用Tony Stark來替換{{name}}。
function replaceFun(str, data) { let self = this return str.replace(/{{([^{}]*)}}/g, (a, b) => { return data[b] }) } replaceFun("My name is {{name}}", { name: "xxx" }) // --> My name is xxx
這個函數(shù)對于如{ name: "xx", age: 18 }的單層屬性對象運(yùn)行良好,但是對于如{ hobbies: { food: "apple", drink: "milk" } }這樣的多層屬性對象卻無能為力。舉個例子,如果模板關(guān)鍵字是{{hobbies.food}},那么replaceFun()函數(shù)就應(yīng)該返回data["hobbies"]["food"]。
為了解決這個問題,再來一個函數(shù):
function getObjProp (obj, propsName) { let propsArr = propsName.split(".") function rec(o, pName) { if (!o[pName] instanceof Array && o[pName] instanceof Object) { return rec(o[pName], propsArr.shift()) } return o[pName] } return rec(obj, propsArr.shift()) } getObjProp({ data: { hobbies: { food: "apple", drink: "milk" } } }, "hobbies.food") // --> return { food: "apple", drink: "milk" }
最終的replaceFun()函數(shù)應(yīng)該是下面這樣子的:
function replaceFun(str, data) { let self = this return str.replace(/{{([^{}]*)}}/g, (a, b) => { let r = self._getObjProp(data, b); console.log(a, b, r) if (typeof r === "string" || typeof r === "number") { return r } else { return self._getObjProp(r, b.split(".")[1]) } }) }一個數(shù)據(jù)綁定的實(shí)例,叫做“Mog”
不為什么,就叫做“Mog”。
class Mog { constructor (options) { this.$data = options.data this.$el = options.el this.$tpl = options.template this._render(this.$tpl, this.$data) } $setData (dataObj, fn) { let self = this let once = false let $d = new Proxy(dataObj, { set (target, property, value) { if (!once) { target[property] = value once = true self._render(self.$tpl, self.$data) } return true } }) fn($d) } _render (tplString, data) { document.querySelector(this.$el).innerHTML = this._replaceFun(tplString, data) } _replaceFun(str, data) { let self = this return str.replace(/{{([^{}]*)}}/g, (a, b) => { let r = self._getObjProp(data, b); console.log(a, b, r) if (typeof r === "string" || typeof r === "number") { return r } else { return self._getObjProp(r, b.split(".")[1]) } }) } _getObjProp (obj, propsName) { let propsArr = propsName.split(".") function rec(o, pName) { if (!o[pName] instanceof Array && o[pName] instanceof Object) { return rec(o[pName], propsArr.shift()) } return o[pName] } return rec(obj, propsArr.shift()) } }
使用:
Hello everyone, my name is {{name}}, I am a mini {{lang}} framework for just {{work}}. I can bind data from {{supports.0}}, {{supports.1}} and {{supports.2}}. What"s more, I was created by {{info.author}}, and was written in {{info.jsVersion}}. My motto is "{{motto}}".
Motto:
let template = document.querySelector("#app").innerHTML let mog = new Mog({ template: template, el: "#app", data: { name: "mog", lang: "javascript", work: "data binding", supports: ["String", "Array", "Object"], info: { author: "Jrain", jsVersion: "Ecma2015" }, motto: "Every dog has his day" } }) document.querySelector("#set-motto").oninput = (e) => { mog.$setData(mog.$data, ($d) => { $d.motto = e.target.value }) }
你可以在這里進(jìn)行在線體驗(yàn)。
后記Mog僅僅是一個用于學(xué)習(xí)數(shù)據(jù)綁定的實(shí)驗(yàn)性質(zhì)的項(xiàng)目,代碼仍然不夠優(yōu)雅,功能也不夠豐富。但是這個小玩具讓我學(xué)習(xí)了很多。如果你對它有興趣,歡迎到這里把項(xiàng)目fork走,并且加入一些你的想法。
感謝閱讀!
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/80836.html
摘要:凡是部署了屬性的數(shù)據(jù)結(jié)構(gòu),就稱為部署了遍歷器接口。調(diào)用這個接口,就會返回一個遍歷器對象。 ES6在2015年6月就得以批準(zhǔn),至今已兩年了。近一年多以來陸續(xù)看過很多ES6的資料,工作項(xiàng)目中也逐步的用上了很多ES6的特性(let,const,promise,Template strings,Class,箭頭函數(shù)等等),不得不說,這些特性給開發(fā)帶來了非常多的便利。但是做決定我的ES6知識其...
摘要:將轉(zhuǎn)換成常見的使用實(shí)現(xiàn)的基于迭代器的迭代。處停止迭代器基于鴨子模型接口這里使用語法僅僅為了說明問題使用支持為了使用迭代器屬性需要引入。生成器是迭代器的子類,包含了附加的與。 原文地址:http://babeljs.io/docs/learn-...本文基于Luke Hoban精妙的文章《es6features》,請把star獻(xiàn)給他,你可以在此嘗試這些特性REPL。 概述 ECMAScr...
摘要:執(zhí)行函數(shù)會返回一個遍歷器對象,每一次函數(shù)里面的都相當(dāng)一次遍歷器對象的方法,并且可以通過方法傳入自定義的來改變函數(shù)的行為。函數(shù)可以通過配合函數(shù)更輕松更優(yōu)雅的實(shí)現(xiàn)異步編程和控制流管理。它和構(gòu)造函數(shù)的不同點(diǎn)類的內(nèi)部定義的所有方法,都是不可枚舉的。 let const的命令 在ES6之前,聲明變量只能用var,var方式聲明變量其實(shí)是很不合理的,準(zhǔn)確的說,是因?yàn)镋S5里面沒有塊級作用域是很不合...
摘要:本文是重溫基礎(chǔ)系列文章的第十四篇。元,是指程序本身。有理解不到位,還請指點(diǎn),具體詳細(xì)的介紹,可以查看維基百科元編程。攔截,返回一個布爾值。 本文是 重溫基礎(chǔ) 系列文章的第十四篇。 這是第一個基礎(chǔ)系列的最后一篇,后面會開始復(fù)習(xí)一些中級的知識了,歡迎持續(xù)關(guān)注呀! 接下來會統(tǒng)一整理到我的【Cute-JavaScript】的JavaScript基礎(chǔ)系列中。 今日感受:獨(dú)樂樂不如眾樂樂...
摘要:嘗試使用新特性,自己來實(shí)現(xiàn)一個及的各種特性。我們可以利用這個特性來實(shí)現(xiàn)對數(shù)據(jù)的監(jiān)聽結(jié)果簡單的操作我們已經(jīng)可以對簡單的數(shù)據(jù)操作進(jìn)行監(jiān)聽雖然還有各種問題,接下來,我們只要在監(jiān)聽到后進(jìn)行數(shù)據(jù)操作即可。 嘗試使用es6新特性,自己來實(shí)現(xiàn)一個mvvm及vue的各種特性。相關(guān)代碼放在github,會持續(xù)更新,歡迎賞個star。本篇文章為系列文章的第一篇,會比較容易理解,后續(xù)會持續(xù)更新后面的記錄。文...
閱讀 2467·2021-09-22 16:01
閱讀 3215·2021-09-22 15:41
閱讀 1239·2021-08-30 09:48
閱讀 565·2019-08-30 15:52
閱讀 3386·2019-08-30 13:57
閱讀 1769·2019-08-30 13:55
閱讀 3742·2019-08-30 11:25
閱讀 821·2019-08-29 17:25