久久久久久久视色,久久电影免费精品,中文亚洲欧美乱码在线观看,在线免费播放AV片

<center id="vfaef"><input id="vfaef"><table id="vfaef"></table></input></center>

    <p id="vfaef"><kbd id="vfaef"></kbd></p>

    
    
    <pre id="vfaef"><u id="vfaef"></u></pre>

      <thead id="vfaef"><input id="vfaef"></input></thead>

    1. 站長資訊網(wǎng)
      最全最豐富的資訊網(wǎng)站

      vuejs實(shí)現(xiàn)雙向綁定的原理是什么

      vuejs實(shí)現(xiàn)雙向綁定的原理:利用數(shù)據(jù)劫持和發(fā)布訂閱模式,通過“Object.defineProperty()”來劫持各個(gè)屬性的setter、getter,在數(shù)據(jù)變動(dòng)時(shí)發(fā)布消息給訂閱者,觸發(fā)相應(yīng)的監(jiān)聽回調(diào),進(jìn)而對視圖進(jìn)行更新。

      vuejs實(shí)現(xiàn)雙向綁定的原理是什么

      本教程操作環(huán)境:windows7系統(tǒng)、vue2.9.6版,DELL G3電腦。

      Vue 數(shù)據(jù)雙向綁定原理

      Vue實(shí)現(xiàn)數(shù)據(jù)雙向綁定主要利用的就是: 數(shù)據(jù)劫持和發(fā)布訂閱模式,利用的 Object.defineProperty() 方法進(jìn)行的數(shù)據(jù)劫持,然后通知發(fā)布者(主題對象)去通知所有觀察者,觀察者收到通知后,就會(huì)對視圖進(jìn)行更新。

      https://jsrun.net/RMIKp/embedded/all/light

      MVVM 框架主要包含兩個(gè)方面,數(shù)據(jù)變化更新視圖,視圖變化更新數(shù)據(jù)。

      視圖變化更新數(shù)據(jù),如果是像 input 這種標(biāo)簽,可以使用 oninput 事件..

      數(shù)據(jù)變化更新視圖可以使用 Object.definProperty() 的 set 方法可以檢測數(shù)據(jù)變化,當(dāng)數(shù)據(jù)改變就會(huì)觸發(fā)這個(gè)函數(shù),然后更新視圖。

      實(shí)現(xiàn)過程

      我們知道了如何實(shí)現(xiàn)雙向綁定了,首先要對數(shù)據(jù)進(jìn)行劫持監(jiān)聽,所以我們需要設(shè)置一個(gè) Observer 函數(shù),用來監(jiān)聽所有屬性的變化。

      如果屬性發(fā)生了變化,那就要告訴訂閱者 watcher 看是否需要更新數(shù)據(jù),如果訂閱者有多個(gè),則需要一個(gè) Dep 來收集這些訂閱者,然后在監(jiān)聽器 observer 和 watcher 之間進(jìn)行統(tǒng)一管理。

      還需要一個(gè)指令解析器 compile,對需要監(jiān)聽的節(jié)點(diǎn)和屬性進(jìn)行掃描和解析。

      因此,流程大概是這樣的:

      • 實(shí)現(xiàn)一個(gè)監(jiān)聽器 Observer,用來劫持并監(jiān)聽所有屬性,如果發(fā)生變動(dòng),則通知訂閱者。

      • 實(shí)現(xiàn)一個(gè)訂閱者 Watcher,當(dāng)接到屬性變化的通知時(shí),執(zhí)行對應(yīng)的函數(shù),然后更新視圖,使用 Dep 來收集這些 Watcher。

      • 實(shí)現(xiàn)一個(gè)解析器 Compile,用于掃描和解析的節(jié)點(diǎn)的相關(guān)指令,并根據(jù)初始化模板以及初始化相應(yīng)的訂閱器。

      vuejs實(shí)現(xiàn)雙向綁定的原理是什么

      顯示一個(gè) Observer

      Observer 是一個(gè)數(shù)據(jù)監(jiān)聽器,核心方法是利用 Object.defineProperty() 通過遞歸的方式對所有屬性都添加 setter、getter 方法進(jìn)行監(jiān)聽。

      var library = {   book1: {     name: "",   },   book2: "", }; observe(library); library.book1.name = "vue權(quán)威指南"; // 屬性name已經(jīng)被監(jiān)聽了,現(xiàn)在值為:“vue權(quán)威指南” library.book2 = "沒有此書籍"; // 屬性book2已經(jīng)被監(jiān)聽了,現(xiàn)在值為:“沒有此書籍”  // 為數(shù)據(jù)添加檢測 function defineReactive(data, key, val) {   observe(val); // 遞歸遍歷所有子屬性   let dep = new Dep(); // 新建一個(gè)dep   Object.defineProperty(data, key, {     enumerable: true,     configurable: true,     get: function() {       if (Dep.target) {         // 判斷是否需要添加訂閱者,僅第一次需要添加,之后就不用了,詳細(xì)看Watcher函數(shù)         dep.addSub(Dep.target); // 添加一個(gè)訂閱者       }       return val;     },     set: function(newVal) {       if (val == newVal) return; // 如果值未發(fā)生改變就return       val = newVal;       console.log(         "屬性" + key + "已經(jīng)被監(jiān)聽了,現(xiàn)在值為:“" + newVal.toString() + "”"       );       dep.notify(); // 如果數(shù)據(jù)發(fā)生變化,就通知所有的訂閱者。     },   }); }  // 監(jiān)聽對象的所有屬性 function observe(data) {   if (!data || typeof data !== "object") {     return; // 如果不是對象就return   }   Object.keys(data).forEach(function(key) {     defineReactive(data, key, data[key]);   }); } // Dep 負(fù)責(zé)收集訂閱者,當(dāng)屬性發(fā)生變化時(shí),觸發(fā)更新函數(shù)。 function Dep() {   this.subs = {}; } Dep.prototype = {   addSub: function(sub) {     this.subs.push(sub);   },   notify: function() {     this.subs.forEach((sub) => sub.update());   }, };

      思路分析中,需要有一個(gè)可以容納訂閱者消息訂閱器 Dep,用于收集訂閱者,在屬性發(fā)生變化時(shí)執(zhí)行對應(yīng)的更新函數(shù)。

      從代碼上看,將訂閱器 Dep 添加在 getter 里,是為了讓 Watcher 初始化時(shí)觸發(fā),,因此,需要判斷是否需要訂閱者。

      在 setter 中,如果有數(shù)據(jù)發(fā)生變化,則通知所有的訂閱者,然后訂閱者就會(huì)更新對應(yīng)的函數(shù)。

      到此為止,一個(gè)比較完整的 Observer 就完成了,接下來開始設(shè)計(jì) Watcher.

      實(shí)現(xiàn) Watcher

      訂閱者 Watcher 需要在初始化的時(shí)候?qū)⒆约禾砑拥接嗛喥?Dep 中,我們已經(jīng)知道監(jiān)聽器 Observer 是在 get 時(shí)執(zhí)行的 Watcher 操作,所以只需要在 Watcher 初始化的時(shí)候觸發(fā)對應(yīng)的 get 函數(shù)去添加對應(yīng)的訂閱者操作即可。

      那給如何觸發(fā) get 呢?因?yàn)槲覀円呀?jīng)設(shè)置了 Object.defineProperty(),所以只需要獲取對應(yīng)的屬性值就可以觸發(fā)了。

      我們只需要在訂閱者 Watcher 初始化的時(shí)候,在 Dep.target 上緩存下訂閱者,添加成功之后在將其去掉就可以了。

      function Watcher(vm, exp, cb) {   this.cb = cb;   this.vm = vm;   this.exp = exp;   this.value = this.get(); // 將自己添加到訂閱器的操作 }  Watcher.prototype = {   update: function() {     this.run();   },   run: function() {     var value = this.vm.data[this.exp];     var oldVal = this.value;     if (value !== oldVal) {       this.value = value;       this.cb.call(this.vm, value, oldVal);     }   },   get: function() {     Dep.target = this; // 緩存自己,用于判斷是否添加watcher。     var value = this.vm.data[this.exp]; // 強(qiáng)制執(zhí)行監(jiān)聽器里的get函數(shù)     Dep.target = null; // 釋放自己     return value;   }, };

      到此為止, 簡單的額 Watcher 設(shè)計(jì)完畢,然后將 Observer 和 Watcher 關(guān)聯(lián)起來,就可以實(shí)現(xiàn)一個(gè)簡單的的雙向綁定了。

      因?yàn)檫€沒有設(shè)計(jì)解析器 Compile,所以可以先將模板數(shù)據(jù)寫死。

      將代碼轉(zhuǎn)化為 ES6 構(gòu)造函數(shù)的寫法,預(yù)覽試試。

      https://jsrun.net/8SIKp/embedded/all/light

      這段代碼因?yàn)闆]有實(shí)現(xiàn)編譯器而是直接傳入了所綁定的變量,我們只在一個(gè)節(jié)點(diǎn)上設(shè)置一個(gè)數(shù)據(jù)(name)進(jìn)行綁定,然后在頁面上進(jìn)行 new MyVue,就可以實(shí)現(xiàn)雙向綁定了。

      并兩秒后進(jìn)行值得改變,可以看到,頁面也發(fā)生了變化。

      // MyVue proxyKeys(key) {     var self = this;     Object.defineProperty(this, key, {         enumerable: false,         configurable: true,         get: function proxyGetter() {             return self.data[key];         },         set: function proxySetter(newVal) {             self.data[key] = newVal;         }     }); }

      上面這段代碼的作用是將 this.data 的 key 代理到 this 上,使得我可以方便的使用 this.xx 就可以取到 this.data.xx。

      實(shí)現(xiàn) Compile

      雖然上面實(shí)現(xiàn)了雙向數(shù)據(jù)綁定,但是整個(gè)過程都沒有解析 DOM 節(jié)店,而是固定替換的,所以接下來要實(shí)現(xiàn)一個(gè)解析器來做數(shù)據(jù)的解析和綁定工作。

      解析器 compile 的實(shí)現(xiàn)步驟:

      • 解析模板指令,并替換模板數(shù)據(jù),初始化視圖。

      • 將模板指定對應(yīng)的節(jié)點(diǎn)綁定對應(yīng)的更新函數(shù),初始化相應(yīng)的訂閱器。

      為了解析模板,首先需要解析 DOM 數(shù)據(jù),然后對含有 DOM 元素上的對應(yīng)指令進(jìn)行處理,因此整個(gè) DOM 操作較為頻繁,可以新建一個(gè) fragment 片段,將需要的解析的 DOM 存入 fragment 片段中在進(jìn)行處理。

      function nodeToFragment(el) {   var fragment = document.createDocumentFragment();   var child = el.firstChild;   while (child) {     // 將Dom元素移入fragment中     fragment.appendChild(child);     child = el.firstChild;   }   return fragment; }

      接下來需要遍歷各個(gè)節(jié)點(diǎn),對含有相關(guān)指令和模板語法的節(jié)點(diǎn)進(jìn)行特殊處理,先進(jìn)行最簡單模板語法處理,使用正則解析“{{變量}}”這種形式的語法。

      function compileElement (el) {     var childNodes = el.childNodes;     var self = this;     [].slice.call(childNodes).forEach(function(node) {         var reg = /{{(.*)}}/; // 匹配{{xx}}         var text = node.textContent;         if (self.isTextNode(node) && reg.test(text)) {  // 判斷是否是符合這種形式{{}}的指令             self.compileText(node, reg.exec(text)[1]);         }         if (node.childNodes && node.childNodes.length) {             self.compileElement(node);  // 繼續(xù)遞歸遍歷子節(jié)點(diǎn)         }     }); }, function compileText (node, exp) {     var self = this;     var initText = this.vm[exp];     updateText(node, initText);  // 將初始化的數(shù)據(jù)初始化到視圖中     new Watcher(this.vm, exp, function (value) {  // 生成訂閱器并綁定更新函數(shù)         self.updateText(node, value);     }); }, function updateText (node, value) {     node.textContent = typeof value == 'undefined' ? '' : value; }

      獲取到最外層的節(jié)點(diǎn)后,調(diào)用 compileElement 函數(shù),對所有的子節(jié)點(diǎn)進(jìn)行判斷,如果節(jié)點(diǎn)是文本節(jié)點(diǎn)切匹配{{}}這種形式的指令,則進(jìn)行編譯處理,初始化對應(yīng)的參數(shù)。

      然后需要對當(dāng)前參數(shù)生成一個(gè)對應(yīng)的更新函數(shù)訂閱器,在數(shù)據(jù)發(fā)生變化時(shí)更新對應(yīng)的 DOM。

      這樣就完成了解析、初始化、編譯三個(gè)過程了。

      接下來改造一個(gè) myVue 就可以使用模板變量進(jìn)行雙向數(shù)據(jù)綁定了。

      https://jsrun.net/K4IKp/embedded/all/light

      添加解析事件

      添加完 compile 之后,一個(gè)數(shù)據(jù)雙向綁定就基本完成了,接下來就是在 Compile 中添加

      贊(0)
      分享到: 更多 (0)
      網(wǎng)站地圖   滬ICP備18035694號-2    滬公網(wǎng)安備31011702889846號