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) => any
d: (a: any
a, b: any
b, c: any
c) => (a: any
a = b: any
b + c: any
c)
}
};
…すると、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: any
a, b: any
b, c: any
c }) => (a: any
a = b: any
b + c: any
c)
}
};
Svelteコンパイラは、d
がどの値に依存しているかを確認できますが、値を注入することはなくなりました。コンポーネントの状態オブジェクトを各算出プロパティに渡すだけです。
繰り返しますが、この変更を手動で行う必要はありません。上記のように、コンポーネントでsvelte-upgradeを実行するだけです。
申し訳ありません、IE11。あなたではなく、…ええ、実際にはそうです。あなたです
Svelte v1はES5コードのみを出力するように注意していたため、Svelteを使用するためにトランスパイラをいじくり回す必要はありませんでした。しかし、今は2018年であり、ほとんどすべてのブラウザが最新のJavaScriptをサポートしています。ES5の制約を廃止することで、より無駄のないコードを生成できます。
IE11などをサポートする必要がある場合は、BabelやBubléのようなトランスパイラを使用する必要があります。
新しいライフサイクルフック
oncreate
とondestroy
に加えて、Svelte v2は状態の変更に対応するための2つのライフサイクルフックを追加します。
export default {
function onstate({ changed, current, previous }: {
changed: any;
current: any;
previous: any;
}): void
onstate({ changed: any
changed, current: any
current, previous: any
previous }) {
// this fires before oncreate, and
// whenever state changes
},
function onupdate({ changed, current, previous }: {
changed: any;
current: any;
previous: any;
}): void
onupdate({ changed: any
changed, current: any
current, previous: any
previous }) {
// this fires after oncreate, and
// whenever the DOM has been updated
// following a state change
}
};
これらのイベントをプログラムでリッスンすることもできます。
component.on('state', ({ changed: any
changed, current: any
current, previous: any
previous }) => {
// ...
});
component.observe
新しいライフサイクルフックにより、component.observe(...)
メソッドは不要になりました。
// before
export default {
function oncreate(): void
oncreate() {
this.observe('foo', foo: any
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
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.
log(`foo is now ${foo: any
foo}`);
});
}
};
// after
export default {
function onstate({ changed, current }: {
changed: any;
current: any;
}): void
onstate({ changed: any
changed, current: any
current }) {
if (changed: any
changed.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
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.
log(`foo is now ${current: any
current.foo}`);
}
}
};
これにより、Svelteが生成する必要があるコード量が削減され、柔軟性が向上します。たとえば、複数のオブザーバーのデバウンスを行わずに、*複数*のプロパティのいずれかが変更されたときにキャンバスを再描画するなどのアクションを実行するのが非常に簡単になりました。
ただし、component.observe(...)
を使用する場合は、svelte-extrasからインストールできます。
import { import observe
observe } from 'svelte-extras';
export default {
methods: {
observe: any;
}
methods: {
observe: any
observe
}
};
component.get
このメソッドは、オプションのkey
引数を受け取りません。代わりに、常に状態オブジェクト全体を返します。
// before
const const foo: any
foo = this.get('foo');
const const bar: any
bar = this.get('bar');
// after
const { const foo: any
foo, const bar: any
bar } = this.get();
この変更は最初は面倒に見えるかもしれませんが、正しい動きです。とりわけ、将来のタイプシステムをより完全に探求するにつれて、タイプシステムとうまく連携する可能性が高くなります。
event_handler.destroy
アプリにカスタムイベントハンドラーがある場合、teardown
メソッドではなく、destroy
メソッドを持つオブジェクトを返す必要があります(これにより、イベントハンドラーがコンポーネントAPIと一致します)。
型強制はもうありません
以前は、コンポーネントに渡された数値は数値として扱われていました。
<Counter start="1" />
これは予期しない動作を引き起こすため、変更されました。リテラルな数値を渡す必要がある場合は、式として渡してください。
<Counter start={1} />
コンパイラの変更
ほとんどの場合、コンパイラを直接扱う必要はないため、これはあなたにとって何のアクションも必要ありません。とにかく注意する価値はあります。コンパイラAPIが変更されました。さまざまなプロパティが混在したオブジェクトの代わりに、コンパイラはjs
、css
、ast
、およびstats
を返します。
const { const js: any
js, const css: any
css, const ast: any
ast, const stats: any
stats } = svelte.compile(source, options);
js
とcss
はどちらも{ code, map }
オブジェクトであり、code
は文字列、map
はソースマップです。 ast
はコンポーネントの抽象構文木であり、stats
オブジェクトにはコンポーネントに関するメタデータとコンパイルに関する情報が含まれています。
以前は、コンポーネントが有効かどうかを確認するsvelte.validate
メソッドがありました。これは削除されました。コンポーネントを実際にコンパイルせずにチェックする場合は、generate: false
オプションを渡すだけです。
アプリが壊れています!助けて!
これで全て網羅されていることを願っています。そして、アップデートは私たちにとってよりもあなたにとって簡単になるはずです。しかし、バグを見つけたり、ここで言及されていないことを発見した場合は、Discordチャットルームにアクセスするか、トラッカーに問題を提起してください。