logo
  • 現在做什麼
  • 關於我

Kalan

文章分類

  • 前端
  • 開發筆記
  • 雜談
  • 年度回顧

快速連結

  • 現在做什麼
  • 關於我
  • 聯絡我
  • 職涯思考🔗

關注我

在福岡生活的開發者,分享軟體開發與日本生活的點點滴滴。

© 2025 Kalan Made with ❤️. All rights reserved.

Svelte Summit 2020 心得

由愷開愷開撰寫2020年10月24日 16:15
首頁/前端
💡

如果想問問題或單純回饋的話可以填寫表單唷

English日文

目錄

  1. 前言
  2. The Zen of Svelte
  3. Prototyping with Svelte
  4. How does Svelte's crossfade function work
  5. Svelte Animation
  6. Unlocking The Power of Svelte action
  7. Demystifying Svelte Transitions
  8. Futuristic Web Development
  9. 後記

前言

Svelte 是我相當喜歡的前端框架之一,簡單的語法與彈性,作者的理念,還有各種動畫、過場的使用方式都讓我相當喜歡,之前也有寫過幾篇關於自己對 Svelte 的看法:

  • Svelte — 是什麼讓我遇見這樣的你
  • Svelte 筆記(1) — 沒有銀彈
  • Svelte 筆記(2) — 編譯器比你聰明多惹

Svelte Summit 在 2020 年 10 月 18 日舉行,因為疫情的關係是以全程線上直播的形式播出,總共 7 個小時與 17 個演講。在這邊紀錄一些我覺得有趣的演講與自己的心得。

The Zen of Svelte

這個演講在說 Svelte 裡頭的各種「哲學」,它的設計與 Python 當中的哲學類似。

Svelte 從語法上著手,盡可能減低學習成本,就算是非前端領域甚至是初學者都可以非常容易上手。從官方文件上也可以略知一二,除了閱讀教學文字之外,另外一邊就是 REPL,你可以直接操作程式碼來看看實際效果如何。

Svelte 官方教學文件

作者 Rich Harris 在 Rethink Reactivity 這場演講曾經提過:

Rich Harris - Rethinking reactivity

Frameworks are not tools for organizing your code, frameworks are tools for organizing your mind 框架不是整理程式碼的工具,而是整理「心智」的工具。 — Rich Harris

舉例來說,React 的出現讓 Component、Immutable、Reactivity、State 等概念在前端造成一波革命,不少後續框架的出現都是以此為根基,徹底改變了前端開發手法。比起框架的「寫法」本身,更重要的是這些框架背後想要傳達的概念與設計哲學。

作者也提到,像 jQuery 這樣的工具出現,讓像他這樣非本科出身、非程式背景的人也可以變成網頁開發的一份子。從這點可以略知一二作者對 Svelte 的願景是什麼。

註:我在這篇文章中有談論更多,有興趣的話可以一起參考

harris-jquery

你懂 HTML, CSS, JavaScript,就代表你會寫 Svelte;反過來也一樣。跟一般 template engine 不太一樣的地方是 Svelte 編譯的方式不同。template engine 往往會和後端做搭配,將變數傳到 template engine 之後變成 HTML 程式碼;在 Svelte 當中全部的程式碼會變成對應的 JavaScript(在不是 SSR 模式時),並且可以動態執行。

我喜歡這樣的哲學,寫程式不一定就要變成工程師,就像做料理不一定要是廚師一樣。有可能只是想動手解決日常生活中的問題。

從另外一個角度來看,或許有人會問跳過 JavaScript 基礎直接學習 Svelte 是件好事嗎?我的想法是:「看目的」。

如果你今天打算成為一位合格的前端工程師,那在某個時間點勢必要去了解背後運作的原理,但如果你今天只是想打造一個網頁解決自己的問題,就算對基礎沒有那麼了解也沒關係。

這個演講裡頭還有提到 The zen of just writting css 這篇文章。在 Svelte 裡頭你可以直接將 style 寫在元件裡頭,達到 HTML, CSS, JavaScript 三者合一的效果,Svelte 會幫你做 hash 所以可以直接避免命名衝突的問題。在 React 當中我們時常會使用 CSS-in-JS 的方案來避免這樣的問題,不過我們是否有更好的方式來做到 styling 這件事呢?

例如在 styled-components 中我們可以這樣子寫:

const Component = styled.div`
  padding: ${props => props.padding}px;
`

// 搭配 theme
const ThemeComponent = styled.div`
  background-color: ${props => props.theme.mainColor};
`;

這是個非常好用且有趣的寫法,我們可以定義 theme 在 context 當中,並且在宣告元件時動態取用。不過也因為動態宣告的原因,這些樣式勢必需要在 runtime 執行,因為我們沒辦法知道可能傳進來的 prop 有哪些,這也就代表樣式沒辦法完全透過靜態生成。

但是在 Svelte 當中可以直接用 SSR 額外渲染出一個獨立的樣式表:

result: {
	js,
	css,
	ast,
	warnings,
	vars,
	stats
} = svelte.compile(source: string, options?: {...})

在元件中宣告的樣式會額外放到 css ,這樣就可以將樣式與程式碼拆開。說實在的這樣是好是壞或許是見仁見智,像是 constant 之類需要共用的顏色、字型等等用起來可能就沒那麼方便(比起 styled-components 等方案)

Prototyping with Svelte

Svelte 內建的 transition 與 animation 等 directive 非常好用,所以就算是設計師也可以馬上作 prototype 來驗證想法。在 REPL 上也可以很快看到結果作驗證。推薦給想要打臉工程師做不出來的設計師們。

How does Svelte's crossfade function work

crossfade 是個相當好用的 transition,什麼是 crossfade 呢?像這樣元素在兩個區域之間做移動就叫做 crossfade。

在這場 talk 當中講者講述 crossfade 在 Svelte 裡頭是如何實作,然後實際上是怎麼使用在他的產品上的。

crossfade 本身使用了 Svelte 當中 key 的概念,每次執行時會去找對應 key 的節點,計算兩者之間的位置之後使用 transition 做過場動畫。因為可以套用 transition 這個 Svelte 內建的 directive,所以使用起來相對方便很多。(範例取自官方教學文件)

Svelte Animation

Svelte 當中有一個內建的 directive 叫做 animate ,在使用迴圈 each 時,如果列表內的元素有變動,就會觸發 animate。使用的方式像下面這樣:

{#each list as item (item.id)}
  <div animate:flip>
    ...
  </div>
{/each}

在 Svelte 當中有內建一個 animate 叫做 flip ,分別是 First, Last, Invert, Play 的簡寫搭配而成,是一個做動畫時的技巧。首先我們先紀錄元素中尚未變化與變化後的位置(First, Last),然後開始計算兩者之間的差異(寬高、移動偏移量等),最後執行動畫。詳細說明可以參考 Tech Bridge 的專欄文章 — FLIP 技巧總複習

Svelte 把這一連串的計算簡化成 flip 給你開箱即用,搭配 crossfade 的效果像這樣:

所有的移動都有一個 transition,感覺就比較舒適了對吧!

Unlocking The Power of Svelte action

這篇是在講 Svelte 當中的 action,有點類似 hook 的感覺。在 Svelte 當中你可以這樣寫:

<div use:keyboard={{shortcut}} class="player-container">
</div>

這邊的 use 就是 Svelte action 的 directive。後面的 keyboard 則是自定義的函數,他的函數簽名會像這樣:

function keyboard(node, options) {}

這個函數可以回傳一個物件,裡頭可以包含 update 與 destory,分別代表當 parameter 有改變時的更新函數與 destory 時會執行的函數。因為第一個函數就是節點本身,所以要做 DOM 相關的操作時非常方便。

講者在影片裡頭講解了他使用 Svelte Action 的心得,值得參考。

Demystifying Svelte Transitions

這篇是在講 Svelte Transition 是怎麼實作的,並且一步步拆解原始碼給你看,相當精彩!Svelte 的 transition 機制非常有趣,並不是用 JavaScript 動態操作的。

在使用 jQuery 的動畫時,jQuery 會根據你給定的參數加入 inline style 給元素,像是:

$('.div').animate({
  ...
})

jQuery 會不斷更新 inline css 的屬性直到動畫停止為止。也就是說內部的實作大概會像這樣(偽程式碼)

while (!stop) {
  updateCSS()
}

setTimeout(() => stop = true, duration);

也就是說這段時間 JavaScript 會一直執行程式碼,當應用變大的時候就會明顯影響效能。

那麼一般的框架是怎麼實作的呢?像是 Vue 與 React 當中,你可以這樣寫:

// vue
<transition name="fade">
    <p v-if="show">hello</p>
</transition>

// React
<CSSTransition in={inProp} timeout={200} classNames="fade">
  <div>
		
  </div>
</CSSTransition>

其中,Vue 會在對應的時間點分別幫你塞入 fade-leave-active fade-enter-active fade-enter 等對應的 class name;而 React 也是類似的原理,他會幫你塞入 fade-enter fade-active fade-exit fade-exit-active 等 class name。但是 transition 的方式,需要你自己在 css 裡頭定義。

這點雖然避免了透過 JavaScript 計算造成的效能問題,但有時候光靠 CSS 做動畫似乎也有些麻煩。有沒有辦法結合 JavaScript 可以動態計算的特性,再加上 CSS 動畫的輔助?有!Svelte 做到了。

在你宣告以下程式碼的時候, Svelte 會做幾件事:

<div transition:scale={{duration: 1000}}>
</div>
  1. transition 代表出場、入場都套用同樣的過場方式
  2. Svelte 開始計算這個動畫需要幾個幀數。假設動畫時間為 1000 毫秒,計算方式為 1000 / 16 為 62.5 幀。這邊取 16 代表更新率 60Hz 的情況下每個幀數所需要的毫秒數約為 16ms(1000 / 60)。
  3. Svelte 會以動畫開始時間點當作 0,動畫結束時間點當作 1,以變數 t 為命名,並且用 easing 函數當作插值給 t
  4. Svelte 會透過動態建立一個 css keyframe,並且加入此 animation 給元素
  5. 透過 stylesheet.insertRule 動態加入 style 到當前 document 中。

我們來看看原始碼:

// src/runtime/internal/style_manager.ts
export function create_rule(node: Element & ElementCSSInlineStyle, a: number, b: number, duration: number, delay: number, ease: (t: number) => number, fn: (t: number, u: number) => string, uid: number = 0) {
	const step = 16.666 / duration;
	let keyframes = '{\\n';

	for (let p = 0; p <= 1; p += step) {
		const t = a + (b - a) * ease(p);
		keyframes += p * 100 + `%{${fn(t, 1 - t)}}\\n`;
	}

	const rule = keyframes + `100% {${fn(b, 1 - b)}}\\n}`;
	const name = `__svelte_${hash(rule)}_${uid}`;
	const doc = node.ownerDocument as ExtendedDoc;
	active_docs.add(doc);
	const stylesheet = doc.__svelte_stylesheet || (doc.__svelte_stylesheet = doc.head.appendChild(element('style') as HTMLStyleElement).sheet as CSSStyleSheet);
	const current_rules = doc.__svelte_rules || (doc.__svelte_rules = {});

	if (!current_rules[name]) {
		current_rules[name] = true;
		stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length);
	}

	const animation = node.style.animation || '';
	node.style.animation = `${animation ? `${animation}, ` : ``}${name} ${duration}ms linear ${delay}ms 1 both`;

	active += 1;
	return name;
}

這個函數執行之後會產生類似下面的 css:

@keyframes __svelte__dynamic_hash_name {
  0% {
    transform: scale(0);
  }
  1.6% {
    transform: scale(0);  
  }
  ...
  100% {
    transform: scale(1);
  }
}

以上為範例程式碼,實際情形會根據 duration 及 easing 函數不同。Svelte 不僅會幫你建立 transition,也會幫你管理這些 transition。例如在 transition 做到一半停止時,Svelte 會幫你把 animation 刪除並停止,也會幫你管理各種生命週期。

這樣一來同時結合了 CSS Animation 的效能,同時也達到用 JavaScript 動態控制的好處。

想要看更多的話,可以實際到影片當中瞧瞧,講者講得非常清楚!

Futuristic Web Development

Rich Harris 本人現身說法,來講述下一代網站開發的流程變化。不過目前的說法都還不是最終定案,所以就先抱著期待的心看看就好。

以往我們打包資源時,都是用像 rollup 或是 webpack 的方式作打包,並透過他們的依賴追蹤機制來更新,當時的瓶頸在於瀏覽器普遍看不懂 ES6 語法及 import 機制,為了讓開發者能夠更好管理程式碼,才催生這些工具產生。現在瀏覽器支援逐漸完整,越來越多的應用是直接仰賴於原生的 ES Module 上面。例如最近出的 VuePress、Snowpack 都是直接走原生的 ES Module 應用,最大的好處在於我們不需要等待那麼長的時間才能看到結果,而是直接透過瀏覽器的機制引入,所以也不用再等 bundler 們把程式碼編譯好。Svelte 在之後也預計使用 snowpack ,有興趣的話可以瞧瞧。

後記

我在這次 IT 鐵人賽有拍了一系列關於 Svelte 的教學影片,講解了 Svelte 的各種功能、進階應用、常見 UI 實作、Svelte 原理,到最後實作一個簡單版本的 Svelte,有興趣的話都可以來瞧瞧。

後續我也會將影片當中的內容轉成文章方便閱讀,請大家持續關注我的部落格或是直接訂閱 RSS!

← 站在中間的那群人 / 做出選擇的那群人從 Kotlin 當中學到的事:Kotlin DSL 與 Annotation →

如果覺得這篇文章對你有幫助的話,可以考慮下面的連結請我喝一杯 ☕ 可以讓我平凡的一天變得閃閃發光 ✨

☕Buy me a coffee

目錄

  1. 前言
  2. The Zen of Svelte
  3. Prototyping with Svelte
  4. How does Svelte's crossfade function work
  5. Svelte Animation
  6. Unlocking The Power of Svelte action
  7. Demystifying Svelte Transitions
  8. Futuristic Web Development
  9. 後記