脑海中来编译一个svelte(1)
https://svelte.dev/repl/99aeea705b1e48fe8610b3ccee948280?version=3.23.2arrow-up-right 这里可以看到一个最简单的svelte组件的输出结果(JS output), 如下:
Copy /* App.svelte generated by Svelte v3.23.2 */
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 ) ;
},
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 ; create_fragment
create_fragment就是给svelte的组件去构建DOM片段。
他的返回的对象里的方法:
c()
create 的短写
包含创建所有元素片段的指令。
这个例子里,就是创建h1元素
m(target, anctor)
mount 短写
就是挂载到对应的target上。
这个例子里就是mount h1到target上。
d(detaching)
destroy 短写
从target中删除某个元素。
这里的例子就是从DOM中移除h1。
export default class App extends SvelteComponent
svelte apiarrow-up-right
初始化的时候就使用类似create_fragment这些信息来组成。Svelte只会传递需要它的信息,并在不需要的时候删除它们。
试试空的组件,会输出什么信息。
https://svelte.dev/repl/1f29ce52adf446fc9116bb957b7200ec?version=3.19.1arrow-up-right
打开上面这个链接看看jsoutput:
create_fragment被改为null去传递了。
Svelte在init函数中设置了大多数内部的内容:
component的props, ctx(后面会解释ctx)和上下文
最后通过create_fragment去创建和挂载元素到DOM上。
并且所有的内部状态(state)和方法都被附加到this.$$上面。
所以你获取组件的$$属性,那么就在访问组件的内部了。
目前,大致了解了基本的组件行为,现在看下如何添加数据之后编译的输出如何改变的。
Svelte REPLarrow-up-right
这个在编译后的输出里有了改变:
可以发现内部的内容被移动到了代码的顶层,并且h1元素的文本内容是一个模板字面量。
这有很多的东西发生在幕后。
添加一个函数去更新name变量。
Svelte REPLarrow-up-right
然后发现编译后改变的地方:
一些新发现:
create_fragment返回的对象新增了一个p(ctx, dirty)方法。
update 短写
<script>中的内容被移到了instance函数中。
你是否发现create_fragment中使用的变量name被替换成了ctx[0],并且也不是模板字符串了。
那么,为什么要改变?
Svelte编译器跟踪所有在<script>标签中声明的变量,他跟踪变量是否存在以下情况:
可否进行改变(mutated)?,类似 count++
是否可以重新赋值?类似name = 'Svelte'
是否在模板中被引用?类似 Hello {name}
是否可写?类似 const i = 1; 和 let i = 1;
当Svelte编译器意识到可以重新分配变量name时,(由于name = 'Svelte';在update函数中),它将h1的文本内容分解为若干部分,以便动态更新部分文本。
实际上,可以看到有一个新方法p来更新文本节点。
p(ctx, dirty)
update 短写
p(ctx, dirty) 包含一些更新元素,他是基于组件中被改变的状态(dirty)和状态(ctx)。
instance variable
编译器发现变量name没有在App的不同组件实例间共享,这就是为什么会把name声明放到名为instance的函数中。
在上一个示例中,无论App组件有多少个实例,变量name的值在这些实例中都是相同的,并且没有变化:
目前的这个例子里,变量name在组件的一个实例里是可以改变的,因此变量name的声明被移到了instance函数中:
instance($$self, $$props, $$invalidate)
instance函数返回一个instance 变量列表,这些变量是:
可以改变(mutated)或重新分配(在组件的一个实例内改变)
在Svelte中,我们将这个实例变量列表称为ctx 。
init函数里,Svelte调用instance函数去创建 ctx ,并使用它去创建组件片段。
现在,不是直接在组件外貌访问变量name,而是直接通过传递的ctx 来访问变量name。
Svelte系统的反应能力是背后秘密是$$invalidate函数。
存在的这些变量只要:
会在改变或重新赋值之后通过$$invalidate函数正确的插入:
$$invalidate函数会标记变量为dirty并为组件安排更新:
Adding event listeners
现在来加一个事件监听:
Svelte REPLarrow-up-right
发现与之前的不同点:
一些发现:
在mount 期间侦听单击事件,并在destroy 中处理它
前面也提到了,instance函数返回的变量列表是被 在模板中引用 和 可变和重新赋值的 。
因为在模板中引用了update函数,所以作为在instance函数返回的 ctx 的一部分。
你如果在模板中不引用update,这个是不会被加到instance函数返回的数组里的。
由于Svelte尽可能的去返回简洁的js输出,没有必要是不会返回额外的变量。
listen and dispose
dispose(处理)
每当在Svelte中添加事件侦听器arrow-up-right 时,Svelte将注入代码来添加事件侦听器arrow-up-right ,并在从DOM中删除DOM片段时将其删除。
尝试多加几个事件监听:
Svelte REPLarrow-up-right
并观察编译后的输出:
Svelte并没有声明和创建一个新变量来删除每个事件侦听器,而是将它们全部分配给一个数组:
这样变量名压缩可以做的更好。
同样,这是Svelte试图生成更小的JavaScript输出的另一个很好的例子。当只有一个事件侦听器时,Svelte不会创建dispose数组。
Svelte语法是HTML的超集,就像ts是js的超集。
当你写Svelte组件的时候,Svelte编译器会分析你的代码并生成优化过的Js代码输出。
输出大致可以分成三个部分:
1. create_fragment
返回片段,他的内部是关于如何构建组件DOM片段的。
返回实例中用到的变量列表(可变和重新赋值的或模板中引用的)
$$invalidate 在实例变量被改变或者重新赋值的时候正确的插入
3. class App extends SvelteComponent
使用create_fragment和instance来初始化组件
Svelte尽可能生成简洁的JavaScript输出:
仅当部分文本可以更新时,将h1的文本内容拆分为单独的文本节点
create_fragment和instance仅仅在需要的时候才会去定义
根据事件侦听器的数量,以数组或函数的形式生成dispose变量
这里介绍了Svelte编译输出的基本结构,而这仅仅是开始。
后面还有,待续
基本就是翻译过来的,原文地址:
https://lihautan.com/compile-svelte-in-your-head-part-1/arrow-up-right