濡備綍瀹炵幇涓€涓-Virtual DOM 鍙婃簮鐮佸垎鏋-- 榫欐仼0707

濡備綍瀹炵幇涓€涓?Virtual DOM 鍙婃簮鐮佸垎鏋?/p>

Virtual DOM绠楁硶

    web椤甸潰鏈変竴涓搴旂殑DOM鏍戯紝鍦ㄤ紶缁熷紑鍙戦〉闈㈡椂锛屾瘡娆¢〉闈㈤渶瑕佽鏇存柊鏃讹紝閮介渶瑕佹墜鍔ㄦ搷浣淒OM鏉ヨ繘琛屾洿鏂帮紝浣嗘槸鎴戜滑鐭ラ亾DOM鎿嶄綔瀵规€ц兘鏉ヨ鏄潪甯镐笉鍙嬪ソ鐨勶紝浼氬奖鍝嶉〉闈㈢殑閲嶆帓锛屼粠鑰屽奖鍝嶉〉闈㈢殑鎬ц兘銆傚洜姝ゅ湪React鍜孷UE2.0+寮曞叆浜嗚櫄鎷烡OM鐨勬蹇碉紝浠栦滑鐨勫師鐞嗘槸锛氭妸鐪熷疄鐨凞OM鏍戣浆鎹㈡垚javascript瀵硅薄鏍戯紝涔熷氨鏄櫄鎷烡OM锛屾瘡娆℃暟鎹渶瑕佽鏇存柊鐨勬椂鍊欙紝瀹冧細鐢熸垚涓€涓柊鐨勮櫄鎷烡OM锛屽苟涓斿拰涓婃鐢熸垚鐨勮櫄鎷烡OM杩涜瀵规瘮锛屽鍙戠敓鍙樺寲鐨勬暟鎹仛鎵归噺鏇存柊銆?--锛堝洜涓烘搷浣淛S瀵硅薄浼氭洿蹇紝鏇寸畝鍗曪紝姣旀搷浣淒OM鏉ヨ)銆?br /> 鎴戜滑鐭ラ亾web椤甸潰鏄敱涓€涓釜HTML鍏冪礌宓屽缁勫悎鑰屾垚鐨勶紝褰撴垜浠娇鐢╦avascript鏉ユ弿杩拌繖浜涘厓绱犵殑鏃跺€欙紝杩欎簺鍏冪礌鍙互绠€鍗曠殑琚〃绀烘垚绾补鐨凧SON瀵硅薄銆?/p>

姣斿濡備笅HTML浠g爜锛?/p>

<div id="container" class="container">
   <ul id="list">
     <li class="item">111li>
     <li class="item">222li>
     <li class="item">333li>
   ul>
   <button class="btn btn-blue"><em>鎻愪氦em>button>
div>

涓婇潰鏄湡瀹炵殑DOM鏍戠粨鏋勶紝鎴戜滑鍙互浣跨敤javascript涓殑json瀵硅薄鏉ヨ〃绀虹殑璇濓紝鍙樻垚濡備笅锛?/p>

var element = {
      tagName: 'div',
      props: {   // DOM鐨勫睘鎬?/span>
        id: 'container',
        class: 'container'
      },
      children: [
        {
          tagName: 'ul',
          props: {
            id: 'list'
          },
          children: [
            {tagName: 'li', props: {class: 'item'}, children: ['111']},
            {tagName: 'li', props: {class: 'item'}, children: ['222']},
            {tagName: 'li', props: {class: 'item'}, children: ['333']}
          ]
        },
        {
          tagName: 'button',
          props: {
            class: 'btn btn-blue'
          },
          children: [
            {
              tagName: 'em',
              children: ['鎻愪氦']
            }
          ]
        }
      ]
   };

鍥犳鎴戜滑鍙互浣跨敤javascript瀵硅薄琛ㄧずDOM鐨勪俊鎭拰缁撴瀯锛屽綋鐘舵€佸彉鏇寸殑鏃跺€欙紝閲嶆柊娓叉煋杩欎釜javascript瀵硅薄鐨勭粨鏋勶紝鐒跺悗鍙互浣跨敤鏂版覆鏌撶殑瀵硅薄鏍戝幓鍜屾棫鐨勬爲鍘诲姣旓紝璁板綍涓ら鏍戠殑宸紓锛屼袱棰楁爲鐨勫樊寮傚氨鏄垜浠渶瑕佸椤甸潰鐪熸鐨凞OM鎿嶄綔锛岀劧鍚庢妸浠栦滑搴旂敤鍒扮湡姝g殑DOM鏍戜笂锛岄〉闈㈠氨寰楀埌鏇存柊銆傝鍥剧殑鏁翠釜缁撴瀯纭疄鍏ㄦ覆鏌撲簡锛屼絾鏄渶鍚庢搷浣淒OM鐨勬椂鍊欙紝鍙彉鏇翠笉鍚岀殑鍦版柟銆?br />鍥犳鎴戜滑鍙互鎬荤粨涓€涓?strong> Virtual DOM绠楁硶锛?/strong>
1. 鐢╦avascript瀵硅薄缁撴瀯鏉ヨ〃绀篋OM鏍戠殑缁撴瀯锛岀劧鍚庣敤杩欎釜鏍戞瀯寤轰竴涓湡姝g殑DOM鏍戯紝鎻掑叆鍒版枃妗d腑銆?br />2. 褰撶姸鎬佸彉鏇寸殑鏃跺€欙紝閲嶆柊鏋勯€犱竴棰楁柊鐨勫璞℃爲锛岀劧鍚庝娇鐢ㄦ柊鐨勫璞℃爲涓庢棫鐨勫璞℃爲杩涜瀵规瘮锛岃褰曚袱棰楁爲鐨勫樊寮傘€?br />3. 鎶婅褰曚笅鏉ョ殑宸紓鐢ㄥ埌姝ラ1鎵€鏋勫缓鐨勭湡姝g殑DOM鏍戜笂銆傝鍥惧氨鏇存柊浜嗐€?/p>

绠楁硶瀹炵幇锛?/strong>
2-1 浣跨敤javascript瀵硅薄妯℃嫙DOM鏍戙€?/strong>
浣跨敤javascript鏉ヨ〃绀轰竴涓狣OM鑺傜偣锛屾湁濡備笂JSON鐨勬暟鎹紝鎴戜滑鍙渶瑕佽褰曞畠鐨勮妭鐐圭被鍨嬶紝灞炴€у拰瀛愯妭鐐瑰嵆鍙€?/p>

element.js 浠g爜濡備笅锛?/p>

function Element(tagName, props, children) {
  this.tagName = tagName;
  this.props = props;
  this.children = children;
}
Element.prototype.render = function() {
  var el = document.createElement(this.tagName);
  var props = this.props;
  // 閬嶅巻瀛愯妭鐐癸紝渚濇璁剧疆瀛愯妭鐐圭殑灞炴€?/span>
  for (var propName in props) {
    var propValue = props[propName];
    el.setAttribute(propName, propValue);
  }
  // 淇濆瓨瀛愯妭鐐?/span>
  var childrens = this.children || [];
  // 閬嶅巻瀛愯妭鐐癸紝浣跨敤閫掑綊鐨勬柟寮?娓叉煋
  childrens.forEach(function(child) {
    var childEl = (child instanceof Element) ? child.render() // 濡傛灉瀛愯妭鐐逛篃鏄櫄鎷烡OM锛岄€掑綊鏋勫缓DOM鑺傜偣
      : document.createTextNode(child);    // 濡傛灉鏄瓧绗︿覆鐨勮瘽锛屽彧鏋勫缓鏂囨湰鑺傜偣
    el.appendChild(childEl);
  });
  return el;
};
module.exports = function(tagName, props, children) {
  return new Element(tagName, props, children);
}

鍏ュ彛index.js浠g爜濡備笅锛?/p>

var el = require('./element');

var element = el('div', {id: 'container', class: 'container'}, [
  el('ul', {id: 'list'},[
    el('li', {class: 'item'}, ['111']),
    el('li', {class: 'item'}, ['222']),
    el('li', {class: 'item'}, ['333']),
  ]),
  el('button', {class: 'btn btn-blue'}, [
    el('em', {class: ''}, ['鎻愪氦'])
  ])
]);

var elemRoot = element.render();
document.body.appendChild(elemRoot);

鎵撳紑椤甸潰鍗冲彲鐪嬪埌鏁堟灉銆?/p>

2-2 姣旇緝涓ら铏氭嫙DOM鏍戠殑宸紓鍙婂樊寮傜殑鍦版柟杩涜dom鎿嶄綔
濡備綍瀹炵幇涓€涓-Virtual DOM 鍙婃簮鐮佸垎鏋-- 榫欐仼0707_第1张图片
涓婇潰鐨刣iv鍙細鍜屽悓涓€灞傜骇鐨刣iv瀵规瘮锛岀浜屽眰绾х殑鍙細鍜岀浜屽眰绾х殑瀵规瘮锛岃繖鏍风殑绠楁硶鐨勫鏉傚害鍙互杈惧埌O(n).
浣嗘槸鍦ㄥ疄闄呬唬鐮佷腑锛屼細瀵规柊鏃т袱棰楁爲杩涜涓€涓繁搴︿紭鍏堢殑閬嶅巻锛屽洜姝ゆ瘡涓妭鐐归兘浼氭湁涓€涓爣璁般€傚涓嬪浘鎵€绀猴細
濡備綍瀹炵幇涓€涓-Virtual DOM 鍙婃簮鐮佸垎鏋-- 榫欐仼0707_第2张图片
鍦ㄩ亶鍘嗙殑杩囩▼涓紝姣忔閬嶅巻鍒颁竴涓妭鐐瑰氨鎶婅鑺傜偣鍜屾柊鐨勬爲杩涜瀵规瘮锛屽鏋滄湁宸紓鐨勮瘽灏辫褰曞埌涓€涓璞¢噷闈€?/p>

鐜板湪鎴戜滑鏉ョ湅涓嬫垜鐨勭洰褰曚笅 鏈夊摢浜涙枃浠讹紱鐒跺悗鍒嗗埆瀵规瘡涓枃浠朵唬鐮佽繘琛岃В璇伙紝鐪嬬湅鍋氫簡鍝簺浜嬫儏锛屾棫鐨勮櫄鎷焏om鍜屾柊鐨勮櫄鎷焏om鏄浣曟瘮杈冪殑锛屼笖鏄浣曟洿鏂伴〉闈㈢殑 濡備笅鐩綍锛?br />鐩綍缁撴瀯濡備笅锛?/strong>

vdom  ---- 宸ョ▼鍚?
|   | ---- index.html  html椤甸潰
|   | ---- element.js  瀹炰緥鍖栧厓绱犵粍鎴恓son鏁版嵁 涓?鎻愪緵render鏂规硶 娓叉煋椤甸潰
|   | ---- util.js     鎻愪緵涓€浜涘叕鐢ㄧ殑鏂规硶
|   | ---- diff.js     姣旇緝鏂版棫鑺傜偣鏁版嵁 濡傛灉鏈夊樊寮備繚瀛樺埌涓€涓璞¢噷闈㈠幓
|   | ---- patch.js    瀵瑰綋鍓嶅樊寮傜殑鑺傜偣鏁版嵁 杩涜DOM鎿嶄綔
|   | ---- index.js    椤甸潰浠g爜鍒濆鍖栬皟鐢?/pre>

棣栧厛鏄?index.js鏂囦欢 椤甸潰娓叉煋瀹屾垚鍚?鍙樻垚濡備笅html缁撴瀯 

<div id="container">
  <h1 style="color: red;">simple virtal domh1>
  <p>the count is :1p>
  <ul>
    <li>Item #0li>
  ul>
div>

鍋囧鍙戠敓鏀瑰彉鍚庯紝鍙樻垚濡備笅缁撴瀯 

<div id="container">
  <h1 style="color: blue;">simple virtal domh1>
  <p>the count is :2p>
  <ul>
    <li>Item #0li>
    <li>Item #1li>
  ul>
div>

鍙互鐪嬪埌 鏂版棫鑺傜偣椤甸潰鏁版嵁鐨勬敼鍙橈紝h1鏍囩浠庡睘鎬?棰滆壊浠庣孩鑹?鍙樹负钃濊壊锛宲鏍囩鐨勬枃鏈彂鐢熸敼鍙橈紝ul鏂板浜嗕竴椤瑰厓绱爈i銆?br />鍩烘湰鐨勫師鐞嗘槸锛氬厛娓叉煋鍑洪〉闈㈡暟鎹嚭鏉ワ紝鐢熸垚绗竴涓ā鏉块〉闈紝鐒跺悗浣跨敤瀹氭椂鍣ㄤ細鐢熸垚涓€涓柊鐨勯〉闈㈡暟鎹嚭鏉ワ紝瀵规柊鏃т袱棰楁爲杩涜涓€涓繁搴︿紭鍏堢殑閬嶅巻锛屽洜姝ゆ瘡涓妭鐐归兘浼氭湁涓€涓爣璁般€?br />鐒跺悗璋冪敤diff鏂规硶瀵规瘮瀵硅薄鏂版棫鑺傜偣閬嶅巻杩涜瀵规瘮锛屾壘鍑轰袱鑰呯殑涓嶅悓鐨勫湴鏂瑰瓨鍏ュ埌涓€涓璞¢噷闈㈠幓锛屾渶鍚庨€氳繃patch.js鎵惧嚭瀵硅薄涓嶅悓鐨勫湴鏂癸紝鍒嗗埆杩涜dom鎿嶄綔銆?/p>

index.js浠g爜濡備笅锛?/p>

var el = require('./element');
var diff = require('./diff');
var patch = require('./patch');

var count = 0;
function renderTree() {
  count++;
  var items = [];
  var color = (count % 2 === 0) ? 'blue' : 'red';
  for (var i = 0; i < count; i++) {
    items.push(el('li', ['Item #' + i]));
  }
  return el('div', {'id': 'container'}, [
    el('h1', {style: 'color: ' + color}, ['simple virtal dom']),
    el('p', ['the count is :' + count]),
    el('ul', items)
  ]);
}

var tree = renderTree()
var root = tree.render()
document.body.appendChild(root)
setInterval(function () {
  var newTree = renderTree()
  var patches = diff(tree, newTree)
  console.log(patches)
  patch(root, patches)
  tree = newTree
}, 1000);

鎵ц var tree = renderTree()鏂规硶鍚庯紝浼氳皟鐢╡lement.js锛?br />1. 渚濇閬嶅巻瀛愯妭鐐?浠庡唴鍒板璋冪敤)渚濇涓?li, h1, p, ul, li鍜宧1鍜宲鏈変竴涓枃鏈瓙鑺傜偣锛屽洜姝ら亶鍘嗗畬鎴愬悗锛宑ount灏辩瓑浜?锛?br />浣嗘槸閬嶅巻ul鐨勬椂鍊欙紝鍥犱负鏈変竴涓瓙鑺傜偣li锛屽洜姝?count += 1; 鎵€浠ヨ皟鐢ㄥ畬鎴愬悗锛寀l鐨刢ount绛変簬2. 鍥犳浼氬姣忎釜element灞炴€ф坊鍔燾ount灞炴€с€傚浜庢渶澶栧眰鐨刢ontainer瀹瑰櫒灏辨槸瀵规瘡涓瓙鑺傜偣鐨勪緷娆″鍔狅紝h1瀛愯妭鐐归粯璁や负1锛屽惊鐜畬鎴愬悗 +1锛涘洜姝ゅ彉涓?锛?p鑺傜偣榛樿涓?锛屽惊鐜畬鎴愬悗 +1锛屽洜姝や篃鍙樹负2锛寀l涓?锛屽惊鐜畬鎴愬悗 +1锛屽洜姝ゅ彉涓?锛屽洜姝ontainer鑺傜偣鐨刢ount=2+2+3 = 7;

element.js閮ㄥ垎浠g爜濡備笅锛?/p>

function Element(tagName, props, children) {
  if (!(this instanceof Element)) {
    // 鍒ゆ柇瀛愯妭鐐?children 鏄惁涓?undefined
    if (!utils.isArray(children) && children !== null) {
      children = utils.slice(arguments, 2).filter(utils.truthy);
    }
    return new Element(tagName, props, children);
  }
  // 濡傛灉娌℃湁灞炴€х殑璇濓紝绗簩涓弬鏁版槸涓€涓暟缁勶紝璇存槑绗簩涓弬鏁颁紶鐨勬槸瀛愯妭鐐?/span>
  if (utils.isArray(props)) {
    children = props;
    props = {};
  }
  this.tagName = tagName;
  this.props = props || {};
  this.children = children || [];
  // 淇濆瓨key閿?濡傛灉鏈夊睘鎬?淇濆瓨key锛屽惁鍒欒繑鍥瀠ndefined
  this.key = props ? props.key : void 0;
  var count = 0;
  
  utils.each(this.children, function(child, i) {
    // 濡傛灉鏄厓绱犵殑瀹炲垪鐨勮瘽
    if (child instanceof Element) {
      count += child.count;
    } else {
      // 濡傛灉鏄枃鏈妭鐐圭殑璇濓紝鐩存帴璧嬪€?/span>
      children[i] = '' + child;
    }
    count++;
  });
  this.count = count;
}

oldTree鏁版嵁鏈€缁堝彉鎴愬涓嬶細

var oldTree = {
  tagName: 'div',
  key: undefined,
  count: 7,
  props: {id: 'container'},
  children: [
    {
      tagName: 'h1',
      key: undefined
      count: 1
      props: {style: 'colod: red'},
      children: ['simple virtal dom']
    },
    {
      tagName: 'p',
      key: undefined
      count: 1
      props: {},
      children: ['the count is :1']
    },
    {
      tagName: 'ul',
      key: undefined
      count: 2
      props: {},
      children: [
        {
          tagName: 'li',
          key: undefined,
          count: 1,
          props: {},
          children: ['Item #0']
        }
      ]
    },
  ]
};

瀹氭椂鍣?鎵ц var newTree = renderTree()鍚庯紝璋冪敤鏂规硶姝ラ杩樻槸鍜岀涓€姝ヤ竴鏍凤細
2. 渚濇閬嶅巻瀛愯妭鐐?浠庡唴鍒板璋冪敤)渚濇涓?li, h1, p, ul, li鍜宧1鍜宲鏈変竴涓枃鏈瓙鑺傜偣锛屽洜姝ら亶鍘嗗畬鎴愬悗锛宑ount灏辩瓑浜?锛屽洜涓烘湁2涓瓙鍏冪礌li锛宑ount閮戒负1锛屽洜姝l姣忔閬嶅巻渚濇鍦ㄥ師鏉ョ殑鍩虹涓婂姞1锛屽洜姝ら亶鍘嗗畬鎴愮涓€涓猯i鏃跺€欙紝ul涓殑count涓?锛屽綋閬嶅巻瀹屾垚绗簩涓猯i鐨勬椂鍊欙紝ul鐨刢ount灏变负4浜嗐€傚洜姝l涓殑count涓?. 瀵逛簬鏈€澶栧眰鐨刢ontainer瀹瑰櫒灏辨槸瀵规瘡涓瓙鍏冪礌渚濇澧炲姞銆?br />鎵€浠?container鑺傜偣鐨刢ount = 2 + 2 + 5 = 9;

newTree鏁版嵁鏈€缁堝彉鎴愬涓嬫暟鎹細

var newTree = {
  tagName: 'div',
  key: undefined,
  count: 9,
  props: {id: 'container'},
  children: [
    {
      tagName: 'h1',
      key: undefined
      count: 1
      props: {style: 'colod: red'},
      children: ['simple virtal dom']
    },
    {
      tagName: 'p',
      key: undefined
      count: 1
      props: {},
      children: ['the count is :1']
    },
    {
      tagName: 'ul',
      key: undefined
      count: 4
      props: {},
      children: [
        {
          tagName: 'li',
          key: undefined,
          count: 1,
          props: {},
          children: ['Item #0']
        },
        {
          tagName: 'li',
          key: undefined,
          count: 1,
          props: {},
          children: ['Item #1']
        }
      ]
    },
  ]
}

var patches = diff(oldTree, newTree);

璋冪敤diff鏂规硶鍙互姣旇緝鏂版棫涓ゆ5鏍戣妭鐐圭殑鏁版嵁锛屾妸涓ら鏍戠殑涓嶅悓鑺傜偣鎵惧嚭鏉ャ€?娉ㄦ剰锛屾煡鐪媎iff瀵规瘮鏁版嵁鐨勬柟娉曪紝鎵惧埌涓嶅悓鐨勮妭鐐癸紝鍙互鏌ョ湅杩欑瘒鏂囩珷diff绠楁硶)濡備笅璋冪敤浠g爜锛?/p>

function diff (oldTree, newTree) {
  var index = 0;
  var patches = {};
  deepWalk(oldTree, newTree, index, patches);
  return patches;
}

鎵цdeepWalk濡備笅浠g爜锛?/p>

function deepWalk(oldNode, newNode, index, patches) {
  var currentPatch = [];
  // 鑺傜偣琚垹闄ゆ帀
  if (newNode === null) {
    // 鐪熸鐨凞OM鑺傜偣鏃?灏嗗垹闄ゆ墽琛岄噸鏂版帓搴?鎵€浠ヤ笉闇€瑕佸仛浠讳綍浜?/span>
  } else if(utils.isString(oldNode) && utils.isString(newNode)) {
    // 鏇挎崲鏂囨湰鑺傜偣
    if (newNode !== oldNode) {
      currentPatch.push({type: patch.TEXT, content: newNode});
    }
  } else if(oldNode.tagName === newNode.tagName && oldNode.key === newNode.key) {
    // 鐩稿悓鐨勮妭鐐癸紝浣嗘槸鏂版棫鑺傜偣鐨勫睘鎬т笉鍚岀殑鎯呭喌涓?姣旇緝灞炴€?/span>
    // diff props
    var propsPatches = diffProps(oldNode, newNode);
    if (propsPatches) {
      currentPatch.push({type: patch.PROPS, props: propsPatches});
    }
    // 涓嶅悓鐨勫瓙鑺傜偣 
    if (!isIgnoreChildren(newNode)) {
      diffChildren(
        oldNode.children,
        newNode.children,
        index,
        patches,
        currentPatch
      )
    }
  } else {
    // 涓嶅悓鐨勮妭鐐癸紝閭d箞鏂拌妭鐐规浛鎹㈡棫鑺傜偣
    currentPatch.push({type: patch.REPLACE, node: newNode});
  }
  if (currentPatch.length) {
    patches[index] = currentPatch;
  }
}

1. 鍒ゆ柇鏂拌妭鐐规槸鍚︿负null锛屽鏋滀负null锛岃鏄庤妭鐐硅鍒犻櫎鎺夈€?br />2. 鍒ゆ柇鏂版棫鑺傜偣鏄惁涓哄瓧绗︿覆锛屽鏋滀负瀛楃涓茶鏄庢槸鏂囨湰鑺傜偣锛屽苟涓旀柊鏃т袱涓枃鏈妭鐐逛笉鍚岀殑璇濓紝瀛樺叆鏁扮粍閲岄潰鍘伙紝濡備笅浠g爜锛?/p>

   currentPatch.push({type: patch.TEXT, content: newNode});
   patch.TEXT 涓?patch.js閲岄潰鐨?TEXT = 3锛沜ontent灞炴€т负鏂拌妭鐐广€?/p>

3. 濡傛灉鏂版棫tagName鐩稿悓鐨勮瘽锛屽苟涓旀柊鏃ц妭鐐圭殑key鐩稿悓鐨勮瘽锛岀户缁瘮杈冩柊鏃ц妭鐐圭殑灞炴€э紝濡備笅浠g爜锛?/p>

var propsPatches = diffProps(oldNode, newNode);

diffProps鏂规硶鐨勪唬鐮佸涓嬶細

function diffProps(oldNode, newNode) {
      var count = 0;
      var oldProps = oldNode.props;
      var newProps = newNode.props;
      var key,
        value;
      var propsPatches = {};
      // 鎵惧嚭涓嶅悓鐨勫睘鎬у€?/span>
      for (key in oldProps) {
        value = oldProps[key];
        if (newProps[key] !== value) {
          count++;
          propsPatches[key] = newProps[key];
        }
      }
      // 鎵惧嚭鏂板灞炴€?/span>
      for (key in newProps) {
        value = newProps[key];
        if (!oldProps.hasOwnProperty(key)) {
          count++;
          propsPatches[key] = newProps[key];
        }
      }
      // 濡傛灉鎵€鏈夌殑灞炴€ч兘鏄浉鍚岀殑璇?/span>
      if (count === 0) {
        return null;
      }
      return propsPatches;
   }

diffProps浠g爜瑙f瀽濡備笅锛?/p>

for (key in oldProps) {
   value = oldProps[key];
   if (newProps[key] !== value) {
      count++;
      propsPatches[key] = newProps[key];
   }
}

濡備笂浠g爜鏄?鍒ゆ柇鏃ц妭鐐圭殑灞炴€у€兼槸鍚﹀湪鏂拌妭鐐逛腑鎵惧埌锛屽鏋滄壘涓嶅埌鐨勮瘽锛宑ount++; 鎶婃柊鑺傜偣鐨勫睘鎬у€艰祴鍊肩粰 propsPatches 瀛樺偍璧锋潵銆?/p>

for (key in newProps) {
   value = newProps[key];
   if (!oldProps.hasOwnProperty(key)) {
      count++;
      propsPatches[key] = newProps[key];
   }
}

濡備笂浠g爜鏄?鍒ゆ柇鏂拌妭鐐圭殑灞炴€ф槸鍚﹁兘鍦ㄦ棫鑺傜偣涓壘鍒帮紝濡傛灉鎵句笉鍒扮殑璇濓紝count++; 鎶婃柊鑺傜偣鐨勫睘鎬у€艰祴鍊肩粰 propsPatches 瀛樺偍璧锋潵銆?/p>

if (count === 0) {
   return null;
}
return propsPatches;

鏈€鍚庡鏋渃ount 绛変簬0鐨勮瘽锛岃鏄庢墍鏈夊睘鎬ч兘鏄浉鍚岀殑璇濓紝鎵€浠ヤ笉闇€瑕佸仛浠讳綍鍙樺寲銆傚惁鍒欑殑璇濓紝杩斿洖鏂板鐨勫睘鎬с€?/p>

濡傛灉鏈?propsPatches 鐨勮瘽锛屾墽琛屽涓嬩唬鐮侊細

if (propsPatches) {
   currentPatch.push({type: patch.PROPS, props: propsPatches});
}

鍥犳currentPatch鏁扮粍閲岄潰涔熸湁瀵瑰簲鐨勬洿鏂扮殑灞炴€э紝props灏辨槸闇€瑕佹洿鏂扮殑灞炴€у璞°€?/p>

缁х画浠g爜锛?/p>

// 涓嶅悓鐨勫瓙鑺傜偣 
if (!isIgnoreChildren(newNode)) {
   diffChildren(
     oldNode.children,
     newNode.children,
     index,
     patches,
     currentPatch
   )
}
function isIgnoreChildren(node) {
  return (node.props && node.props.hasOwnProperty('ignore'));
}

濡備笂浠g爜鍒ゆ柇瀛愯妭鐐规槸鍚︾浉鍚岋紝diffChildren浠g爜濡備笅锛?/p>

function diffChildren(oldChildren, newChildren, index, patches, currentPatch) {
    var diffs = listDiff(oldChildren, newChildren, 'key');
    newChildren = diffs.children;

    if (diffs.moves.length) {
      var recorderPatch = {type: patch.REORDER, moves: diffs.moves};
      currentPatch.push(recorderPatch);
    }

    var leftNode = null;
    var currentNodeIndex = index;
    utils.each(oldChildren, function(child, i) {
      var newChild = newChildren[i];
      currentNodeIndex = (leftNode && leftNode.count) ? currentNodeIndex + leftNode.count + 1 : currentNodeIndex + 1;
      // 閫掑綊
      deepWalk(child, newChild, currentNodeIndex, patches);
      leftNode = child;
    });
  }

濡備笂浠g爜锛歷ar diffs = listDiff(oldChildren, newChildren, 'key'); 鏂版棫鑺傜偣鎸夌収key鏉ユ瘮杈冿紝鐩墠key涓簎ndefined锛屾墍浠iffs 涓哄涓嬶細

diffs = {
    moves: [],
    children: [
      {
        tagName: 'h1',
        key: undefined
        count: 1
        props: {style: 'colod: blue'},
        children: ['simple virtal dom']
      },
      {
        tagName: 'p',
        key: undefined
        count: 1
        props: {},
        children: ['the count is :2']
      },
      {
        tagName: 'ul',
        key: undefined
        count: 4
        props: {},
        children: [
          {
            tagName: 'li',
            key: undefined,
            count: 1,
            props: {},
            children: ['Item #0']
          },
          {
            tagName: 'li',
            key: undefined,
            count: 1,
            props: {},
            children: ['Item #1']
          }
        ]
      }
    ]
  };

newChildren = diffs.children;
oldChildren鏁版嵁濡備笅锛?/p>

oldChildren = [
    {
      tagName: 'h1',
      key: undefined
      count: 1
      props: {style: 'colod: red'},
      children: ['simple virtal dom']
    },
    {
      tagName: 'p',
      key: undefined
      count: 1
      props: {},
      children: ['the count is :1']
    },
    {
      tagName: 'ul',
      key: undefined
      count: 2
      props: {},
      children: [
        {
          tagName: 'li',
          key: undefined,
          count: 1,
          props: {},
          children: ['Item #0']
        }
      ]
    }
  ];

鎺ョ潃灏辨槸閬嶅巻 oldChildren, 绗竴娆¢亶鍘嗘椂 leftNode 涓簄ull锛屽洜姝?currentNodeIndex = currentNodeIndex + 1 = 0 + 1 = 1; 涓嶆槸绗竴娆¢亶鍘嗭紝閭d箞leftNode閮戒负涓婁竴娆¢亶鍘嗙殑瀛愯妭鐐癸紝鍥犳涓嶆槸绗竴娆¢亶鍘嗙殑璇濓紝閭d箞 currentNodeIndex = currentNodeIndex + leftNode.count + 1; 
鐒跺悗閫掑綊璋冪敤 deepWalk(child, newChild, currentNodeIndex, patches); 鏂规硶锛屾帴鐫€鎶奵hild璧嬪€肩粰leftNode锛宭eftNode = child;

鎵€浠ヤ竴鐩撮€掑綊閬嶅巻锛屾渶缁堟妸涓嶇浉鍚岀殑鑺傜偣 浼氬瓨鍌ㄥ埌 currentPatch 鏁扮粍鍐呫€傛渶鍚庢墽琛?

if (currentPatch.length) {
   patches[index] = currentPatch;
}

鎶婂搴旂殑currentPatch 瀛樺偍鍒?patches瀵硅薄鍐呬腑鐨勫搴旈」锛屾渶鍚庡氨杩斿洖 patches瀵硅薄銆?/p>

4. 杩斿洖鍒癷ndex.js 浠g爜鍐咃紝鎶婁袱棰椾笉鐩稿悓鐨勬爲鑺傜偣鐨勬彁鍙栧嚭鏉ュ悗锛岄渶瑕佽皟鐢╬atch.js鏂规硶浼犺繘锛涙妸涓嶇浉鍚岀殑鑺傜偣搴旂敤鍒扮湡姝g殑DOM涓?
涓嶇浉鍚岀殑鑺傜偣 patches鏁版嵁濡備笅锛?/p>

patches = {
    1: [{type: 2, props: {style: 'color: blue'}}],
    4: [{type: 3, content: 'the count is :2'}],
    5: [
          { 
              type: 1, 
              moves: [
                { index: 1, 
                   item: {
                      tagName: 'li',
                      props: {},
                      count: 1,
                      key: undefined,
                      children: ['Item #1']
                    }
                }
              ]
           }
        ]
    }

濡備笅浠g爜璋冪敤锛?br /> patch(root, patches);
鎵цpatch鏂规硶锛屼唬鐮佸涓嬶細

function patch(node, patches) {
  var walker = {index: 0};
  deepWalk(node, walker, patches);
}

deepWalk 浠g爜濡備笅锛?/p>

function deepWalk(node, walker, patches) {
   var currentPatches = patches[walker.index];
      // node.childNodes 杩斿洖鎸囧畾鍏冪礌鐨勫瓙鍏冪礌闆嗗悎锛屽寘鎷琀TML鑺傜偣锛屾墍鏈夊睘鎬э紝鏂囨湰鑺傜偣銆?/span>
   var len = node.childNodes ? node.childNodes.length : 0;
   for (var i = 0; i < len; i++) {
      var child = node.childNodes[i];
      walker.index++;
      // 娣卞害澶嶅埗 閫掑綊閬嶅巻
      deepWalk(child, walker, patches);
   }
   if (currentPatches) {
      applyPatches(node, currentPatches);
   }
}

1. 棣栨璋冪敤patch鐨勬柟娉曪紝root灏辨槸container鐨勮妭鐐癸紝鍥犳璋冪敤deepWalk鏂规硶锛屽洜姝?var currentPatches = patches[0] = undefined,
var len = node.childNodes ? node.childNodes.length : 0; 鍥犳 len = 3; 寰堟槑鏄捐瀛愯妭鐐圭殑闀垮害涓?锛屽洜涓哄瓙鑺傜偣鏈?h1, p, 鍜寀l鍏冪礌锛?/p>


2. 鐒跺悗杩涜for寰幆锛岃幏鍙栬鐖惰妭鐐圭殑瀛愯妭鐐癸紝鍥犳绗竴涓瓙鑺傜偣涓?h1 鍏冪礌锛寃alker.index++; 鍥犳walker.index = 1; 鍐嶈繘琛岄€掑綊 deepWalk(child, walker, patches); 姝ゆ椂瀛愯妭鐐逛负h1, walker.index涓?锛?鍥犳鑾峰彇 currentPatches = patches[1]; 鑾峰彇鍊硷紝鍐嶈幏鍙?h1鐨勫瓙鑺傜偣鐨勯暱搴︼紝len = 1; 鐒跺悗鍐峟or寰幆锛岃幏鍙朿hild涓烘枃鏈妭鐐癸紝姝ゆ椂 walker.index++; 鎵€浠ユ鏃秝alker.index 涓?, 鍦ㄨ皟鐢╠eepwalk鏂规硶閫掑綊锛屽洜姝ゅ啀缁х画鑾峰彇 currentPatches = patches[2]; 鍊间负undefined锛屽啀鑾峰彇len = 0; 鍥犱负鏂囨湰鑺傜偣涔堟湁瀛愯妭鐐癸紝鎵€浠or寰幆璺冲嚭锛屾墍浠ュ垽鏂璫urrentPatches鏄惁鏈夊€硷紝鍥犱负姝ゆ椂 currentPatches 涓簎ndefined锛屾墍浠ラ€掑綊缁撴潫锛屽啀杩斿洖鍒?h1鍏冪礌涓婃潵锛屾墍浠urrentPatches = patches[1]; 鎵€浠ユ湁鍊硷紝鎵€浠ヨ皟鐢?applyPatches()鏂规硶鏉ユ洿鏂癲om鍏冪礌銆?/p>


3. 缁х画寰幆 i, 姝ゆ椂i = 1锛?鑾峰彇瀛愯妭鐐?child = p鍏冪礌锛寃alker.index++锛屾鏃秝alker.index = 3, 缁х画璋冪敤 deepWalk鏂规硶锛岃幏鍙?var currentPatches = patches[walker.index] = patches[3]鐨勫€硷紝var len = 1; 鍥犱负p鍏冪礌涓嬫湁涓€涓瓙鑺傜偣(鏂囨湰鑺傜偣)锛屽啀杩沠or寰幆锛屾鏃?walker.index++; 鍥犳walker.index = 4; child姝ゆ椂涓烘枃鏈妭鐐癸紝鍦ㄨ皟鐢?deepwalk鏂规硶鐨勬椂鍊欙紝鍐嶈幏鍙杤ar currentPatches = patches[walker.index] = patches[4]; 鍐嶆墽琛宭en 浠g爜鐨勬椂鍊?len = 0;鍥犳璺冲嚭for寰幆锛屽垽鏂?currentPatches鏄惁鏈夊€硷紝鏈夊€肩殑璇濓紝鏇存柊瀵瑰簲鐨凞OM鍏冪礌銆?br />

4. 缁х画寰幆i = 2; 鑾峰彇瀛愯妭鐐?child = ul鍏冪礌锛寃alker.index++; 姝ゆ椂walker.index = 5; 鍦ㄨ皟鐢╠eepWalk鏂规硶閫掑綊锛屽洜姝ゅ啀鑾峰彇 var currentPatches = patches[walker.index] = patches[5]; 鐒跺悗len = 1, 鍥犱负ul鍏冪礌涓嬫湁涓€涓猯i鍏冪礌锛屽湪缁х画for寰幆閬嶅巻锛岃幏鍙栧瓙鑺傜偣li锛屾鏃秝alker.index++; walker.index = 6; 鍐嶉€掑綊璋冪敤deepwalk鏂规硶锛屽啀鑾峰彇var currentPatches = patches[walker.index] = patches[6]; len = 1; 鍥犱负li鐨勫厓绱犱笅鏈変竴涓枃鏈妭鐐癸紝鍐嶈繘琛宖or寰幆锛屾鏃禼hild涓烘枃鏈妭鐐癸紝walker.index++锛涙鏃秝alker.index = 7; 鍐嶆墽琛?deepwalk鏂规硶锛屽啀鑾峰彇 var currentPatches = patches[walker.index] = patches[7]; 杩欐椂鍊?len = 0浜嗭紝鍥犳璺冲嚭for寰幆锛屽垽鏂?褰撳墠鐨刢urrentPatches鏄惁鏈夊€硷紝娌℃湁锛屽氨璺冲嚭锛岀劧鍚庡啀杩斿洖ul鍏冪礌锛岃幏鍙栬鑷繁li鐨勬椂鍊欙紝walker.index 绛変簬5锛屽洜姝ar currentPatches = patches[walker.index] = patches[5]; 鐒跺悗鍒ゆ柇 currentPatches鏄惁鏈夊€硷紝鏈夊€煎氨杩涜鏇存柊DOM鍏冪礌銆?/p>

鏈€鍚庡氨鏄?applyPatches 鏂规硶鏇存柊dom鍏冪礌浜嗭紝濡備笅浠g爜锛?/p>

function applyPatches(node, currentPatches) {
  utils.each(currentPatches, function(currentPatch) {
    switch (currentPatch.type) {
      case REPLACE:
        var newNode = (typeof currentPatch.node === 'string') 
          ? document.createTextNode(currentPatch.node)
          : currentPatch.node.render();
        node.parentNode.replaceChild(newNode, node);
        break;
      case REORDER:
        reorderChildren(node, currentPatch.moves);
        break;
      case PROPS: 
        setProps(node, currentPatch.props);
        break;
      case TEXT:
        if(node.textContent) {
          node.textContent = currentPatch.content;
        } else {
          // ie bug
          node.nodeValue = currentPatch.content;
        }
        break;
      default:
        throw new Error('Unknow patch type' + currentPatch.type);
    }
  });
}

鍒ゆ柇绫诲瀷锛屾浛鎹㈠搴旂殑灞炴€у拰鑺傜偣銆?br />鏈€鍚庡氨鏄瀛愯妭鐐硅繘琛屾帓搴忕殑鎿嶄綔锛屼唬鐮佸涓嬶細

// 瀵瑰瓙鑺傜偣杩涜鎺掑簭
function reorderChildren(node, moves) {
  var staticNodeList = utils.toArray(node.childNodes);
  var maps = {};
  utils.each(staticNodeList, function(node) {
    // 濡傛灉鏄厓绱犺妭鐐?/span>
    if (node.nodeType === 1) {
      var key = node.getAttribute('key');
      if (key) {
        maps[key] = node;
      }
    }
  })
  utils.each(moves, function(move) {
    var index = move.index;
    if (move.type === 0) {
      // remove Item
      if (staticNodeList[index] === node.childNodes[index]) {
        node.removeChild(node.childNodes[index]);
      }
      staticNodeList.splice(index, 1);
    } else if(move.type === 1) {
      // insert item
      var insertNode = maps[move.item.key] 
        ? maps[move.item.key].cloneNode(true)
        : (typeof move.item === 'object') ? move.item.render() : document.createTextNode(move.item);
      staticNodeList.splice(index, 0, insertNode);
      node.insertBefore(insertNode, node.childNodes[index] || null);
    }
  });
}

閬嶅巻moves锛屽垽鏂璵oves.type 鏄瓑浜?杩樻槸绛変簬1锛岀瓑浜?鐨勮瘽鏄垹闄ゆ搷浣滐紝绛変簬1鐨勮瘽鏄柊澧炴搷浣溿€傛瘮濡傜幇鍦╩oves鍊煎彉鎴愬涓嬶細

moves = {
  index: 1,
  type: 1,
  item: {
    tagName: 'li',
    key: undefined,
    props: {},
    count: 1,
    children: ['#Item 1']
  }
};

node鑺傜偣 灏辨槸 'ul'鍏冪礌锛寁ar staticNodeList = utils.toArray(node.childNodes); 鎶妘l鐨勬棫瀛愯妭鐐筶i杞垚Array褰㈠紡锛岀敱浜庢病鏈夊睘鎬ey锛屾墍浠ョ洿鎺ヨ烦鍒颁笅闈㈤亶鍘嗕唬鐮佹潵锛岄亶鍘唌oves,鑾峰彇鏌愪竴椤圭殑绱㈠紩index锛屽垽鏂璵ove.type 绛変簬0 杩樻槸绛変簬1锛?鐩墠绛変簬1锛屾槸鏂板涓€椤癸紝浣嗘槸娌℃湁key锛屽洜姝よ皟鐢╩ove.item.render(); 娓叉煋瀹屽悗锛屽staticNodeList鏁扮粍閲岄潰鐨勬棫鑺傜偣鐨刲i椤逛粠绗簩椤瑰紑濮嬫彃鍏ヨ妭鐐筶i锛岀劧鍚庢墽琛宯ode.insertBefore(insertNode, node.childNodes[index] || null); node灏辨槸ul鐖惰妭鐐癸紝insertNode鑺傜偣鎻掑叆鍒?node.childNodes[1]鐨勫墠闈€傚洜姝ゆ妸鍦ㄧ浜岄」鐨勫墠闈㈡彃鍏ョ涓€椤广€?br />鏌ョ湅github涓婃簮鐮?/a>

你可能感兴趣的:(濡備綍瀹炵幇涓€涓-Virtual DOM 鍙婃簮鐮佸垎鏋-- 榫欐仼0707)