mirror of https://github.com/sveltejs/svelte
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
355 lines
12 KiB
355 lines
12 KiB
6 years ago
|
---
|
||
2 years ago
|
title: Svelte components
|
||
6 years ago
|
---
|
||
|
|
||
|
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.
|
||
|
|
||
2 years ago
|
```svelte
|
||
6 years ago
|
<script>
|
||
|
// logic goes here
|
||
|
</script>
|
||
|
|
||
3 years ago
|
<!-- markup (zero or more items) goes here -->
|
||
|
|
||
6 years ago
|
<style>
|
||
|
/* styles go here */
|
||
|
</style>
|
||
|
```
|
||
|
|
||
2 years ago
|
## <script>
|
||
6 years ago
|
|
||
|
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:
|
||
|
|
||
2 years ago
|
### 1. `export` creates a component prop
|
||
6 years ago
|
|
||
2 years ago
|
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](/docs/basic-markup#attributes-and-props) for more information).
|
||
6 years ago
|
|
||
2 years ago
|
```svelte
|
||
6 years ago
|
<script>
|
||
|
export let foo;
|
||
5 years ago
|
|
||
|
// Values that are passed in as props
|
||
|
// are immediately available
|
||
|
console.log({ foo });
|
||
|
</script>
|
||
|
```
|
||
|
|
||
2 years ago
|
---
|
||
|
|
||
|
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).
|
||
5 years ago
|
|
||
2 years ago
|
In development mode (see the [compiler options](/docs/svelte-compiler#svelte-compile)), 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`.
|
||
5 years ago
|
|
||
2 years ago
|
```svelte
|
||
5 years ago
|
<script>
|
||
5 years ago
|
export let bar = 'optional default initial value';
|
||
5 years ago
|
export let baz = undefined;
|
||
|
</script>
|
||
|
```
|
||
|
|
||
2 years ago
|
If you export a `const`, `class` or `function`, it is readonly from outside the component. Functions are valid prop values, however, as shown below.
|
||
3 years ago
|
|
||
2 years ago
|
```svelte
|
||
2 years ago
|
<!--- file: App.svelte --->
|
||
5 years ago
|
<script>
|
||
|
// these are readonly
|
||
|
export const thisIs = 'readonly';
|
||
6 years ago
|
|
||
2 years ago
|
/** @param {string} name */
|
||
5 years ago
|
export function greet(name) {
|
||
|
alert(`hello ${name}!`);
|
||
6 years ago
|
}
|
||
6 years ago
|
|
||
5 years ago
|
// this is a prop
|
||
2 years ago
|
export let format = (n) => n.toFixed(2);
|
||
5 years ago
|
</script>
|
||
|
```
|
||
|
|
||
2 years ago
|
Readonly props can be accessed as properties on the element, tied to the component using [`bind:this` syntax](/docs/component-directives#bind-this).
|
||
5 years ago
|
|
||
|
You can use reserved words as prop names.
|
||
|
|
||
2 years ago
|
```svelte
|
||
2 years ago
|
<!--- file: App.svelte --->
|
||
5 years ago
|
<script>
|
||
2 years ago
|
/** @type {string} */
|
||
5 years ago
|
let className;
|
||
|
|
||
|
// creates a `class` property, even
|
||
|
// though it is a reserved word
|
||
|
export { className as class };
|
||
6 years ago
|
</script>
|
||
|
```
|
||
|
|
||
2 years ago
|
### 2. Assignments are 'reactive'
|
||
6 years ago
|
|
||
|
To change component state and trigger a re-render, just assign to a locally declared variable.
|
||
|
|
||
|
Update expressions (`count += 1`) and property assignments (`obj.x = y`) have the same effect.
|
||
|
|
||
2 years ago
|
```svelte
|
||
6 years ago
|
<script>
|
||
|
let count = 0;
|
||
|
|
||
2 years ago
|
function handleClick() {
|
||
5 years ago
|
// calling this function will trigger an
|
||
|
// update if the markup references `count`
|
||
6 years ago
|
count = count + 1;
|
||
|
}
|
||
|
</script>
|
||
|
```
|
||
|
|
||
3 years ago
|
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](/tutorial/updating-arrays-and-objects).
|
||
3 years ago
|
|
||
2 years ago
|
```svelte
|
||
3 years ago
|
<script>
|
||
|
let arr = [0, 1];
|
||
|
|
||
2 years ago
|
function handleClick() {
|
||
3 years ago
|
// this method call does not trigger an update
|
||
|
arr.push(2);
|
||
|
// this assignment will trigger an update
|
||
|
// if the markup references `arr`
|
||
2 years ago
|
arr = arr;
|
||
3 years ago
|
}
|
||
|
</script>
|
||
|
```
|
||
|
|
||
2 years ago
|
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.
|
||
|
|
||
2 years ago
|
```svelte
|
||
2 years ago
|
<script>
|
||
|
export let person;
|
||
|
// this will only set `name` on component creation
|
||
|
// it will not update when `person` does
|
||
|
let { name } = person;
|
||
|
</script>
|
||
|
```
|
||
|
|
||
2 years ago
|
### 3. `$:` marks a statement as reactive
|
||
6 years ago
|
|
||
|
---
|
||
|
|
||
2 years ago
|
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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/label). Reactive statements run after other script code and before the component markup is rendered, whenever the values that they depend on have changed.
|
||
6 years ago
|
|
||
2 years ago
|
```svelte
|
||
6 years ago
|
<script>
|
||
|
export let title;
|
||
2 years ago
|
export let person;
|
||
6 years ago
|
|
||
|
// 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}`);
|
||
|
}
|
||
2 years ago
|
|
||
|
// this will update `name` when 'person' changes
|
||
|
$: ({ name } = person);
|
||
|
|
||
|
// don't do this. it will run before the previous line
|
||
|
let name2 = name;
|
||
6 years ago
|
</script>
|
||
|
```
|
||
|
|
||
4 years ago
|
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`.
|
||
|
|
||
2 years ago
|
```svelte
|
||
2 years ago
|
<!--- file: App.svelte --->
|
||
4 years ago
|
<script>
|
||
|
let x = 0;
|
||
|
let y = 0;
|
||
2 years ago
|
|
||
2 years ago
|
/** @param {number} value */
|
||
4 years ago
|
function yPlusAValue(value) {
|
||
|
return value + y;
|
||
|
}
|
||
2 years ago
|
|
||
4 years ago
|
$: total = yPlusAValue(x);
|
||
|
</script>
|
||
|
|
||
|
Total: {total}
|
||
2 years ago
|
<button on:click={() => x++}> Increment X </button>
|
||
4 years ago
|
|
||
2 years ago
|
<button on:click={() => y++}> Increment Y </button>
|
||
4 years ago
|
```
|
||
|
|
||
2 years ago
|
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:
|
||
|
|
||
2 years ago
|
```svelte
|
||
2 years ago
|
<script>
|
||
|
let x = 0;
|
||
|
let y = 0;
|
||
2 years ago
|
|
||
2 years ago
|
/** @param {number} value */
|
||
|
function setY(value) {
|
||
2 years ago
|
y = value;
|
||
2 years ago
|
}
|
||
2 years ago
|
|
||
2 years ago
|
$: yDependent = y;
|
||
|
$: setY(x);
|
||
|
</script>
|
||
|
```
|
||
|
|
||
2 years ago
|
Moving the line `$: yDependent = y` below `$: setY(x)` will cause `yDependent` to be updated when `x` is updated.
|
||
2 years ago
|
|
||
6 years ago
|
If a statement consists entirely of an assignment to an undeclared variable, Svelte will inject a `let` declaration on your behalf.
|
||
|
|
||
2 years ago
|
```svelte
|
||
2 years ago
|
<!--- file: App.svelte --->
|
||
6 years ago
|
<script>
|
||
2 years ago
|
/** @type {number} */
|
||
6 years ago
|
export let num;
|
||
|
|
||
|
// we don't need to declare `squared` and `cubed`
|
||
|
// — Svelte does it for us
|
||
|
$: squared = num * num;
|
||
|
$: cubed = squared * num;
|
||
|
</script>
|
||
|
```
|
||
|
|
||
2 years ago
|
### 4. Prefix stores with `$` to access their values
|
||
6 years ago
|
|
||
2 years ago
|
A _store_ is an object that allows reactive access to a value via a simple _store contract_. The [`svelte/store` module](/docs/svelte-store) contains minimal store implementations which fulfil this contract.
|
||
5 years ago
|
|
||
3 years ago
|
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.
|
||
6 years ago
|
|
||
5 years ago
|
Assignments to `$`-prefixed variables require that the variable be a writable store, and will result in a call to the store's `.set` method.
|
||
|
|
||
6 years ago
|
Note that the store must be declared at the top level of the component — not inside an `if` block or a function, for example.
|
||
|
|
||
2 years ago
|
Local variables (that do not represent store values) must _not_ have a `$` prefix.
|
||
6 years ago
|
|
||
2 years ago
|
```svelte
|
||
6 years ago
|
<script>
|
||
|
import { writable } from 'svelte/store';
|
||
|
|
||
|
const count = writable(0);
|
||
|
console.log($count); // logs 0
|
||
|
|
||
|
count.set(1);
|
||
|
console.log($count); // logs 1
|
||
5 years ago
|
|
||
|
$count = 2;
|
||
|
console.log($count); // logs 2
|
||
6 years ago
|
</script>
|
||
|
```
|
||
|
|
||
2 years ago
|
#### Store contract
|
||
5 years ago
|
|
||
2 years ago
|
```ts
|
||
|
// @noErrors
|
||
5 years ago
|
store = { subscribe: (subscription: (value: any) => void) => (() => void), set?: (value: any) => void }
|
||
5 years ago
|
```
|
||
|
|
||
2 years ago
|
You can create your own stores without relying on [`svelte/store`](/docs/svelte-store), by implementing the _store contract_:
|
||
5 years ago
|
|
||
|
1. A store must contain a `.subscribe` method, which must accept as its argument a subscription function. This subscription function must be immediately and synchronously called with the store's current value upon calling `.subscribe`. All of a store's active subscription functions must later be synchronously called whenever the store's value changes.
|
||
|
2. The `.subscribe` method must return an unsubscribe function. Calling an unsubscribe function must stop its subscription, and its corresponding subscription function must not be called again by the store.
|
||
2 years ago
|
3. A store may _optionally_ contain a `.set` method, which must accept as its argument a new value for the store, and which synchronously calls all of the store's active subscription functions. Such a store is called a _writable store_.
|
||
5 years ago
|
|
||
5 years ago
|
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.
|
||
|
|
||
2 years ago
|
## <script context="module">
|
||
6 years ago
|
|
||
|
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.
|
||
|
|
||
|
You can `export` bindings from this block, and they will become exports of the compiled module.
|
||
|
|
||
|
You cannot `export default`, since the default export is the component itself.
|
||
|
|
||
2 years ago
|
> Variables defined in `module` scripts are not reactive — reassigning them will not trigger a rerender even though the variable itself will update. For values shared between multiple components, consider using a [store](/docs/svelte-store).
|
||
5 years ago
|
|
||
2 years ago
|
```svelte
|
||
6 years ago
|
<script context="module">
|
||
|
let totalComponents = 0;
|
||
|
|
||
2 years ago
|
// the export keyword allows this function to imported with e.g.
|
||
6 years ago
|
// `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>
|
||
|
```
|
||
|
|
||
2 years ago
|
## <style>
|
||
6 years ago
|
|
||
|
CSS inside a `<style>` block will be scoped to that component.
|
||
|
|
||
|
This works by adding a class to affected elements, which is based on a hash of the component styles (e.g. `svelte-123xyz`).
|
||
|
|
||
2 years ago
|
```svelte
|
||
6 years ago
|
<style>
|
||
|
p {
|
||
|
/* this will only affect <p> elements in this component */
|
||
|
color: burlywood;
|
||
|
}
|
||
|
</style>
|
||
|
```
|
||
|
|
||
|
To apply styles to a selector globally, use the `:global(...)` modifier.
|
||
|
|
||
2 years ago
|
```svelte
|
||
6 years ago
|
<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;
|
||
|
}
|
||
3 years ago
|
|
||
|
p:global(.red) {
|
||
2 years ago
|
/* this will apply to all <p> elements belonging to this
|
||
3 years ago
|
component with a class of red, even if class="red" does
|
||
2 years ago
|
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
|
||
3 years ago
|
when updating the element's classList property directly. */
|
||
|
}
|
||
6 years ago
|
</style>
|
||
6 years ago
|
```
|
||
5 years ago
|
|
||
|
If you want to make @keyframes that are accessible globally, you need to prepend your keyframe names with `-global-`.
|
||
|
|
||
|
The `-global-` part will be removed when compiled, and the keyframe then be referenced using just `my-animation-name` elsewhere in your code.
|
||
|
|
||
2 years ago
|
```svelte
|
||
5 years ago
|
<style>
|
||
2 years ago
|
@keyframes -global-my-animation-name {
|
||
|
/* code goes here */
|
||
|
}
|
||
5 years ago
|
</style>
|
||
|
```
|
||
3 years ago
|
|
||
|
There should only be 1 top-level `<style>` tag per component.
|
||
|
|
||
|
However, it is possible to have `<style>` tag nested inside other elements or logic blocks.
|
||
|
|
||
|
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.
|
||
|
|
||
2 years ago
|
```svelte
|
||
3 years ago
|
<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>
|
||
3 years ago
|
```
|