组件是 Svelte 应用的构建块。 它们使用 HTML 的超集写入 .svelte
文件中。
英Components are the building blocks of Svelte applications. They are written into .svelte
files, using a superset of HTML.
所有三个部分 — 脚本、样式和标记 — 是可选的。
英All three sections — script, styles and markup — are optional.
<script>
// logic goes here
</script>
<!-- markup (zero or more items) goes here -->
<style>
/* styles go here */
</style>
<script>permalink
<script>
块包含创建组件实例时运行的 JavaScript。 在顶层声明(或导入)的变量是来自组件标记的 'visible'。 还有四项附加规则:
英A <script>
block contains JavaScript that runs when a component instance is created. Variables declared (or imported) at the top level are 'visible' from the component's markup. There are four additional rules:
1.export 创建一个组件 prop(1. export creates a component prop)permalink
Svelte 使用 export
关键字将变量声明标记为属性或属性,这意味着组件的使用者可以访问它(有关更多信息,请参阅有关 属性和参数 的部分)。
英Svelte uses the export
keyword to mark a variable declaration as a property or prop, which means it becomes accessible to consumers of the component (see the section on attributes and props for more information).
<script>
export let foo;
// Values that are passed in as props
// are immediately available
console.log({ foo });
</script>
你可以为 prop 指定默认初始值。 如果组件的使用者在实例化组件时未指定组件上的 prop(或者其初始值为 undefined
),则会使用它将。 请注意,如果随后更新 props 的值,则任何未指定值的 prop 将被设置为 undefined
(而不是其初始值)。
英You can specify a default initial value for a prop. It will be used if the component's consumer doesn't specify the prop on the component (or if its initial value is undefined
) when instantiating the component. Note that if the values of props are subsequently updated, then any prop whose value is not specified will be set to undefined
(rather than its initial value).
在开发模式下(参见 编译器选项),如果未提供默认初始值并且消费者未指定值,则会打印警告。 要消除此警告,请确保指定默认初始值,即使它是 undefined
。
英In development mode (see the compiler options), a warning will be printed if no default initial value is provided and the consumer does not specify a value. To squelch this warning, ensure that a default initial value is specified, even if it is undefined
.
<script>
export let bar = 'optional default initial value';
export let baz = undefined;
</script>
如果导出 const
、class
或 function
,则它从组件外部是只读的。 然而,函数是有效的 prop 值,如下所示。
英If you export a const
, class
or function
, it is readonly from outside the component. Functions are valid prop values, however, as shown below.
<script>
// these are readonly
export const thisIs = 'readonly';
/** @param {string} name */
export function greet(name) {
alert(`hello ${name}!`);
}
// this is a prop
export let format = (n) => n.toFixed(2);
</script>
<script lang="ts">
// these are readonly
export const thisIs = 'readonly';
export function greet(name: string) {
alert(`hello ${name}!`);
}
// this is a prop
export let format = (n) => n.toFixed(2);
</script>
只读 props 可以作为元素上的属性进行访问,并使用 bind:this
语法 绑定到组件。
英Readonly props can be accessed as properties on the element, tied to the component using bind:this
syntax.
你可以使用保留字作为属性名称。
英You can use reserved words as prop names.
<script>
/** @type {string} */
let className;
// creates a `class` property, even
// though it is a reserved word
export { className as class };
</script>
<script lang="ts">
let className: string;
// creates a `class` property, even
// though it is a reserved word
export { className as class };
</script>
2.作业为 'reactive'(2. Assignments are 'reactive')permalink
要更改组件状态并触发重新渲染,只需分配给本地声明的变量即可。
英To change component state and trigger a re-render, just assign to a locally declared variable.
更新表达式 (count += 1
) 和属性赋值 (obj.x = y
) 具有相同的效果。
英Update expressions (count += 1
) and property assignments (obj.x = y
) have the same effect.
<script>
let count = 0;
function handleClick() {
// calling this function will trigger an
// update if the markup references `count`
count = count + 1;
}
</script>
由于 Svelte 的反应性基于分配,因此使用 .push()
和 .splice()
等数组方法不会自动触发更新。 需要后续分配才能触发更新。 此信息以及更多详细信息也可以在 tutorial 中找到。
英Because Svelte's reactivity is based on assignments, using array methods like .push()
and .splice()
won't automatically trigger updates. A subsequent assignment is required to trigger the update. This and more details can also be found in the tutorial.
<script>
let arr = [0, 1];
function handleClick() {
// this method call does not trigger an update
arr.push(2);
// this assignment will trigger an update
// if the markup references `arr`
arr = arr;
}
</script>
Svelte 的 <script>
块仅在创建组件时运行,因此当 prop 更新时,<script>
块内的分配不会自动再次运行。 如果你想跟踪属性的更改,请参阅下一节中的下一个示例。
英Svelte's <script>
blocks are run only when the component is created, so assignments within a <script>
block are not automatically run again when a prop updates. If you'd like to track changes to a prop, see the next example in the following section.
<script>
export let person;
// this will only set `name` on component creation
// it will not update when `person` does
let { name } = person;
</script>
3. $: 将语句标记为响应式(3. $: marks a statement as reactive)permalink
任何顶层语句(即不在块或函数内)都可以通过在其前面添加 $:
JS 标签语法 来使其成为响应式语句。 只要响应式语句所依赖的值发生更改,响应式语句就会在其他脚本代码之后、渲染组件标记之前运行。
英Any top-level statement (i.e. not inside a block or a function) can be made reactive by prefixing it with the $:
JS label syntax. Reactive statements run after other script code and before the component markup is rendered, whenever the values that they depend on have changed.
<script>
export let title;
export let person;
// this will update `document.title` whenever
// the `title` prop changes
$: document.title = title;
$: {
console.log(`multiple statements can be combined`);
console.log(`the current title is ${title}`);
}
// this will update `name` when 'person' changes
$: ({ name } = person);
// don't do this. it will run before the previous line
let name2 = name;
</script>
只有直接出现在 $:
块中的值才会成为反应语句的依赖。 例如,在下面的代码中,只有当 x
发生变化时,total
才会更新,而 y
则不会。
英Only values which directly appear within the $:
block will become dependencies of the reactive statement. For example, in the code below total
will only update when x
changes, but not y
.
<script>
let x = 0;
let y = 0;
/** @param {number} value */
function yPlusAValue(value) {
return value + y;
}
$: total = yPlusAValue(x);
</script>
Total: {total}
<button on:click={() => x++}> Increment X </button>
<button on:click={() => y++}> Increment Y </button>
<script lang="ts">
let x = 0;
let y = 0;
function yPlusAValue(value: number) {
return value + y;
}
$: total = yPlusAValue(x);
</script>
Total: {total}
<button on:click={() => x++}> Increment X </button>
<button on:click={() => y++}> Increment Y </button>
值得注意的是,反应式块是在编译时通过简单的静态分析进行排序的,并且编译器查看的所有变量都是分配给块本身并在块本身内使用的变量,而不是在它们调用的任何函数中。 这意味着在以下示例中更新 x
时,yDependent
不会更新:
英It is important to note that the reactive blocks are ordered via simple static analysis at compile time, and all the compiler looks at are the variables that are assigned to and used within the block itself, not in any functions called by them. This means that yDependent
will not be updated when x
is updated in the following example:
<script>
let x = 0;
let y = 0;
/** @param {number} value */
function setY(value) {
y = value;
}
$: yDependent = y;
$: setY(x);
</script>
将 $: yDependent = y
行移到 $: setY(x)
下方将导致 x
更新时 yDependent
也更新。
英Moving the line $: yDependent = y
below $: setY(x)
will cause yDependent
to be updated when x
is updated.
如果语句完全包含对未声明变量的赋值,Svelte 将代表你注入 let
声明。
英If a statement consists entirely of an assignment to an undeclared variable, Svelte will inject a let
declaration on your behalf.
<script>
/** @type {number} */
export let num;
// we don't need to declare `squared` and `cubed`
// — Svelte does it for us
$: squared = num * num;
$: cubed = squared * num;
</script>
<script lang="ts">
export let num: number;
// we don't need to declare `squared` and `cubed`
// — Svelte does it for us
$: squared = num * num;
$: cubed = squared * num;
</script>
4. 使用 $ 前缀存储来访问它们的值(4. Prefix stores with $ to access their values)permalink
存储是一个对象,允许通过简单的存储合约对值进行反应式访问。 svelte/store
模块 包含履行此合同的最小存储实现。
英A store is an object that allows reactive access to a value via a simple store contract. The svelte/store
module contains minimal store implementations which fulfil this contract.
每当你引用存储时,都可以通过在组件中添加 $
字符作为前缀来访问其值。 这会导致 Svelte 声明前缀变量,在组件初始化时订阅存储并在适当时取消订阅。
英Any time you have a reference to a store, you can access its value inside a component by prefixing it with the $
character. This causes Svelte to declare the prefixed variable, subscribe to the store at component initialization and unsubscribe when appropriate.
对 $
前缀变量的赋值要求该变量是可写存储,并且将导致对存储的 .set
方法的调用。
英Assignments to $
-prefixed variables require that the variable be a writable store, and will result in a call to the store's .set
method.
请注意,存储必须在组件的顶层声明 — 例如,不在 if
块或函数内。
英Note that the store must be declared at the top level of the component — not inside an if
block or a function, for example.
局部变量(不代表存储值)不得具有 $
前缀。
英Local variables (that do not represent store values) must not have a $
prefix.
<script>
import { writable } from 'svelte/store';
const count = writable(0);
console.log($count); // logs 0
count.set(1);
console.log($count); // logs 1
$count = 2;
console.log($count); // logs 2
</script>
存储合同(Store contract)permalink
ts
store = {subscribe : (subscription : (value : any) => void) => (() => void),set ?: (value : any) => void }
你可以在不依赖 svelte/store
的情况下创建自己的存储,通过实现存储合约:
英You can create your own stores without relying on svelte/store
, by implementing the store contract:
- 存储必须包含
.subscribe
方法,该方法必须接受订阅函数作为其参数。 调用.subscribe
后,必须立即使用存储的当前值同步调用此订阅函数。 每当存储的值发生变化时,以后都必须同步调用存储的所有活动订阅函数。 .subscribe
方法必须返回取消订阅函数。 调用取消订阅函数必须停止其订阅,并且其对应的订阅函数不得被存储再次调用。- 存储可以选择包含
.set
方法,该方法必须接受存储的新值作为其参数,并且同步调用存储的所有活动订阅函数。 这样的存储称为可写存储。
为了与 RxJS Observables 的互操作性,.subscribe
方法也允许使用 .unsubscribe
方法返回一个对象,而不是直接返回取消订阅函数。 但请注意,除非 .subscribe
同步调用订阅(Observable 规范不需要),否则 Svelte 会将存储的值视为 undefined
,直到它执行为止。
英For interoperability with RxJS Observables, the .subscribe
method is also allowed to return an object with an .unsubscribe
method, rather than return the unsubscription function directly. Note however that unless .subscribe
synchronously calls the subscription (which is not required by the Observable spec), Svelte will see the value of the store as undefined
until it does.
<script context="module">permalink
具有 context="module"
属性的 <script>
标记在模块首次评估时运行一次,而不是针对每个组件实例运行。 在此块中声明的值可以从常规 <script>
(和组件标记)访问,但反之则不然。
英A <script>
tag with a context="module"
attribute runs once when the module first evaluates, rather than for each component instance. Values declared in this block are accessible from a regular <script>
(and the component markup) but not vice versa.
你可以从此块中进行 export
绑定,它们将成为已编译模块的导出。
英You can export
bindings from this block, and they will become exports of the compiled module.
你不能 export default
,因为默认导出是组件本身。
英You cannot export default
, since the default export is the component itself.
module
脚本中定义的变量不是响应式的 — 即使变量本身会更新,重新分配它们也不会触发重新渲染。 对于多个组件之间共享的值,请考虑使用 store。
<script context="module">
let totalComponents = 0;
// the export keyword allows this function to imported with e.g.
// `import Example, { alertTotal } from './Example.svelte'`
export function alertTotal() {
alert(totalComponents);
}
</script>
<script>
totalComponents += 1;
console.log(`total number of times this component has been created: ${totalComponents}`);
</script>
<style>permalink
<style>
块内的 CSS 范围将限于该组件。
英CSS inside a <style>
block will be scoped to that component.
这是通过向受影响的元素添加一个类来实现的,该类基于组件样式的哈希(例如 svelte-123xyz
)。
英This works by adding a class to affected elements, which is based on a hash of the component styles (e.g. svelte-123xyz
).
<style>
p {
/* this will only affect <p> elements in this component */
color: burlywood;
}
</style>
要将样式全局应用到选择器,请使用 :global(...)
修饰符。
英To apply styles to a selector globally, use the :global(...)
modifier.
<style>
:global(body) {
/* this will apply to <body> */
margin: 0;
}
div :global(strong) {
/* this will apply to all <strong> elements, in any
component, that are inside <div> elements belonging
to this component */
color: goldenrod;
}
p:global(.red) {
/* this will apply to all <p> elements belonging to this
component with a class of red, even if class="red" does
not initially appear in the markup, and is instead
added at runtime. This is useful when the class
of the element is dynamically applied, for instance
when updating the element's classList property directly. */
}
</style>
如果你想使 @keyframes 可以全局访问,你需要在关键帧名称前添加 -global-
。
英If you want to make @keyframes that are accessible globally, you need to prepend your keyframe names with -global-
.
编译时,-global-
部分将被删除,然后在代码中的其他位置仅使用 my-animation-name
来引用关键帧。
英The -global-
part will be removed when compiled, and the keyframe then be referenced using just my-animation-name
elsewhere in your code.
<style>
@keyframes -global-my-animation-name {
/* code goes here */
}
</style>
每个组件只能有 1 个顶层 <style>
标签。
英There should only be 1 top-level <style>
tag per component.
但是,可以将 <style>
标签嵌套在其他元素或逻辑块内。
英However, it is possible to have <style>
tag nested inside other elements or logic blocks.
在这种情况下,<style>
标签将按原样插入到 DOM 中,不会对 <style>
标签进行范围界定或处理。
英In that case, the <style>
tag will be inserted as-is into the DOM, no scoping or processing will be done on the <style>
tag.
<div>
<style>
/* this style tag will be inserted as-is */
div {
/* this will apply to all `<div>` elements in the DOM */
color: red;
}
</style>
</div>