摘要:目標(biāo)你好上面是最常見的的用法現(xiàn)在我就只實(shí)現(xiàn)一件事改變執(zhí)行這一句時(shí)頁面會(huì)及時(shí)更新開始動(dòng)工第一步先聲明一個(gè)類我們一開始定義的屬性是定義在中的我們想要這樣賦值的時(shí)候和的相關(guān)聯(lián)需要中間做一個(gè)代理修改代碼執(zhí)行函數(shù)實(shí)現(xiàn)代理觀察的屬性要想實(shí)現(xiàn)這樣賦值的時(shí)
目標(biāo)
htmljs let myMvvm = new Mvvm({ el: document.getElementById("app"), data: { someStr: "你好" } }){{ someStr }}
上面是最常見的vue的用法, 現(xiàn)在我就只實(shí)現(xiàn)一件事
myMvvm.someStr = "改變" // 執(zhí)行這一句時(shí), 頁面會(huì)及時(shí)更新開始動(dòng)工
1、 第一步, 先聲明一個(gè)Mvvm類
class Mvvm { constructor (option) { this.$option = option || {} } }
我們一開始定義的 someStr屬性是定義在option.data中的, 我們想要 myMvvm.someStr這樣賦值的時(shí)候和option的data相關(guān)聯(lián), 需要中間做一個(gè)代理,修改代碼
class Mvvm { constructor (option) { this.$option = option || {} this._proxyData(option.data, this) // 執(zhí)行函數(shù)實(shí)現(xiàn)代理 } _proxyData (obj, context) { Object.keys(obj).forEach(key => { Object.defineProperty(context, key, { configurable: false, enumerable: true, get () { return obj[key] }, set (val) { obj[key] = val } }) }) } }
2、觀察option的data屬性
要想實(shí)現(xiàn) myMvvm.someStr = 1 這樣賦值的時(shí)候,頁面能及時(shí)更新,那么我們就要對(duì)someStr的賦值過程做一個(gè)監(jiān)聽才行, 開心的是 , Object.defineProperty可以輕易做到這點(diǎn)
寫一個(gè)observe類
class Observe { constructor (obj) { Object.keys(obj).forEach(key => { this.defineReactive(obj, key, obj[key]) }) } defineReactive (obj, key, val) { let initVal = val Object.defineProperty(obj, key, { enumerable: true, configurable: false, get () { return initVal }, set (val) { // 每一次的復(fù)制我們都可以在這里獲知,自然可以為所欲為了 initVal = val return initVal } }) } } 然后修改一下Mvvm這個(gè)類的constructor constructor (option) { this.$option = option || {} this._proxyData(option.data, this) new Observe(option.data) }
3、實(shí)現(xiàn)元素的實(shí)時(shí)更新
現(xiàn)在為止, 還只是顯示 一個(gè) {{someStr}} 而已, 我們現(xiàn)在需要做的是讓能變成 你好 這個(gè)值
寫一個(gè)Compile類
{{someStr}}是一個(gè)文本節(jié)點(diǎn),先聲明一個(gè)可以渲染文本節(jié)點(diǎn)的函數(shù) let compileText = function (node, vm, str) { let val = vm[str] if (val) { node.nodeValue = val } } class Compile { constructor(el, vm) { // el 是 #app這個(gè)元素 vm是Mvvm這個(gè)實(shí)例 let frag = this.node2Fragment(el) this.vm = vm this.compileElement(frag) // 讀取子節(jié)點(diǎn)進(jìn)行渲染 el.appendChild(frag) } node2Fragment(el) { // 創(chuàng)建一個(gè)文檔片段把#app元素的子節(jié)點(diǎn)拷貝 let frag = document.createDocumentFragment() let child while (child = el.firstChild) { frag.appendChild(child) } return frag } compileElement(el) { // 渲染節(jié)點(diǎn) let childNodes = el.childNodes; [].forEach.call(childNodes, (node) => { // 遍歷所有的子節(jié)點(diǎn) if (this.isElementNode(node)) { // 如果是元素節(jié)點(diǎn), 重復(fù)便利 this.compileElement(node) } else if (this.isTextNode(node)) { // 如果是文本節(jié)點(diǎn) let matchStr = this.isMustache(node.nodeValue) // 判斷這個(gè)文本值是不是 {{}} 這種類型 if (matchStr) { // 如果有匹配到 compileText(node, this.vm, matchStr) } } }) } isElementNode(node) { // 元素節(jié)點(diǎn) return node.nodeType === 1 } isTextNode(node) { // 文本節(jié)點(diǎn) return node.nodeType === 3 } isMustache(str) { if (!str) { return null } let reg = /{{(.*)}}/ let arr = str.match(reg) return arr ? arr[1].replace(/s/g, "") : null } } 現(xiàn)在修改一下 Mvvm這個(gè)類的constructor函數(shù) constructor(option) { this.$option = option || {} this._proxyData(option.data, this) new Observe(option.data) new Compile(option.el, this) }
現(xiàn)在你好這個(gè)值終于是被渲染出來, 我們踏出了第一步, 現(xiàn)在開始實(shí)現(xiàn) myMvvm.someStr = 1 也能及時(shí)更新
在實(shí)現(xiàn)complie的時(shí)候, 我們知道渲染的時(shí)候調(diào)用了compileText函數(shù),那么我們現(xiàn)在更改someStr時(shí)及時(shí)渲染,就只要再執(zhí)行這個(gè)函數(shù)就可以了, 我們可以把這個(gè)更新函數(shù)放到一個(gè)隊(duì)列里, 每次更新someStr的時(shí)候, 把這個(gè)隊(duì)列里的更新函數(shù)執(zhí)行一遍就可以了
我們實(shí)現(xiàn)一個(gè)Dep類
// 這里聲明兩個(gè)變量待會(huì)使用 let updateFn let canMount class Dep { constructor () { this.queue = [] } mount () { this.queue.push(updateFn) } notify () { this.queue.forEach(fn => fn()) } } 然后修改一下Observe類的defineReactive函數(shù) defineReactive(obj, key, val) { let initVal = val let dep = new Dep() Object.defineProperty(obj, key, { enumerable: true, configurable: false, get() { if (canMount) { // 防止每次get都會(huì)執(zhí)行這里 dep.mount() } return initVal }, set(val) { // 每一次的復(fù)制我們都可以在這里獲知,自然可以為所欲為了 if (val !== initVal) { initVal = val dep.notify() } return initVal } }) } 實(shí)現(xiàn)一個(gè)生成更新函數(shù) 的方法 let bindTextUpdater = function (node, vm, matchStr) { canMount = true updateFn = compileText.bind(null, node, vm, matchStr) updateFn() canMount = false } 然后最后一步 修改一下Complie類的compileElement方法 let childNodes = el.childNodes; [].forEach.call(childNodes, (node) => { // 遍歷所有的子節(jié)點(diǎn) if (this.isElementNode(node)) { // 如果是元素節(jié)點(diǎn), 重復(fù)便利 this.compileElement(node) } else if (this.isTextNode(node)) { // 如果是文本節(jié)點(diǎn) let matchStr = this.isMustache(node.nodeValue) // 判斷這個(gè)文本值是不是 {{}} 這種類型 if (matchStr) { // 如果有匹配到 bindUpdater(node, this.vm, matchStr) // 綁定更新函數(shù) } } })
現(xiàn)在執(zhí)行 myMvvm.someStr = 155 會(huì)發(fā)現(xiàn)簡(jiǎn)單的例子實(shí)現(xiàn)了
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/52897.html
摘要:目標(biāo)你好上面是最常見的的用法現(xiàn)在我就只實(shí)現(xiàn)一件事改變執(zhí)行這一句時(shí)頁面會(huì)及時(shí)更新開始動(dòng)工第一步先聲明一個(gè)類我們一開始定義的屬性是定義在中的我們想要這樣賦值的時(shí)候和的相關(guān)聯(lián)需要中間做一個(gè)代理修改代碼執(zhí)行函數(shù)實(shí)現(xiàn)代理觀察的屬性要想實(shí)現(xiàn)這樣賦值的時(shí) 目標(biāo) html {{ someStr }} js let myMvvm = new Mvvm({ el: documen...
摘要:目標(biāo)你好上面是最常見的的用法現(xiàn)在我就只實(shí)現(xiàn)一件事改變執(zhí)行這一句時(shí)頁面會(huì)及時(shí)更新開始動(dòng)工第一步先聲明一個(gè)類我們一開始定義的屬性是定義在中的我們想要這樣賦值的時(shí)候和的相關(guān)聯(lián)需要中間做一個(gè)代理修改代碼執(zhí)行函數(shù)實(shí)現(xiàn)代理觀察的屬性要想實(shí)現(xiàn)這樣賦值的時(shí) 目標(biāo) html {{ someStr }} js let myMvvm = new Mvvm({ el: documen...
摘要:兼容性更詳細(xì)的可以看一下實(shí)現(xiàn)思路系列的雙向綁定,關(guān)鍵步驟實(shí)現(xiàn)數(shù)據(jù)監(jiān)聽器,用重寫數(shù)據(jù)的,值更新就在中通知訂閱者更新數(shù)據(jù)。 showImg(https://segmentfault.com/img/remote/1460000015375220?w=640&h=426); 前言 現(xiàn)在的前端面試不管你用的什么框架,總會(huì)問你這個(gè)框架的雙向綁定機(jī)制,有的甚至要求你現(xiàn)場(chǎng)實(shí)現(xiàn)一個(gè)雙向綁定出來,那對(duì)于...
摘要:雙嘆號(hào)強(qiáng)制類型轉(zhuǎn)換為布爾值。官方示例代碼用注冊(cè)了全局組件,會(huì)把自動(dòng)注冊(cè)為屬性,所以沒有手動(dòng)寫屬性。如果對(duì)象是響應(yīng)的,將觸發(fā)視圖更新。這是用來布爾值,又學(xué)了一招和分別代表單擊和雙擊事件綁定。 如果覺得有幫助,歡迎 star哈~ https://github.com/jiangjiu/blog-md/issues/11 感謝作者 @尤小右 大大邊寫的超級(jí)帶感的 Vue.js 前端框架,贈(zèng)送...
摘要:無論是還是都提倡單向數(shù)據(jù)流管理狀態(tài),那我們今天要談的雙向綁定是否和單向數(shù)據(jù)流理念有所違背我覺得不是,從上篇文章語法樹轉(zhuǎn)函數(shù)了解到,雙向綁定,實(shí)質(zhì)是的單向綁定和事件偵聽的語法糖。源碼解析今天涉及到的代碼全在文件夾下。 通過對(duì) Vue2.0 源碼閱讀,想寫一寫自己的理解,能力有限故從尤大佬2016.4.11第一次提交開始讀,準(zhǔn)備陸續(xù)寫: 模版字符串轉(zhuǎn)AST語法樹 AST語法樹轉(zhuǎn)rend...
閱讀 1158·2021-11-24 10:43
閱讀 3116·2021-11-22 09:34
閱讀 3555·2021-10-08 10:04
閱讀 3939·2021-09-23 11:58
閱讀 3123·2019-08-30 15:44
閱讀 490·2019-08-30 13:01
閱讀 1164·2019-08-28 18:07
閱讀 1453·2019-08-26 13:42