绑定
Text inputsbind:value
<script>
let name = 'world';
</script>
<input value={name}>
<h1>Hello {name}!</h1>
对比vue:v-model=
=>bind:value=
Numeric inputsbind:value
<script>
let a = 1;
let b = 2;
</script>
<label>
<input type=number bind:value={a} min=0 max=10>
<input type=range bind:value={a} min=0 max=10>
</label>
<label>
<input type=number bind:value={b} min=0 max=10>
<input type=range bind:value={b} min=0 max=10>
</label>
<p>{a} + {b} = {a + b}</p>
复选框bind:checked
<script>
let yes = false;
</script>
<label>
<input type=checkbox bind:checked={yes}>
Yes! Send me regular email spam
</label>
{#if yes}
<p>Thank you. We will bombard your inbox and sell your personal details.</p>
{:else}
<p>You must opt in to continue. If you're not paying, you're the product.</p>
{/if}
<button disabled={!yes}>
Subscribe
</button>
输入框组绑定(单选框组、复选框组)bind:group
<script>
let scoops = 1;
let flavours = ['Mint choc chip'];
function join(flavours) {
if (flavours.length === 1) return flavours[0];
return `${flavours.slice(0, -1).join(', ')} and ${flavours[flavours.length - 1]}`;
}
let menus = [
'Cookies and cream',
'Mint choc chip',
'Raspberry ripple'
];
</script>
<h2>Size</h2>
<label>
<input type=radio bind:group={scoops} value={1}>
One scoop
</label>
<label>
<input type=radio bind:group={scoops} value={2}>
Two scoops
</label>
<label>
<input type=radio bind:group={scoops} value={3}>
Three scoops
</label>
<h2>Flavours</h2>
{#each menus as menu}
<label>
<input type=checkbox bind:group={flavours} value={menu}>
{menu}
</label>
{/each}
{#if flavours.length === 0}
<p>Please select at least one flavour</p>
{:else if flavours.length > scoops}
<p>Can't order more flavours than scoops!</p>
{:else}
<p>
You ordered {scoops} {scoops === 1 ? 'scoop' : 'scoops'}
of {join(flavours)}
</p>
{/if}
文本域绑定(多行文本)bind:value
<script>
let value = `Some words are *italic*, some are **bold**`;
</script>
<style>
textarea { width: 100%; height: 200px; }
</style>
<textarea bind:value={value}></textarea>
{@html value}
选择框绑定(多选框)bind:value
<script>
let questions = [
{ id: 1, text: `Where did you go to school?` },
{ id: 2, text: `What is your mother's name?` },
{ id: 3, text: `What is another personal fact that an attacker could easily find with Google?` }
];
let selected;
let answer = '';
function handleSubmit() {
alert(`answered question ${selected.id} (${selected.text}) with "${answer}"`);
}
</script>
<style>
input { display: block; width: 500px; max-width: 100%; }
</style>
<h2>Insecurity questions</h2>
<form on:submit|preventDefault={handleSubmit}>
<select bind:value={selected} on:change="{() => answer = ''}">
{#each questions as question}
<option value={question}>
{question.text}
</option>
{/each}
</select>
<input bind:value={answer}>
<button disabled={!answer} type=submit>
Submit
</button>
</form>
<p>selected question {selected ? selected.id : '[waiting...]'}</p>
选择框的multiple属性
选择框含有一个名为 multiple 的属性,在这种情况下,它将会被设置为数组而不是单值。
<script>
let scoops = 1;
let flavours = ['Mint choc chip'];
let menus = [
'Cookies and cream',
'Mint choc chip',
'Raspberry ripple'
];
function join(flavours) {
if (flavours.length === 1) return flavours[0];
return `${flavours.slice(0, -1).join(', ')} and ${flavours[flavours.length - 1]}`;
}
</script>
<select multiple bind:value={flavours}>
{#each menus as menu}
<option value={menu}>{menu}</option>
{/each}
</select>
{flavours}
contenteditable的绑定bind:innerHTML
支持 contenteditable="true"属性的标签,可以使用 textContent 与 innerHTML 属性的绑定:
<script>
let html = '<p>Write some text!</p>';
</script>
<div contenteditable="true" bind:innerHTML={html}></div>
<pre>{html}</pre>
<style>
[contenteditable] {
padding: 0.5em;
border: 1px solid #eee;
border-radius: 4px;
}
</style>
媒体标签的绑定bind:currentTime、bind:duration
和 同样支持部分的属性绑定
<video
poster="https://sveltejs.github.io/assets/caminandes-llamigos.jpg"
src="https://sveltejs.github.io/assets/caminandes-llamigos.mp4"
on:mousemove={handleMousemove}
on:mousedown={handleMousedown}
bind:currentTime={time}
bind:duration
bind:paused
></video>
尺寸的绑定bind:clientWidth
每个块级标签都可以对 clientWidth、clientHeight、offsetWidth 以及 offsetHeight 属性进行绑定:
<div bind:clientWidth={w} bind:clientHeight={h}>
<span style="font-size: {size}px">{text}</span>
</div>
this
this可以绑定到任何标签 (或组件) 并允许你获取对渲染标签的引用。 例如,我们对<canvas>
标签进行绑定
<script>
import { onMount } from 'svelte';
let canvas;
onMount(() => {
const ctx = canvas.getContext('2d');
let frame;
(function loop() {
frame = requestAnimationFrame(loop);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
for (let p = 0; p < imageData.data.length; p += 4) {
const i = p / 4;
const x = i % canvas.width;
const y = i / canvas.height >>> 0;
const t = window.performance.now();
const r = 64 + (128 * x / canvas.width) + (64 * Math.sin(t / 1000));
const g = 64 + (128 * y / canvas.height) + (64 * Math.cos(t / 1000));
const b = 128;
imageData.data[p + 0] = r;
imageData.data[p + 1] = g;
imageData.data[p + 2] = b;
imageData.data[p + 3] = 255;
}
ctx.putImageData(imageData, 0, 0);
}());
return () => {
cancelAnimationFrame(frame);
};
});
</script>
<style>
canvas {
width: 100%;
height: 100%;
background-color: #666;
-webkit-mask: url(svelte-logo-mask.svg) 50% 50% no-repeat;
mask: url(svelte-logo-mask.svg) 50% 50% no-repeat;
}
</style>
<canvas
bind:this={canvas}
width={32}
height={32}
></canvas>
组件绑定
正如可以绑定到DOM元素的属性一样,你也可以将组件的属性绑定。例如,我们能绑定位于<Keypad>
组件内的 value 属性,就如同一个表单标签一般:
<script>
import Keypad from './Keypad.svelte';
let pin;
$: view = pin ? pin.replace(/\d(?!$)/g, '•') : 'enter your pin';
function handleSubmit() {
alert(`submitted ${pin}`);
}
</script>
<h1 style="color: {pin ? '#333' : '#ccc'}">{view}</h1>
<Keypad bind:value={pin} on:submit={handleSubmit}/>
生命周期
onMount
它在组件首次呈现到 DOM 后运行
<script>
import { onMount } from 'svelte';
let photos = [];
onMount(async () => {
const res = await fetch(`https://jsonplaceholder.typicode.com/photos?_limit=20`);
photos = await res.json();
})
</script>
<style>
.photos {
width: 100%;
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-gap: 8px;
}
figure, img {
width: 100%;
margin: 0;
}
</style>
<h1>Photo album</h1>
<div class="photos">
{#each photos as photo}
<figure>
<img src={photo.thumbnailUrl} alt={photo.title}>
<figcaption>{photo.title}</figcaption>
</figure>
{:else}
<!-- this block renders when photos.length === 0 -->
<p>loading...</p>
{/each}
</div>
onDestroy
在组件被销毁时运行
<script>
import { onDestroy } from 'svelte';
let seconds = 0;
const interval = setInterval(() => seconds += 1, 1000);
onDestroy(() => {
clearInterval(interval)
})
</script>
<p>
The page has been open for
{seconds} {seconds === 1 ? 'second' : 'seconds'}
</p>
beforeUpdate和afterUpdate
beforeUpdate
函数实现在DOM渲染完成前执行。afterUpdate
函数则相反,它会运行在你的异步数据加载完成后。
let div;
let autoscroll;
beforeUpdate(() => {
autoscroll = div && (div.offsetHeight + div.scrollTop) > (div.scrollHeight - 20);
});
afterUpdate(() => {
if (autoscroll) div.scrollTo(0, div.scrollHeight);
});
tick
tick函数不同于其他生命周期函数,因为你可以随时调用它,而不用等待组件首次初始化。它返回一个带有resolve方法的 ,每当组件pending状态变化便会立即体现到DOM中 (除非没有pending状态变化)。
import { tick } from 'svelte';
await tick();
this.selectionStart = selectionStart;
this.selectionEnd = selectionEnd;
Stores
Writable stores
- writable
import { writable } from 'svelte/store';
export const count = writable(0);
- update
<script>
import { count } from './stores.js';
function increment() {
// TODO increment the count
count.update(n => n+1);
}
</script>
<button on:click={increment}>
+
</button>
- set
<script>
import { count } from './stores.js';
function reset() {
// TODO reset the count
count.set(0)
}
</script>
<button on:click={reset}>
reset
</button>
- subscribe
<script>
import { count } from './stores.js';
import Incrementer from './Incrementer.svelte';
import Decrementer from './Decrementer.svelte';
import Resetter from './Resetter.svelte';
let count_value;
const unsubscribe = count.subscribe(value => {
count_value = value;
});
</script>
<h1>The count is {count_value}</h1>
<Incrementer/>
<Decrementer/>
<Resetter/>
Auto-subscriptions
$xxx:包含订阅和取消订阅
<script>
import { count } from './stores.js';
import Incrementer from './Incrementer.svelte';
import Decrementer from './Decrementer.svelte';
import Resetter from './Resetter.svelte';
let count_value;
const unsubscribe = count.subscribe(value => {
count_value = value;
});
</script>
<h1>The count is {$count}</h1>
<Incrementer/>
<Decrementer/>
<Resetter/>
readable stores
并非所有stores都需要在其他地方写入,比如,你可能有一个代表鼠标位置或者用户地理位置的stores,这样的stores从其他地方写入并无意义,对于这种情况,我们有 只读(readable) stores。
点击到 stores.js 选项卡, 第一个参数 readable可以一个是个初始值,也可以为 null 或 undefined ,第二个参数是 start 函数,该函数有个 set 回调方法,并返回一个 stop函数。 当stores首次被subscriber 时调用start函数,stop则是最后当subscriber被unsubscribes时调用。
export const time = readable(new Date(), function start(set) {
const interval = setInterval(() => {
set(new Date());
}, 1000);
return function stop() {
clearInterval(interval);
};
});
stores derived
你可以创建一个stores,其内的值可以派生(derived)于一个或多个 其他 stores。在前面的示例的基础上,我们可以创建派生时间到其他页面:
export const elapsed = derived(
time,
$time => Math.round(($time - start) / 1000)
);
自定义stores(封装)
只要一个对象正确的使用 subscribe ,它就是可以称之为store。因此,使用特定语法来创建自定义 stores变得非常容易。
例如, 这个 count store 在前面的例子中包含 increment、 decrement 和 reset组件,以防止暴露 set 和update方法,让我们改写一下:
import { writable } from 'svelte/store';
function createCount() {
const { subscribe, set, update } = writable(0);
return {
subscribe,
increment: () => update(n => n + 1),
decrement: () => update(n => n - 1),
reset: () => set(0)
};
}
export const count = createCount();
使用:
<script>
import { count } from './stores.js';
</script>
<h1>The count is {$count}</h1>
<button on:click={count.increment}>+</button>
<button on:click={count.decrement}>-</button>
<button on:click={count.reset}>reset</button>
绑定Store
如果 store可写入的(即具有set方法),则可以绑定其值,就像可以绑定局部组件状态一样。
<input bind:value={$name}>
<button on:click="{() => $name += '!'}">
Add exclamation mark!
</button>