Svelte 提供了各种内置的特殊元素。
首个介绍的是
,它表示当前组件,允许在某些情况递归自身,这对于展示文件夹树之类的视图很有用,其中文件夹可以包含 其他 文件夹。
下方以展示文件夹树 Folder
为例:
Folder.svelte
- {name}
{#each folders as f}
-
{/each}
App.svelte
当前的结果只显示了根目录:
要显示所有子目录,这样是不可以的:
Folder.svelte
{#each folders as f}
{/each}
也就是说,你不可以在 Folder.svelte 中直接使用
自身,因为模块不能导入自身。但是我们可以使用
代表自身:
Folder.svelte
{#each folders as f}
{/each}
最终的结果:
基于
的作用是引用自身组件的构造函数,因此它十分容易导致堆栈溢出。
试想一下,Folder
组件中又使用了 Folder
组件,如果不加以“限制”,默认将会无穷无尽地渲染下去,直到爆栈报错。
所以,
必须包裹在 #if
块或者 #each
块中,或者由插槽
传给组件,其余情况均是禁止的。
组件可以通过
进行 “变身”,这样可以省掉一系列的if
块...
例如我们有3个十分简单的组件(显示不同颜色的文本):
Red.svelte
red text
Green.svelte
green text
Blue.svelte
blue text
如果我们的主程序中需要根据某个下拉框的选择的不同,展示不同的颜色文本:
那么程序可能需要很多的 if
来做判断:
App.svelte
{#if selected == 'red'}
{:else if selected == 'green'}
{:else }
{/if}
通过使用
,可以消除掉诸多 if
,更为动态地使用这3个组件:
App.svelte
this
的值可以是任何组件的构造函数,如果提供的是一个假值,则不会渲染这个组件。
正如任何 DOM 元素都可以将监听事件一样,你可以通过
来监听window
对象的事件,例如鼠标移动的事件:
App.svelte
({x, y} = e) } />
鼠标位置:{x}, {y}
你也可以添加像 preventDefault
这样的 事件修饰符,这与 DOM 元素的方法并无二致。
绑定
我们还可以绑定一些window
的属性,某些业务我们可能需要监测浏览器窗口的大小,原生的写法是通过监听 window.onresize
来实现的。
在 Svelte 中,它更为简单,并且支持绑定:
App.svelte
Window Size: width={width}, height={height}
支持绑定的属性列表如下:
innerWidth
- 获得浏览器窗口的内容区域的宽度,包含垂直滚动条(如果有的话)innerHeight
- 获得浏览器窗口的内容区域的高度,包含水平滚动条(如果有的话)outerWidth
- 获得浏览器窗口的宽度outerHeight
- 获得浏览器窗口的高度scrollX
- document 在水平方向已滚动的像素值scrollY
- document 在垂直方向已滚动的像素值online
- window.navigator.onLine
的别名,代表当浏览器能够访问网络。除了 scrollX
和 scrollY
两个属性之外,其余均是只读的。
与
类似,
元素允许你监听document.body
上的事件。这对于mouseenter
和mouseleave
事件来说十分有用,它们无法在window
上触发。
App.svelte
msg = 'Enter'}
on:mouseleave={() => msg = 'Leave'}
/>
{msg}
元素允许你将其他元素插入到文档中的中:
App.svelte
Hello world!
中可以插入例如
、
、
和 等等元素:
App.svelte
在服务器端渲染 (SSR) 模式下,
的内容与 HTML 的其余部分是分别返回的。
回顾第13章《插槽》中的第3小节 —— 命名插槽,我们向命名插槽
提供内容时,无可避免需要借助一个元素或者组件,例如:
42 Wallaby Way Sydney
如果没有 span
元素,那么 slot="address"
无处安放。
假设在某些情况下,我们借助别的元素还可能会影响到组件原有的布局,甚至要被迫放弃原有的 UI 描述方案,例如下方用于罗列菠萝包制作所需的材料列表的例子:
Material.svelte
菠萝包的食材用料:
- 盐
- 砂糖
- 奶粉
例子中预留了一个位置供使用端补充更多的用料,比如在 bottom
位置再添加一个“高筋面粉”:
App.svelte
高筋面粉
从展示的结果图来看,目前十分完美:
<svelte:fragment> 示例
菠萝包的制作用料中还有“低筋面粉”,如果你也想放到 bottom
位置,那么尴尬的事情就发生了:
App.svelte
高筋面粉
低筋面粉
Svelte 不允许提供两个相同 slot 名称的内容(参考第13章《插槽》中的第3小节),你将收到一个错误信息:“Duplicate slot name "bottom" in
下一个立即被想到的方案是这样:
App.svelte
高筋面粉
低筋面粉
为赋新词强说愁。强加了一个 div
作为容器,看看结果似乎又是对的:
但不要被表面蒙蔽,以下是实际的 HTML:
如果将所有用料使用横向布局,这个问题原形毕露:
Material.svelte
...
你可能留意到,最后一个样式规则,使用的是:global( )
,这是因为通过添加的
用料项目, 不会自动应用与 Material 组件中的
相同的样式。
因为样式在组件中默认就是局部作用域
的。
这种情况下,你必须将这个样式使用全局
样式(通过 :global 伪类),内容里的
才可以应用得 Material 中
的样式。
结果如下所示:
这就是前面所述的“可能会影响到组件原有的布局”的问题, App.svelte 例如你希望为组件生成属性访问器(默认不生成): App.svelte {x} 通过给 你可以留意到,变量 生成属性访问器的目的,是提供给使用端更容易去访问的,这通常是你编写了一个 Svelte 组件,想同时在 React 或者 Vue 等地方上用时才需要这个选项。 下列是允许在此处设置可选用的值: 有关这些选项的详细信息,请参考API文档。 对于
正是为了解决这个问题而生,只需要将
,问题迎刃而解:
7、
允许你配置编译器的选项。
svelte:options
加入 accessors
后,最终生成的 App
类如下:class App extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance, create_fragment, safe_not_equal, { x: 0 });
}
get x() {
return this.$$.ctx[0];
}
set x(x) {
this.$set({ x });
flush();
}
}
export default App;
x
已经生成为 App
类的一个属性,如果没有指定这个编译选项,生成的 App 类则是这样:class App extends SvelteComponent {
constructor(options) {
super();
init(this, options, instance, create_fragment, safe_not_equal, { x: 0 });
}
}
export default App;
immutable={true}
:你确信不会使用可变数据,因此编译器可以执行简单的引用相等性检查来确定值是否已更改。immutable={false}
:默认值,Svelte 将按默认的方式处理可变对象的更改accessors={true}
:为组件的 props 添加 getter 和 setteraccessors={false}
:默认值namespace="..."
:将使用此组件的命名空间,最常见的就是 "svg"
tag="..."
:将组件编译为自定义元素时,指定的标签名称总结
来说,99.9% 的情况下,你无需手工干预编译器的默认选项,除非你有十分明确的理由。
跟 Vue 的 功能近似,都是期望作为元素的容器,但同时自身无需渲染。
提供了一些十分有用的绑定,例如与尺寸相关的 innerWidth
和 innerHeight
,以及与滚动条相关的 scrollX
和 scrollY
,这两个还支持赋值,它可以使编写这方面的程序变得更简单。