最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時(shí)間:2020年11月11日 09:05:30   作者:黑哪吒_blackNezha  
這篇文章主要介紹了vue mvvm數(shù)據(jù)響應(yīng)實(shí)現(xiàn)的方法,幫助大家更好的理解和使用vue,感興趣的朋友可以了解下

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

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

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

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

<div id="app">
    {{message}}
</div>
let vm = new Vue({
  el:"#app",
  data:{
    message:"測(cè)試數(shù)據(jù)"
  }
})
setTimeout(()=>{
  vm.message = "修改的數(shù)據(jù)";
},1000)

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

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

根據(jù)vue調(diào)用方式。定義Vue類(lèi)來(lái)實(shí)現(xiàn)各種功能。將初次渲染過(guò)程定義成編譯compile函數(shù)渲染視圖。通過(guò)傳入的配置以及操作dom來(lái)實(shí)現(xiàn)渲染。大概思路是通過(guò)正則查找html 里 #app 作用域內(nèi)的表達(dá)式,然后查找數(shù)據(jù)做對(duì)應(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)并沒(méi)對(duì)深層次的dom結(jié)構(gòu)做處理也就是如下情況:

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

渲染結(jié)果如上

發(fā)現(xiàn)結(jié)果并沒(méi)有達(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);
        }
      }
    })
  }

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

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

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

  • 如何知道數(shù)據(jù)變動(dòng)了? 這里就需要用到數(shù)據(jù)攔截了,或者叫數(shù)據(jù)觀察。把會(huì)變動(dòng)的data數(shù)據(jù)觀察起來(lái)。當(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),通過(guò)defineProperty劫持的對(duì)象屬性下都會(huì)有g(shù)et及set方法。那么當(dāng)我們獲取或者設(shè)置數(shù)據(jù)的時(shí)候就能出發(fā)對(duì)應(yīng)的get及set 。這樣就能攔截?cái)?shù)據(jù)做后續(xù)操作。

還有沒(méi)有其他方式達(dá)到數(shù)據(jù)劫持的效果呢?ES6中出現(xiàn)了Proxy 代理對(duì)象同樣也可以達(dá)到類(lèi)似劫持?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直接攔截的是對(duì)象而defineProperty攔截的是對(duì)象屬性。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ù)方式后,接下來(lái)需要實(shí)現(xiàn)的就是當(dāng)修改數(shù)據(jù)的時(shí)候?qū)⑿聰?shù)據(jù)渲染到視圖。如何辦到呢?會(huì)發(fā)現(xiàn),需要在data設(shè)置的時(shí)候觸發(fā)視圖的compile編譯。二者之間互相影響,此時(shí)可以想到利用觀察者模式,通過(guò)觀察者模式讓二者產(chǎn)生關(guān)聯(liá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);
        }
      }
    })
  }
}

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

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

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

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);
  }
}

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

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

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

這里需要從概念來(lái)看

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

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

好了 暫時(shí)先over 。 如果覺(jué)得有收獲的話(huà)可以點(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)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Vue實(shí)現(xiàn)todo應(yīng)用的示例

    Vue實(shí)現(xiàn)todo應(yīng)用的示例

    這篇文章主要介紹了Vue實(shí)現(xiàn)todo應(yīng)用的示例,幫助大家更好的理解和學(xué)習(xí)使用vue框架,感興趣的朋友可以了解下
    2021-02-02
  • 詳解如何在你的Vue項(xiàng)目配置vux

    詳解如何在你的Vue項(xiàng)目配置vux

    這篇文章主要介紹了詳解如何在你的Vue項(xiàng)目配置vux,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-06-06
  • Vue頁(yè)面渲染中key的應(yīng)用實(shí)例教程

    Vue頁(yè)面渲染中key的應(yīng)用實(shí)例教程

    這篇文章主要給大家介紹了關(guān)于Vue頁(yè)面渲染中key的應(yīng)用的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • Vue3中如何使用SCSS編寫(xiě)樣式

    Vue3中如何使用SCSS編寫(xiě)樣式

    在Vue模板中啟用這些表現(xiàn)力庫(kù)插件的最簡(jiǎn)單方法是在初始化項(xiàng)目時(shí)安裝它們,或使用 npm install(或 yarn add)安裝包,這篇文章主要介紹了Vue3中如何使用SCSS編寫(xiě)樣式,需要的朋友可以參考下
    2023-12-12
  • 詳解vuex中mutation/action的傳參方式

    詳解vuex中mutation/action的傳參方式

    這篇文章主要介紹了詳解vuex中mutation/action的傳參方式,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-08-08
  • Vue+element+cookie記住密碼功能的簡(jiǎn)單實(shí)現(xiàn)方法

    Vue+element+cookie記住密碼功能的簡(jiǎn)單實(shí)現(xiàn)方法

    這篇文章主要給大家介紹了Vue+element+cookie記住密碼功能的簡(jiǎn)單實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • vue-cli3項(xiàng)目配置eslint代碼規(guī)范的完整步驟

    vue-cli3項(xiàng)目配置eslint代碼規(guī)范的完整步驟

    這篇文章主要給大家介紹了關(guān)于vue-cli3項(xiàng)目配置eslint代碼規(guī)范的完整步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • 使用json-editor-vue3(Json字段編輯器)

    使用json-editor-vue3(Json字段編輯器)

    文章介紹了如何在Vue3+Ts項(xiàng)目中使用json-editor-vue3插件,并解決了導(dǎo)入過(guò)程中遇到的模塊識(shí)別問(wèn)題,通過(guò)步驟說(shuō)明和代碼示例,展示了如何在項(xiàng)目中成功引入和使用該插件,并提供了一些常見(jiàn)參數(shù)和事件說(shuō)明
    2025-01-01
  • vue2老項(xiàng)目vite升級(jí)改造過(guò)程記錄

    vue2老項(xiàng)目vite升級(jí)改造過(guò)程記錄

    目前vite主要默認(rèn)是支持給vue3使用的,并且如果使用官方的cli創(chuàng)建的項(xiàng)目一樣會(huì)默認(rèn)使用vue3去構(gòu)建項(xiàng)目,此時(shí)對(duì)于一些vue2的老項(xiàng)目就顯得不友好了,下面這篇文章主要給大家介紹了關(guān)于vue2老項(xiàng)目vite升級(jí)改造的相關(guān)資料,需要的朋友可以參考下
    2022-12-12
  • vue-cli項(xiàng)目中使用Mockjs詳解

    vue-cli項(xiàng)目中使用Mockjs詳解

    這篇文章主要介紹了vue-cli項(xiàng)目中使用Mockjs詳解,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05

最新評(píng)論

蒙阴县| 镇赉县| 山西省| 兴山县| 武夷山市| 固始县| 湖南省| 光泽县| 宝山区| 沁源县| 喜德县| 枣庄市| 刚察县| 定南县| 神木县| 楚雄市| 伊金霍洛旗| 商丘市| 巫溪县| 闽侯县| 米林县| 酉阳| 启东市| 屏东市| 常山县| 阜宁县| 镇赉县| 嘉义市| 方正县| 灵丘县| 布拖县| 乐至县| 桂阳县| 富顺县| 樟树市| 广河县| 高要市| 邹城市| 婺源县| 攀枝花市| 南靖县|