初识Svelte

什么是Svelte?

首先, svelte是一个前端框架, 在国内使用人数非常少, 但是如果你到github上去看它的repo,你会发现居然有25.3k的star.
什么? 居然这么多? 为啥我没听过呢? 不是只有三大框架么? (问号脸?)
那今天就来给大家介绍一下它和我们熟悉的另外几个常见框架react vue 有什么区别?

Svelte有什么特点

让我们先看一下用svelte来书写组件是什么样子的:

try.svelte

1
2
3
4
5
6
7
8
9
10
11
<script>
let count = 0;

function handleClick() {
count += 1;
}
</script>

<button on:click={handleClick}>
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

我们有一说一, 是不是有点像vue的写法? 看想去很像是.vue的文件嘛!? 但又好少点什么?

没有错! 就是少了很多东西, 首先旧少了框架的引入, 也少了很多声明试的代码,如果让你用vue react 来写这个代码逻辑的话,
恐怕都没法写的这么精简把? 其实这就是Svelte框架对外宣称的第一个特点: ‘Write less code’, 整个代码几乎没有任何样板代码,
看上去十分清爽, 也刚容易理解

实际上通常一个功能的代码量, svelte要比 react少不少:
对比

让我们看看代码为什么这么少?我们发现代码在对变量更新的时候并没有使用类似reactsetState方法, 而是直接对变量进行了再赋值.
仅仅是对变量进行了赋值就可以引发视图的变化, 很显然是数据响应的, 这就是svelte的第二个特点: ‘truly reactive’

但是它又很奇怪,因为它像vue一样吧变量声明到一个对象中来进行数据劫持, 那到底是怎么做到的呢?

让我们看看他编译以后的代码, 编译以后变成了这样…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/* App.svelte generated by Svelte v3.12.1 */
import { SvelteComponent, append, detach, element, init,
insert, listen, noop, safe_not_equal, set_data, space, text
} from "svelte/internal";

function create_fragment(ctx) {
var button, t0, t1, t2, t3_value = ctx.count === 1 ? 'time' : 'times' + "", t3, dispose;
return {
c() {
button = element("button"); // 创建一个button
t0 = text("Clicked ");
t1 = text(ctx.count); // 变量count 的textnode
t2 = space(); // 空格?
t3 = text(t3_value); // 之前的三元表达式的结果 的 textnode
dispose = listen(button, "click", ctx.handleClick); // 给button元素添加点击时间
},
m(target, anchor) {
insert(target, button, anchor); // 插入到目标元素
append(button, t0); // 把文本添加到button上
append(button, t1);
append(button, t2);
append(button, t3);
},
p(changed, ctx) { // 数据到dom的操作函数, 这里直接改document上的对应元素了!
if (changed.count) { set_data(t1, ctx.count); } // 更新 count textnode
if ((changed.count) && t3_value !== (t3_value = ctx.count === 1 ? 'time' : 'times' + "")) {
set_data(t3, t3_value); // 更新三元表达式的 textnode
}
},
i: noop, o: noop,
d(detaching) { // 销毁组件
if (detaching) { detach(button); }
dispose();
}
};
}
function instance($$self, $$props, $$invalidate) {
let count = 0; // 真正的变量声明初始化
function handleClick() { // click回调 初始化
$$invalidate('count', count += 1);
}
return { count, handleClick };
}
// 组件的构造函数
class App extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance, create_fragment, safe_not_equal, []);
}
}
export default App;

what? 没法看了,对不对? 让我们回过头仔细看看代码…

看完注释, 是不是大概搞明白了. 这不就是jQuery么!

没错, 大概意思差不多, 其实把svelte称作一个前端框架是不恰当的, 他更应该被称作编译器, 把开头的.svelet文件编译成上面的可执行代码.
而且编译后的代码中调用的大多数方法都是一些helper function, 所以svelte的runtime代码量是非常少的,它主要的工作都是在编译期完成的.

并且我们发现,数据和视图的绑定,使用的是vanillaJs, 数据的变动,直接修改对应的dom节点,也就是说,他并没有用virtual dom!
这就是svelte的第三个卖点: ‘no virtual dom’. 显然我们不需要猜测也知道, 他的性能会很好.

作者在官网上说:

虚拟DOM不是一种功能。而是一种声明性的,状态驱动的UI开发,的最终手段.因为它使你无需考虑状态转换即可构建应用程序,并且性能通常足够好。这意味着更少的错误代码,而将更多的时间花费构建功能上,而不是乏味的工作上. 但是事实证明,我们无需使用虚拟DOM就可以实现类似的编程模型-这就是Svelte的用武之地。

实际上最后, svelte编译好的代码是不再需要任何依赖的,是一个天然的组件, 只要把导出的类直接实例化, 组件就被正确的添加了

1
2
3
4
const el = new App({
target: document.body,
props: {},
})

所以svelte很容易被嵌入到其他框架中去使用

svelte潜在的问题

  • 虽然它只有很少的运行时代码,但他构建后的代码,有可能比用virtual dom构建的代码多,并且有很多运行时代码的重复调用. 随着项目的扩大,运行时代码的体量优势也会越来越小, 最终实际生产项目中能有多少尺寸的优势很难说
  • 虽然编译后的代码性能几乎和 vanillaJs持平, 现在主流框架的virtual dom 也可以做到只比vanillaJs慢1.5倍左右, 这也证明了为了web页面的性能的瓶颈很可能不在dom本身,而在框架
  • svelte的这种实现逻辑导致它无法被像virtual dom这样, 容易静态分析,继而移植为服务端渲染, 也很难基于vNode进行测试

结语

不论如何,不得不说, 作者在整个框架思路上,非常的清奇,与众不同. 看完让我眼前一亮. 果然virtual dom 并不一定是前端的的最佳实践 前端还会有很多路要走, 我们也有很多代码值得去看.
最后提一句, 作者操作这么秀, 一定不是普通人,对吧? 他就是前端轮子哥, 也是rollup的作者 Rich-Harris. 这是他的github

推荐阅读

svelte 官网
Truly reactive programming with Svelte 3.0