メインコンテンツにスキップ

Svelte v2が登場!

知っておくべきこと

Svelteのイシュートラッカーでバージョン2について初めて話し合ってからほぼ1年、ついに破壊的な変更を行う時が来ました。このブログ記事では、変更点、変更理由、そしてアプリケーションを最新の状態にするために必要なことについて説明します。

要約

これらの項目はそれぞれ以下で詳しく説明します。行き詰まった場合は、フレンドリーなDiscordチャットルームで助けを求めてください。

  • npmからSvelte v2をインストールする
  • svelte-upgradeを使用してテンプレートをアップグレードする
  • component.observeの呼び出しを削除するか、svelte-extrasからobserveメソッドを追加する
  • component.get('foo')の呼び出しをcomponent.get().fooに書き換える
  • カスタムイベントハンドラーからteardownではなくdestroyを返す
  • コンポーネントに数値文字列プロップを渡していないことを確認する

新しいテンプレート構文

最も目に見える変更点:テンプレート構文を改善しました。

よく寄せられたフィードバックは「うぇ〜、Mustache」または「うぇ〜、Handlebars」でした。過去のWeb開発時代において文字列ベースのテンプレートシステムを使用していた多くの人々は、それらを*本当に*嫌っています。Svelteはこれらの言語から{{curly}}を採用したため、多くの人が、奇妙なスコープ規則や任意のJavaScript式を使用できないことなど、これらのツールの制限を何らかの形で共有していると想定していました。

その上、JSXは二重中括弧が不要であることを証明しました。そのため、単一の中括弧を採用することで、テンプレートをより...スベルトにしました。その結果、見た目はずっと軽くなり、入力するのがより快適になります。

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

他にもいくつかの更新があります。しかし、手動で行う必要はありません。svelte-upgradeをコードベースで実行するだけです。

npx svelte-upgrade v2 src

これは、src内のすべての.htmlファイルがSvelteコンポーネントであると想定しています。好きなディレクトリを指定したり、別のディレクトリをターゲットにすることができます。たとえば、Sapperアプリを更新するには、npx svelte-upgrade v2 routesを実行します。

変更点の全リストについては、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コードのみを出力するように注意していたため、Svelteを使用するためにトランスパイラをいじくり回す必要はありませんでした。しかし、今は2018年であり、ほとんどすべてのブラウザが最新のJavaScriptをサポートしています。ES5の制約を廃止することで、より無駄のないコードを生成できます。

IE11などをサポートする必要がある場合は、BabelBubléのようなトランスパイラを使用する必要があります。

新しいライフサイクルフック

oncreateondestroyに加えて、Svelte v2は状態の変更に対応するための2つのライフサイクルフックを追加します。

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

アプリにカスタムイベントハンドラーがある場合、teardownメソッドではなく、destroyメソッドを持つオブジェクトを返す必要があります(これにより、イベントハンドラーがコンポーネントAPIと一致します)。

型強制はもうありません

以前は、コンポーネントに渡された数値は数値として扱われていました。

<Counter start="1" />

これは予期しない動作を引き起こすため、変更されました。リテラルな数値を渡す必要がある場合は、式として渡してください。

<Counter start={1} />

コンパイラの変更

ほとんどの場合、コンパイラを直接扱う必要はないため、これはあなたにとって何のアクションも必要ありません。とにかく注意する価値はあります。コンパイラAPIが変更されました。さまざまなプロパティが混在したオブジェクトの代わりに、コンパイラはjscssast、およびstatsを返します。

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チャットルームにアクセスするか、トラッカーに問題を提起してください。