You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
//Name.svelte<scriptlang='typescript'>
export let name = "yuxl"
</script><span>{name}</span>//Age.svelte<scriptlang='typescript'>
export let age = 18
</script><span>{age}</span>//index.svelte<script>
import Name from './Name.svelte'
import Age from './Age.svelte'
</script><div><Namename="some name"/><Ageage={20}/>
</div>
<scriptlang='typescript'>
const promise = new Promise((resolve)=>{setTimeout(()=>{resolve("success")},2000)})
</script><div>{#await promise}<!--promiseispending--><p>waiting for the promise to resolve...</p>{:thenvalue}<!--promisewasfulfilled--><p>The value is {value}</p>{/await}
</div>
<script>
import {fly} from 'svelte/transition';
let visible = true;
</script><label><inputtype="checkbox"bind:checked={visible}>
visible
</label>{#if visible}<ptransition:fly="{{ y: 200, duration: 2000 }}">
Flies in and out
</p>{/if}
<script>
import {fade} from "svelte/transition";
let visible = false
function handleClick(){visible=true}</script><div><divon:click={handleClick}>点击</div>{#if visible}<divtransition:fade="{{ duration: 2000 }}">
fades in and out
</div>{/if}
</div>
<script>
let m = {x: 0,y: 0};
function handleMousemove(event) {m.x=event.clientX;m.y=event.clientY;}</script><divon:mousemove={handleMousemove}>Themousepositionis{m.x}x{m.y}</div>
深入浅出svelte.js
最近有一个官网页,打算用svelte体验一下,顺便学习了一下svelte(发音:[svelt]),整体来说,svelte是比较简洁的,上手很快。不过与其说是一个前端框架,不如说是一个“dom操作编译器”。svelte的开发代码,在编译阶段会被编译成一系列的dom操作的代码,运行时的代码很少。因此svelte.js的体积很小(只保留了脏值检测更新和封装dom操作API等core代码)。本文从一下几个方面聊一聊对于svelte的认识。
一、svelte初体验
我们直接来看官网的例子:
实现的功能也很简单,就是两个Input的值求和,然后展示出来。用svelte编写的代码为:
上述代码很简洁,像vue一样也是类似style dom script的三段式写法,不过比vue更加简洁一点,比如dom不需要template包裹等等。
同样的上述的例子的代码如果用react书写:
上述react的写法,必须要先弄懂useState的含义等,此外缺少了默认的双向数据绑定,代码有一点冗余。
同样的上述的例子的代码如果用vue书写:
三者对比:
单纯的说,svelte编码只需要145个字符,比vue和react少,因此得出说svelte的编码体积更小,这样是不对的,因为svelte会在编译阶段将代码编译到更加贴近dom操作的代码,上述例子的代码,编译后的结果为:
在编译后生成的代码其实代码量也不小,是远远大于145个字符的,也不能说因为编译后的代码量大,所以说svelte有点名不副实,并不能减少运行时代码的体积。要考虑到svelte的运行时代码是很少的.我们来对比一下:
从上述对比中可以看出,svelte的体积很少,虽然其业务代码在编译后会生产较多的代码。得益于较少的运行时代码。虽然svelte代码的随着业务的编写增量速度比较快,得益于其很小的包体积1.6k,对于一般中小型项目而言,整体运行的代码(编译后的代码+包体积)还是比较小的,所以可以说svelte项目的代码较小。不过对于大型项目而言,因为svelte随着业务的进行,运行时代码增量陡峭,大型项目体积并不会比react、vue等小,因此需要辩证看待。
此外虽说svelte的代码在编译后体积很大,但是在编译前的代码,其实很简洁,这种简洁,一定程度上,可以增强开发体验。
二、 svelte的语法
svelte的写法跟vue有点类似,是指令式和响应式的。
基本用法
这是一个最简单的hello world的例子,上述代码中很简洁。在编译后的代码分为js编译和css编译。
svelte/internal包中是一些封装了dom操作的函数。
css编译结果:
css是通过创建style标签引入到最后的dom中的。
指令形式和数据绑定
还是以上面的例子为例,上述就是一个指令形式+数据绑定的形式。跟vue的写法很相似,改例子绑定了input和a, input和b.效果如下:

这里的$total: 就是reactive statement. 类似vue中的计算属性。
组件compose
在svelte中的组件的compose也是跟react中类似的,不同的是在react中export的属性就是组件的props,写法上比较简洁,此外,export const 和export function、export class这3个组件的props是只读的,不可写。
模版语法
在svelte中,html相关的场景适用于模版语法,最简单的模版语法为:
这里介绍几个在svelte中几个比较有趣的模版语法。
运行debugger的结果为:

@debug 在后面跟的参数name发生变化的时候会进行debugger,从上图我们看到debugger的地方上下文的代码是编译后运行时,跟编码的时候有一点区别,也进一步说明,svelte可以看作是一个前端的编译框架,真正运行时的代码是编译后的结果。
#await
用法为:{#await expression}...{:then name}...{:catch name}...{/await}
动画效果
在svelte中,对于原始的dom元素,自带了一些动画指令,在一般的官网或者活动页中,场景最多的就是动画效果,svelte自带的动画指令,因此在写官网的时候方便了不少。
以transition:fly为例:
最后的结果为:
当然在svelte中也支持自定义动画指令。
组件的生命周期
svelte组件也提供了完整的生命周期。onMount、
beforeUpdate
、afterUpdate
、onDestroy
等。见名思意,这里不一一介绍,跟react & vue的组件生命周期近似。除了上述之外,svelte还支持自定义元素(custom element), store以及context等等。
三、Virtual Dom和Dom
这个其实可以,比较客观的去看待,svelte的作者认为,Virtual Dom的性能并没有太大的问题,不管是diff算法还是render的过程都没有什么性能问题,不过作者认为,svelte不需要diff,还是有一点优势的。虽然diff很快,但是没有diff的话,显然会更快的得到渲染结果。
svelte的编译后的结果来看,所有的dom的变动都变为了直接的dom操作行为,是不需要做diff的,这种方法,没有diff/patch,因此从速度来看,肯定更快一些。 比如:
上述这个例子中,修改了visible,编译后的代码知道这个行为,这是一个确定的会如何影响dom的行为,编译后的结果部分为:

可以看到,state的改变如何影响dom在svelte的编译结果中都是很确定的。
除了性能问题,svelte的作者认为,因为virtualDom的存在,需要保存new object和old object的虚拟dom对象,在react的编程中,每一次渲染都有这两个对象,这两个对象,在正常的开发中,很容易添加一些冗余代码:
在这个例子中,为每一个li都绑定了一个事件,这是不过度优化情况下的正常下发,因为virtualDom虚拟dom的存在,每一次state更新的时候,每一个new object和old object都包含了每一个li的绑定函数,这些是冗余的代码,增加了代码的体积等。
四、优缺点
个人归纳了一下几个优缺点:
优点:
缺点
五、源码阅读
首先svelte的源码分为两部分,compiler和runtime,compiler主要的作用是将开发代码编译成运行时的代码,具体如何编译不是本文所要关注的代码。本文主要关注的是编译后的运行时的代码runtime。
dom操作相关core api
我们以最简单的hello world为例:
svelte编译前源码:
svelte编译后的代码:
这里的App就可以直接使用了,比如渲染到一个父dom中可以这样使用:
上述方法就可以把App这个编译后的运行时组件渲染到body中,我们来看编译后的代码。
在svelte组件中,与dom相关的部分封装在了create_fragment中,该函数创建了一个Fragment, 该函数返回一个包含dom操作的对象:
在上述的例子中,c对应创建一个子dom元素,m表示创建元素要渲染元素时需要执行的函数,d表示删除元素时的操作。上述的例子中:
在m中的intert和d中的detach方法,都是原生的dom操作方法,上述Fragment的意思是创建了h1这个dom,并在渲染的时候插入到目标dom节点中,在Fragment这个组件元素被销毁的时候,销毁被创建的子dom元素 h1。
element、insert、detach等方法都是原生的dom操作,具体源码如下所示:
SvelteComponent组件定义了如何销毁组件以及如何设置组件的属性,以及如何增加监听函数,其中最重要的是定义了组件的实例属性 .
发现SvelteComponent组件确实包含了ctx上下文内容,以及组件的生命周期属性,以及组件的脏值检测等相关的属性。
init函数
`js
export function init(component, options, instance, create_fragment, not_equal, props, dirty = [-1]) {
init函数在SvelteComponent组件内部调用,用于实例属性的初始化。这里最重要的是$$.ctx的赋值部分,后续会用来做脏值检测。ctx中保存了所有的再多次渲染中都存在的值,包含了内部的state以及监听处理函数等等。
脏值检测和更新部分
这里我们以一个带有鼠标时间的svelte组件为例,
编译前的代码:
svelte编译后的代码与hello world相比增加的代码:
这里多了一个instance函数,而这个instance函数在svelteComponent的init函数中就是用作脏值检测和更新的。
如果值发生了变动,就触发make_dirty函数:
make_dirty标记了哪一些脏组件,然后对脏组件执行schedule_update方法来更新组件:
schedule_update在需要更新时候,在下一个微任务重执行flush:
简化后的flush方法如上所示,就是遍历整个脏组件,执行所有的脏组件中的更新方法update.update方法的定义为:
update方法标记自身组件为脏,并且制定自身组件fragment中的p(全名:update)也就是前面的fragment中的:
在p方法中,直接操作dom改变UI。
总结来看,组件更新的步骤为以下几步:
The text was updated successfully, but these errors were encountered: