初识Svelte

5,052 阅读4分钟

简介

  第一次听说Svelte是在2019.6.8Vueconf分享会的视频里,尤大提到了Svelte的更新采用了静态分析,于是产生了比较浓厚的兴趣,同时尤大在视频里也提到了,Vue3.0也借鉴了部分思路,并且有可能不会实现类似react的fibber切片渲染,接下来分享一下我所了解的Svelte

什么是Svelte

引用官方介绍:

Svelte is a radical new approach to building user interfaces. 
Whereas traditional frameworks like React and Vue do the bulk of their work in the browser, Svelte shifts that work into a compile step that happens when you build your app.Instead of using techniques like virtual DOM diffing, Svelte writes code that surgically updates the DOM when the state of your app changes.

Svelte是一种构建用户界面的全新方法。虽然像React和Vue这样的传统框架在浏览器中完成了大部分工作,但是Svelte将这项工作转移到了构建应用程序时发生的编译步骤。

Svelte不是使用像虚拟DOM差异这样的技术,而是编写在应用状态发生变化时通过外部更新DOM的代码。

通过以上介绍,我们可以看到,Svelte内部是不存在虚拟DOM的,但是语法和Vue还是有高度相似,且更新机制是在编译阶段静态分析出来, 这意味着有更好的屏加载时间和更新性能。

通过静态编译减少框架运行时的代码量,不需要任何runtime,适合构建一些第三方的插件或者SDK

缺点,编译后单个文件量增大,且生态不活跃,成熟UI库没有

Svelte基础语法

官方提供的打包工具为rollup,那接下来,我们去了解一下Sevelte的基础语法知识来感受一下与Vue的区别

先看一下rollup的基础配置

import svelte from 'rollup-plugin-svelte';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';
const production = !process.env.ROLLUP_WATCH;
export default {
	input: 'src/main.js',
	output: {
		sourcemap: true,
		format: 'iife',
		name: 'app',
		file: 'public/bundle.js'
	},
	plugins: [
		svelte({
			dev: !production,
			css: css => {
				css.write('public/bundle.css');
			}
		}),
		resolve({ browser: true }),
		commonjs(),
		!production && livereload('public'),
		production && terser({
			compress: false, //禁止代码压缩,是为了更好查看编译后代码原理
			mangle: false,
			ie8: true //支持ie8
		})
	],
	watch: {
		clearScreen: false
	}
};

感受一下基础语法

// App.svelte
<div>
    <p>{title}</p>
    <img src={src} />
</div>
<script>
    //作用域内部声明变量
    let title = 'Sevelte'
    let src = 'XXX'
</script>
<style>
    div {}
</style>

怎么样,感觉是不是和Vue很像,我们可以直接在script标签内部定义模板中变量,每一个script标签对应的就是当前组件的作用域,接下来我们再看一下生命周期的写法

// App.svelte
<div>
    <p>{title}</p>
    <img src={src} />
</div>
<script>
    import { onMount } from 'svelte';
    //作用域内部声明变量
    let title = 'Sevelte'
    let src = 'XXX'
   onMount(() => {
       console.log('the component has mounted');
   });</script>
<style>
    div {}
</style>

感觉是不是很熟悉,和即将发布的Vue3.0是不是有很多相似的地方,Svelte还有很多特性,事件传递、组件传参、动画过渡等等,大家可以去看一下官网。

svelte工作机制

Svelte在编译后内置的runtime代码量其实很少,未压缩时候,大约在200行,其核心更新和创建集中在两个函数create_fragment和instance,instance函数用来创建和返回,在script标签中定义的变量,在Sevlte中称之为ctx上下文,createFragment函数返回一些列的钩子,如创建、跟新、销毁等等,利用闭包的特性呢,能精确的记住每一个DOM节点,针对每一个DOM节点精准更新,下面,我们来实际看一下代码

let title = "Sevelte";
let src = "XXX";
function instance($$self) {
	onMount(() => {
		console.log("the component has mounted")
	});
	return {}
}
//创建节点
function create_fragment(ctx) {
        //创建闭包变量
	var div, p, t0, t1, img;
	return {
                //create创建
		c() {
			div = element("div");
			p = element("p");
			t0 = text(title);
			t1 = space();
			img = element("img");
			img.src = src;
			div.className = "svelte-ums2o4"
		},
                //mounted挂载
		m(target, anchor) {
			insert(target, div, anchor);
			append(div, p);
			append(p, t0);
			append(div, t1);
			append(div, img)
		},
		p: noop,
		i: noop,
		o: noop,
                //destroy销毁
		d(detaching) {
			if (detaching) {
				detach(div)
			}
		}
	}
}

Svelte与Vue更新性能分析对比

同等10000个简单节点更新对比,省去了虚拟DOM生成以及patch对比,Svelte耗时60ms,Vue耗时300ms,性能优于Vue5倍,但是Svelte构建大型项目的能力,暂时未知。

Vue3.0出来以后旧项目可能更新不及时或者无法更新,或许在遇到性能瓶颈时候,可以参考以下思路,在已有程序中渐进式采用Svelte,两者结合,可能会提高部分性能

在Vue中优雅的使用Svelte

可以借助svelte-loader进行编译

cnpm i svelte svelte-loader -S

在vue.config.js中进行配置

......
configureWebpack: config => {
    config.module.rules.push({
        test: /\.svelte$/,
        use: 'svelte-loader'
    })
}
......

App.vue

<script>
    import main from './main.svelte'
    export default {
        data () {
            return {
                title: 'Svelte'
            }
        }
        render (c) {
            return c('div', [
                c('span', {
                    on: {
                        click () {
                            this.title = '梧桐树下'
                        }
                    }
                })
            ])
        },
        watch: {
            title (val) {
                this.main.$set({
                    title: val
                })
            }
        },
        mounted () {
            this.main = new main({
                target: this.$el,
                props: {
                    title: this.title
                }
            })
        }
    }
</script>

Main.svelte

<div>{title}</div>
<script>
    //抛出props,进行占位
    export let title = 'placeholder'
</script>

总结

刚刚了解Svelte,总结有限,大家在开发过程中具体怎么使用,看实际业务场景。