Svelte之基础知识二:绑定、生命周期、Stores

绑定

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>
  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值