涓�鏂囪娓呫�孷irtualDOM銆嶇殑鍚箟涓庡疄鐜�

鏈枃鏉ヨ嚜銆婁竴鏂囪娓匳irtualDOM鐨勫惈涔変笌瀹炵幇銆�锛屽鏋滆寰椾笉閿欙紝娆㈣繋缁�Star Github浠撳簱銆�

鎽樿

闅忕潃 React 鐨勫叴璧凤紝Virtual DOM 鐨勫師鐞嗗拰瀹炵幇涔熷紑濮嬪嚭鐜板湪鍚勫ぇ鍘傞潰璇曞拰绀惧尯鐨勬枃绔犱腑銆傚叾瀹炶繖绉嶅仛娉曟棭鍦� d3.js 涓氨鏈夊疄鐜帮紝鏄� react 鐢熸�佺殑蹇�熷缓绔嬭瀹冩寮忚繘鍏ヤ簡骞垮ぇ寮�鍙戣�呯殑瑙嗚銆�

鍦ㄦ寮忓紑濮嬪墠锛屾姏鍑哄嚑涓棶棰樻潵寮曞鎬濊矾锛岃繖浜涢棶棰樹篃浼氬湪涓嶅悓鐨勫皬鑺備腑锛岄�愭瑙e喅锛�

  • 馃锔� 鎬庝箞鐞嗚В VDom锛�
  • 馃锔� 濡備綍琛ㄧず VDom锛�
  • 馃锔� 濡備綍姣旇緝 VDom 鏍戯紝骞朵笖杩涜楂樻晥鏇存柊锛�

鈿狅笍 鏁寸悊鍚庣殑浠g爜鍜屾晥鏋滃浘鍧囧瓨鏀惧湪github.com/dongyuanxin銆�

濡備綍鐞嗚В VDom锛�

鏇剧粡锛屽墠绔父鍋氱殑浜嬫儏灏辨槸鏍规嵁鏁版嵁鐘舵�佺殑鏇存柊锛屾潵鏇存柊鐣岄潰瑙嗗浘銆傚ぇ瀹堕�愭笎鎰忚瘑鍒帮紝瀵逛簬澶嶆潅瑙嗗浘鐨勭晫闈紝棰戠箒鍦版洿鏂� DOM锛屼細閫犳垚鍥炴祦鎴栬�呴噸缁橈紝寮曞彂鎬ц兘涓嬮檷锛岄〉闈㈠崱椤裤��

鍥犳锛屾垜浠渶瑕佹柟娉曢伩鍏嶉绻佸湴鏇存柊 DOM 鏍�銆傛�濊矾涔熷緢绠�鍗曪紝鍗筹細瀵规瘮 DOM 鐨勫樊璺濓紝鍙洿鏂伴渶瑕侀儴鍒嗚妭鐐癸紝鑰屼笉鏄洿鏂颁竴妫垫爲銆傝�屽疄鐜拌繖涓畻娉曠殑鍩虹锛屽氨闇�瑕侀亶鍘� DOM 鏍戠殑鑺傜偣锛屾潵杩涜姣旇緝鏇存柊銆�

涓轰簡澶勭悊鏇村揩锛屼笉浣跨敤 DOM 瀵硅薄锛岃�屾槸鐢� JS 瀵硅薄鏉ヨ〃绀猴紝瀹冨氨鍍忔槸 JS 鍜� DOM 涔嬮棿鐨勪竴灞傜紦瀛�銆�

濡備綍琛ㄧず VDom锛�

鍊熷姪 ES6 鐨� class锛岃〃绀� VDom 璇箟鍖栨洿寮恒�備竴涓熀纭�鐨� VDom 闇�瑕佹湁鏍囩鍚嶃�佹爣绛惧睘鎬т互鍙婂瓙鑺傜偣锛屽涓嬫墍绀猴細

class Element {
  constructor(tagName, props, children) {
    this.tagName = tagName;
    this.props = props;
    this.children = children;
  }
}

涓轰簡鏇存柟渚胯皟鐢紙涓嶇敤姣忔閮藉啓new锛夛紝灏嗗叾灏佽杩斿洖瀹炰緥鐨勫嚱鏁帮細

function el(tagName, props, children) {
  return new Element(tagName, props, children);
}

姝ゆ椂锛屽鏋滄兂琛ㄨ揪涓嬮潰鐨� DOM 缁撴瀯锛�

span1

鐢� VDom 灏辨槸锛�

// 瀛愯妭鐐规暟缁勭殑鍏冪礌鍙互鏄枃鏈紝涔熷彲浠ユ槸VDom瀹炰緥
const span = el("span", {}, ["span1"]);
const div = el("div", { class: "test" }, [span]);

涔嬪悗鍦ㄥ姣斿拰鏇存柊涓ゆ5 VDom 鏍戠殑鏃跺�欙紝浼氭秹鍙婂埌灏� VDom 娓叉煋鎴愮湡姝g殑 Dom 鑺傜偣銆傚洜姝わ紝涓�class Element澧炲姞render鏂规硶锛�

class Element {
  constructor(tagName, props, children) {
    this.tagName = tagName;
    this.props = props;
    this.children = children;
  }

  render() {
    const dom = document.createElement(this.tagName);
    // 璁剧疆鏍囩灞炴�у��
    Reflect.ownKeys(this.props).forEach(name =>
      dom.setAttribute(name, this.props[name])
    );

    // 閫掑綊鏇存柊瀛愯妭鐐�
    this.children.forEach(child => {
      const childDom =
        child instanceof Element
          ? child.render()
          : document.createTextNode(child);
      dom.appendChild(childDom);
    });

    return dom;
  }
}

濡備綍姣旇緝 VDom 鏍戯紝骞朵笖杩涜楂樻晥鏇存柊锛�

鍓嶉潰宸茬粡璇存槑浜� VDom 鐨勭敤娉曚笌鍚箟锛屽涓� VDom 灏变細缁勬垚涓�妫佃櫄鎷熺殑 DOM 鏍戙�傚墿涓嬮渶瑕佸仛鐨勫氨鏄細鏍规嵁涓嶅悓鐨勬儏鍐碉紝鏉ヨ繘琛屾爲涓婅妭鐐圭殑澧炲垹鏀圭殑鎿嶄綔銆傝繖涓繃绋嬫槸鍒嗕负diff鍜�patch锛�

  • diff锛氶�掑綊瀵规瘮涓ゆ5 VDom 鏍戠殑銆佸搴斾綅缃殑鑺傜偣宸紓
  • patch锛氭牴鎹笉鍚岀殑宸紓锛岃繘琛岃妭鐐圭殑鏇存柊

鐩墠鏈変袱绉嶆�濊矾锛屼竴绉嶆槸鍏� diff 涓�閬嶏紝璁板綍鎵�鏈夌殑宸紓锛屽啀缁熶竴杩涜 patch锛�鍙﹀涓�绉嶆槸 diff 鐨勫悓鏃讹紝杩涜 patch銆傜浉杈冭�岃█锛岀浜岀鏂规硶灏戜簡涓�娆¢�掑綊鏌ヨ锛屼互鍙婁笉闇�瑕佹瀯閫犺繃澶氱殑瀵硅薄锛屼笅闈㈤噰鍙栫殑鏄浜岀鎬濊矾銆�

鍙橀噺鐨勫惈涔�

灏� diff 鍜� patch 鐨勮繃绋嬶紝鏀惧叆updateEl鏂规硶涓紝杩欎釜鏂规硶鐨勫畾涔夊涓嬶細

/**
 *
 * @param {HTMLElement} $parent
 * @param {Element} newNode
 * @param {Element} oldNode
 * @param {Number} index
 */
function updateEl($parent, newNode, oldNode, index = 0) {
  // ...
}

鎵�鏈変互$寮�澶寸殑鍙橀噺锛屼唬琛ㄧ潃鐪熷疄鐨� DOM銆�

鍙傛暟index琛ㄧずoldNode鍦�$parent鐨勬墍鏈夊瓙鑺傜偣鏋勬垚鐨勬暟缁勭殑涓嬫爣浣嶇疆銆�

鎯呭喌 1锛氭柊澧炶妭鐐�

濡傛灉 oldNode 涓� undefined锛岃鏄� newNode 鏄竴涓柊澧炵殑 DOM 鑺傜偣銆傜洿鎺ュ皢鍏惰拷鍔犲埌 DOM 鑺傜偣涓嵆鍙細

function updateEl($parent, newNode, oldNode, index = 0) {
  if (!oldNode) {
    $parent.appendChild(newNode.render());
  }
}

鎯呭喌 2锛氬垹闄よ妭鐐�

濡傛灉 newNode 涓� undefined锛岃鏄庢柊鐨� VDom 鏍戜腑锛屽綋鍓嶄綅缃病鏈夎妭鐐癸紝鍥犳闇�瑕佸皢鍏朵粠瀹為檯鐨� DOM 涓垹闄ゃ�傚垹闄ゅ氨璋冪敤$parent.removeChild()锛岄�氳繃index鍙傛暟锛屽彲浠ユ嬁鍒拌鍒犻櫎鍏冪礌鐨勫紩鐢細

function updateEl($parent, newNode, oldNode, index = 0) {
  if (!oldNode) {
    $parent.appendChild(newNode.render());
  } else if (!newNode) {
    $parent.removeChild($parent.childNodes[index]);
  }
}

鎯呭喌 3锛氬彉鍖栬妭鐐�

瀵规瘮 oldNode 鍜� newNode锛屾湁 3 绉嶆儏鍐碉紝鍧囧彲瑙嗕负鏀瑰彉锛�

  1. 鑺傜偣绫诲瀷鍙戠敓鍙樺寲锛氭枃鏈彉鎴� vdom锛泇dom 鍙樻垚鏂囨湰
  2. 鏂版棫鑺傜偣閮芥槸鏂囨湰锛屽唴瀹瑰彂鐢熸敼鍙�
  3. 鑺傜偣鐨勫睘鎬у�煎彂鐢熷彉鍖�

棣栧厛锛屽�熷姪Symbol鏇村ソ鍦拌涔夊寲澹版槑杩欎笁绉嶅彉鍖栵細

const CHANGE_TYPE_TEXT = Symbol("text");
const CHANGE_TYPE_PROP = Symbol("props");
const CHANGE_TYPE_REPLACE = Symbol("replace");

閽堝鑺傜偣灞炴�у彂鐢熸敼鍙橈紝娌℃湁鐜版垚鐨� api 渚涙垜浠壒閲忔洿鏂般�傚洜姝ゅ皝瑁�replaceAttribute锛屽皢鏂� vdom 鐨勫睘鎬х洿鎺ユ槧灏勫埌 dom 缁撴瀯涓婏細

function replaceAttribute($node, removedAttrs, newAttrs) {
  if (!$node) {
    return;
  }

  Reflect.ownKeys(removedAttrs).forEach(attr => $node.removeAttribute(attr));
  Reflect.ownKeys(newAttrs).forEach(attr =>
    $node.setAttribute(attr, newAttrs[attr])
  );
}

缂栧啓checkChangeType鍑芥暟鍒ゆ柇鍙樺寲鐨勭被鍨嬶紱濡傛灉娌℃湁鍙樺寲锛屽垯杩斿洖绌猴細

function checkChangeType(newNode, oldNode) {
  if (
    typeof newNode !== typeof oldNode ||
    newNode.tagName !== oldNode.tagName
  ) {
    return CHANGE_TYPE_REPLACE;
  }

  if (typeof newNode === "string") {
    if (newNode !== oldNode) {
      return CHANGE_TYPE_TEXT;
    }
    return;
  }

  const propsChanged = Reflect.ownKeys(newNode.props).reduce(
    (prev, name) => prev || oldNode.props[name] !== newNode.props[name],
    false
  );

  if (propsChanged) {
    return CHANGE_TYPE_PROP;
  }
  return;
}

鍦�updateEl涓紝鏍规嵁checkChangeType杩斿洖鐨勫彉鍖栫被鍨嬶紝鍋氬搴旂殑澶勭悊銆傚鏋滅被鍨嬩负绌猴紝鍒欎笉杩涜澶勭悊銆傚叿浣撻�昏緫濡備笅锛�

function updateEl($parent, newNode, oldNode, index = 0) {
  let changeType = null;

  if (!oldNode) {
    $parent.appendChild(newNode.render());
  } else if (!newNode) {
    $parent.removeChild($parent.childNodes[index]);
  } else if ((changeType = checkChangeType(newNode, oldNode))) {
    if (changeType === CHANGE_TYPE_TEXT) {
      $parent.replaceChild(
        document.createTextNode(newNode),
        $parent.childNodes[index]
      );
    } else if (changeType === CHANGE_TYPE_REPLACE) {
      $parent.replaceChild(newNode.render(), $parent.childNodes[index]);
    } else if (changeType === CHANGE_TYPE_PROP) {
      replaceAttribute($parent.childNodes[index], oldNode.props, newNode.props);
    }
  }
}

鎯呭喌 4锛氶�掑綊瀵瑰瓙鑺傜偣鎵ц Diff

濡傛灉鎯呭喌 1銆�2銆�3 閮芥病鏈夊懡涓紝閭d箞璇存槑褰撳墠鏂版棫鑺傜偣鑷韩娌℃湁鍙樺寲銆傛鏃讹紝闇�瑕侀亶鍘嗗畠浠紙Virtual Dom锛夌殑children鏁扮粍锛圖om 瀛愯妭鐐癸級锛岄�掑綊杩涜澶勭悊銆�

浠g爜瀹炵幇闈炲父绠�鍗曪細

function updateEl($parent, newNode, oldNode, index = 0) {
  let changeType = null;

  if (!oldNode) {
    $parent.appendChild(newNode.render());
  } else if (!newNode) {
    $parent.removeChild($parent.childNodes[index]);
  } else if ((changeType = checkChangeType(newNode, oldNode))) {
    if (changeType === CHANGE_TYPE_TEXT) {
      $parent.replaceChild(
        document.createTextNode(newNode),
        $parent.childNodes[index]
      );
    } else if (changeType === CHANGE_TYPE_REPLACE) {
      $parent.replaceChild(newNode.render(), $parent.childNodes[index]);
    } else if (changeType === CHANGE_TYPE_PROP) {
      replaceAttribute($parent.childNodes[index], oldNode.props, newNode.props);
    }
  } else if (newNode.tagName) {
    const newLength = newNode.children.length;
    const oldLength = oldNode.children.length;
    for (let i = 0; i < newLength || i < oldLength; ++i) {
      updateEl(
        $parent.childNodes[index],
        newNode.children[i],
        oldNode.children[i],
        i
      );
    }
  }
}

鏁堟灉瑙傚療

灏�github.com/dongyuanxin/pure-virtual-dom鐨勪唬鐮� clone 鍒版湰鍦帮紝Chrome 鎵撳紑index.html銆�

鏂板 dom 鑺傜偣.gif:

鏇存柊鏂囨湰鍐呭.gif锛�

鏇存敼鑺傜偣灞炴��.gif锛�

鈿狅笍 缃戦�熻緝鎱㈢殑鍚屽璇风Щ姝� github 浠撳簱

鍙傝�冮摼鎺�

你可能感兴趣的:(javascript,react.js,virtual-dom,es6,绠楁硶-鏁版嵁缁撴瀯)