跳至主要內容

Svelte v2 發布了!

以下是你需要知道的

在我們首次在 Svelte 的 issue tracker 上討論版本 2 之後將近一年,終於到了進行一些重大變更的時候了。這篇部落格文章將說明變更了什麼、為什麼變更,以及您需要做什麼才能將您的應用程式更新到最新狀態。

tl;dr

以下每個項目都會在下面更詳細地說明。如果您遇到困難,請在我們友善的 Discord 聊天室中尋求協助。

  • 從 npm 安裝 Svelte v2
  • 使用 svelte-upgrade 升級您的模板
  • 移除對 component.observe 的呼叫,或從 svelte-extras 新增 observe 方法
  • 將對 component.get('foo') 的呼叫重寫為 component.get().foo
  • 從您的自訂事件處理常式傳回 destroy,而不是 teardown
  • 確保您沒有將數字字串 props 傳遞給元件

新的模板語法

最明顯的變更是:我們對模板語法做了一些改進。

我們聽到一個常見的回饋是「噁,Mustache」或「噁,Handlebars」。許多在先前網路開發時代使用基於字串的模板系統的人真的不喜歡它們。由於 Svelte 從那些語言中採用了 {{curlies}},許多人認為我們在某種程度上也存在這些工具的限制,例如奇怪的作用域規則或無法使用任意 JavaScript 表達式。

除此之外,JSX 證明了雙花括號是不必要的。因此,我們透過採用單花括號,讓我們的模板更加...svelte。結果看起來更加輕巧,而且更容易輸入

<h1>Hello {name}!</h1>

還有其他一些更新。但您不需要手動進行它們 — 只要在您的程式碼庫上執行 svelte-upgrade 即可

npx svelte-upgrade v2 src

這假設 src 中的任何 .html 檔案都是 Svelte 元件。您可以指定您喜歡的任何目錄,或將目標設為不同的目錄 — 例如,您會執行 npx svelte-upgrade v2 routes 來更新 Sapper 應用程式。

若要查看完整的變更集,請參閱 svelte-upgrade README

計算屬性

關於 Svelte,人們經常感到困惑的另一件事是計算屬性的運作方式。總結一下,如果您有一個具有以下內容的元件...

export default {
	
computed: {
    d: (a: any, b: any, c: any) => any;
}
computed
: {
d: (a: any, b: any, c: any) => anyd: (a: anya, b: anyb, c: anyc) => (a: anya = b: anyb + c: anyc) } };

...然後 Svelte 會先查看函式引數,以查看 d 依賴哪些值,然後它會編寫程式碼,透過將這些值注入函式中,在這些值變更時更新 d。這很酷,因為它允許您從元件的輸入衍生複雜的值,而不必擔心何時需要重新計算它們,但它也很...奇怪。JavaScript 並不是這樣運作的!

在 v2 中,我們改用 解構

export default {
	
computed: {
    d: ({ a, b, c }: {
        a: any;
        b: any;
        c: any;
    }) => any;
}
computed
: {
d: ({ a, b, c }: {
    a: any;
    b: any;
    c: any;
}) => any
d
: ({ a: anya, b: anyb, c: anyc }) => (a: anya = b: anyb + c: anyc)
} };

Svelte 編譯器仍然可以看到 d 依賴哪些值,但它不再注入值 — 它只是將元件狀態物件傳遞到每個計算屬性中。

同樣,您不需要手動進行此變更 — 只要如上所示在您的元件上執行 svelte-upgrade 即可。

抱歉,IE11。不是你的錯,是...嗯,其實,是的。是你的錯

Svelte v1 非常小心地只發出 ES5 程式碼,因此您不會被迫為了使用它而大費周章地使用轉譯器。但現在是 2018 年,幾乎所有瀏覽器都支援現代 JavaScript。透過放棄 ES5 的限制,我們可以產生更精簡的程式碼。

如果您需要支援 IE11 和其他瀏覽器,您將需要使用像 BabelBublé 這樣的轉譯器。

新的生命週期勾點

除了 oncreateondestroy 之外,Svelte v2 還新增了另外兩個 生命週期勾點,用於回應狀態變更

export default {
	
function onstate({ changed, current, previous }: {
    changed: any;
    current: any;
    previous: any;
}): void
onstate
({ changed: anychanged, current: anycurrent, previous: anyprevious }) {
// this fires before oncreate, and // whenever state changes },
function onupdate({ changed, current, previous }: {
    changed: any;
    current: any;
    previous: any;
}): void
onupdate
({ changed: anychanged, current: anycurrent, previous: anyprevious }) {
// this fires after oncreate, and // whenever the DOM has been updated // following a state change } };

您也可以透過程式設計方式監聽這些事件

component.on('state', ({ changed: anychanged, current: anycurrent, previous: anyprevious }) => {
	// ...
});

component.observe

有了新的生命週期勾點,我們不再需要 component.observe(...) 方法

// before
export default {
	function oncreate(): voidoncreate() {
		this.observe('foo', foo: anyfoo => {
			var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without calling require('console').

Warning: The global console object’s methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
//   Error: Whoops, something bad happened
//     at [eval]:5:15
//     at Script.runInThisContext (node:vm:132:18)
//     at Object.runInThisContext (node:vm:309:38)
//     at node:internal/process/execution:77:19
//     at [eval]-wrapper:6:22
//     at evalScript (node:internal/process/execution:76:60)
//     at node:internal/main/eval_string:23:3

const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);

myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err

const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
@seesource
console
.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100
log
(`foo is now ${foo: anyfoo}`);
}); } }; // after export default {
function onstate({ changed, current }: {
    changed: any;
    current: any;
}): void
onstate
({ changed: anychanged, current: anycurrent }) {
if (changed: anychanged.foo) { var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without calling require('console').

Warning: The global console object’s methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
//   Error: Whoops, something bad happened
//     at [eval]:5:15
//     at Script.runInThisContext (node:vm:132:18)
//     at Object.runInThisContext (node:vm:309:38)
//     at node:internal/process/execution:77:19
//     at [eval]-wrapper:6:22
//     at evalScript (node:internal/process/execution:76:60)
//     at node:internal/main/eval_string:23:3

const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);

myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err

const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
@seesource
console
.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100
log
(`foo is now ${current: anycurrent.foo}`);
} } };

這縮減了 Svelte 需要產生的程式碼量,並讓您擁有更大的彈性。例如,現在很容易在數個屬性中的任何一個變更時採取動作,例如在不對多個觀察器進行防抖的情況下重新繪製畫布。

但是,如果您偏好使用 component.observe(...),則可以從 svelte-extras 安裝它

import { import observeobserve } from 'svelte-extras';

export default {
	
methods: {
    observe: any;
}
methods
: {
observe: anyobserve } };

component.get

此方法不再採用可選的 key 引數 — 相反地,它始終傳回整個狀態物件

// before
const const foo: anyfoo = this.get('foo');
const const bar: anybar = this.get('bar');

// after
const { const foo: anyfoo, const bar: anybar } = this.get();

此變更最初可能看起來很煩人,但這是正確的舉動:除其他事項外,當我們未來更全面地探索該領域時,它可能會更好地與類型系統搭配使用。

event_handler.destroy

如果您的應用程式具有自訂事件處理常式,它們必須傳回具有 destroy 方法的物件,而不是 teardown 方法(這會將事件處理常式與元件 API 對齊)。

不再進行類型強制轉換

先前,傳遞給元件的數值會被視為數字

<Counter start="1" />

這會導致意外的行為,並且已變更:如果您需要傳遞字面數字,請將其作為表達式執行

<Counter start={1} />

編譯器變更

在大多數情況下,您永遠不需要直接處理編譯器,因此這不應要求您執行任何動作。無論如何,值得注意的是:編譯器 API 已變更。編譯器現在傳回 jscssaststats,而不是具有雜亂屬性的物件

const { const js: anyjs, const css: anycss, const ast: anyast, const stats: anystats } = svelte.compile(source, options);

jscss 都是 { code, map } 物件,其中 code 是字串,而 map 是原始碼對應。ast 是您元件的抽象語法樹,而 stats 物件包含有關元件的中繼資料以及有關編譯的資訊。

之前,有一個 svelte.validate 方法可以檢查您的元件是否有效。這已被移除 — 如果您想要檢查元件而不實際編譯它,只需傳遞 generate: false 選項即可。

我的應用程式壞了!幫幫我!

希望這涵蓋了一切,並且更新對您來說應該比對我們來說更容易。但是,如果您發現錯誤,或發現這裡沒有提及的事項,請到 Discord 聊天室或在 追蹤器上提出問題。