跳至主要内容

Svelte v2 发布了!

你需要知道这些

在 Svelte 问题跟踪器上首次讨论版本 2 差不多一年后,我们终于可以进行一些重大更改了。这篇博文将解释发生了哪些变化、变化的原因以及你需要采取哪些措施来更新你的应用。

tl;dr

下面将更详细地描述每个项目。如果你遇到问题,请在我们的友好Discord 聊天室中寻求帮助。

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

新的模板语法

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

我们听到的一个常见反馈是“啊,小胡子”或“啊,Handlebars”。许多在 Web 开发早期使用基于字符串的模板系统的人非常不喜欢它们。由于 Svelte 从这些语言中采用了{{curlies}},因此很多人认为我们以某种方式共享了这些工具的局限性,例如奇怪的作用域规则或无法使用任意 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 }) {
// 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 聊天室 或在 跟踪器 上提出问题。