摘要:嘗試使用新特性,自己來實現(xiàn)一個及的各種特性。我們可以利用這個特性來實現(xiàn)對數(shù)據(jù)的監(jiān)聽結(jié)果簡單的操作我們已經(jīng)可以對簡單的數(shù)據(jù)操作進行監(jiān)聽雖然還有各種問題,接下來,我們只要在監(jiān)聽到后進行數(shù)據(jù)操作即可。
最簡單的watcher嘗試使用es6新特性,自己來實現(xiàn)一個mvvm及vue的各種特性。
相關(guān)代碼放在github,會持續(xù)更新,歡迎賞個star。
本篇文章為系列文章的第一篇,會比較容易理解,后續(xù)會持續(xù)更新后面的記錄。
文章首發(fā)于本人博客
從開始接觸Vue開始,我們便對它的“數(shù)據(jù)響應(yīng)”贊嘆不絕,那么我們首先,來實現(xiàn)一個最簡單的watcher,來監(jiān)聽數(shù)據(jù),以進行對應(yīng)的操作,類似后續(xù)會涉及的dom操作等。
Proxy我們都知道,Vue使用Object.defineProperty來進行數(shù)據(jù)監(jiān)聽,監(jiān)聽obj的get和set方法。在ES6中,Proxy可以攔截某些操作的默認行為,也就是對目標(biāo)對象的訪問進行攔截,過濾和改寫。我們可以利用這個特性來實現(xiàn)對數(shù)據(jù)的監(jiān)聽:
const watcher = (obj, fn) => { return new Proxy(obj, { get (target, prop, receiver) { return Reflect.get(target, prop, receiver) }, set (target, prop, value) { const oldValue = Reflect.get(target, prop, receiver) const result = Reflect.set(target, prop, value) fn(value, oldValue) return result } }) }
結(jié)果:
let obj = watcher({ a: 1 }, (val, oldVal) => { console.log("old =>> ", oldVal) console.log("new =>> ", val) }) obj.a = 2 // old =>> , 1 // new =>> , 2簡單的Dom操作
我們已經(jīng)可以對簡單的數(shù)據(jù)操作進行監(jiān)聽(雖然還有各種問題),接下來,我們只要在監(jiān)聽到dom后進行數(shù)據(jù)操作即可。解析模板什么的我們就先不做了,我們可以繼續(xù)利用Proxy實現(xiàn)一個dom輔助函數(shù):
const dom = new Proxy({}, { get (target, tagName) { return (attrs = {}, ...childrens) => { // 創(chuàng)建節(jié)點 const elem = document.createElement(tagName) // 添加attribute attrs.forEach(attr => elem.addAttribute(attr, attrs[attr]) // 添加子元素 childrens.forEach(child => { const child = typeof child === "string" ? document.createTextNode(child) : child elem.appendChild(child) }) return elem } } })
也就是說,我們?yōu)閐om的各屬性進行監(jiān)聽,當(dāng)訪問對應(yīng)的節(jié)點時,我們創(chuàng)建并且為他添加各種屬性等:
dom.div( {class: "wrap"}, "helloworld", dom.a({ href: "https://www.#" }, "welcome to 360") ) // 輸出拼接基礎(chǔ)框架helloworld welcome to 360
我們在這里給我們的這個小架子起名為"W",讓它可以真正的運行起來。類似Vue的語法,我們需要在進行實例化的時候,watch我們的data,并且更新dom。類似這樣:
const vm = new W({ el: "body", data () { return { msg: "hello world" } }, render () { return dom.div({ class: "wrap" }, dom.a({ href: "http://www.#" }, this.msg) ) } })
因此,我們需要實現(xiàn)這樣一個類,來處理我們的參數(shù),并進行實例的初始化,監(jiān)聽,以及渲染控制等。
export default class W { constructor (config) {} /** * observe data */ _initData () {} /** * 渲染節(jié)點 */ _renderDom () {} }初始化數(shù)據(jù)
首先,我們進行數(shù)據(jù)初始化,將數(shù)據(jù)置為observable,在對其修改的時候進行監(jiān)聽:
import watcher from "./data.js" class W { constructor (config) { const { data = () => {} } = config this._config = config this._initData(data) return this._vm } } _initData (data) { this._vm = watcher(Object.assign({}, this, data()), this._renderDom.bind(this)) }
在這里我們需要注意兩點:
我們的data參數(shù)為一個function
這個原因在vue官方文檔已經(jīng)說過,當(dāng)我們直接使用對象的時候,不同的實例間會共享同一個對象,導(dǎo)致出現(xiàn)對一個組件進行修改,另一個組件也進行修改的問題。具體可以查看data-必須是函數(shù)
我們返回的是this._vm而不是this
我們這里做了兩步操作,首先將this與data進行合并,再將整個對象進行監(jiān)聽,并賦值到_vm屬性上。
這樣,我們通過new W()初始化的實例,則可以訪問到我們的data屬性及方法,并且具有數(shù)據(jù)驅(qū)動的特性了。
更新DOM我們已經(jīng)為watcher的回調(diào)添加了dom更新的事件,我們只要在這里執(zhí)行render函數(shù),并掛載到對應(yīng)的el上即可:
const { render, el } = this._config const targetEl = document.querySelector(el) const renderDom = render() targetEl.innerHTML = "" targetEl.appendChild(renderDom)綁定this
我們會發(fā)現(xiàn),我們在config的render函數(shù)中,使用了this.msg來訪問data的msg屬性,因此我們需要實現(xiàn)在各組件中,通過this可以訪問到本實例的特性。我猜你已經(jīng)想到了,我們可以使用bind,call和apply來實現(xiàn)它:
/** * 為所有的函數(shù)綁定this */ bindVM () { const { _config } = this for(let key of Object.keys(_config)) { const val = _config[key] if (typeof(val) === "function") { _config[key] = val.bind(this._vm) } } }測試
簡單的架子拼接完成,我們來進行測試下我們的成果,我們需要實現(xiàn)兩點功能:
可以按照我們的render函數(shù)正常掛載,并可訪問到data上的數(shù)據(jù)
通過對實例進行修改,修改會自動更新到節(jié)點上
代碼:
const vm = new W({ el: "body", data () { return { msg: "hello world" } }, render () { return dom.div({ class: "wrap" }, dom.a({ href: "http://www.#" }, this.msg) ) } }) // 測試修改vm setInterval(_ => { vm.msg = "hello world =>>>" + new Date() }, 1000)
結(jié)果:
最基本的功能已經(jīng)實現(xiàn)啦!
結(jié)語本次我們只實現(xiàn)了最最最簡單的數(shù)據(jù)驅(qū)動功能,后續(xù)還有很多需要進行處理,我們也會對其一一進行梳理和實現(xiàn),大家可以持續(xù)關(guān)注下,例如:
數(shù)組變動監(jiān)聽
object深度監(jiān)聽
更新隊列
render過程中記錄僅相關(guān)的屬性
模板渲染
v-model
...等等
相關(guān)代碼放在github,會持續(xù)更新,歡迎賞個star。
敬請期待!
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/51052.html
摘要:嘗試使用新特性,自己來實現(xiàn)一個及的各種特性。我們可以利用這個特性來實現(xiàn)對數(shù)據(jù)的監(jiān)聽結(jié)果簡單的操作我們已經(jīng)可以對簡單的數(shù)據(jù)操作進行監(jiān)聽雖然還有各種問題,接下來,我們只要在監(jiān)聽到后進行數(shù)據(jù)操作即可。 嘗試使用es6新特性,自己來實現(xiàn)一個mvvm及vue的各種特性。相關(guān)代碼放在github,會持續(xù)更新,歡迎賞個star。本篇文章為系列文章的第一篇,會比較容易理解,后續(xù)會持續(xù)更新后面的記錄。文...
摘要:可以在該鉤子中進一步地更改狀態(tài),不會觸發(fā)附加的重渲染過程。我工作中只用到,對和不怎么熟與的區(qū)別相同點都支持指令內(nèi)置指令和自定義指令都支持過濾器內(nèi)置過濾器和自定義過濾器都支持雙向數(shù)據(jù)綁定都不支持低端瀏覽器。 看看面試題,只是為了查漏補缺,看看自己那些方面還不懂。切記不要以為背了面試題,就萬事大吉了,最好是理解背后的原理,這樣面試的時候才能侃侃而談。不然,稍微有水平的面試官一看就能看出,是...
摘要:目前已經(jīng)在大大小小多個線上產(chǎn)品中使用了,也收集了一些有效的建議好了,該看下一個最簡單的組件長什么樣吧免費領(lǐng)取驗證碼內(nèi)容安全短信發(fā)送直播點播體驗包及云服務(wù)器等套餐更多網(wǎng)易技術(shù)產(chǎn)品運營經(jīng)驗分享請訪問網(wǎng)易云社區(qū)。文章來源網(wǎng)易云社區(qū) 本文由作者鄭海波授權(quán)網(wǎng)易云社區(qū)發(fā)布。 此文摘自regularjs的指南, 目前指南正在全面更新, 把老文檔的【接口/語法部分】統(tǒng)一放到了獨立的 Reference...
摘要:前端的發(fā)展歷程什么是前端前端針對瀏覽器的開發(fā),代碼在瀏覽器運行后端針對服務(wù)器的開發(fā),代碼在服務(wù)器運行前端三劍客超文本標(biāo)記語言是構(gòu)成世界的基石。 前端的發(fā)展歷程 什么是前端 前端:針對瀏覽器的開發(fā),代碼在瀏覽器運行 后端:針對服務(wù)器的開發(fā),代碼在服務(wù)器運行 前端三劍客 HTML CSS JavaScript HTML HTML(超文本標(biāo)記語言——HyperText Markup ...
摘要:前端面試題總結(jié)持續(xù)更新中是哪個組件的屬性模塊的組件。都提供合理的鉤子函數(shù),可以讓開發(fā)者定制化地去處理需求。 前端面試題總結(jié)——VUE(持續(xù)更新中) 1.active-class是哪個組件的屬性? vue-router模塊的router-link組件。 2.嵌套路由怎么定義? 在 VueRouter 的參數(shù)中使用 children 配置,這樣就可以很好的實現(xiàn)路由嵌套。 //引入兩個組件 ...
閱讀 2865·2021-11-22 11:56
閱讀 3565·2021-11-15 11:39
閱讀 909·2021-09-24 09:48
閱讀 769·2021-08-17 10:14
閱讀 1336·2019-08-30 15:55
閱讀 2764·2019-08-30 15:55
閱讀 1320·2019-08-30 15:44
閱讀 2790·2019-08-30 10:59