Vue,React类含虚拟DOM声明式和Svelte无虚拟DOM命令式对比

378 阅读3分钟

一.Vue和React的虚拟DOM

(一) 在Vue和React的响应式中,使用的是声明式编程(declarative)都是在runtime中用VirtualDOM。 虚拟DOM不是免费的,他需要消耗资源。在首次渲染时需要:建立VirtualDOM,再根据virtualDOM渲染实际DOM,比直接渲染多了一步,所以在第一次渲染时甚至还要比直接操作DOM要慢,因此一个网页如果在渲染之后不会改变就不需要使用Vue或者React。

(二) 在React中更新元素需要进行VirtualDOM的比较,举个例子:

props.name = World
function HelloMessage(props) {
  return (
    <div className="greeting">
      Hello {props.name}
    </div>
  );
}

假如我们要改变上面这个HelloMessage中的props.name属性从World改成EveryOne,需要做这么几件事:

1.在虚拟DOM中建立一个节点编译保存<div> 中的内容
2.遍历原来的<div>中的所有属性,并且创建一个新的<div>比较他们之间的差别,看看有没有发生新增,删除,修改
3.如果有变化就更新真实DOM(realDOM)

第3步是我们的最终目的,第1,2步是实现最终目的的方法,那么做什么改进让这个过程更快呢?
vue2到vue3的一个升级就是改变了虚拟DOM diffing算法,速度有了提升,这就是优化第2步。
但是如果能不经历1,2两步,直接到第3步,讲道理的话完全省去了比较是不是应该就快了?这就是svelte干的事

二.Svelte直接编译

(一) 和Vue,React在run time使用VirtualDOM不同,Sevelte运行在build time,使用命令式编程(imperative),直接在更底层(low-level)直接去操纵DOM,他不像Vue一样在解释命令,而是像JAVA一样先编译好。
Svelte是怎么去衡量DOM改变的呢?举个例子,我们新建一个APP.svelte文件,里面输入:

<h1>Hello world!</h1>

它会被编译成:

/* App.svelte generated by Svelte v3.49.0 */
import {
	SvelteComponent,
	detach,
	element,
	init,
	insert,
	noop,
	safe_not_equal
} from "svelte/internal";

function create_fragment(ctx) {
	let h1;

	return {
		c() {
			h1 = element("h1");
			h1.textContent = "Hello world!";
		},
		m(target, anchor) {
			insert(target, h1, anchor);
		},
		p: noop,
		i: noop,
		o: noop,
		d(detaching) {
			if (detaching) detach(h1);
		}
	};
}

class App extends SvelteComponent {
	constructor(options) {
		super();
		init(this, options, null, create_fragment, safe_not_equal, {});
	}
}

export default App;

注意看这个部分,svelte在编译时设置了修改、新增、删除方程,并且把可修的DOM元素放了进去。如果我们再放一个元素进去:

<h1>Hello world!</h1>
<h2>你好</h2>

他会编译成:

/* App.svelte generated by Svelte v3.49.0 */
import {
	SvelteComponent,
	detach,
	element,
	init,
	insert,
	noop,
	safe_not_equal,
	space
} from "svelte/internal";

function create_fragment(ctx) {
	let h1;
	let t1;
	let h2;

	return {
		c() {
			h1 = element("h1");
			h1.textContent = "Hello world!";
			t1 = space();
			h2 = element("h2");
			h2.textContent = "你好";
		},
		m(target, anchor) {
			insert(target, h1, anchor);
			insert(target, t1, anchor);
			insert(target, h2, anchor);
		},
		p: noop,
		i: noop,
		o: noop,
		d(detaching) {
			if (detaching) detach(h1);
			if (detaching) detach(t1);
			if (detaching) detach(h2);
		}
	};
}

class App extends SvelteComponent {
	constructor(options) {
		super();
		init(this, options, null, create_fragment, safe_not_equal, {});
	}
}

export default App;

注意看,两行之间的两行之间有多少个空格、回车都被识别为同一个空格元素,并且在其中以t1显示。
这里面的c(create),m(mount),d(detach)就是新增DOM元素、在真实DOM中插入、移除元素。而p(update),i(intro),o(outro)因为第一次加载没有涉及到,更新DOM元素靠的就是p()函数。p()函数具体怎么更新DOM元素的,之后等我看完svelte源码更新(我也是响应式的:-))。

参考资料:
github.com/sveltejs/sv…
www.sveltejs.cn/blog/virtua…
www.sveltejs.cn/blog/svelte…
www.youtube.com/watch?v=AdN…
zhuanlan.zhihu.com/p/448469958