跟隨大神實現簡單的Vue框架

本身用vue也不久了,學習之初就看過vue實現的原理,當時看也是迷迷糊糊,能說出來最基本的,可是感受仍是理解的不深刻,最近找到了以前收藏的文章,跟着大神一步步敲了一下簡易的實現,算是又加深了理解。javascript

原文連接html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>

<body>
  <div id="app">
    {{name}}
    <p v-if="isShow">

      <span>{{name}}</span>
    </p>
    <input type="text" id="a" v-model="name">

  </div>

  <script>

    function compile(node, vm) {

      var reg = /\{\{(.*)\}\}/;
      if (node.nodeType === 1) {
        var attr = Array.prototype.slice.call(node.attributes);
        //解析屬性
        for (var i = 0; i < attr.length; i++) {
          if (attr[i].nodeName == 'v-model') {
            var name = attr[i].nodeValue;
            node.addEventListener('input', function (e) {

              vm[name] = e.target.value;
              //eval(`vm.data.${name}=e.target.value`)
              console.log(vm)
            })
            node.value = eval(`vm.${name}`);
            node.removeAttribute('v-model');
          }
          if (attr[i].nodeName == 'v-if') {// 這裏是我本身加的指令,真正確定不是這樣玩的吧
            var name = attr[i].nodeValue;
            var isInsert = eval(`vm.${name}`);
            if (!isInsert) {
              node = '';
              return node;
            } else {
              node.removeAttribute('v-if');
            }

          }
        }

      }
      if (node.nodeType === 3) {
        if (reg.test(node.nodeValue)) {
          var name = RegExp.$1;
          name = name.trim();
          //node.nodeValue = eval(`vm.data.${name}`);
          new Watcher(vm, node, name)//這裏給每一個屬性文本節點生成一個Watcher對象,嗯,大體跟vue的原理類似
        }
      }
      return node;
    }

    function nodeToFragment(node, vm) {
      var flag = document.createDocumentFragment();
      var child;

      while (child = node.firstChild) {
        child = compile(child, vm)
        if (child !== "") {
          if (child.childNodes.length != 0) {
            child.append(nodeToFragment(child, vm));
          }
        } else {
          node.removeChild(node.firstChild)
        }

        flag.append(child);
      }
      return flag;
    }

    function defineReactive(obj, key, val) {
      var dep = new Dep();//這裏給每一個屬性生成一個數據訂閱中心,它能夠存儲訂閱它的全部watcher,
      Object.defineProperty(obj, key, {
        get: function () {
          if (Dep.target) dep.addSub(Dep.target);//這裏的Dep.target是對應的Watcher對象,這裏是dep對象調用addSub,我看別人說的vue源碼是在Watcher對象實現addSub操做的
          return val;
        },
        set: function (newVal) {
          if (newVal === val) return;
          console.log('修改了', key)

          val = newVal;
          dep.notify();//數據更新了,就通知全部的觀察者實例
        }
      })
    }

    function observer(obj, vm) {
      Object.keys(obj).forEach(function (key) {
        defineReactive(vm, key, obj[key]);
      })
    }

    function Watcher(vm, node, name) {
      Dep.target = this;//在實例化新的watcher對象時把Dep.target賦值爲this,也就是每一個指令對應的那個watcher對象,這樣在下面調用this.update,從而調用this.get時觸發數據的get方法,從而觸發dep.addSub(Dep.target),這樣這個watcher就被添加進去
      this.name = name;
      this.node = node;
      this.vm = vm;
      this.update();
      Dep.target = null;//爲了保證全局只有一個,在最後須要清空,爲下一個指令作準備
    }
    Watcher.prototype = {
      update: function () {
        this.get();//更新時調用get()
        this.node.nodeValue = this.value;

      },
      get: function () {
        this.value = this.vm[this.name]; //會觸發vm.data中屬性的get方法,進而能夠添加watcher到Dep中
      }
    }

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

    function Vue(options) {
      this.data = options.data;
      var id = options.el;
      var data = this.data;
      observer(data, this)
      var dom = nodeToFragment(document.getElementById(id), this);
      document.getElementById(id).appendChild(dom);
    }
    var vm = new Vue({
      el: 'app',
      data: {
        text: {
          name: 'jay'
        },
        'name': 'zxf',
        isShow: true
      }
    })

  </script>
</body>

</html>

  以上的備註是我看了文章後本身的一些理解,這個得簡單實現跟vue的源碼好像是有區別,好比前端

function defineReactive(obj, key, val) {
    var dep = new Dep()
    var childOb = Observer.create(val)
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function metaGetter() {
            // 若是Dep.target存在,則進行依賴收集
            if (Dep.target) {
                dep.depend()
                if (childOb) {
                    childOb.dep.depend()
                }
            }
            return val
        },
        set: function metaSetter(newVal) {
            if (newVal === val) return
            val = newVal
            childOb = Observer.create(newVal)
            dep.notify()
        }
    })
}
Watcher.prototype.addDep = function (dep) {
    var id = dep.id
    if (!this.newDeps[id]) {
        this.newDeps[id] = dep
        if (!this.deps[id]) {
            this.deps[id] = dep
            dep.addSub(this)
        }
    }
}

做者:百度外賣大前端技術團隊
連接:https://juejin.im/post/5a44b15e51882538fe631406
來源:掘金
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。

 源碼中在dep的get裏面並無直接調用addsub,而是調用vue

dep.depend();

而dep.depend() 實際執行了 Watcher.addDep()  這樣最終是在Watcher對象的方法裏面實現了增長訂閱者,感受好像是訂閱者主動要求dep添加本身,而簡易框架是,dep主動添加,感受都差很少吧。,可能原文做者爲了方便,而vue可能處於更全面的考慮吧

另外我也懂了vue原理中watcher,dep,observer,compile等這幾個對象的概念和他們之間的關係,observer是數據觀察者,主要是劫持data數據,添加set,get,並觀察數據的變更,若觸發了set,就調用dep的notify方法,若觸發了get,就會新增watcher到dep的存放觀察者的數組中。java

另外這個圖片也能夠說明一切,終於看懂了,之前知道這麼鏈接,只是不知道爲何這麼鏈接的,另外還有一篇文章是講解關於這幅圖的理解的那個是對源碼進行解讀的,比較詳細node

vue源碼解讀,另外還有一篇文章也是大牛本身實現vue的簡易框架的,與這個得實現思路略微有所不一樣:vue簡易框架2c#

相關文章
相關標籤/搜索