都2020年了你还不知道Svelte(1)—— 初识Svelte

4,075 阅读13分钟

在《都2020年了你还不知道Svelte》这个系列里,我们将会介绍 Svelte 框架的基本用法、特性、优缺点、基本原理和源码分析。

目录

前端领域是发展迅速,各种轮子层出不穷的行业。最近这些年,随着三大框架 ReactVueAngular 版本逐渐稳定,前端技术栈的迭代似乎缓慢下来,React 16版本推出了 Fiber, Vue 3.0 也已经在襁褓之中。

展望未来,有哪些框架有可能会挑战 React 或者 Vue 江湖地位呢? 我们认为,崭露头角的 Svelte 应该是其中的选项之一。

你可能会说, Vue、 React它不香吗,不急我们慢慢往下看~

神奇消失的 svelte 框架

image-20200313150537316

Svelte 叫法是 [Svelte], 本意是苗条纤瘦的,是一个新兴热门的前端框架。

在最近的《State of JS survey of 2019》,它被预测为未来十年可能取代React和Vue等其他框架的新兴技术。如果你不确定自己是否该了解 Svelte,可以先看一下 Svelte 的一些发展趋势

开发者满意度

image-20200308175207693

开发者兴趣度

image-20200308175255470

市场占有率

当然,如果你还没有听说过,也不用紧张,因为svelte 目前仍是小众的开发框架,在社区里仍然没有流行开来。24.7% 的开发者没有听说过这款框架,有 44.9% 的开发者听说过,并且愿意接下来尝试使用。

image-20200308175507775

从框架使用的公司规模来看,svelte 目前在大公司使用率不高,可能和 svelte 目前生态环境和周边欠缺有关。svelte 在小公司中使用率比较高,可能和容易上手有关

image-20200308180054813

背后的大佬——Rich Harris

image-20200313150707012

Svelte 作者是前端轮子哥 Rich Harris,同时也是 Rollup 的作者(再次膜拜一下)。Rich Harris 作者本人在介绍 Svelte 时,有一个非常精彩的演讲《Rethinking reactivity》,油管连接 www.youtube.com/watch?v=AdN…

他设计 Svelte 的核心思想在于『通过静态编译减少框架运行时的代码量』,也就是说,vue 和 react 这类传统的框架,都必须引入运行时 (runtime) 代码,用于虚拟dom、diff 算法。Svelted完全溶入JavaScript,应用所有需要的运行时代码都包含在bundle.js里面了,除了引入这个组件本身,你不需要再额外引入一个运行代码。

svelte 的使用 非常灵活,你可以在整个项目的使用Svelte 整个的构建体系,也可以渐进地将 svelte添加到您的代码中,还可以将 svelte 组件作为独立的 npm 包发布

和Vue, React 相比,Svelte 优势有哪些

说了那么多,真的学不动了怎么办。

不慌,先看一下 Svelte 和React,Vue 相比,有哪些优势:

No Runtime —— 无运行时代码

上文说了,React 和 Vue都是基于运行时runtime的框架,框架本身所依赖的代码也会被打包到最终的构建产物中,当用户在你的页面进行各种操作改变组件的状态时,框架的runtime会根据新的组件状态(state)计算(diff)出哪些DOM节点需要被更新,从而更新视图。

这就不可避免增加了打包后的体积,有一部分的体积增加是不可避免的,那么这部分体积大约是多少呢?请看下面的数据:

image-20200303193723173

从上面的表格可以看出,常用的框架中,最小的Vue都有58kReact更有97.5k,这个体积说实话,是有一点大的。假设我们使用React开发一个小型组件SDK,即使里面的逻辑代码很简单,但是打包出来的bundle size轻轻松松都要100k起步。

这对于大型后台管理系统来说,100k 不算什么,但是对于特别注重用户端加载性能的场景来说,一个组件100k 多,还是太大了。下面是 Jacek Schae 大神的统计,使用市面上主流的框架,来编写同样的Realword 应用:

image-20200303194159261

从上图的统计,Svelte简直是神奇!竟然只有 9.7 KB ! 果然魔法消失 UI 框架,无愧其名。

可以看出,Sveltebundle size大小是Vue的1/4,是React的1/20

Less-Code ——写更少的代码

开发者的梦想之一,就是敲更少的代码。因为更少的代码量,往往意味着有更好的语义性,也有更少的几率写出bug。

在写 svelte 组件时,你就会发现,和 Vue 或 React 相比只需要更少的代码。下面的例子,可以看出 SvelteReact 的不同:

  1. React 的代码
const [count, setCount] = useState(0);

function increment() {
  setCount(count + 1);
}
  1. Svelte 的代码
let count = 0;

function increment() {
  count += 1;
}

虽然用上了16版本最新的 hooks,但是和 svelte 相比,代码还是很冗余。

React中,我们要么使用useState钩子,要么使用setState设置状态。而在Svelte中,可以直接使用赋值操作符更新状态。

下面还是 Jacek Schae 老哥的统计,编写同样的Realword 应用,各个框架所需要的行数:

image-20200311151849014

Vue 和 React 打了平手,Svelte 遥遥领先,可以少些 1000 行代码耶~ 早日下班,指日可待。

Hight-Performance ——高性能

What ??

Virtual Dom 已经是前端框架标配的今天, Svelte 不要Virtual Dom, 怎么还能保证高性能呢?

不急,慢慢看。

性能测评

Jacek Schae 在《A RealWorld Comparison of Front-End Frameworks with Benchmarks》 中用主流的前端框架来编写 RealWorld 应用,使用 Chrome 的 Lighthouse Audit 测试性能,得出数据是 Svelte 略逊于Vue, 但好于 React

image-20200311151142073

是不是很惊奇? 另外一个前端框架性能对比的项目也给出了同样的答案。 github.com/krausest/js…

image-20200311150654800

为什么 Svelte 性能还不错,至少没有我们预期的那么糟糕?我们接下来就一点一点分析。

Virtual Dom 真的高效吗

Rich Harris 不用 Virtual DOM 是因为觉得Virtual DOM Diff 的过程是非常低效的。

在他的一文《Virtual DOM is pure overhead》原文连接在这里, www.sveltejs.cn/blog/virtua… 有介绍,感兴趣的同学可以翻一下,

人们觉得 Virtual DOM高效的一个理由,就是它不会直接操作原生的DOM节点。在浏览器当中,JavaScript的运算在现代的引擎中非常快,但DOM本身是非常缓慢的东西。当你调用原生DOM API的时候,浏览器需要在JavaScript引擎的语境下去接触原生的DOM的实现,这个过程有相当的性能损耗。

但其实 Virtual DOM 有时候会做很多无用功,这体现在很多组件会被“无缘无故”进行重渲染(re-render)。

image-20200311160012192

为了解决这个问题,React 提供 pureComponent , shouldComponentUpdateuseMemo, useCallback让开发者来操心哪些subtree 是需要重新渲染的,哪些是不需要重新渲染的。究其本质,是因为 React 采用 jsx 语法过于灵活,不理解开发者写出代码所代表的意义,没有办法做出优化。

那么 Svelte 是如何解决这个问题的?

React 采用 jsx 语法本质不理解数据代表的意义,没有办法做出优化。

Svelte 采用了 Templates 语法(类似于 Vue 的写法),更加严格和具有语义性,可以在编译的过程中就进行优化操作。

那么,为什么 Templates 语法可以解决这个问题呢?

Template 带来的优势

关于 JSX 与 Templates ,可以看成是两种不同的前端框架渲染机制,有兴趣的同学可以翻一下尤雨溪的演讲《在框架设计中寻求平衡》 www.bilibili.com/video/av800…

一方面, JSX 的代表框架有 React 以及所有 react-like 库,比如 pre-act、 stencil, infernal 等;另一方面, Templates 代表性的解决方案有 Vue、Svelte、 ember,各有优缺点。

JSX 优缺点

jsx 具有 JavaScript 的完整表现力,非常具有表现力,可以构建非常复杂的组件。

但是灵活的语法,也意味着引擎难以理解,无法预判开发者的用户意图,从而难以优化性能。你很可能会写出下面的代码:

image-20200311143836716

在使用 JavaScript 的时候,编译器不可能hold住所有可能发生的事情,因为 JavaScript 太过于动态化。也有人对这块做了很多尝试,但从本质上来说很难提供安全的优化。

所以,React 选择的优化思路是,引入了运行时调度并发时间切片的概念,不减少渲染的工作量,而是让用户“看上去” 不会卡。

Template优缺点

根据定义,模板是一种非常有约束的语言,你只能以某种方式去编写模板。

例如,当你写出这样的代码的时候,编译器可以立刻明白:”哦!这些 p 标签的顺序是不会变的,这个 id 是不会变的,这些 class 也不会变的,唯一会变的就是这个“

image-20200311144950627

在编译时,编译器对你的意图可以做更多的预判,从而给它更多的空间去做执行优化。

我们来看看 SVELTE 编译代码时,会把模板编译成直接可执行的javascript 代码。

image-20200311145046490

左侧 template 中,其他所有内容都是静态的,只有 name 可能会发生改变。

右侧 p 函数是编译生成的最终的产物,会在有脏数据时被调用。p 函数唯一做的事情就是,当 name 发生变更的时候,调用原生方法把 t1 这个原生DOM节点更新。

具体的原理,可以看本系列的第二篇文章。

小节

回到最初的问题, 我们就想知道 SVELTE 与 VDOM Diff 算法哪个更快?

取决于组件的复杂程度,当组件内元素数量越多时, VDOM Diff 所需要的时间越长,而SVELTE 相对性能会更好。

image-20200311161139795

Svelte 劣势

在构建大型前端项目时,我们在选择框架的时候就需要考虑更多的事情。

Svelte 目前尚处在起步阶段,对于大型项目必要的 单元测试 并没有完整的方案。目前在大型应用中使用 Svelte , 需要谨慎评。

和Vue, React框架的对比

类目 Svelte Vue React
UI 组件库 Material design ( 坦率的说,不好用 ) Element UI / AntD AntD / Material design
状态管理 官网自带 Vuex Redux/MobX
路由 Svelte-router Vue-router React-router
服务端渲染 支持 支持 支持
测试工具 官方网站没有相关内容 @vue/test-utils Jest

我们在用 Svelte 开发公司级别中大型项目时,也发现了其他的一些主要注意的点

  • 没有像AntD那样成熟的UI库。比如说需求方想加一个toast提示,或者弹窗,pm:”很简单的,不用出UI稿,就直接用之前的样式好啦~“

    但是。。。em。。 Svelte 需要从0开始 ”抄“ 出来一个toast或者弹窗组件出来,可能会带来额外的开发量和做好加班的准备。

  • Svelte 原生不支持预处理器,比如说 less/ scss ,需要自己单独的配置 webpack loader。

  • Svelte 原生脚手架没有目录划分

  • 暂时不支持typescript,虽然官方说了会支持, 但是不知道什么时候.

还需要注意的一点是,React / Vue等框架自带的runtime虽然会增加首屏加载的bundle.js,可是当项目变得越来越大的时候,框架的runtimebundle.js里面占据的比例也会越来越小,这个时候我们就得考虑一下是不是存在一个Svelte生成的代码大于React和Vue生成的代码的阈值了

Svelte 用法

说实话,Svelte 用法这里,不太想做官网搬运工。一方面,Svelte 官网做的已经很好,可以直接跑demo;另一方面,本身语法非常简单。我当时看了一下午基本上就可以写出demo, 建议大家也可以去看一下官网 svelte.dev/tutorial/ba…

下面就简单过一下,我觉得,Svelte 官网中miss掉的点:

响应式数据

vuesvelte 都是template 语法, 一切与原生写法都很类似。

数据默认就是响应式的,在html中直接使用{}就可以拿到js中定义的数据。

<script>
	let name = 'world';
</script>

<h1>Hello {name.toUpperCase()}!</h1>

添加事件也是非常方便的:

<script>
	let count = 0;

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

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

下面就简单过一下,我觉得,Svelte 官网中miss掉的点:

组件

子 Svelte 组件通过 emit 事件向上传递,如果多层组件,必须层层传递,上层组件才可以捕获到。Svelte 提供了语法糖

<script>
    import Inner from './Inner.svelte';
</script>
// 只需要在中间传递层,`on:message` 就可以了
<Inner on:message/>

生命周期

onMount 挂载的钩子,建议放异步请求,方便做服务端渲染

import { onMount } from 'svelte';
onMount(async () => {
  const res = await fetch(`https://jsonplaceholder.typicode.com/photos?_limit=20`);
  photos = await res.json();
})

onDestroy销毁,比如说订阅的全局 store, 否则会内存泄露

import { onDestroy } from 'svelte';
const unsubscribe = count.subscribe(value => {
        count_value = value;
});
onDestroy(unsubscribe)

tick() 方法,类似于 Vue 中的 $nextTick(), 等待 dom 的节点更新完毕

await tick();
this.selectionStart = selectionStart;
this.selectionEnd = selectionEnd;

全局的数据流

初始化

/store/inex.js

import {writable} from 'svelte/store'
export const xx = writable(0);

订阅

xx.subscribe((value) => {
  localValue = value;
})

更新

xx.update((n) => {
  return n + 1
})

重置

xx.set(0)

class

可以用三目运算

<button
    class="{current === 'foo' ? 'active' : ''} box"
    on:click="{() => current = 'foo'}"
>foo</button>

也可以用简洁写法,

<div class:xxx="{activ}" class="box"></div>

总结

这一篇主要介绍了 Svelte 目前的发展态势、Svelte的优势和劣势。如果在合适的项目中,就大胆的使用吧,预祝使用愉快。

最后,字节跳动大量招人人人人!!

字节跳动(杭州|北京|上海)大量招人。福利超级棒,薪资水平秒BAT。上班不打卡、每天下午茶、免费零食无限供应、免费三餐(我念下菜单,大闸蟹鲍鱼扇贝海鲜烤鱼片黑椒牛柳咖喱牛肉麻辣小龙虾)。 免费健身房、入职配16寸顶配最新mbp、每月还有租房房补。 这次真的机会多多,年后研发人数要扩招n倍,技术氛围好,大牛多,加班少,还犹豫什么?快发简历到下方邮箱,就现在!

仅仅是一小部的JD, 更多的欢迎加微信~

前端部分JD

JAVA 部分JD

持续招聘大量前端、服务端、客户端、测试、产品,实习社招都阔以

加微信 dujuncheng1,可以聊天聊地聊人生,请注明来自掘金以及要投递哪里的岗位