vue核心之vdom 參考自snabbdom源碼解讀

前些天看vue2流程的時候,vnode這一塊先跳過了,現在來補

http-server執行了下example/hero文件夾

(function e(t,n,r){ // t: 各模塊映射到對象{1: [function(){}, 當前模塊所依賴模塊對象{"../../h.js":2}],2: [...],...}
// n:緩存模塊的對象
// r:入口模塊的數組  
  function s(o,u){
    if(!n[o]){
      if(!t[o]){
        var a=typeof require=="function"&&require;
        if(!u&&a)
          return a(o,!0);
        if(i)
          return i(o,!0);
        var f=new Error("Cannot find module '"+o+"'");
        throw f.code="MODULE_NOT_FOUND",f
      }
      var l=n[o]={ // 定義了module,因爲瀏覽器中不支持Commonjs,沒有require也沒有module對象,所以要重新定義
        exports:{}
      };

      t[o][0].call( // 參數1是this指向,參數2,3,4...是各模塊中的(require,module,exports)
        l.exports,
        function(e){  // require定義,例如require('../../snabbdom.js'),e就是'../../snabbdom.js'字符串,後續就是取模塊並存至n對象
          var n=t[o][1][e];
          return s(n?n:e)
        },
        l, // 定義了module, 參考var l的定義
        l.exports, // 定義了exports, 取自module.exports
        e,
        t,
        n,
        r
      )
    }
    return n[o].exports
  }
  var i=typeof require=="function"&&require;
  // 依次調用入口模塊
  for(var o=0;o<r.length;o++)
    s(r[o]);
  return s
})({
  1:[
    function(require,module,exports){
      /* jshint esnext: true */
      'use strict';

      var snabbdom = require('../../snabbdom.js');
      // 🤓開始了!
      // 將arg[0]的modules綁至hook上,init返回的是🎄🎄🎄🎄function(oldVnode, vnode)
      var patch = snabbdom.init([require('../../modules/class'), require('../../modules/hero'), require('../../modules/style'), require('../../modules/eventlisteners')]);
      var h = require('../../h.js');

      var vnode;

      var data = {
        selected: undefined,
        movies: [
            { rank: 1, title: 'This is an', desc: 'Lorem ipsum...lus elit.' }, 
            { rank: 2, title: 'example of', desc: 'Consequuntur...wisi.' }
        ]
      };
      function render() {
        vnode = patch(vnode, view(data));
      }
      var detailView = function detailView(movie) {
        return h('div.page', { style: fadeInOutStyle }, [h('div.header',...)])
      }
      var overviewView = function overviewView(movies) {
        return h('div.page', { style: fadeInOutStyle }, [h('div.header', [h('div.header-content.overview', {...})])
      }
      var view = function view(data) {
        return h('div.page-container', [data.selected ? detailView(data.selected) : overviewView(data.movies)]);
      };
      // 省略code

      window.addEventListener('DOMContentLoaded', function () {
        var container = document.getElementById('container');
        // 往下扒拉snabbdom.init的返回值function🎄🎄🎄🎄,記得回來
        vnode = patch(container, view(data));
        render();
      });

    },
    {
      "../../h.js":2,
      "../../modules/class":4,
      "../../modules/eventlisteners":5,
      "../../modules/hero":6,
      "../../modules/style":7,
      "../../snabbdom.js":8
    }
  ],
  2:[
    function(require,module,exports){
      'use strict';
      var VNode = require('./vnode');
      // 省略code

    },
    {
      "./is":3,
      "./vnode":9
    }
  ],
  3:[
    function(require,module,exports){
      'use strict';
    
      module.exports = {
        array: Array.isArray,
        primitive: function primitive(s) {
          return typeof s === 'string' || typeof s === 'number';
        }
      };

    },
    {}
  ],
  4:[
    function(require,module,exports){
      'use strict';
      // 省略code
      module.exports = { create: updateClass, update: updateClass };

    },
    {}
  ],
  5:[
    function(require,module,exports){
      'use strict';

      var is = require('../is');
      // 省略code
      module.exports = { create: updateEventListeners, update: updateEventListeners };

    },
    {
      "../is":3
    }
  ],
  6:[
    function(require,module,exports){
      'use strict';
      // 省略code
      module.exports = { pre: pre, create: create, destroy: destroy, post: post };
    },
    {}
  ],
  7:[
    function(require,module,exports){
      'use strict';
      // 省略code
      module.exports = { create: updateStyle, update: updateStyle, destroy: applyDestroyStyle, remove: applyRemoveStyle };

    },
    {}
  ],
  8:[
    function(require,module,exports){
      // jshint newcap: false
      /* global require, module, document, Element */
      'use strict';

      // 省略code

      var hooks = ['create', 'update', 'remove', 'destroy', 'pre', 'post'];

      function init(modules) {
        var i,
            j,
            cbs = {};
        // 爲每個hook綁定相關module執行方法,存至cbs對象
        for (i = 0; i < hooks.length; ++i) {
          cbs[hooks[i]] = [];
          for (j = 0; j < modules.length; ++j) {
            if (modules[j][hooks[i]] !== undefined) cbs[hooks[i]].push(modules[j][hooks[i]]);
          }
        }
        // 省略code
        // 🎈🎈🎈🎈🎈🎈
        function patchVnode(oldVnode, vnode, insertedVnodeQueue) {
          var i, hook;
          if (isDef(i = vnode.data) && isDef(hook = i.hook) && isDef(i = hook.prepatch)) {
            i(oldVnode, vnode);
          }
          if (isDef(i = oldVnode.data) && isDef(i = i.vnode)) oldVnode = i;
          if (isDef(i = vnode.data) && isDef(i = i.vnode)) vnode = i;
          // 將oldVnode的elm 賦值 給新的vnode.elm
          var elm = vnode.elm = oldVnode.elm,
              oldCh = oldVnode.children,
              ch = vnode.children;
          // 新舊vnode相等情況(一般不出現),結束
          if (oldVnode === vnode) return;
          // 執行update相關hook(內部cbs.update和外部i.update)
          if (isDef(vnode.data)) {
            for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode);
            i = vnode.data.hook;
            if (isDef(i) && isDef(i = i.update)) i(oldVnode, vnode);
          }
          // 新vnode無text         【elm、text、children】
          if (isUndef(vnode.text)) {
            // 檢查children
            // 新舊都有children 
            if (isDef(oldCh) && isDef(ch)) {
              if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue);
            } else if (isDef(ch)) {
            // 有新children,無舊children => 新增新children
              addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue);
            } else if (isDef(oldCh)) {
            // 有舊children,無新children => 移除舊children
              removeVnodes(elm, oldCh, 0, oldCh.length - 1);
            }
          } else if (oldVnode.text !== vnode.text) {
            // text賦新值
            elm.textContent = vnode.text;
          }
          // 執行vnode.data.hook.postpatch
          if (isDef(hook) && isDef(i = hook.postpatch)) {
            i(oldVnode, vnode);
          }
        }

        return function (oldVnode, vnode) {
        // 🎄🎄🎄🎄
          var i;
          var insertedVnodeQueue = [];
          for (i = 0; i < cbs.pre.length; ++i) cbs.pre[i]();
          // 如果snabbdom.init(oldVnode, vnode)第一個參數oldVnode是真實DOM而非vnode實例
          if (oldVnode instanceof Element) {
            if (oldVnode.parentElement !== null) {
              createElm(vnode, insertedVnodeQueue);
              oldVnode.parentElement.replaceChild(vnode.elm, oldVnode);
            } else {
              // 爲oldVnode創建對應vnode實例
              oldVnode = emptyNodeAt(oldVnode);
              // 執行上面定義的方法patchVnode🎈🎈🎈🎈🎈🎈
              patchVnode(oldVnode, vnode, insertedVnodeQueue);
            }
          } else {
            patchVnode(oldVnode, vnode, insertedVnodeQueue);
          }
          for (i = 0; i < insertedVnodeQueue.length; ++i) {
            insertedVnodeQueue[i].data.hook.insert(insertedVnodeQueue[i]);
          }
          for (i = 0; i < cbs.post.length; ++i) cbs.post[i]();
          return vnode;
        };
      }

      module.exports = { init: init };

    },
    {
      "./is":3,
      "./vnode":9
    }
  ],
  9:[
    function(require,module,exports){
      "use strict";

      module.exports = function (sel, data, children, text, elm) {
        var key = data === undefined ? undefined : data.key;
        return { sel: sel, data: data, children: children,
          text: text, elm: elm, key: key };
      };

    },
    {}
  ]
},
{}, // 第二個參數是n,此處用一個空對象存儲
[1]
);

🎄🎄🎄🎄patch方法:

  •     執行patchVnode並返回vnode對象

🎈🎈🎈🎈🎈🎈patchVnode(oldVnode, vnode, insertedVnodeQueue)方法:

  •     從oldVnode中取出elm,並根據新vnode真實改變該dom,一路順帶執行了相關hook

 window.addEventListener('DOMContentLoaded',..)中,我想不明白爲什麼還要再render()一遍

答:事實證明可以不調用,該綁定的都綁定了,新舊JSON.stringify(vnode)之後值一樣

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章