Skip to main content

Svelte v2 发布了!

以下是你需要知道的内容

在我们首次在 Svelte issue 追踪器上讨论版本 2 近一年后,现在终于是时候做一些突破性的改变了。这篇博文将解释什么发生了变化,为什么要改变,以及您需要做什么来更新您的应用程序。

简要说明

以下每一项在下文中都有详细描述。如果遇到困难,请在我们友好的 Discord 聊天室寻求帮助。

  • 从 npm 安装 Svelte v2
  • 使用 svelte-upgrade 升级您的模板
  • 移除对 component.observe 的调用,或从 svelte-extras 添加 observe 方法
  • component.get('foo') 的调用重写为 component.get().foo
  • 从您的自定义事件处理程序返回 destroy 而不是 teardown
  • 确保您没有向组件传递数字字符串属性

新的模板语法

最明显的变化:我们对模板语法做了一些改进。

我们经常听到的反馈是”呃,Mustache”或"呃,Handlebars”。许多在之前web开发时代使用过基于字符串的模板系统的人_真的_不喜欢它们。因为 Svelte 采用了这些语言的 {{大括号}},许多人认为我们某种程度上也有这些工具的局限性,比如奇怪的作用域规则或无法使用任意的 JavaScript 表达式。

此外,JSX 证明了双大括号是不必要的。所以我们通过采用单大括号使我们的模板更加...简洁。结果看起来更轻便,输入也更愉快:

<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 }) {
// 这在 oncreate 之前触发, // 并且在状态改变时触发 },
function onupdate({ changed, current, previous }: {
    changed: any;
    current: any;
    previous: any;
}): void
onupdate
({ changed: anychanged, current: anycurrent, previous: anyprevious }) {
// 这在 oncreate 之后触发, // 并且在 DOM 在状态改变后 // 更新时触发 } };

你也可以以编程方式监听这些事件:

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

component.observe

有了新的生命周期钩子,我们不再需要 component.observe(...) 方法:

// 之前
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 现在是 ${foo: anyfoo}`);
}); } }; // 之后 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 现在是 ${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 参数 — 相反,它总是返回整个状态对象:

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

// 之后
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 选项。

我的应用坏了!救命!

希望这里涵盖了所有内容,对你来说更新应该比对我们来说更容易。但如果你发现 bug,或发现这里没有提到的事情,请访问 Discord 聊天室 或在 追踪器 上提出问题。