摘要:一起來(lái)實(shí)現(xiàn)一個(gè)框架最近手癢,當(dāng)然也是為了近階段的跳槽做準(zhǔn)備,利用周五時(shí)光,仿照用法,實(shí)現(xiàn)一下的雙向綁定數(shù)據(jù)代理大胡子模板指令,等。
一起來(lái)實(shí)現(xiàn)一個(gè)mvvm框架
最近手癢,當(dāng)然也是為了近階段的跳槽做準(zhǔn)備,利用周五時(shí)光,仿照vue用法,實(shí)現(xiàn)一下mvvm的雙向綁定、數(shù)據(jù)代理、大胡子{{}}模板、指令v-on,v-bind等。當(dāng)然由于時(shí)間緊迫,里面的編碼細(xì)節(jié)沒(méi)有做優(yōu)化,還請(qǐng)各位看官多多包涵!看招:
實(shí)現(xiàn)原理數(shù)據(jù)的劫持觀察(observe)
觀察者模式(watcher)
使用es6的類class實(shí)現(xiàn)(當(dāng)然,沒(méi)有考慮到兼容性,只是為了實(shí)現(xiàn)而已)
代碼:數(shù)據(jù)劫持
_observe(obj){ // 遞歸遍歷 // let value; for (const key in obj) { let value; if (obj.hasOwnProperty(key)){ // 利用原理 劫持?jǐn)?shù)據(jù)---發(fā)布訂閱 value = obj[key]; if (typeof value === "object") { console.log("value", value) this._observe(value) } // 訂閱(key)數(shù)據(jù) if (!this._binding[key]) {this._binding[key]= []}; let binding = this._binding[key] // 重寫getter, setter Object.defineProperty(obj, key, { enumerable: true, configurable: true, get() { return value }, set(newVal) { if (value === newVal) return false; value = newVal console.log("newvalue", value) // 主要value更新,就發(fā)布通知(監(jiān)聽(tīng)這個(gè)key的所有的)watcher更新(改變dom) binding.forEach(watcher => { console.log("watcher", watcher) watcher.update() }); } }) } } }
實(shí)例代理數(shù)據(jù)
_proxyData(data, vm) { // data身上的所有屬性全部掛載到vm實(shí)例上 for (const key in data) { // let val = data[key]; // ctx.key = val; Object.defineProperty(vm, key, { configurable: true, enumerable: true, get() { return data[key]; }, set(newVal) { data[key] = newVal; vm._observe(newVal) } }) } }
模板編譯,添加發(fā)布訂閱
_compile(root){ // 獲取所有節(jié)點(diǎn) let nodes = root.childNodes // 遞歸編譯 Array.from(nodes).forEach(node => { // 針對(duì)每一個(gè)節(jié)點(diǎn)進(jìn)行處理 // 元素節(jié)點(diǎn) if (node.nodeType === 1) {//只考慮綁定了一個(gè)指令 // 獲取節(jié)點(diǎn)的屬性集合 const attributes = Array.from(node.attributes); // 指令進(jìn)行編譯 if (hasDirective(attributes, "v-bind")) { const attrVal = getDirectiveValue(node, attributes, "v-bind"); const exp = getDirectiveParams(attributes, "v-bind"); // const node.setAttribute(exp, this.$data[attrVal]) this._binding[attrVal].push(new watcher({ vm: this, el: node, exp, attr: attrVal })) } if (hasDirective(attributes, "v-on")) { const eventName = getDirectiveParams(attributes, "v-on"); node.addEventListener(eventName, (e) => { this.$methods[getDirectiveValue(node, attributes, "v-on")].call(this) }) } if (node.hasAttribute("v-model") && node.tagName === "INPUT" || node.tagName === "TEXTAREA") { let attrVal = node.getAttribute("v-model"); this._binding[attrVal].push(new Watcher({ vm: this, el: node, attr: attrVal, name: "v-model" })) node.addEventListener("input", e=> { this.$data[attrVal] = node.value; }) node.value = this.$data[attrVal] } // 遞歸接著處理 if (node.hasChildNodes()) { this._compile(node) } } // 文本節(jié)點(diǎn) if (node.nodeType === 3) { let text = node.textContent; let keyArr = []; // 獲取{{變量}},用正則去匹配;watcher去觀察{{變量}}(包裹元素), let newText = text.replace(/{{(w+)}}/g, (match, p0)=> { keyArr = [...keyArr, p0]; // 替換屬性為真正的屬性值 return this.$data[p0] }) node.textContent = newText; // 把整個(gè)文本節(jié)點(diǎn)進(jìn)行監(jiān)控{{v1}}-----{{v2}};添加到訂閱到數(shù)組里等待通知 keyArr.forEach(key => { // !this._binding[key] && (this._binding[key] = []) this._binding[key].push(new Watcher({ vm: this, el: node, attr: text })) }) } }) }
觀察者實(shí)例
class Watcher { constructor({ vm, name, el, exp, attr, }) { this.vm = vm; this.el = el; this.name = name; this.exp = exp; this.attr = attr; } // 更新text,或更新屬性 update() { // 改變節(jié)點(diǎn)的屬性 if (this.el.nodeType === 1) { // this.el.value = this.vm.$data[this.exp] if (this.name === "v-model") { console.log("value", this.el) this.el.value = this.vm.$data[this.attr] } this.el[this.attr] = this.vm.$data[this.exp] } // 文本節(jié)點(diǎn) else { let text = this.attr; // 獲取{{變量}},用正則去匹配;watcher去觀察{{變量}}(包裹元素), let newText = text.replace(/{{(w+)}}/g, (match, p0)=> { // 替換屬性為真正的屬性值 return this.vm.$data[p0] }) this.el.textContent = newText; } } }
整體代碼
vue實(shí)現(xiàn) {{title}}
{{text}}
我點(diǎn)的時(shí)候就會(huì)變化{{number}}---{{number}}
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/94277.html
摘要:少年,我看你骨骼精奇,是萬(wàn)中無(wú)一的武學(xué)奇才,我這有本圖片流秘籍,見(jiàn)與你有緣,就送于你了。文件大小,單位為字節(jié),該屬性只讀。用來(lái)讀取或文件數(shù)據(jù),基于文件大小不同,讀取的過(guò)程為異步。 showImg(https://segmentfault.com/img/remote/1460000016276887); 少年,我看你骨骼精奇,是萬(wàn)中無(wú)一的武學(xué)奇才,我這有本《圖片流》秘籍,見(jiàn)與你有緣,就...
摘要:方案回退時(shí),跳到頁(yè)面頂部。踏坑第九式日期轉(zhuǎn)換的問(wèn)題將日期字符串的格式符號(hào)替換成。歡迎感興趣的各路武林豪杰加入。 前言 少俠,請(qǐng)留步,相見(jiàn)必是緣分,贈(zèng)與你一部《踏坑秘籍》 扎馬步 踏坑第一式 ios豎屏拍照上傳,圖片被旋轉(zhuǎn)問(wèn)題 解決方案 // 幾個(gè)步驟 // 1.通過(guò)第三方插件exif-js獲取到圖片的方向 // 2.new一個(gè)FileReader對(duì)象,加載讀取上傳的圖片 // 3.在f...
摘要:方案回退時(shí),跳到頁(yè)面頂部。踏坑第九式日期轉(zhuǎn)換的問(wèn)題將日期字符串的格式符號(hào)替換成。歡迎感興趣的各路武林豪杰加入。 前言 少俠,請(qǐng)留步,相見(jiàn)必是緣分,贈(zèng)與你一部《踏坑秘籍》 扎馬步 踏坑第一式 ios豎屏拍照上傳,圖片被旋轉(zhuǎn)問(wèn)題 解決方案 // 幾個(gè)步驟 // 1.通過(guò)第三方插件exif-js獲取到圖片的方向 // 2.new一個(gè)FileReader對(duì)象,加載讀取上傳的圖片 // 3.在f...
閱讀 2890·2021-11-22 15:22
閱讀 19638·2021-09-22 15:00
閱讀 1470·2021-09-07 09:58
閱讀 1265·2019-08-30 13:01
閱讀 2474·2019-08-29 16:27
閱讀 2371·2019-08-26 13:25
閱讀 1649·2019-08-26 12:13
閱讀 976·2019-08-26 11:53