虚拟DOM,模板编译,以及diff更新机制

40 阅读2分钟

虚拟dom的优势

1、提高性能

JS对象描述DOM相较于真实的DOM对象,内容上轻很多,如果直接对真实的DOM进行diff操作,会有大量额外的属性比对。

2、跨平台优势

虚拟DOM只是对内容的一个描述,通过不同的宿主环境,可以通过执行不同的渲染代码,生产出不同平台的内容。

vdom更新机制(diff算法)

Snabbdom源码地址 github.com/coconilu/Bl…

源码

var snabbdom = require('snabbdom'); // 引入库

// patch其实就是个snabbdom的初始化方法,后面一堆引入可以不关注
var patch = snabbdom.init([ // Init patch function with chosen modules
  require('snabbdom/modules/class').default, // makes it easy to toggle classes
  require('snabbdom/modules/props').default, // for setting properties on DOM elements
  require('snabbdom/modules/style').default, // handles styling on elements with support for animations
  require('snabbdom/modules/eventlisteners').default, // attaches event listeners
]);

// 引入h函数
var h = require('snabbdom/h').default; // helper function for creating vnodes

// 拿到一个dom元素
var container = document.getElementById('container');

// 利用h函数生成一个vnode
var vnode = h('div#container.two.classes', {on: {click: someFn}}, [
  h('span', {style: {fontWeight: 'bold'}}, 'This is bold'),
  ' and this is just normal text',
  h('a', {props: {href: '/foo'}}, 'I\'ll take you places!')
]);

// 下面这句相当于新建了一个vnode,把他与一个空dom进行对比,vnode替换空dom
// Patch into empty DOM element – this modifies the DOM as a side effect
patch(container, vnode);

// 新建一个vnode
var newVnode = h('div#container.two.classes', {on: {click: anotherEventHandler}}, [
  h('span', {style: {fontWeight: 'normal', fontStyle: 'italic'}}, 'This is now italic type'),
  ' and this is still just normal text',
  h('a', {props: {href: '/bar'}}, 'I\'ll take you places!')
]);

// 下面这句是说旧vnode和新vnode进行比对,如果新不同于旧,那么就用新的代替旧的。
// Second `patch` invocation
patch(vnode, newVnode); // Snabbdom efficiently updates the old view to the new state

diff算法主要是由patch函数完成的。

return function patch (oldVnode: VNode | Element, vnode: VNode): VNode {
	// 第一个参数是一个vnode或者dom,第二个参数是vnode
	...

    // 如果第一个参数不是 vnode
    if (!isVnode(oldVnode)) {
      // 创建一个空的vnode ,并且要继承这个DOM元素的属性
      oldVnode = emptyNodeAt(oldVnode);
    }

    // 如果是相同的 vnode(key 和 sel(选择器)都相等)
    if (sameVnode(oldVnode, vnode)) {
      // 进行 vnode 对比
      patchVnode(oldVnode, vnode, insertedVnodeQueue);
    // 如果是不同的 vnode ,直接删掉旧的,重建新的
    } else {
      // 先获取父级dom
      elm = oldVnode.elm!; 
      parent = api.parentNode(elm);

      // 重建
      createElm(vnode, insertedVnodeQueue);
		
	  // 父级不为空,就把重建的vnode挂在下面
      if (parent !== null) {
        api.insertBefore(parent, vnode.elm!, api.nextSibling(elm));
        removeVnodes(parent, [oldVnode], 0, 0);
      }
    }

	...

    return vnode;
  };

vue是源码直接作为模板引擎产生vdom,react把引擎交给用户自己,更灵活

原博地址: blog.csdn.net/pagnzong/ar…