国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

190行代碼實(shí)現(xiàn)mvvm模式

Pink / 2066人閱讀

摘要:原理如圖,實(shí)現(xiàn)一個(gè),需要幾個(gè)輔助工具,分別是。我的模式中的功能有兩個(gè)。對(duì)將中的數(shù)據(jù)綁定到上下文環(huán)境上,對(duì)數(shù)據(jù)進(jìn)行劫持,當(dāng)數(shù)據(jù)變化的時(shí)候通知。到此就全部完成了模式。

前言

網(wǎng)上講 vue 原理,mvvm 模式的實(shí)現(xiàn),數(shù)據(jù)雙向綁定的文章一搜一大堆,不管寫的誰(shuí)好誰(shuí)壞,都是寫的自己的理解,我也發(fā)一篇文章記錄自己的理解,如果對(duì)看官有幫助,那也是我莫大的榮幸,不過看完之后,你們以后如果再被面試官問到 vue 的原理的時(shí)候,千萬(wàn)不要只用一句【通過 javascrit 的 Object.defineProperty 將 data 進(jìn)行劫持,發(fā)生改變的時(shí)候改變對(duì)應(yīng)節(jié)點(diǎn)的值】這么籠統(tǒng)的話來(lái)應(yīng)付了。如果有不懂的,可以問我。話不多說,上效果圖:

效果

以及代碼


    

{{a}}

怎么樣,是不是跟vue的寫法很像,跟著我的思路,你們也可以的。

原理

talk is cheap, show you the picture

如圖,實(shí)現(xiàn)一個(gè)mvvm,需要幾個(gè)輔助工具,分別是 Observer, Compile, Dep, Watcher。每個(gè)工具各司其職,再由 MVVM 統(tǒng)一掉配從而實(shí)現(xiàn)數(shù)據(jù)的雙向綁定,下面我分別介紹下接下來(lái)出場(chǎng)的幾位菇?jīng)?/p>

Compile 能夠?qū)㈨?yè)面中的頁(yè)面初始化,對(duì)指令進(jìn)行解析,把 data 對(duì)應(yīng)的值渲染上去的同時(shí),new 一個(gè) Watcher,并告訴它,當(dāng)渲染的這個(gè)數(shù)據(jù)發(fā)生改變時(shí)告訴我,我好更新視圖。

Observer 能夠?qū)崿F(xiàn)將 data 中的數(shù)據(jù)通過Object.defineProperty進(jìn)行劫持,當(dāng)獲取 data 中的值的時(shí)候,觸發(fā)get里方法,把 Compile 新建的 Watcher 抓過來(lái),關(guān)到 Dep(發(fā)布訂閱者模式)的小黑屋里狂...,當(dāng)值修改的時(shí)候,觸發(fā) set 里的方法,通知小黑屋(Dep)里所有 Watcher 菇?jīng)鰝儯銈兘夥爬病?/p>

Dep 就是傳說中的小黑屋了,其內(nèi)在原理是發(fā)布訂閱者模式,不了解發(fā)布訂閱者模式的話可以看我 這篇文章

Watcher 們從小黑屋里逃出來(lái)之后就趕緊跑到對(duì)應(yīng)的 Compile 那,告訴他開始更新視圖吧,看,我是愛你的。

哈哈,通過我很(lao)幽(si)默(ji)的講解。你們是不是都想下車了?

嗯,知道大概是怎么回事之后,我分別講他們的功能。不過話說前面,mvvm 模式之前有千絲萬(wàn)縷的聯(lián)系,必須要全部看完,才能真正理解 mvvm 的原理。

Observe

我的 mvvm 模式中 Observe 的功能有兩個(gè)。1.對(duì)將data中的數(shù)據(jù)綁定到上下文環(huán)境上,2.對(duì)數(shù)據(jù)進(jìn)行劫持,當(dāng)數(shù)據(jù)變化的時(shí)候通知 Dep。下面用一個(gè) demo 來(lái)看看,如何將數(shù)據(jù)綁定到環(huán)境中,并劫持?jǐn)?shù)據(jù)




  
  Document





可以看到將 data 數(shù)據(jù)綁定到 window 上,當(dāng)數(shù)據(jù)變化時(shí)候,會(huì)打印 "值更新啦",那么 data 變化 是如何通知 Dep 的呢?首先我們要明白,observe 只執(zhí)行一遍,將數(shù)據(jù)綁定到 mvvm 實(shí)例上,Dep也只有一個(gè),之前說把所有的 Watcher 抓過來(lái),全放在這個(gè) Dep 里,還是看代碼說話把。

function observe (obj, vm) {
    if (!obj || typeof obj !== "object") return;
    return new Observer(obj, vm)
}
class Observer {
    constructor(obj, vm) {
        // vm 代表上下文環(huán)境,也是指向 mvvm 的實(shí)例 (調(diào)用的時(shí)候會(huì)傳入)
        this.walk(obj, vm);
        // 實(shí)例化一個(gè) Dep;
        this.dep = new Dep();
    }
    walk (obj, vm) {
        var self = this;
        Object.keys(obj).forEach(key => {
            Object.defineProperty(vm, key, {
                configurable: true,
                enumerable: true,
                get () {
                    // 當(dāng)獲取 vm 的值的時(shí)候,如果 Dep 有 target 時(shí)執(zhí)行,目的是將 Watcher 抓過來(lái),后面還會(huì)說明
                    if (Dep.target) {
                        self.dep.depend();
                    }
                    return obj[key];
                },
                set (newVal) {
                    var val = obj.key;
                    if (val === newVal) return;
                    obj[key] = newVal;
                    // 當(dāng) 劫持的值發(fā)生變化時(shí)候觸發(fā),通知 Dep
                    self.dep.notify();
                }
            })
        })
    }
}
Dep

接下來(lái)講講 Dep 的實(shí)現(xiàn),Dep 功能很簡(jiǎn)單,難點(diǎn)是如何將 watcher 聯(lián)系起來(lái),先看代碼吧。

class Dep {
  constructor (props) {
    this.subs = [];
    this.uid = 0;
  }
  addSub (sub) {
    this.subs.push(sub);
    this.uid++;
  }
  notify () {
    this.subs.forEach(sub => {
      sub.update();
    })
  }
  depend (sub) {
    Dep.target.addDep(this, sub);
  }
}
Dep.target = null;

subs 是一個(gè)數(shù)組,用來(lái)存儲(chǔ) Watcher 的,當(dāng)數(shù)據(jù)更新時(shí)候(由Observer告知),會(huì)觸發(fā) Dep 的 notify 方法,調(diào)用 subs 里所有 Watcher 的 update 方法。
接下來(lái)是不是迫不及待的想知道 Dep 是如何將 Watcher 抓過來(lái)的吧(污污污),別著急我們先看看 Watcher 是如何誕生的。

Compile

我覺得 Compile 是 mvvm 中最勞苦功高的一個(gè)了,它的任務(wù)是頁(yè)面過來(lái)時(shí)候,初始化視圖,將頁(yè)面中的{{.*}}解析成對(duì)應(yīng)的值,還有指令解析,如綁定值的 v-text、v-html 還有綁定的事件 v-on,還有創(chuàng)造 Watcher 去監(jiān)聽值的變化,當(dāng)值變化的時(shí)候又要更新節(jié)點(diǎn)的視圖。
我們先看看 Compile 是如何初始化視圖的




  
  Document


  

{{a}}

額,感覺還好理解吧,這里只是講了 Compile 是如何將data中的值渲染到視圖上,買了個(gè)關(guān)子,沒有說如何創(chuàng)建 Watcher 的,思考一下,如果要?jiǎng)?chuàng)建 Watcher ,應(yīng)該在哪個(gè)位置創(chuàng)建比較好呢?
答案是渲染值的同時(shí),同時(shí)創(chuàng)造一個(gè) Watcher 來(lái)監(jiān)聽,上代碼:

class Compile {
  constructor (el, vm) {
    this.$el = this.isElementNode(el) ? el : document.querySelector(el);
    this.$vm = vm;
    if (this.$el) {
      this.$fragment = this.nodeFragment(this.$el);
      this.compileElement(this.$fragment);
      this.$el.appendChild(this.$fragment);
    }
  }
  nodeFragment (el) {
    let fragment = document.createDocumentFragment();
    let child;
    while (child = el.firstChild) {
      fragment.appendChild(child);
    }
    return fragment;
  }
  compileElement (el) {
    var childNodes = Array.from(el.childNodes);
    if (childNodes.length > 0) {
      childNodes.forEach(child => {
        var childArr = Array.from(child.childNodes);
        // 匹配{{}}里面的內(nèi)容
        var reg = /{{((?:.)+?)}}/;
        if (childArr.length > 0) {
          this.compileElement(child)
        } 
        if (this.isTextNode(child)) {
          var text = child.textContent.trim();
          var matchTextArr = reg.exec(text);
          var matchText;
          if (matchTextArr && matchTextArr.length > 1) {
            matchText = matchTextArr[1];
            this.compileText(child, matchText);
          }
        } else if (this.isElementNode(child)) {
          this.compileNode(child);
        }
      })
    }

  }
  compileText(node, exp) {
    this.bind(node, this.$vm, exp, "text");
  }
  compileNode (node) {
    var attrs = Array.from(node.attributes);
    attrs.forEach(attr => {
      if (this.isDirective(attr.name)) {
        var directiveName = attr.name.substr(2);
        if (directiveName.includes("on")) {
          node.removeAttribute(attr.name);
          var eventName = directiveName.split(":")[1];
          this.addEvent(node, eventName, attr.value);
        } else if (directiveName.includes("model")) {
          // v-model
          this.bind(node, this.$vm, attr.value, "value");
          node.addEventListener("input", (e) => {
            this.$vm[attr.value] = e.target.value;
          })
        }else{
          // v-text v-html
          node.removeAttribute(attr.name);
          this.bind(node, this.$vm, attr.value, directiveName);
        }
      }
    })
  }
  addEvent(node, eventName, exp) {
    node.addEventListener(eventName, this.$vm.$options.methods[exp].bind(this.$vm));
  }
  bind (node, vm, exp, dir) {
    if (dir === "text") {
      node.textContent = vm[exp];
    } else if (dir === "html") {
      node.innerHTML = vm[exp];
    } else if (dir === "value") {
      node.value = vm[exp];
    }
    new Watcher(exp, vm, function () {
      if (dir === "text") {
        node.textContent = vm[exp];
      } else if (dir === "html") {
        node.innerHTML = vm[exp];
      }
    })
  }
  hasChildNode (node) {
    return node.children && node.children.length > 0;
  }
  // 是否是指令
  isDirective (attr) {
    if (typeof attr !== "string") return;
    return attr.includes("v-");
  }
  // 元素節(jié)點(diǎn)
  isElementNode (node) {
    return node.nodeType === 1;
  }
  // 文本節(jié)點(diǎn)
  isTextNode (node) {
    return node.nodeType === 3;
  }
}

這里比上面演示的demo多創(chuàng)建一個(gè)文檔碎片,可以加快解析速度,另外在 80 行創(chuàng)建了 Watcher,當(dāng)數(shù)據(jù)變化時(shí),執(zhí)行回調(diào)函數(shù),從而更新視圖。

Watcher

期待已久的 Watcher 終于出來(lái)了,我們先看看它長(zhǎng)什么樣:

class Watcher {
  constructor (exp, vm, cb) {
    this.$vm = vm;
    this.$exp = exp;
    this.depIds = {};
    this.getter = this.parseGetter(exp);
    this.value = this.get();
    this.cb = cb;
  }
  update () {
    let newVal = this.get();
    let oldVal = this.value;
    if (oldVal === newVal) return;
    this.cb.call(this.vm, newVal);
    this.value = newVal;
  }
  get () {
    Dep.target = this;
    var value = this.getter.call(this.$vm, this.$vm);
    Dep.target = null;
    return value;
  }
  parseGetter (exp) {
    if (/[^w.$]/.test(exp)) return;
    return function (obj) {
      if (!obj) return;
      obj = obj[exp];
      return obj;
    }
  }
  addDep (dep) {
    if (!this.depIds.hasOwnProperty(dep.id)) {
      this.depIds[dep.id] = dep;
      dep.subs.push(this);
    }
  }
}

也不怎么樣嘛,只有30多行代碼,接下來(lái)睜大眼睛啦,看看它是怎么被 Dep 抓過來(lái)的。

當(dāng) Compile 創(chuàng)建 Watcher 出來(lái)的時(shí)候,也將 Dep.target 指向了 Watcher。同時(shí)獲取了該節(jié)點(diǎn)要渲染的值,觸發(fā)了 Observer 中的 get 方法,Dep.target 有值了,就執(zhí)行 self.dep.depend();

depend 方法里執(zhí)行 Dep.target.addDep(this); 而現(xiàn)在 Dep.target 指向 Watcher,所以執(zhí)行的是 Watcher 里的 addDep 方法 同時(shí)把 Dep 實(shí)例傳過去。

Watcher 里的 addDep 方法是將 Watcher 放在的 Dep實(shí)例的 subs 數(shù)組里。

當(dāng)vm里的值放生變化時(shí),觸發(fā) Observer 的 set 方法,觸發(fā)所有 subs 里的 Watcher 執(zhí)行 Watcher 里的 update 方法。

update 方法里有 Compile 的回調(diào),從而更新視圖。

好吧,真想大白了,原來(lái) Watcher 是引誘 Dep 把自己裝進(jìn)小黑屋的。哈哈~
源碼已放在我自己的git庫(kù)里,點(diǎn)擊這里獲取源碼
講了半天,正主該出來(lái)了,mvvm 是如何將上面四個(gè)小伙伴給自己打工的呢,其實(shí)很簡(jiǎn)單,上代碼

class MVVM {
  constructor (options) {
    this.$options = options;
    var data = this._data = this.$options.data;
    observe(data, this);
    new Compile(options.el || document.body, this);
  }
}

就是實(shí)例 MVVM 的時(shí)候,調(diào)用數(shù)據(jù)劫持,和 Compile 初始化視圖。到此就全部完成了mvvm模式。

參考

合格前端系列第三彈-實(shí)現(xiàn)一個(gè)屬于我們自己的簡(jiǎn)易MVVM庫(kù)

vue.js 權(quán)威指南

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/95489.html

相關(guān)文章

  • 190代碼實(shí)現(xiàn)mvvm模式

    摘要:原理如圖,實(shí)現(xiàn)一個(gè),需要幾個(gè)輔助工具,分別是。我的模式中的功能有兩個(gè)。對(duì)將中的數(shù)據(jù)綁定到上下文環(huán)境上,對(duì)數(shù)據(jù)進(jìn)行劫持,當(dāng)數(shù)據(jù)變化的時(shí)候通知。到此就全部完成了模式。 前言 網(wǎng)上講 vue 原理,mvvm 模式的實(shí)現(xiàn),數(shù)據(jù)雙向綁定的文章一搜一大堆,不管寫的誰(shuí)好誰(shuí)壞,都是寫的自己的理解,我也發(fā)一篇文章記錄自己的理解,如果對(duì)看官有幫助,那也是我莫大的榮幸,不過看完之后,你們以后如果再被面試官問...

    liangzai_cool 評(píng)論0 收藏0
  • 前端MVVM模式及其在Vue和React中的體現(xiàn)

    摘要:在模式中一般把層算在層中,只有在理想的雙向綁定模式下,才會(huì)完全的消失。層將通過特定的展示出來(lái),并在控件上綁定視圖交互事件,一般由框架自動(dòng)生成在瀏覽器中。三大框架的異同三大框架都是數(shù)據(jù)驅(qū)動(dòng)型的框架及是雙向數(shù)據(jù)綁定是單向數(shù)據(jù)綁定。 MVVM相關(guān)概念 1) MVVM典型特點(diǎn)是有四個(gè)概念:Model、View、ViewModel、綁定器。MVVM可以是單向綁定也可以是雙向綁定甚至是不綁...

    沈建明 評(píng)論0 收藏0
  • 前端框架模式的變遷

    摘要:現(xiàn)在在前端的框架都是的模式,還有像和之類的變種獨(dú)特的單向數(shù)據(jù)流框架。只要將數(shù)據(jù)流進(jìn)行規(guī)范,那么原來(lái)的模式還是大有可為的。我們可以來(lái)看一下,框架的圖示從圖中,我們可以看到形成了一條到,再到,之后是的,一條單向數(shù)據(jù)流。 前言 前端框架的變遷,體系架構(gòu)的完善,使得我們只知道框架,卻不明白它背后的道理。我們應(yīng)該抱著一顆好奇心,在探索框架模式的變遷過程中,體會(huì)前人的一些理解和思考 本篇將講述的是...

    ssshooter 評(píng)論0 收藏0
  • VUE - MVVM - part5 - Observe

    摘要:具體代碼執(zhí)行方式進(jìn)入到的目錄下,命令行運(yùn)行即可。確保為一個(gè)對(duì)象如果對(duì)象下有則不需要再次生成函數(shù)返回該對(duì)象的實(shí)例,這里判斷了如果該對(duì)象下已經(jīng)有實(shí)例,則直接返回,不再去生產(chǎn)實(shí)例。這就確保了一個(gè)對(duì)象下的實(shí)例僅被實(shí)例化一次。 看這篇之前,如果沒有看過之前的文章,可拉到文章末尾查看之前的文章。 回顧 在 step4 中,我們大致實(shí)現(xiàn)了一個(gè) MVVM 的框架,由3個(gè)部分組成: defineRe...

    xi4oh4o 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<