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

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

手動實現(xiàn)vue2.0的雙向數(shù)據(jù)綁定原理詳解

瀏覽:151日期:2022-10-06 11:15:50
一句話概括:數(shù)據(jù)劫持(Object.defineProperty)+發(fā)布訂閱模式

雙向數(shù)據(jù)綁定有三大核心模塊(dep 、observer、watcher),它們之間是怎么連接的,下面來一一介紹。

為了大家更好的理解雙向數(shù)據(jù)綁定原理以及它們之間是如何實現(xiàn)關(guān)聯(lián)的,先帶領(lǐng)大家復(fù)習(xí)一下發(fā)布訂閱模式。

一.首先了解什么是發(fā)布訂閱模式

直接上代碼:

一個簡單的發(fā)布訂閱模式,幫助大家更好的理解雙向數(shù)據(jù)綁定原理

//發(fā)布訂閱模式function Dep() { this.subs = []//收集依賴(也就是手機(jī)watcher實例),}Dep.prototype.addSub = function (sub) { //添加訂閱者 this.subs.push(sub); //實際上添加的是watcher這個實例}Dep.prototype.notify = function (sub) { //發(fā)布,這個方法的作用是遍歷數(shù)組,讓每個訂閱者的update方法去執(zhí)行 this.subs.forEach((sub) => sub.update())}function Watcher(fn) { this.fn = fn;}Watcher.prototype.update = function () { //添加一個update屬性讓每一個實例都可以繼承這個方法 this.fn();}let watcher = new Watcher(function () { alert(1)});//訂閱let dep = new Dep();dep.addSub(watcher);//添加依賴,添加訂閱者dep.notify();//發(fā)布,讓每個訂閱者的update方法執(zhí)行二.new Vue()的時候做了什么?

只是針對雙向數(shù)據(jù)綁定做說明

<template> <div id='app'> <div>obj.text的值:{{obj.text}}</div> <p>word的值:{{word}}</p> <input type='text' v-model='word'> </div></template><script> new Vue({ el: '#app', data: { obj: {text: '向上', }, word: '學(xué)習(xí)' }, methods:{ // ... } })</script>

Vue構(gòu)造函數(shù)都干什么了?

function Vue(options = {}) { this.$options = options;//接收參數(shù) var data = this._data = this.$options.data; observer(data);//對data中的數(shù)據(jù)進(jìn)型循環(huán)遞歸綁定 for (let key in data) { let val = data[key]; observer(val); Object.defineProperty(this, key, { enumerable: true, get() {return this._data[key]; }, set(newVal) {this._data[key] = newVal; } }) } new Compile(options.el, this)};

在new Vue({…})構(gòu)造函數(shù)時,首先獲取參數(shù)options,然后把參數(shù)中的data數(shù)據(jù)賦值給當(dāng)前實例的_data屬性上(this._data = this.$options.data),重點(diǎn)來了,那下面的遍歷是為什么呢?首先我們在操作數(shù)據(jù)的時候是this.word獲取,而不是this._data.word,所以是做了一個映射,在獲取數(shù)據(jù)的時候this.word,其實是獲取的this._data.word的值,大家可以在自己項目中輸出this查看一下

手動實現(xiàn)vue2.0的雙向數(shù)據(jù)綁定原理詳解

1.接下來看看observer方法干了什么

function observer(data) { if (typeof data !== 'object') return; return new Observer(data);//返回一個實例}function Observer(data) { let dep = new Dep();//創(chuàng)建一個dep實例 for (let key in data) {//對數(shù)據(jù)進(jìn)行循環(huán)遞歸綁定 let val = data[key]; observer(val); Object.defineProperty(data, key, { enumerable: true, get() {Dep.target && dep.depend(Dep.target);//Dep.target就是Watcher的一個實例return val; }, set(newVal) {if (newVal === val) { return;}val = newVal;observer(newVal);dep.notify() //讓所有方法執(zhí)行 } }) }}

Observer構(gòu)造函數(shù),首先let dep=new Dep(),作為之后的觸發(fā)數(shù)據(jù)劫持的get方法和set方法時,去收集依賴和發(fā)布時調(diào)用,主要的操作就是通過Object.defineProperty對data數(shù)據(jù)進(jìn)行循環(huán)遞歸綁定,使用getter/setter修改其默認(rèn)讀寫,用于收集依賴和發(fā)布更新。

2.再來看看Compile具體干了那些事情

function Compile(el, vm) { vm.$el = document.querySelector(el); let fragment = document.createDocumentFragment(); //創(chuàng)建文檔碎片,是object類型 while (child = vm.$el.firstChild) { fragment.appendChild(child); };//用while循環(huán)把所有節(jié)點(diǎn)都添加到文檔碎片中,之后都是對文檔碎片的操作,最后再把文檔碎片添加到頁面中,這里有一個很重要的特性是,如果使用appendChid方法將原dom樹中的節(jié)點(diǎn)添加到fragment中時,會刪除原來的節(jié)點(diǎn)。 replace(fragment); function replace(fragment) { Array.from(fragment.childNodes).forEach((node) => {//循環(huán)所有的節(jié)點(diǎn) let text = node.textContent; let reg = /{{(.*)}}/; if (node.nodeType === 3 && reg.test(text)) {//判斷當(dāng)前節(jié)點(diǎn)是不是文本節(jié)點(diǎn)且符不符合{{obj.text}}的輸出方式,如果滿足條件說明它是雙向的數(shù)據(jù)綁定,要添加訂閱者(watcher)console.log(RegExp.$1); //obj.textlet arr = RegExp.$1.split('.'); //轉(zhuǎn)換成數(shù)組的方式[obj,text],方便取值let val = vm;arr.forEach((key) => { //實現(xiàn)取值this.obj.text val = val[key];});new Watcher(vm, RegExp.$1, function (newVal) { node.textContent = text.replace(/{{(.*)}}/, newVal)});node.textContent = text.replace(/{{(.*)}}/, val); //對節(jié)點(diǎn)內(nèi)容進(jìn)行初始化的賦值 } if (node.nodeType === 1) { //說明是元素節(jié)點(diǎn)let nodeAttrs = node.attributes;Array.from(nodeAttrs).forEach((item) => { if (item.name.indexOf('v-') >= 0) {//判斷是不是v-model這種指令 node.value = vm[item.value]//對節(jié)點(diǎn)賦值操作 } //添加訂閱者 new Watcher(vm, item.value, function (newVal) { node.value = vm[item.value] }); node.addEventListener('input', function (e) { let newVal = e.target.value; vm[item.value] = newVal; })}) } if (node.childNodes) { //這個節(jié)點(diǎn)里還有子元素,再遞歸replace(node); } }) } //這是頁面中的文檔已經(jīng)沒有了,所以還要把文檔碎片放到頁面中 vm.$el.appendChild(fragment);}

Compile(編譯方法)

首先解釋一下DocuemntFragment(文檔碎片)它是一個dom節(jié)點(diǎn)收容器,當(dāng)你創(chuàng)造了多個節(jié)點(diǎn),當(dāng)每個節(jié)點(diǎn)都插入到文檔當(dāng)中都會引發(fā)一次回流,也就是說瀏覽器要回流多次,十分耗性能,而使用文檔碎片就是把多個節(jié)點(diǎn)都先放入到一個容器中,最后再把整個容器直接插入就可以了,瀏覽器只回流了1次。

Compile方法首先遍歷文檔碎片的所有節(jié)點(diǎn),1.判斷是否是文本節(jié)點(diǎn)且符不符合{{obj.text}}的雙大括號的輸出方式,如果滿足條件說明它是雙向的數(shù)據(jù)綁定,要添加訂閱者(watcher),new Watcher(vm,動態(tài)綁定的變量,回調(diào)函數(shù)fn) 2.判斷是否是元素節(jié)點(diǎn)且屬性中是否含有v-model這種指令,如果滿足條件說明它是雙向的數(shù)據(jù)綁定,要添加訂閱者(watcher),new Watcher(vm,動態(tài)綁定的變量,回調(diào)函數(shù)fn) ,直至遍歷完成。

最后別忘了把文檔碎片放到頁面中

3.Dep構(gòu)造函數(shù)(怎么收集依賴的)

var uid=0;//發(fā)布訂閱function Dep() { this.id=uid++; this.subs = [];}Dep.prototype.addSub = function (sub) { //訂閱 this.subs.push(sub); //實際上添加的是watcher這個實例}Dep.prototype.depend = function () { // 訂閱管理器 if(Dep.target){//只有Dep.target存在時采取添加 Dep.target.addDep(this); }}Dep.prototype.notify = function (sub) { //發(fā)布,遍歷數(shù)組讓每個訂閱者的update方法去執(zhí)行 this.subs.forEach((sub) => sub.update())}

Dep構(gòu)造函數(shù)內(nèi)部有一個id和一個subs,id=uid++ ,id用于作為dep對象的唯一標(biāo)識,subs就是保存watcher的數(shù)組。depend方法就是一個訂閱的管理器,會調(diào)用當(dāng)前watcher的addDep方法添加訂閱者,當(dāng)觸發(fā)數(shù)據(jù)劫持(Object.defineProperty)的get方法時會調(diào)用Dep.target && dep.depend(Dep.target)添加訂閱者,當(dāng)數(shù)據(jù)改變時觸發(fā)數(shù)據(jù)劫持(Object.defineProperty)的set方法時會調(diào)用dep.notify方法更新操作。

4.Watcher構(gòu)造函數(shù)干了什么

function Watcher(vm, exp, fn) { this.fn = fn; this.vm = vm; this.exp = exp // this.newDeps = []; this.depIds = new Set(); this.newDepIds = new Set(); Dep.target = this; //this是指向當(dāng)前(Watcher)的一個實例 let val = vm; let arr = exp.split('.'); arr.forEach((k) => { //取值this.obj.text val = val[k] //取值this.obj.text,就會觸發(fā)數(shù)據(jù)劫持的get方法,把當(dāng)前的訂閱者(watcher實例)添加到依賴中 }); Dep.target = null;}Watcher.prototype.addDep = function (dep) { var id=dep.id; if(!this.newDepIds.has(id)){ this.newDepIds.add(id); this.newDeps.push(dep); if(!this.depIds.has(id)){ dep.addSub(this); } } }Watcher.prototype.update = function () { //這就是每個綁定的方法都添加一個update屬性 let val = this.vm; let arr = this.exp.split('.'); arr.forEach((k) => { val = val[k] //取值this.obj.text,傳給fn更新操作 }); this.fn(val); //傳一個新值}

Watcher構(gòu)造函數(shù)干了什么

1 接收參數(shù),定義了幾個私有屬性( this.newDep ,this.depIds,this.newDepIds)

2. Dep.target = this,通過參數(shù)進(jìn)行data取值操作,這就會觸發(fā)Object.defineProperty的get方法,它會通過訂閱者管理器(dep.depend())添加訂閱者,添加完之后再將Dep.target=null置為空;

3.原型上的addDep是通過id這個唯一標(biāo)識,和幾個私有屬性的判斷防止訂閱者被多次重復(fù)添加

4.update方法就是當(dāng)數(shù)據(jù)更新時,dep.notify()執(zhí)行,觸發(fā)訂閱者的update這個方法, 執(zhí)行發(fā)布更新操作。

總結(jié)一下

vue2.0中雙向數(shù)據(jù)綁定,簡單來說就是Observer、Watcher、Dep三大部分;

1.首先用Object.defineProperty()循環(huán)遞歸實現(xiàn)數(shù)據(jù)劫持,為每個屬性分配一個訂閱者集合的管理數(shù)組dep;

2.在編譯的時候,創(chuàng)建文檔碎片,把所有節(jié)點(diǎn)添加到文檔碎片中,遍歷文檔碎片的所有結(jié)點(diǎn),如果是{{}},v-model這種,new Watcher()實例并向dep的subs數(shù)組中添加該實例

3.最后修改值就會觸發(fā)Object.defineProperty()的set方法,在set方法中會執(zhí)行dep.notify(),然后循環(huán)調(diào)用所有訂閱者的update方法更新視圖。

到此這篇關(guān)于手動實現(xiàn)vue2.0的雙向數(shù)據(jù)綁定原理的文章就介紹到這了,更多相關(guān)vue2.0雙向數(shù)據(jù)綁定內(nèi)容請搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Vue
相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
美国三级日本三级久久99| 麻豆精品av| 四虎884aa成人精品最新| 国产一区二区久久久久| 四季av一区二区凹凸精品| 成人片免费看| 久久精品国产亚洲夜色av网站| 天堂网av成人| 亚洲精品小说| 日本一区福利在线| 日韩极品在线观看| 久久69成人| 久久国产日韩| 免费人成在线不卡| 国产精品高清一区二区| 韩国久久久久久| 2023国产精品久久久精品双| 在线一区欧美| 清纯唯美亚洲综合一区| 国产精品久久久久久久久久10秀| 99精品综合| 午夜性色一区二区三区免费视频| 国产精品magnet| 成人免费电影网址| 亚洲精品欧美| 精品视频黄色| 国产亚洲毛片在线| 欧美黑人巨大videos精品| 久久在线免费| 欧美精品三级在线| 天堂av在线| 美国欧美日韩国产在线播放| 久久不见久久见中文字幕免费 | 欧美日韩四区| 青草国产精品| 久久高清精品| 日韩精品免费视频人成| 中文字幕在线视频久| 免费久久精品视频| 国产成人精品一区二区三区在线| 亚洲综合欧美| 红杏一区二区三区| 日本不卡高清| 亚洲精品在线影院| 国产午夜精品一区在线观看| 午夜精品影院| 国产成人精品一区二区三区视频| 中文字幕av一区二区三区人| 日韩av免费| 国产精品丝袜在线播放| 六月天综合网| 日韩综合一区| 亚洲精品欧美| 欧美日韩水蜜桃| 狂野欧美性猛交xxxx| 亚洲乱码久久| 午夜久久影院| 亚洲伊人av| 麻豆国产一区| 欧美永久精品| 亚洲精品一级| 丝袜美腿亚洲一区二区图片| 三级精品视频| 日韩成人精品一区| 国产精品分类| 97久久超碰| 丝袜诱惑制服诱惑色一区在线观看| 日韩久久电影| 精品国产91| 国产精品成人国产| 日本成人在线网站| 日韩精品一二区| 亚洲在线免费| 尤物在线精品| 欧美午夜不卡| 国产一区久久| 伊人精品一区| 视频福利一区| 欧美成人基地 | 超级白嫩亚洲国产第一| 国产欧美高清视频在线| 日韩av不卡一区二区| 亚洲有吗中文字幕| 视频一区免费在线观看| 亚洲激情国产| 中文一区二区| 日韩午夜在线| 日韩在线观看一区二区| 欧美特黄一级大片| 啪啪国产精品| 久久亚洲国产| 亚洲精品极品少妇16p| 自由日本语亚洲人高潮| 99re国产精品| 日韩中文字幕区一区有砖一区 | 青草av.久久免费一区| 免费日韩av片| 日韩精品一二三| 中文字幕av一区二区三区四区| 亚洲欧美激情诱惑| 日韩中文字幕麻豆| 日韩影片在线观看| 国产色噜噜噜91在线精品| 日本一区二区三区视频在线看| 婷婷精品久久久久久久久久不卡| 亚洲18在线| 久久激情五月婷婷| 美女精品一区二区| 国产精品久久久久久久免费观看| 日韩福利一区| 99久久99久久精品国产片果冰| 国产精品99一区二区| 丝袜a∨在线一区二区三区不卡| 中文字幕日韩高清在线| 国产亚洲一区| 国产在线看片免费视频在线观看| 中文字幕系列一区| 欧美专区在线| 国产麻豆一区二区三区精品视频| 久久99国产精品视频| 久久天堂精品| 免费观看在线综合色| 日韩不卡一区二区| 国产不卡人人| 久久成人国产| 欧美片网站免费| 美女一区网站| 亚洲精品影院在线观看| 国产精品男女| av在线日韩| 蜜桃免费网站一区二区三区| 国产乱论精品| 久久久久99| 亚洲欧洲av| 日韩av自拍| 日韩视频一区| 欧美在线首页| 久久久久99| 热久久久久久| 精品捆绑调教一区二区三区| 99国产精品| 欧美国产极品| 国产精品色网| 精品视频在线一区二区在线| 不卡在线一区二区| 国产日韩欧美三区| 欧美中文一区二区| 久久精品99国产国产精| 激情婷婷亚洲| 国产视频网站一区二区三区| 在线日韩中文| 国产精品扒开腿做爽爽爽软件| 久久久久国产| 国产九一精品| 视频一区二区国产| 国产美女久久| 日韩午夜黄色| 国产91在线播放精品| 亚洲a成人v| 日韩不卡免费高清视频| 日韩国产一二三区| 伊人久久婷婷| 中文字幕成在线观看| 91精品国产自产精品男人的天堂| 99精品视频精品精品视频| 国产精品成人一区二区网站软件| 亚洲激情av| 国产不卡精品| 国产精品毛片视频| 亚洲精品国模| 蜜桃tv一区二区三区| 久久亚洲国产精品尤物| 伊人久久亚洲| 亚洲国产不卡| 在线一区av| 久久久久亚洲精品中文字幕| 亚洲青青久久| 在线亚洲成人| 久久蜜桃av| 日韩免费高清| 精品一区二区男人吃奶| 91成人在线精品视频| 在线观看视频免费一区二区三区| 婷婷成人在线| 热三久草你在线| 久久不见久久见免费视频7| 日韩专区一卡二卡| 欧美日韩国产探花| 久久久蜜桃一区二区人| 精品九九在线| 麻豆91小视频| 欧美经典一区| 国产精品亚洲综合色区韩国| 日韩高清成人在线| 日韩av字幕| 国产日产高清欧美一区二区三区| 少妇高潮一区二区三区99| 视频一区二区三区在线| 爽好久久久欧美精品| 另类国产ts人妖高潮视频|