vue 雙向資料繫結原理
Vue的雙向資料繫結原理是什麼?
vue.js是採用資料劫持結合釋出者-訂閱者模式的方式,通過Object.defineProperty()來劫持各個屬性的setter, getter,在資料變動時釋出訊息給訂閱者,出發相應的監聽回撥。
具體步驟:
首先Vue會使用documentfragment劫持根元素裡包含的所有節點,這些節點不僅包括標籤元素,還包括文字,甚至換行的回車。
然後Vue會把data中所有的資料,用defindProperty()變成Vue的訪問器屬性,這樣每次修改這些資料的時候,就會觸發相應屬性的get,set方法。
接下來編譯處理劫持到的dom節點,遍歷所有節點,根據nodeType來判斷節點型別,根據節點本身的屬性(是否有v-model等屬性)或者文字節點的內容(是否符合{{文字插值}}的格式)來判斷節點是否需要編譯。對v-model,繫結事件當輸入的時候,改變Vue中的資料。對文字節點,將他作為一個觀察者watcher放入觀察者列表,當Vue資料改變的時候,會有一個主題物件,對列表中的觀察者們釋出改變的訊息,觀察者們再更新自己,改變節點中的顯示,從而達到雙向繫結的目的。
demo
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>雙向繫結demo</title> <style> body{margin: 200px auto;width: 800px;} </style> </head> <body> <div id="app"> <input type="text" id="input" v-model="text"> {{ text }} </div> <script> // Vue建構函式 function Vue(options){ this.data = options.data; //釋出訂閱模式,資料繫結 var data = this.data; observe(data,this); var id = options.el; var dom = nodeToFragment(document.getElementById(id),this); //編譯完成後返回到app中 document.getElementById("app").appendChild(dom); } //遍歷傳入例項的data物件的屬性,將其設定為Vue物件的訪問器屬性 function observe(obj,vm){ Object.keys(obj).forEach(function(key){ defineReactive(vm,key,obj[key]); }); } //訪問器屬性是物件中的一種特殊屬性,它不能直接在物件中設定,而必須通過defineProperty()方法單獨定義。 //訪問器屬性的"值"比較特殊,讀取或設定訪問器屬性的值,實際上是呼叫其內部特性:get和set函式。 function defineReactive(obj,key,val){ //這裡用到了觀察者(訂閱/釋出)模式,它定義了一種一對多的關係,讓多個觀察者監聽一個主題物件,這個主題物件的狀態發生改變時會通知所有觀察者物件,觀察者物件就可以更新自己的狀態。 //例項化一個主題物件,物件中有空的觀察者列表 var dep = new Dep(); //將data的每一個屬性都設定為Vue物件的訪問器屬性,屬性名和data中相同 //所以每次修改Vue.data的時候,都會呼叫下邊的get和set方法。然後會監聽v-model的input事件,當改變了input的值,就相應的改變Vue.data的資料,然後觸發這裡的set方法 Object.defineProperty(obj,key,{ get: function(){ //Dep.target指標指向watcher,增加訂閱者watcher到主體物件Dep if(Dep.target){ dep.addSub(Dep.target); } return val; }, set: function(newVal){ if(newVal === val){ return } val = newVal; //console.log(val); //給訂閱者列表中的watchers發出通知 dep.notify(); } }); } //主題物件Dep建構函式 function Dep(){ this.subs = []; } //Dep有兩個方法,增加觀察者 和 釋出訊息 Dep.prototype = { addSub: function(sub){ this.subs.push(sub); }, notify: function(){ this.subs.forEach(function(sub){ sub.update(); }); } } //DocumentFragment(文件片段)可以看作節點容器,它可以包含多個子節點,當我們將它插入到DOM中時,只有它的子節點會插入目標節點,所以把它看作一組節點的容器。使用DocumentFragment處理節點,速度和效能遠遠優於直接操作DOM。Vue進行編譯時,就是將掛載目標的所有子節點劫持(真的是劫持)到DocumentFragment中,經過一番處理後,再將DocumentFragment整體返回插入掛載目標。 //var dom = nodeToFragment(document.getElementById("app")); //console.log(dom); //返回到app中 //document.getElementById("app").appendChild(dom); function nodeToFragment(node,vm){ var flag = document.createDocumentFragment(); var child; //劫持node的所有子節點(真的在dom樹中消失了,所以要在下邊重新返回搭到app中) while (child = node.firstChild){ //先編譯所有的子節點,再劫持到文件片段中 compile(child,vm); flag.appendChild(child); } return flag; } //編譯節點,初始化資料繫結 function compile(node,vm){ //該正則匹配的是 :{{任意內容}} var reg = /\{\{(.*)\}\}/; //節點型別為元素 if(node.nodeType === 1){ var attr = node.attributes; //解析屬性,不同的屬性不用的處理方式,這裡只寫了v-model屬性 for(var i=0;i<attr.length;i++){ if (attr[i].nodeName == "v-model") { //獲取節點中v-model屬性的值,也就是繫結的屬性名 var name = attr[i].nodeValue; node.addEventListener("input",function(e){ //當觸發input事件時改變vue.data中相應的屬性的值,進而觸發該屬性的set方法 vm[name] = e.target.value; }); //改變之後,通過屬性名取得資料 node.value = vm.data[name]; //用完刪,所以瀏覽器中編譯之後的節點上沒有v-model屬性 node.removeAttribute("v-model"); } } } //節點型別為text if(node.nodeType === 3){ //text是否滿足文字插值的寫法:{{任意內容}} if(reg.test(node.nodeValue)){ //獲取匹配到的字串:這裡的RegExp.$1是RegExp的一個屬性 //該屬性表示正則表示式reg中,第一個()裡邊的內容,也就是 //{{任意內容}} 中的 文字【任意內容】 var name = RegExp.$1; //去掉前後空格,並將處理後的資料寫入節點 name = name.trim(); //node.nodeValue = vm.data[name]; //例項化一個新的訂閱者watcher new Watcher(vm,node,name); return; } } } //觀察者建構函式。 //上邊例項化新的觀察者的時候執行這個函式:通過get()取得Vue.data中對應的資料,然後通過update()方法把資料更新到節點中。 function Watcher(vm,node,name){ //讓全域性變數Dep的target屬性的指標指向該watcher例項 Dep.target = this; this.vm = vm; this.node = node; this.name = name; //放入Dep.target才能update()? this.update(); Dep.target = null; } // 觀察者使用update方法,實際上是 Watcher.prototype = { update: function(){ this.get(); this.node.nodeValue = this.value; }, //獲取data中的屬性值 get: function(){ this.value = this.vm[this.name]; //觸發相應屬性的get } } var vm = new Vue({ el: "app", data: { text: "Hello Vue" } }) </script> </body> </html>
相關推薦
vue雙向資料繫結原理
Vue應用的是mvvm框架,view和model分離,然後通過vm雙向資料繫結,` <code class="hljs handlebars has-numbering" style="display: block; padding: 0px; color: inh
vue 雙向資料繫結原理
Vue的雙向資料繫結原理是什麼?vue.js是採用資料劫持結合釋出者-訂閱者模式的方式,通過Object.defineProperty()來劫持各個屬性的setter, getter,在資料變動時釋出訊息給訂閱者,出發相應的監聽回撥。具體步驟:首先Vue會使用document
Vue雙向資料繫結原理解析
首先上原始碼,模擬vue的雙向資料繫結原理<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Two-way data-
vue中實現雙向資料繫結原理,使用了Object.defineproperty()方法,方法簡單
在vue中雙向資料繫結原理,我們一般都是用v-model來實現的 ,但一般在面試話會問到其實現的原理, 方法比較簡單,就是利用了es5中的一個方法.Object.defineproperty(),它有三個引數, Object.defineproperty(obj,'val',attrObject), 引數
詳解 vue 雙向資料繫結的原理,並實現一組雙向資料繫結
1:vue 雙向資料繫結的原理: Object.defineProperty是ES5新增的一個API,其作用是給物件的屬性增加更多的控制Object.defineProperty(obj, prop, descriptor)引數 obj: 需要定義屬性的物件(目標物件)prop: 需被定義或修改的屬性名(物
Vue.js雙向資料繫結原理
vue雙向繫結就是指model層與view層的同步,兩者之間任意一個發生變化都會同步更新到另一者。 View為檢視層,Model為資料層,ViewModel為邏輯控制層。 vue.js採用資料劫持結合釋出者-訂閱者模式的方法,通過Object.defin
vue雙向資料繫結的原理
有關雙向資料繫結的原理 最近兩次面試的時候,被問到了vue中雙向資料繫結的原理,因為初學不精,只是使用而沒有深入研究,所以答不出來。之後就在網上查找了別人寫的部落格,學習一下。 下面是部落格園一篇部落格,以及MDN上講解Object.defineProper
深入vue原始碼,瞭解vue的雙向資料繫結原理
大家都知道vue是一種MVVM開發模式,資料驅動檢視的前端框架,並且內部已經實現了雙向資料繫結,那麼雙向資料繫結是怎麼實現的呢? 先手動擼一個最最最簡單的雙向資料繫結 1 <div> 2 <input type="text" name="" id="te
vue 雙向資料繫結的實現學習(二)- 監聽器的實現
廢話:上一篇https://www.cnblogs.com/adouwt/p/9928278.html 提到了vue實現的基本實現原理:Object.defineProperty() -資料劫持 和 釋出訂閱者模式(觀察者),下面講的就是資料劫持在程式碼中的具體實現。 1.先看如何呼
vue雙向資料繫結的實現
總所周知,偵測一個物件變化的方法,常用的兩種是Object.defineProperty,和es6的proxy,下面就基於Object.defineProperty實現簡單的雙向繫結 其實vue主要是通過Object.defineProperty實現的,至於vue3.0會不會重寫
模擬Vue雙向資料繫結
事件物件 function EventEmit(){ // { // "message":['事件1','事件2'] // } this.callbacks={} } EventEmit.prototype.on=function(eventNa
Vue 雙向資料繫結實現
<!DOCTYPE html> <html> <head> <title>myVue</title> <style> #app{ text-align: center;
Angular雙向資料繫結原理
Angular是通過髒檢測來進行雙向資料繫結 Angular比不是通過定時去進行檢測 Angular在$digest cycle流程裡面,會從rootscope開始遍歷,檢查所有的watcher。
angularjs雙向資料繫結原理
angular並不存在定時髒檢測。angular對常用的dom事件,xhr事件等做了封裝, 在裡面觸發進入angular的digest流程。在digest流程裡面, 會從rootscope開始遍歷,
angularjs雙向資料繫結原理解析
angularjs的雙向資料繫結 髒值(發生了變化的值)檢查不等於定時輪詢,而是特定事件觸發才會執行 只有指定事件觸發後才會進入髒值輪詢。 - DOM事件,譬如使用者輸入文字,點選按鈕等。(ng-click) - XHR(ajax)響應事件 (http)
JS框架雙向資料繫結原理及思考
本文章資訊點 雙向資料繫結原理(vue) 如何設計搭建自己的框架 程式碼暫略,詳見,github [地址]https://github.com/yyccmmkk/Bi-directionalDataBindingDemo 什麼是單向什麼是雙向? 單向指是資料從mod
Vue雙向資料繫結的理解
一、什麼是MVVM框架 所謂MVVM,是Model - View - ViewModel 的簡寫。 我的理解是頁面上所看到的就是View。 使用Vue.js或者是其他MVVM框架,是在操作ViewModel,實現View - ViewModel的雙向互動。 然後Mod
雙向資料繫結原理
1. 釋出者-訂閱者模式(backbone.js) 一般通過sub, pub的方式實現資料和檢視的繫結監聽,更新資料方式通常做法是 vm.set(‘property’, value),雖然老套古板,這種方式的優點在於相容ie8以下版本。 2. 髒
轉 vue實現雙向資料繫結之原理及實現篇 vue的雙向繫結原理及實現
轉自:canfoo#! vue的雙向繫結原理及實現 前言 先上個成果圖來吸引各位: 程式碼: &nb
vue.js和angular雙向資料繫結的實現原理
一、vue雙向資料繫結 1、原理 資料劫持: vue.js 是採用資料劫持結合釋出者-訂閱者模式的方式,通過Object.defineProperty()來劫持各個屬性的setter,getter,在資料變動時釋出訊息給訂閱者,觸發相應的監聽回撥。 2、實現步驟 要實現mv