日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術(shù)文章
文章詳情頁

vue mvvm數(shù)據(jù)響應(yīng)實(shí)現(xiàn)

瀏覽:25日期:2022-11-01 17:16:03

為什么實(shí)現(xiàn)數(shù)據(jù)響應(yīng)式

當(dāng)前vue、react等框架流行。無論是vue、還是react框架大家最初的設(shè)計(jì)思路都是類似的。都是以數(shù)據(jù)驅(qū)動(dòng)視圖,數(shù)據(jù)優(yōu)先。希望能夠通過框架減少開發(fā)人員直接操作節(jié)點(diǎn),讓開發(fā)人員能夠把更多的精力放在業(yè)務(wù)上而不是過多的放在操作節(jié)點(diǎn)上。另一方面,框架會(huì)通過虛擬dom及diff算法提高頁面性能。這其中需要數(shù)據(jù)優(yōu)先最根本的思路就是實(shí)現(xiàn)數(shù)據(jù)響應(yīng)式。so,本次來看下如何基于原生實(shí)現(xiàn)數(shù)據(jù)響應(yīng)式。

vue中的數(shù)據(jù)響應(yīng)

vue中會(huì)根據(jù)數(shù)據(jù)將數(shù)據(jù)通過大胡子語法及指令渲染到視圖上,這里我們以大胡子語法為例。如下:

<div id='app'> {{message}}</div>

let vm = new Vue({ el:'#app', data:{ message:'測試數(shù)據(jù)' }})setTimeout(()=>{ vm.message = '修改的數(shù)據(jù)';},1000)

如上代碼,很簡單 。vue做了兩件事情。一、把message數(shù)據(jù)初次渲染到視圖。二、當(dāng)message數(shù)據(jù)改變的時(shí)候視圖上渲染的message數(shù)據(jù)同時(shí)也會(huì)做出響應(yīng)。以最簡單的案例。帶著問題來看,通過原生js如何實(shí)現(xiàn)??這里為了簡化操作便于理解,這里就不去使用虛擬dom。直接操作dom結(jié)構(gòu)。

實(shí)現(xiàn)數(shù)據(jù)初次渲染

根據(jù)vue調(diào)用方式。定義Vue類來實(shí)現(xiàn)各種功能。將初次渲染過程定義成編譯compile函數(shù)渲染視圖。通過傳入的配置以及操作dom來實(shí)現(xiàn)渲染。大概思路是通過正則查找html 里 #app 作用域內(nèi)的表達(dá)式,然后查找數(shù)據(jù)做對應(yīng)的替換即可。具體實(shí)現(xiàn)如下:

class Vue { constructor(options) { this.opts = options; this.compile(); } compile() { let ele = document.querySelector(this.opts.el); // 獲取所有子節(jié)點(diǎn) let childNodes = ele.childNodes; childNodes.forEach(node => { if (node.nodeType === 3) {// 找到所有的文本節(jié)點(diǎn)let nodeContent = node.textContent;// 匹配“{{}}”let reg = /{{s*([^{}s]+)s*}}/g;if (reg.test(nodeContent)) { let $1 = RegExp.$1; // 查找數(shù)據(jù)替換 “{{}}” node.textContent = node.textContent.replace(reg, this.opts.data[$1]);} } }) }}

如上完成了初次渲染,將message數(shù)據(jù)渲染到了視圖上。但是會(huì)返現(xiàn)并沒對深層次的dom結(jié)構(gòu)做處理也就是如下情況:

<div id='app'> 1{{ message }}2 <div> hello , {{ message }} </div> </div>

vue mvvm數(shù)據(jù)響應(yīng)實(shí)現(xiàn)

渲染結(jié)果如上

發(fā)現(xiàn)結(jié)果并沒有達(dá)到預(yù)期。so,需要改下代碼,讓節(jié)點(diǎn)可以深層次查找就可以了。代碼如下:

compile() { let ele = document.querySelector(this.opts.el); this.compileNodes(ele); } compileNodes(ele) { // 獲取所有子節(jié)點(diǎn) let childNodes = ele.childNodes; childNodes.forEach(node => { if (node.nodeType === 3) {// 找到所有的文本節(jié)點(diǎn)let nodeContent = node.textContent;// 匹配“{{}}”let reg = /{{s*([^{}s]+)s*}}/g;if (reg.test(nodeContent)) { let $1 = RegExp.$1; // 查找數(shù)據(jù)替換 “{{}}” node.textContent = node.textContent.replace(reg, this.opts.data[$1]);} } else if (node.nodeType === 1) {if (node.childNodes.length > 0) { this.compileNodes(node);} } }) }

上述代碼通過遞歸查找節(jié)點(diǎn) 實(shí)現(xiàn)深層次節(jié)點(diǎn)的渲染工作。如此,就實(shí)現(xiàn)了視圖的初次渲染。

數(shù)據(jù)劫持

回過頭來看下上面說的第二個(gè)問題:當(dāng)message數(shù)據(jù)改變的時(shí)候視圖上渲染的message數(shù)據(jù)同時(shí)也會(huì)做出響應(yīng)。如何實(shí)現(xiàn)數(shù)據(jù)響應(yīng)式?簡而言之就是數(shù)據(jù)變動(dòng)影響視圖變動(dòng)?再將問題拆分下 1. 如何知道數(shù)據(jù)變動(dòng)了? 2.如何根據(jù)數(shù)據(jù)變動(dòng)來更改視圖?

如何知道數(shù)據(jù)變動(dòng)了? 這里就需要用到數(shù)據(jù)攔截了,或者叫數(shù)據(jù)觀察。把會(huì)變動(dòng)的data數(shù)據(jù)觀察起來。當(dāng)他變動(dòng)的時(shí)候我們可以做后續(xù)的渲染事情。如何攔截?cái)?shù)據(jù)呢 ?vue2里采取的是definePrototype。

let obj = { myname:'張三'}Object.defineProperty(obj,’myname’,{ configurable:true, enumerable:true, get(){ console.log('get.') return '張三'; }, set(newValue){ console.log('set') console.log(newValue); }})console.log(obj);

上述代碼會(huì)發(fā)現(xiàn),通過defineProperty劫持的對象屬性下都會(huì)有g(shù)et及set方法。那么當(dāng)我們獲取或者設(shè)置數(shù)據(jù)的時(shí)候就能出發(fā)對應(yīng)的get及set 。這樣就能攔截?cái)?shù)據(jù)做后續(xù)操作。

vue mvvm數(shù)據(jù)響應(yīng)實(shí)現(xiàn)

還有沒有其他方式達(dá)到數(shù)據(jù)劫持的效果呢?ES6中出現(xiàn)了Proxy 代理對象同樣也可以達(dá)到類似劫持?jǐn)?shù)據(jù)的功能。如下代碼:

let obj = { myname:'張三'}let newObj = new Proxy(obj,{ get(target,key){ console.log('get...') return '張三' }, set(target,name,newValue){ console.log('set...'); return Reflect.set(target,name,newValue); }})

兩種方式都可以實(shí)現(xiàn)數(shù)據(jù)劫持。proxy功能更加強(qiáng)大,很多方法是defineProperty所不具備的。且proxy直接攔截的是對象而defineProperty攔截的是對象屬性。so,可以利用上述方式將data數(shù)據(jù)做劫持,代碼如下:

observe(data){ let keys = Object.keys(data); keys.forEach(key=>{ let value = data[key]; Object.defineProperty(data,key,{configurable:true,enumerable:true,get(){ return value;},set(newValue){ value = newValue;} }); }) }

觀察者模式實(shí)現(xiàn)數(shù)據(jù)響應(yīng)

有了劫持?jǐn)?shù)據(jù)方式后,接下來需要實(shí)現(xiàn)的就是當(dāng)修改數(shù)據(jù)的時(shí)候?qū)⑿聰?shù)據(jù)渲染到視圖。如何辦到呢?會(huì)發(fā)現(xiàn),需要在data設(shè)置的時(shí)候觸發(fā)視圖的compile編譯。二者之間互相影響,此時(shí)可以想到利用觀察者模式,通過觀察者模式讓二者產(chǎn)生關(guān)聯(lián),如下:

vue mvvm數(shù)據(jù)響應(yīng)實(shí)現(xiàn)

圖略小,代碼也貼上吧。

class Vue extends EventTarget { constructor(options) { super(); this.opts = options; this.observe(this.opts.data); this.compile(); } observe(data){ let keys = Object.keys(data); let _this = this; keys.forEach(key=>{ let value = data[key]; Object.defineProperty(data,key,{configurable:true,enumerable:true,get(){ return value;},set(newValue){ _this.dispatchEvent(new CustomEvent(key,{ detail:newValue })); value = newValue;} }); }) } compile() { let ele = document.querySelector(this.opts.el); this.compileNodes(ele); } compileNodes(ele) { // 獲取所有子節(jié)點(diǎn) let childNodes = ele.childNodes; childNodes.forEach(node => { if (node.nodeType === 3) {// 找到所有的文本節(jié)點(diǎn)let nodeContent = node.textContent;// 匹配“{{}}”let reg = /{{s*([^{}s]+)s*}}/g;if (reg.test(nodeContent)) { let $1 = RegExp.$1; // 查找數(shù)據(jù)替換 “{{}}” node.textContent = node.textContent.replace(reg, this.opts.data[$1]); this.addEventListener($1,e=>{ let oldValue = this.opts.data[$1]; let newValue = e.detail; let reg = new RegExp(oldValue); node.textContent = node.textContent.replace(reg,newValue); })} } else if (node.nodeType === 1) {if (node.childNodes.length > 0) { this.compileNodes(node);} } }) }}

如上,成功的通過觀察者模式實(shí)現(xiàn)了數(shù)據(jù)的響應(yīng)。但是會(huì)發(fā)現(xiàn)data與compile之間需要通過鍵名來進(jìn)行關(guān)聯(lián)。如果data數(shù)據(jù)結(jié)構(gòu)嵌套關(guān)系復(fù)雜后面會(huì)比較難處理。有沒有一種方式讓二者松解耦呢?這時(shí)候可以用發(fā)布訂閱模式來進(jìn)行改造。

發(fā)布訂閱模式改造響應(yīng)式

vue mvvm數(shù)據(jù)響應(yīng)實(shí)現(xiàn)

還是略小,也還是貼上代碼:

class Vue { constructor(options) { this.opts = options; this.observe(this.opts.data); this.compile(); } observe(data){ let keys = Object.keys(data); let _this = this; keys.forEach(key=>{ let value = data[key]; let dep = new Dep(); Object.defineProperty(data,key,{configurable:true,enumerable:true,get(){ if(Dep.target){ dep.addSub(Dep.target); } return value;},set(newValue){ dep.notify(newValue); value = newValue;} }); }) } compile() { let ele = document.querySelector(this.opts.el); this.compileNodes(ele); } compileNodes(ele) { // 獲取所有子節(jié)點(diǎn) let childNodes = ele.childNodes; childNodes.forEach(node => { if (node.nodeType === 3) {// 找到所有的文本節(jié)點(diǎn)let nodeContent = node.textContent;// 匹配“{{}}”let reg = /{{s*([^{}s]+)s*}}/g;if (reg.test(nodeContent)) { let $1 = RegExp.$1; // 查找數(shù)據(jù)替換 “{{}}” node.textContent = node.textContent.replace(reg, this.opts.data[$1]); new Watcher(this.opts.data,$1,(newValue)=>{ let oldValue = this.opts.data[$1]; let reg = new RegExp(oldValue); node.textContent = node.textContent.replace(reg,newValue); })} } else if (node.nodeType === 1) {if (node.childNodes.length > 0) { this.compileNodes(node);} } }) }}class Dep{ constructor(){ this.subs = []; } addSub(sub){ this.subs.push(sub); } notify(newValue){ this.subs.forEach(sub=>{ sub.update(newValue); }) }}class Watcher{ constructor(data,key,cb){ Dep.target = this; data[key]; this.cb = cb; Dep.target = null; } update(newValue){ this.cb(newValue); }}

如上代碼思路是 針對每個(gè)數(shù)據(jù)會(huì)生成一個(gè)dep(依賴收集器)在數(shù)據(jù)get的時(shí)候收集watcher,將watcher 添加到dep里保存。數(shù)據(jù)一旦有改變觸發(fā)notify發(fā)布消息從而影響compile編譯更新視圖。這個(gè)流程也可以參看下圖:

vue mvvm數(shù)據(jù)響應(yīng)實(shí)現(xiàn)

如上就完成了視圖響應(yīng)。通過上述代碼,我們可以看出實(shí)現(xiàn)數(shù)據(jù)響應(yīng)兩個(gè)核心點(diǎn)1.數(shù)據(jù)劫持。2.觀察者和發(fā)布訂閱。在這我們可以思考一個(gè)問題,2個(gè)設(shè)計(jì)模式都是可以實(shí)現(xiàn)的但是有什么區(qū)別呢?

觀察者與發(fā)布訂閱

這里需要從概念來看

觀察者模式:定義一個(gè)對象與其他對象之間的一種依賴關(guān)系,當(dāng)對象發(fā)生某種變化的時(shí)候,依賴它的其它對象都會(huì)得到更新,一對多的關(guān)系。 發(fā)布訂閱模式:是一種消息范式,消息的發(fā)送者(稱為發(fā)布者)不會(huì)將消息直接發(fā)送給特定的接收者(稱為訂閱者)。而是將發(fā)布的消息分為不同的類別,無需了解哪些訂閱者(如果有的話)可能存在。同樣的,訂閱者可以表達(dá)對一個(gè)或多個(gè)類別的興趣,只接收感興趣的消息,無需了解哪些發(fā)布者(如果有的話)存在。

vue mvvm數(shù)據(jù)響應(yīng)實(shí)現(xiàn)

兩者之間關(guān)系,發(fā)布訂閱是三者之間關(guān)系。發(fā)布訂閱會(huì)多了一個(gè)關(guān)系器來組織主題和觀察者之間的關(guān)系。這樣做的好處就是松解耦。看上面響應(yīng)式例子可以看出觀察者需要通過事件名稱來進(jìn)行關(guān)聯(lián)。發(fā)布訂閱定義dep管理器之后data和compile徹底解耦,讓二者松散解耦。在處理多層數(shù)據(jù)結(jié)構(gòu)上發(fā)布訂閱會(huì)更清晰。松解耦能夠應(yīng)對更多變化,把模塊之間依賴降到最低。發(fā)布訂閱廣義上是觀察者模式。

好了 暫時(shí)先over 。 如果覺得有收獲的話可以點(diǎn)個(gè)贊,贈(zèng)人玫瑰,手有余香!!!!

以上就是vue mvvm數(shù)據(jù)響應(yīng)實(shí)現(xiàn)的詳細(xì)內(nèi)容,更多關(guān)于vue mvvm數(shù)據(jù)響應(yīng)的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Vue
相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
亚洲人成网站在线在线观看| 日韩国产91| 国产精品尤物| 国产精品久久| 国产精品第一国产精品| 欧美日韩一视频区二区| 青青青国产精品| 国产精品毛片视频| 欧美激情麻豆| 国产一区二区三区不卡视频网站| 国产人成精品一区二区三| 国产探花在线精品| 成人在线丰满少妇av| 国产精品久久久久av电视剧| 午夜免费一区| 日韩综合小视频| 精品在线网站观看| 久久在线电影| 亚洲综合中文| 精品视频高潮| 久久不射中文字幕| 久久不见久久见免费视频7| 欧美精选视频一区二区| 免费日韩av| 久久精品国产精品亚洲毛片| 色婷婷狠狠五月综合天色拍| 亚州精品视频| 婷婷综合六月| 91国内精品| 久久精品亚洲欧美日韩精品中文字幕| 国产视频一区免费看| 欧美日韩午夜电影网| 久久国产影院| 国产精品久久久久久模特| 成人羞羞在线观看网站| 日本久久二区| 美女少妇全过程你懂的久久| 国产日韩免费| 免费黄网站欧美| 日本少妇一区| 欧美日韩一区自拍| 亚洲欧美日韩专区| 久久精品1区| 开心激情综合| 日韩av一区二| 亚洲欧美日韩国产| 久久五月天小说| 国产一区二区三区黄网站| 中文在线日韩| 女人天堂亚洲aⅴ在线观看| 水蜜桃久久夜色精品一区| 欧美一区不卡| 日本午夜免费一区二区| 日韩中文字幕亚洲一区二区va在线 | 一本一本久久| 99精品在线观看| 日韩在线二区| 日韩一区二区在线免费| 精品日产乱码久久久久久仙踪林| 日本视频中文字幕一区二区三区| 亚洲人妖在线| 日韩激情av在线| 日本国产一区| 国产激情精品一区二区三区| 美女尤物国产一区| 精品一区二区三区中文字幕视频| 免费在线欧美黄色| 国产白浆在线免费观看| 欧美aa在线视频| 国产成人调教视频在线观看| 国产精品成人a在线观看| 秋霞国产精品| 亚洲欧美日韩精品一区二区| 天堂久久av| 国产精品高清一区二区| 国产精品yjizz视频网| 久久中文字幕av| 丝瓜av网站精品一区二区| 日韩中文字幕视频网| 国产精品99久久久久久董美香| 精品国产一级| 日韩亚洲在线| 久久精品福利| 中文在线а√在线8| 激情欧美丁香| 91p九色成人| 久久国产电影| 国产免费久久| 亚洲不卡av不卡一区二区| 亚洲香蕉久久| 久草免费在线视频| 在线精品亚洲| 精品久久97| 亚洲免费福利一区| 成人午夜网址| 69堂精品视频在线播放| 日韩欧美综合| 国产精品伊人| 99久久久久久中文字幕一区| 日韩美女国产精品| 亚洲福利国产| 国产一区二区三区四区五区传媒| 91九色精品国产一区二区| 国产精品一区二区三区四区在线观看 | 亚洲成人不卡| 国产精品中文字幕亚洲欧美| 国产农村妇女精品一二区 | 亚洲二区三区不卡| 久久中文在线| 青青草91视频| 亚洲欧美成人综合| 欧美13videosex性极品| 国产精品主播在线观看| 亚洲精品视频一二三区| 黄色成人91| 亚洲一级黄色| 国产一区日韩一区| 午夜av不卡| 97se综合| 日韩三区免费| 亚洲va中文在线播放免费| 精品九九在线| 国产精品国码视频| 久久激情综合网| 亚洲精品伊人| 国产日韩中文在线中文字幕 | 日韩激情网站| 日韩激情一区二区| 亚洲一区二区日韩| 99在线|亚洲一区二区| 在线一区免费观看| 久久亚洲风情| 亚洲欧美日韩专区| 亚洲2区在线| 你懂的国产精品永久在线| 风间由美中文字幕在线看视频国产欧美| 国产剧情在线观看一区| 精品国产一区二区三区2021| 精品亚洲a∨一区二区三区18| 国内精品伊人| 欧美在线观看视频一区| 免费日韩av片| 国产精品密蕾丝视频下载| 麻豆极品一区二区三区| 欧美日韩在线观看首页| 五月婷婷六月综合| 一区二区高清| 国产精品.xx视频.xxtv| 在线天堂资源www在线污| 91成人超碰| 国产亚洲精品美女久久| 激情国产在线| 日韩欧美中文在线观看| 卡一卡二国产精品| 99精品99| 精品亚洲a∨一区二区三区18| 欧美成人精品| 久久精品xxxxx| 成人午夜国产| 91精品国产自产精品男人的天堂| 欧美日韩在线观看首页| 日韩精品成人| 精精国产xxxx视频在线野外| 亚洲天堂日韩在线| 日韩在线短视频| 日本欧美韩国一区三区| 日韩精品欧美| 奇米狠狠一区二区三区| 欧美高清一区| 国模大尺度视频一区二区| 中文字幕日韩亚洲| 日韩综合精品| 日韩一区免费| 91精品国产调教在线观看| 麻豆国产91在线播放| 亚洲我射av| 免费日韩av片| 久久精品国产大片免费观看| 国产精品亚洲片在线播放| 中文在线一区| 亚洲电影在线一区二区三区| 国产中文在线播放| 国产精品伦理久久久久久| 国产精久久久| 国产区精品区| 国产日韩一区| 国产精品地址| 国产日韩欧美一区二区三区| 中文字幕免费精品| 中文一区一区三区免费在线观 | av在线最新| 国产精品13p| 精品亚洲a∨| 久久久久亚洲精品中文字幕| 久久精品 人人爱| 国产精品日韩精品中文字幕| 日韩精品视频在线看| 亚洲精一区二区三区| 亚洲精品一级| 国产精品久久久久久久久久齐齐|