国产精品爱久久久久久久小说,女人扒开腿让男人桶到爽 ,亚洲欧美国产双大乳头,国产成人精品综合久久久久,国产精品制服丝袜无码,免费无码精品黄av电影,黑色丝袜无码中中文字幕,乱熟女高潮一区二区在线

            Vue的響應(yīng)式原理(MVVM)深入解析

            2019-6-6    seo達(dá)人

            如果您想訂閱本博客內(nèi)容,每天自動(dòng)發(fā)到您的郵箱中, 請(qǐng)點(diǎn)這里

            1. 如何實(shí)現(xiàn)一個(gè)響應(yīng)式對(duì)象
            最近在看 Vue 的源碼,其中最核心基礎(chǔ)的一塊就是 Observer/Watcher/Dep, 簡(jiǎn)而言之就是,Vue 是如何攔截?cái)?shù)據(jù)的讀寫, 如果實(shí)現(xiàn)對(duì)應(yīng)的監(jiān)聽,并且特定的監(jiān)聽執(zhí)行特定的回調(diào)或者渲染邏輯的。總的可以拆成三大塊來(lái)說(shuō)。這一塊,主要說(shuō)的是 Vue 是如何將一個(gè) plain object 給處理成 reactive object 的,也就是,Vue 是如何攔截?cái)r截對(duì)象的 get/set 的

            我們知道,用 Object.defineProperty 攔截?cái)?shù)據(jù)的 get/set 是 vue 的核心邏輯之一。這里我們先考慮一個(gè)最簡(jiǎn)單的情況 一個(gè) plain obj 的數(shù)據(jù),經(jīng)過(guò)你的程序之后,使得這個(gè) obj 變成 Reactive Obj (不考慮數(shù)組等因素,只考慮最簡(jiǎn)單的基礎(chǔ)數(shù)據(jù)類型,和對(duì)象):

            如果這個(gè) obj 的某個(gè) key 被 get, 則打印出 get ${key} - ${val} 的信息 
            如果這個(gè) obj 的某個(gè) key 被 set, 如果監(jiān)測(cè)到這個(gè) key 對(duì)應(yīng)的 value 發(fā)生了變化,則打印出 set ${key} - ${val} - ${newVal} 的信息。 
            對(duì)應(yīng)的簡(jiǎn)要代碼如下:

            Observer.js

            export class Observer {
              constructor(obj) {
                this.obj = obj;
                this.transform(obj);
              }
              // 將 obj 里的所有層級(jí)的 key 都用 defineProperty 重新定義一遍, 使之 reactive 
              transform(obj) {
                const _this = this;
                for (let key in obj) {
                  const value = obj[key];
                  makeItReactive(obj, key, value);
                }
              }
            }
            function makeItReactive(obj, key, val) {
              // 如果某個(gè) key 對(duì)應(yīng)的 val 是 object, 則重新迭代該 val, 使之 reactive 
              if (isObject(val)) {
                const childObj = val;
                new Observer(childObj);
              }
              // 如果某個(gè) key 對(duì)應(yīng)的 val 不是 Object, 而是基礎(chǔ)類型,我們則對(duì)這個(gè) key 進(jìn)行 defineProperty 定義 
              Object.defineProperty(obj, key, {
                enumerable: true,
                configurable: true,
                get: () => {
                  console.info(`get ${key}-${val}`)
                  return val;
                },
                set: (newVal) => {
                  // 如果 newVal 和 val 相等,則不做任何操作(不執(zhí)行渲染邏輯)
                  if (newVal === val) {
                    return;
                  }
                  // 如果 newVal 和 val 不相等,且因?yàn)?newVal 為 Object, 所以先用 Observer迭代 newVal, 使之 reactive, 再用 newVal 替換掉 val, 再執(zhí)行對(duì)應(yīng)操作(渲染邏輯)
                  else if (isObject(newVal)) {
                    console.info(`set ${key} - ${val} - ${newVal} - newVal is Object`);
                    new Observer(newVal);
                    val = newVal;
                  }
                  // 如果 newVal 和 val 不相等,且因?yàn)?newVal 為基礎(chǔ)類型, 所以用 newVal 替換掉 val, 再執(zhí)行對(duì)應(yīng)操作(渲染邏輯)
                  else if (!isObject(newVal)) {
                    console.info(`set ${key} - ${val} - ${newVal} - newVal is Basic Value`);
                    val = newVal;
                  }
                }
              })
            }

            function isObject(data) {
              if (typeof data === 'object' && data != 'null') {
                return true;
              }
              return false;
            }

            index.js

            import { Observer } from './source/Observer.js';
            // 聲明一個(gè) obj,為 plain Object
            const obj = {
              a: {
                aa: 1
              },
              b: 2,
            }
            // 將 obj 整體 reactive 化
            new Observer(obj);
            // 無(wú)輸出
            obj.b = 2;
            // set b - 2 - 3 - newVal is Basic Value
            obj.b = 3;
            // set b - 3 - [object Object] - newVal is Object
            obj.b = {
              bb: 4
            }
            // get b-[object Object]
            obj.b;
            // get a-[object Object]
            obj.a;
            // get aa-1
            obj.a.aa
            // set aa - 1 - 3 - newVal is Basic Value
            obj.a.aa = 3

            這樣,我們就完成了 Vue 的第一個(gè)核心邏輯, 成功把一個(gè)任意層級(jí)的 plain object 轉(zhuǎn)化成 reactive object

            2. 如何實(shí)現(xiàn)一個(gè) watcher
            前面講的是如何將 plain object 轉(zhuǎn)換成 reactive object. 接下來(lái)講一下,如何實(shí)現(xiàn)一個(gè)watcher.

            實(shí)現(xiàn)的偽代碼應(yīng)如下:

            偽代碼

            // 傳入 data 參數(shù)新建新建一個(gè) vue 對(duì)象
            const v = new Vue({
                data: {
                    a:1,
                    b:2,
                }
            });
            // watch data 里面某個(gè) a 節(jié)點(diǎn)的變動(dòng)了,如果變動(dòng),則執(zhí)行 cb
            v.$watch('a',function(){
                console.info('the value of a has been changed !');
            });

            //  watch data 里面某個(gè) b 節(jié)點(diǎn)的變動(dòng)了,如果變動(dòng),則執(zhí)行 cb
            v.$watch('b',function(){
                console.info('the value of b has been changed !');
            })

            Vue.js

            // 引入將上面中實(shí)現(xiàn)的 Observer
            import { Observer } from './Observer.js';
            import { Watcher } from './Watcher.js';

            export default class Vue {
              constructor(options) {
                // 在 this 上掛載一個(gè)公有變量 $options ,用來(lái)暫存所有參數(shù)
                this.$options = options
                // 聲明一個(gè)私有變量 _data ,用來(lái)暫存 data
                let data = this._data = this.$options.data
                // 在 this 上掛載所有 data 里的 key 值, 這些 key 值對(duì)應(yīng)的 get/set 都被代理到 this._data 上對(duì)應(yīng)的同名 key 值
                Object.keys(data).forEach(key => this._proxy(key));
                // 將 this._data 進(jìn)行 reactive 化
                new Observer(data, this)
              }
              // 對(duì)外暴露 $watch 的公有方法,可以對(duì)某個(gè) this._data 里的 key 值創(chuàng)建一個(gè) watcher 實(shí)例
              $watch(expOrFn, cb) {
                // 注意,每一個(gè) watcher 的實(shí)例化都依賴于 Vue 的實(shí)例化對(duì)象, 即 this
                new Watcher(this, expOrFn, cb)
              }
              //  將 this.keyName 的某個(gè) key 值的 get/set 代理到  this._data.keyName 的具體實(shí)現(xiàn)
              _proxy(key) {
                var self = this
                Object.defineProperty(self, key, {
                  configurable: true,
                  enumerable: true,
                  get: function proxyGetter() {
                    return self._data[key]
                  },
                  set: function proxySetter(val) {
                    self._data[key] = val
                  }
                })
              }
            }

            Watch.js

            // 引入Dep.js, 是什么我們待會(huì)再說(shuō)
            import { Dep } from './Dep.js';

            export class Watcher {
              constructor(vm, expOrFn, cb) {
                this.cb = cb;
                this.vm = vm;
                this.expOrFn = expOrFn;
                // 初始化 watcher 時(shí), vm._data[this.expOrFn] 對(duì)應(yīng)的 val
                this.value = this.get();
              }
              // 用于獲取當(dāng)前 vm._data 對(duì)應(yīng)的 key = expOrFn 對(duì)應(yīng)的 val 值
              get() {
                Dep.target = this;
                const value = this.vm._data[this.expOrFn];
                Dep.target = null;
                return value;
              }
              // 每次 vm._data 里對(duì)應(yīng)的 expOrFn, 即 key 的 setter 被觸發(fā),都會(huì)調(diào)用 watcher 里對(duì)應(yīng)的 update方法
              update() {
                this.run();
              }
              run() {
                // 這個(gè) value 是 key 被 setter 調(diào)用之后的 newVal, 然后比較 this.value 和 newVal, 如果不相等,則替換 this.value 為 newVal, 并執(zhí)行傳入的cb.
                const value = this.get();
                if (value !== this.value) {
                  this.value = value;
                  this.cb.call(this.vm);
                }
              }
            }

            對(duì)于什么是 Dep, 和 Watcher 里的 update() 方法到底是在哪個(gè)時(shí)候被誰(shuí)調(diào)用的,后面會(huì)說(shuō)

            3. 如何收集 watcher 的依賴
            前面我們講了 watcher 的大致實(shí)現(xiàn),以及 Vue 代理 data 到 this 上的原理。現(xiàn)在我們就來(lái)梳理一下,Observer/Watcher 之間的關(guān)系,來(lái)說(shuō)明它們是如何調(diào)用的.

            首先, 我們要來(lái)理解一下 watcher 實(shí)例的概念。實(shí)際上 Vue 的 v-model, v-bind , {{ mustache }}, computed, watcher 等等本質(zhì)上是分別對(duì) data 里的某個(gè) key 節(jié)點(diǎn)聲明了一個(gè) watcher 實(shí)例.

            <input v-model="abc">
            <span>{{ abc }}</span>
            <p :data-key="abc"></p>
            ...

            const v = new Vue({
                data:{
                    abc: 111,
                }
                computed:{
                    cbd:function(){
                        return `${this.abc} after computed`;
                    }
                watch:{
                    abc:function(val){
                        console.info(`${val} after watch`)
                    }
                 }  
                }
            })

            這里,Vue 一共聲明了 4 個(gè) watcher 實(shí)例來(lái)監(jiān)聽abc, 1個(gè) watcher 實(shí)例來(lái)監(jiān)聽 cbd. 如果 abc 的值被更改,那么 4 個(gè) abc - watcher 的實(shí)例會(huì)執(zhí)行自身對(duì)應(yīng)的特定回調(diào)(比如重新渲染dom,或者是打印信息等等)

            不過(guò),Vue 是如何知道,某個(gè) key 對(duì)應(yīng)了多少個(gè) watcher, 而 key 對(duì)應(yīng)的 value 發(fā)生變化后,又是如何通知到這些 watcher 來(lái)執(zhí)行對(duì)應(yīng)的不同的回調(diào)的呢?

            實(shí)際上更深層次的邏輯是:

            在 Observer階段,會(huì)為每個(gè) key 都創(chuàng)建一個(gè) dep 實(shí)例。并且,如果該 key 被某個(gè) watcher 實(shí)例 get, 把該 watcher 實(shí)例加入 dep 實(shí)例的隊(duì)列里。如果該 key 被 set, 則通知該 key 對(duì)應(yīng)的 dep 實(shí)例, 然后 dep 實(shí)例會(huì)將依次通知隊(duì)列里的 watcher 實(shí)例, 讓它們?nèi)?zhí)行自身的回調(diào)方法

            dep 實(shí)例是收集該 key 所有 watcher 實(shí)例的地方.

            watcher 實(shí)例用來(lái)監(jiān)聽某個(gè) key ,如果該 key 產(chǎn)生變化,便會(huì)執(zhí)行 watcher 實(shí)例自身的回調(diào) 


            相關(guān)代碼如下:

            Dep.js

            export class Dep {
              constructor() {
                this.subs = [];
              }
              // 將 watcher 實(shí)例置入隊(duì)列
              addSub(sub) {
                this.subs.push(sub);
              }
              // 通知隊(duì)列里的所有 watcher 實(shí)例,告知該 key 的 對(duì)應(yīng)的 val 被改變
              notify() {
                this.subs.forEach((sub, index, arr) => sub.update());
              }
            }

            // Dep 類的的某個(gè)靜態(tài)屬性,用于指向某個(gè)特定的 watcher 實(shí)例.
            Dep.target = null
            observer.js

            import {Dep} from './dep'
            function makeItReactive(obj, key, val) {
             var dep = new Dep()
            Object.defineProperty(obj, key, {
              enumerable: true,
              configurable: true,
              get: () => {
                // 收集依賴! 如果該 key 被某個(gè) watcher 實(shí)例依賴,則將該 watcher 實(shí)例置入該 key 對(duì)應(yīng)的 dep 實(shí)例里
                if(Dep.target){
                  dep.addSub(Dep.target)
                }
                return val
              },
              set: (newVal) => {
                if (newVal === val) {
                  return;
                }
                else if (isObject(newVal)) {
                  new Observer(newVal);
                  val = newVal;
                // 通知 dep 實(shí)例, 該 key 被 set,讓 dep 實(shí)例向所有收集到的該 key 的 watcher 實(shí)例發(fā)送通知
                dep.notify()
                }
                else if (!isObject(newVal)) {
                  val = newVal;
                // 通知 dep 實(shí)例, 該 key 被 set,讓 dep 實(shí)例向所有收集到的該 key 的 watcher 發(fā)送通知
                dep.notify()
                }
              }
            })
                 }    

            watcher.js

            import { Dep } from './Dep.js';

            export class Watcher {
              constructor(vm, expOrFn, cb) {
                this.cb = cb;
                this.vm = vm;
                this.expOrFn = expOrFn;
                this.value = this.get();
              }
              get() {
                // 在實(shí)例化某個(gè) watcher 的時(shí)候,會(huì)將Dep類的靜態(tài)屬性 Dep.target 指向這個(gè) watcher 實(shí)例
                Dep.target = this;
                // 在這一步 this.vm._data[this.expOrFn] 調(diào)用了 data 里某個(gè) key 的 getter, 然后 getter 判斷類的靜態(tài)屬性 Dep.target 不為null, 而為 watcher 的實(shí)例, 從而把這個(gè) watcher 實(shí)例添加到 這個(gè) key 對(duì)應(yīng)的 dep 實(shí)例里。 巧妙!
                const value = this.vm._data[this.expOrFn];
                // 重置類屬性 Dep.target 
                Dep.target = null;
                return value;
              }

              // 如果 data 里的某個(gè) key 的 setter 被調(diào)用,則 key 會(huì)通知到 該 key 對(duì)應(yīng)的 dep 實(shí)例, 該Dep實(shí)例, 該 dep 實(shí)例會(huì)調(diào)用所有 依賴于該 key 的 watcher 實(shí)例的 update 方法。
              update() {
                this.run();
              }
              run() {
                const value = this.get();
                if (value !== this.value) {
                this.value = value;
                // 執(zhí)行 cb 回調(diào)
                this.cb.call(this.vm);
                }
              }
            }

            總結(jié):
            至此, Watcher, Observer , Dep 的關(guān)系全都梳理完成。而這些也是 Vue 實(shí)現(xiàn)的核心邏輯之一。再來(lái)簡(jiǎn)單總結(jié)一下三者的關(guān)系,其實(shí)是一個(gè)簡(jiǎn)單的 觀察-訂閱 的設(shè)計(jì)模式, 簡(jiǎn)單來(lái)說(shuō)就是, 觀察者觀察數(shù)據(jù)狀態(tài)變化, 一旦數(shù)據(jù)發(fā)生變化,則會(huì)通知對(duì)應(yīng)的訂閱者,讓訂閱者執(zhí)行對(duì)應(yīng)的業(yè)務(wù)邏輯 。我們熟知的事件機(jī)制,就是一種典型的觀察-訂閱的模式

            Observer, 觀察者,用來(lái)觀察數(shù)據(jù)源變化. 
            Dep, 觀察者和訂閱者是典型的 一對(duì)多 的關(guān)系,所以這里設(shè)計(jì)了一個(gè)依賴中心,來(lái)管理某個(gè)觀察者和所有這個(gè)觀察者對(duì)應(yīng)的訂閱者的關(guān)系, 消息調(diào)度和依賴管理都靠它。 
            Watcher, 訂閱者,當(dāng)某個(gè)觀察者觀察到數(shù)據(jù)發(fā)生變化的時(shí)候,這個(gè)變化經(jīng)過(guò)消息調(diào)度中心,最終會(huì)傳遞到所有該觀察者對(duì)應(yīng)的訂閱者身上,然后這些訂閱者分別執(zhí)行自身的業(yè)務(wù)回調(diào)即可 
            參考 
            Vue源碼解讀-滴滴FED 
            代碼參考
            藍(lán)藍(lán)設(shè)計(jì)www.dzxscac.cn )是一家專注而深入的界面設(shè)計(jì)公司,為期望卓越的國(guó)內(nèi)外企業(yè)提供卓越的UI界面設(shè)計(jì)、BS界面設(shè)計(jì) 、 cs界面設(shè)計(jì) 、 ipad界面設(shè)計(jì) 、 包裝設(shè)計(jì) 、 圖標(biāo)定制 、 用戶體驗(yàn) 、交互設(shè)計(jì)、 網(wǎng)站建設(shè) 平面設(shè)計(jì)服務(wù)

            日歷

            鏈接

            個(gè)人資料

            存檔

            主站蜘蛛池模板: 国内精品视频在线观看九九| 护士张开腿被奷日出白浆| 亚洲国内成人精品网| 国产一级片a| 国产激情久久久久久熟女老人 | 天堂中文在线8最新版地址| 91精品婷婷国产综合久久| 免费裸体无遮挡黄网站免费看| 亚洲欧美人成网站在线观看看 | 女人大荫蒂毛茸茸视频| 国产精品视频观看裸模| 日本黄图| 18禁裸乳无遮挡自慰免费动漫| 亚洲乱码在线卡一卡二卡新区豆瓣| 亚洲国产日韩在线| 国产成人精品区一区二区| 红桃av| 欧美精品播放| 欧美成 人版在线观看| 欧美熟妇乱码在线一区| 亚洲精品成人网线在线播放va | 小sao货水好多真紧h无码视频| 国产乱沈阳女人高潮乱叫老| 人妻丰满熟妇岳av无码区hd| 青草青在线视频在线观看| 无码专区 人妻系列 在线| 欧美成人猛交69| 亚洲αv| 亚洲国产精品久久久天堂麻豆宅男 | 久久男人| 色婷婷在线影院| 国产人在线成免费视频| av一区二区三区四区| 欧美一级色片| 乱色精品无码一区二区国产盗 | 香蕉性视频| 日本人妻伦在线中文字幕| 92午夜福利少妇系列| 天堂91| 国产a网| 国产老熟女伦老熟妇露脸|