日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > vue >内容正文

vue

记一次对vue双向绑定的理解

發(fā)布時(shí)間:2023/12/20 vue 54 豆豆
生活随笔 收集整理的這篇文章主要介紹了 记一次对vue双向绑定的理解 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

之前有看過一次vue雙向綁定原理實(shí)現(xiàn)相關(guān)的博客,看得似懂非懂的,然后也就擱淺了。
昨天腦海里又突然燃起了要不這塊搞懂的沖動(dòng),于是乎又開始了一輪博客轟炸,綜合研究了多位大神寫得關(guān)于vue雙向綁定的實(shí)現(xiàn)原理,然后結(jié)合自己的一些理解,就有了這篇文章了。
關(guān)于雙向綁定,我所知的兩類:(小的不才,目前只接觸過vue和angular)
一是angular1的臟檢查機(jī)制
二是vue的數(shù)據(jù)劫持配合觀察者模式(網(wǎng)上有很多寫的都是訂閱-發(fā)布模式,我特意去查看了這兩種模式有什么區(qū)別,大致是說觀察者模式中觀察者是被動(dòng)接收統(tǒng)一的消息,而訂閱-發(fā)布模式中是訂閱者是可以自定義接收行為的,而vue中watcher對象中update方法都是一致的,就是同步數(shù)據(jù),所有鄙人覺得用觀察者模式來形容可能會(huì)更貼切些,如果理解的不對,歡迎大家輕吐~)
下面我先貼一張我整理的原理圖


我的理解:
1.編譯器會(huì)解析DOM元素,比如碰到v-model指令時(shí),會(huì)對該DOM元素添加input監(jiān)聽事件,當(dāng)事件發(fā)生時(shí),給data屬性值賦值,進(jìn)而觸發(fā)該屬性的setter方法(這樣就實(shí)現(xiàn)了從view到model的同步);除了增加監(jiān)聽事件外,還會(huì)實(shí)例化一個(gè)watch對象。
2.實(shí)例化一個(gè)watch對象主要有兩個(gè)作用,一個(gè)是通過調(diào)用data元素的getter方法,觸發(fā)Dep的add方法將watcher對象加入到訂閱器中(為了防止在除watch對象中其他其他調(diào)用了getter方法,進(jìn)而重復(fù)添加監(jiān)聽器,我們在watcher對象中給Dep添加一個(gè)不為空全局變量,當(dāng)全局變量不為空時(shí)才添加,添加完后又將全局變量置為null),第二個(gè)作用是聲明一個(gè)update方法,用來接收到訂閱器的通知后同步數(shù)據(jù)給節(jié)點(diǎn)進(jìn)行渲染。
3.上面我們已經(jīng)在屬性的getter方法中將watcher對象加入了訂閱器,當(dāng)model層屬性值發(fā)生變化時(shí)會(huì)觸發(fā)setter方法,我們在setter中再去觸發(fā)訂閱器的notify,并在notify中觸發(fā)watcher對象的update方法(這樣就實(shí)現(xiàn)了從model到view的同步)
從圖中也可看出,主要有4大塊,我們依照流程圖的順序來分析一下:

  • compile
    先擼一波
function Compile(node, vm) {if(node) {this.$frag = this.nodeToFragment(node, vm);return this.$frag;}}Compile.prototype = {nodeToFragment: function(node, vm) {var self = this;var frag = document.createDocumentFragment();var child;while(child = node.firstChild) {self.compileElement(child, vm);frag.append(child); // 將所有子節(jié)點(diǎn)添加到fragment中}return frag;},compileElement: function(node, vm) {var reg = /\{\{(.*)\}\}/;//節(jié)點(diǎn)類型為元素if(node.nodeType === 1) {var attr = node.attributes;// 解析屬性for(var i = 0; i < attr.length; i++ ) {if(attr[i].nodeName == 'v-model') {var name = attr[i].nodeValue; // 獲取v-model綁定的屬性名node.addEventListener('input', function(e) {// 給相應(yīng)的data屬性賦值,進(jìn)而觸發(fā)該屬性的set方法vm[name]= e.target.value;});// node.value = vm[name]; // 將data的值賦給該nodenew Watcher(vm, node, name, 'value');}};}//節(jié)點(diǎn)類型為textif(node.nodeType === 3) {if(reg.test(node.nodeValue)) {var name = RegExp.$1; // 獲取匹配到的字符串name = name.trim();// node.nodeValue = vm[name]; // 將data的值賦給該nodenew Watcher(vm, node, name, 'nodeValue');}}},}

代碼分析:
(1)使用 DocumentFragment 處理節(jié)點(diǎn),速度和性能遠(yuǎn)遠(yuǎn)優(yōu)于直接操作 DOM。Vue 進(jìn)行編譯時(shí),就是將掛載目標(biāo)的所有子節(jié)點(diǎn)劫持(真的是劫持,通過 append 方法,DOM 中的節(jié)點(diǎn)會(huì)被自動(dòng)刪除)到 DocumentFragment 中,經(jīng)過一番處理后,再將 DocumentFragment 整體返回插入掛載目標(biāo)。
(2)node.nodeType判斷節(jié)點(diǎn)類型,如果是元素的話,判斷該元素有沒有v-model
屬性,有則監(jiān)聽input事件,將輸入框的值同步到變量中,同時(shí)實(shí)例化一個(gè)watcher。如果是文本的話,看是不是符合{{}}正則表達(dá)式,符合則是我們需要加入訂閱器的對象。

  • watcher-觀察者
function Watcher(vm, node, name, type) {Dep.target = this;this.name = name;this.node = node;this.vm = vm;this.type = type;this.update();Dep.target = null;}Watcher.prototype = {update: function() {this.get();this.node[this.type] = this.value; // 訂閱者執(zhí)行相應(yīng)操作},// 獲取data的屬性值get: function() {this.value = this.vm[this.name]; //觸發(fā)相應(yīng)屬性的get}}

代碼分析:
(1)實(shí)例化watcher對象是有獲取對象的值this.vm[this.name],這對出發(fā)該對象的get方法,而在對象的get方法中就可以把這個(gè)觀察者加入到Dep中了,后面oberver可以看到
(2)update()方法就是觀察者接收到通知后用來同步數(shù)據(jù)給節(jié)點(diǎn)進(jìn)行渲染的作用。

  • Observe-數(shù)據(jù)監(jiān)測器
function defineReactive (obj, key, val) {var dep = new Dep();Object.defineProperty(obj, key, {get: function() {//添加訂閱者watcher到主題對象Depif(Dep.target) {// JS的瀏覽器單線程特性,保證這個(gè)全局變量在同一時(shí)間內(nèi),只會(huì)有同一個(gè)監(jiān)聽器使用dep.addSub(Dep.target);}return val;},set: function (newVal) {if(newVal === val) return;val = newVal;console.log(val);// 作為發(fā)布者發(fā)出通知dep.notify();}})}function observe(obj, vm) {Object.keys(obj).forEach(function(key) {defineReactive(vm, key, obj[key]);})}

代碼分析:
(1)數(shù)據(jù)監(jiān)測器用到的知識點(diǎn)就是ES5的Object.defineProperty方法,改寫對象的set和get方法,在set方法中通知觀察該對象的所有watcher更新,在get方法中實(shí)現(xiàn)的是當(dāng)watcher第一次調(diào)用get方法的時(shí)候把自己綁定給訂閱器進(jìn)行管理

  • Dep-訂閱器
function Dep() {this.subs = [];}Dep.prototype = {addSub: function(sub) {this.subs.push(sub);},notify: function() {this.subs.forEach(function(sub) {sub.update();})}}

(1)它的addsub方法在observer的get方法中被調(diào)用,notify方法在observer的set方法中被調(diào)用
至此,四大塊就分析完了,回過頭來再去看那張圖,你理解的會(huì)更深刻一點(diǎn)。
下面終極大boss出場了MVVM.js

function Vue(options) {this.data = options.data;var data = this.data;observe(data, this);var id = options.el;var dom =new Compile(document.getElementById(id),this);// 編譯完成后,將dom返回到app中document.getElementById(id).appendChild(dom);}

index.html

<!DOCTYPE html><head></head><body><div id="app"><input type="text" id="a" v-model="text">{{text}}</div><script src="src/Dep.js"></script><script src="src/Observe.js"></script><script src="src/Watcher.js"></script><script src="src/Compile.js"></script><script src="src/MVVM.js"></script><script>var vm = new Vue({el: 'app',data: {text: 'hello world'}});</script></body> </html>

代碼分析:
這里自定義了vue對象,解析包含的html片段,實(shí)現(xiàn)的雙向綁定。

··························分隔符···························
參考資料:
http://www.cnblogs.com/kidney/p/6052935.html?utm_source=gold_browser_extension

總結(jié)

以上是生活随笔為你收集整理的记一次对vue双向绑定的理解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。