Svelte
https://gyazo.com/6a5dd38f343c9efa4deaaef9e9984c2f
擁有相對優秀的執行速度與檔案大小表現
安裝
code:bash
npm init vite
# choose svelte
code: bash
npm create svelte@latest my-app
cd my-app
npm install
npm run dev
基礎樣板
類似其他框架,使用獨有的.svelte檔案撰寫
code:svelte(html)
<script>
/* js gones here */
</script>
<style>
/* style goes here */
</style>
<!-- markup (zero or more items) goes here -->
代入變數
變數可直接於<script>標籤中常規宣告
使用{}代入/顯示變數
可使用於attribute的雙括號中
<p class="primary-{bar}">Some text</p>
同名則可省略
<img src={src} alt="A man dances.">
<img {src} alt="A man dances.">
匯入component 時,同樣於<script>中引入
import Nested from './Nested.svelte';
code:javascript
import App from "./App.svelte";
const app = new App({
target: document.body,
props: {
// we'll learn about props later
answer: 42,
},
});
<App />;
資料綁定
<input bind:value={name}>
會將值同步更新回變數
或將值反應到input上
也可用於套用DOM屬性
<input type="checkbox" bind:checked={isChecked} />
<input type="radio" bind:group={selected} vale={1} />
DOM元素
<input type="text" bind:this="{inputNode}" />
class
class:active={active}
class與變數同名時,可以省略掉後方的括弧
class:active
括號中可撰寫陳述式
class:active={status == 'playing'}
code:svelte(html)
<script>
let active = false;
</script>
<style>
.btn {
padding: 1em;
border-radius: .5em;
}
.active {
}
</style>
<button class="btn" class:active={active} on:click={() => active = !active}>
Click me
</button>
屬性傳遞
需於component中的變數前加上export
export let answer;
為Svelte用法,與JavaScript中的export用法不同
可於宣告時指定預設值
export let answer = 'a mystery';
將值傳入component中
<Nested answer={answer}>
<Info {...pkg}/>
意指指派值時會觸發的相關行為
類似Vue的computed
以$:宣告reactivity內容
component的狀態改變時,會自動更新$:後面的行為內容到DOM上
$: doubled = count * 2;
$:宣告的變數不需再加上let等宣告
$:裡亦可使用如console.log()等函式監看數值變化
reactivity只會於賦值時觸發
code:javascript
$: obj;
// 若是二段式的賦值,不會觸發更新
const foo = obj.foo;
foo.bar = "baz";
// 除非在最後加上 obj=obj
obj = obj;
如Array的push和splice不會觸發更新
push要改寫成numbers = [...numbers, numbers.length + 1];
賦值變數(等號左邊的變數)必須為$:宣告的更新變數
進行多段處裡時,加上{}即可
code:javascript
let length = "5";
let lengthNum;
$: {
lengthNum = parseInt(length);
console.log(lengthNum);
}
code:javascript
let package_name = 'svelte';
let download_count = 0;
.then(response => response.json())
.then(data => download_count = data.downloads || 0);
// Updating package_name will asynchronously update download_count
code:javascript
import { writable, derived } from 'svelte/store';
const package_name = writable('svelte');
const download_count = derived(
package_name,
($package_name, set) => {
.then(response => response.json())
.then(data => set(data.downloads));
return () => {
// We override the set function to eliminate race conditions
// This does *not* abort running fetch() requests, it only prevents
// them from overriding the store.
// To learn about canceling fetch requests, search the internet for AbortController
set = () => {}
}
}
);
// Updating $package_name will asynchronously update $download_count
分歧
依條件決定是否要算繪內容
code:svelte(javascript)
<script>
let x = 7;
</script>
{#if x > 10}
<p>{x} is greater than 10</p>
{:else if 5 > x}
<p>{x} is less than 5</p>
{:else}
<p>{x} is between 5 and 10</p>
{/if}
遞迴
遞迴陣列或array-like型態的資料
{#each 變數 as 區域變數, index}開頭,{/each}結尾
可於裡頭使用{區域變數.值}顯示內容
或先行解構{#each 變數 as { 值1, 值2 }} ... {/each}
替item加上key:{#each things as thing (thing.id)}
非同步
code:svelte(html)
<script>
export id;
const api = fetch(/post/${id}).then(res => res.json());
</script>
{#await api}
<p>...waiting</p>
{:then response}
<p>The response is {response}</p>
{:catch error}
<p style="color: red">{error.message}</p>
{/await}
事件
<div on:mousemove={handleMousemove}></div>
也可使用inline寫法
<div on:click={() => console.log("clicked");}></div>
修飾子
以|加在event的後方
preventDefault
stopPropagation
passive
capture
once
self
trusted
code:Event.svelte(html)
<script>
import { onMount, createEventDispatcher } from "svelte";
const dispatch = createEventDispatcher();
onMount(() => {
setTimeout(() => dispatch("completed", { elapsedTime: 3000 }), 3000);
});
</script>
code:App.svelte(html)
<script>
import Event from "./Event.svelte";
let completed = false;
let name = "world";
let once = false;
function handleClick(e) {
console.log("clicked");
}
</script>
<Event on:completed={() => completed = true}></Event>
{#if completed}
<span>Finished</span>
{/if}
初始化
beforeUpdate->onMount->afterUpdate
元件更新
beforeUpdate->afterUpdate
元件銷毀
onDestroy
掛載元件時
onMount
SSR模式不會呼叫
回傳函式會在unmount/銷毀時呼叫
狀態更新後、元件更新前
beforeUpdate
會在onMount之前執行
bind之類於onMount階段執行的處理會報錯
可能會更新狀態資料後,沒有反應到DOM上
狀態更新後、元件更新後
afterUpdate
元件銷毀時
onDestroy
code:javascript
import { onMount } from "svelte";
onMount(() => {
// execution here
});
Store
統一保存資料管理狀態
用於資料經常變動,並且需要跨元件使用時
提供3個可存取範圍不同的function
將想存於store的值以此function宣告即可
例如const count = writable(0);
writable()
可以使用update()或set()從外部修改store的值
code:javascript
import { writable } from 'svelte/store';
const countdown = writable(10);
countdown.update((currentValue) => {
return 9;
});
countodnw.set(9);
readable()
只能從內部元件使用set()修改
外部元件只能取得資料
例如滑鼠的所在位置,或是使用者的地理座標
第一個參數為預設值,第二個參數為修改資料的function
code:javascript
import { readable } from 'svelte/store';
chatSub = readable([], (set) => {
let chatList = [];
set([]...chatList, chat]);
})
derived()
接收一至多個store,進行加工
code:svelte(html)
<script>
import { derived } from "svelte/store";
const ids = writable(1, 2); set(list.filter((val, i) => ids.inclueds(i)));
});
</script>
{$selectedList}
以及 subscribe()
用以處理元件的生命週期問題
所有的store都有subscribe方法
值更新時會進行通知
以及處理元件的生命週期問題
呼叫後會回傳一個unsubscribe函數
可以搭配生命週期的onDestroy使用,避免memory leak問題
假如主應用中元件A 需要存取store
當使用者切換到另一個頁面,原本的元件A會銷毀並切換為元件B
但元件A的資料取用行為還存在(instance依然存在)
就會造成memory leak與無法預期的行為
code:store.js
import { writable } from "svelte/store";
export const count = writable(0);
code:Incrementer.svelte(html)
<script>
import { count } from "./stores.js";
function increment() {
count.update((n) => n + 1);
}
</script>
<button on:click="{count.increment}">+</button>
code:App.svelte(html)
<script>
import { onDestroy } from "svelte";
import { count } from "./stores.js";
import Incrementer from "./Incrementer.svelte"; // 對 store 進行存取操作的 component
let count_value;
const unsubscribe = count.subscribe((value) => {
count_value = value;
});
onDestroy(unsubscribe);
</script>
<h1>The count is {count_value}</h1>
<Incrementer />
Auto-subscription
或者在變數名稱前方加上$
Svelte會自動subscribe此store,同時處理unsubscribe邏輯
code:svelte(html)
<script>
import { count } from "./stores.js";
import Incrementer from "./Incrementer.svelte";
let count_value;
const unsubscribe = count.subscribe((value) => {
count_value = value;
});
</script>
<h1>The count is {$count}</h1>
<Incrementer />
Context
用於資料幾乎不會變動
或跨元件溝通時
沒有reactive效果
subscribe, unsubscribe
需在元件中使用才有效果
只作用在元件樹中
svelte會去尋找距離元件最近的context
code:App.svelte(html)
<script>
import Profile from "./Profile.svelte";
import { setContext } from "svelte";
const user = setContext("user", {
name: "Kalan",
age: 25,
});
</script>
<Profile />
code:Profile.svelte(html)
<script>
import { getContext } from "svelte";
const user = setContext("user");
</script>
<h2>{user.name}</h2>
<p>{user.age}</p>
Slot
可以將自定義元件/內容放進子元件中
code:Card.svelte(html)
<style>
.card {
width: 300px;
padding: 15px;
}
</style>
<div class="card">
<slot>
<p>Default Text</p>
</slot>
</div>
code:App.svelte(html)
<script>
import Card from "./Card.svelte";
let name = "world";
</script>
<Card>
<h2>Title</h2>
<p>Body</p>
</Card>
可以命名,以同時使用多個slot
可將變數傳給父元件
code:Card.svelte
<style>
.card {
width: 300px;
padding: 15px;
}
.image {
height: 150px;
background-position: center;
background-size: cover;
background-repeat: no-repeat;
}
</style>
<div class="card">
<slot name="card-image" imageURL="{imageURL}">
<div class="image" style="background-image: url({imageURL});"></div>
</slot>
<slot name="card-body">
<p>Default Text</p>
</slot>
</div>
code:App.svelte(html)
<script>
import Card from "./Card.svelte";
let name = "world";
let title;
let imageURL;
</script>
<style>
img {
max-width: 100%;
}
</style>
<Card imageURL={''}>
<img slot="card-image" let:imageURL={imageURL} src={imageURL} />
<p slot="card-body">
Body
</p>
</Card>
內建Element
可在正確的時間點幫忙處理自訂與銷毀監聽器
<svelte:self />
遞迴使用元件
<svelte:window />
操作window物件
<svelte:body />
類似<svelte:window />
操作body
<svelte:head />
操作head
<svelte:component />
動態引入svelte元件
<svelte:options />
對元件分別指定編譯選項
code:svelte(html)
<script>
export let count = 3;
</script>
{#if count > 0}
<p>countdown ... {count}</p>
<svelte:self count={count - 1} />
{:else}
<p>empty</p>
{/if}
code:svelte(html)
<script>
let innerWidth;
$: console.log(innerWidth);
</script>
<svelte:window
bind:innerWidth={innerWidth}
/>