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) => 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 代码,这样你就不必被迫折腾转译器就能使用它。但现在是2018年了,几乎所有浏览器都支持现代 JavaScript。通过放弃 ES5 限制,我们可以生成更精简的代码。
如果你需要支持 IE11 及其同类浏览器,你需要使用像 Babel 或 Bublé 这样的转译器。
新的生命周期钩子
除了 oncreate
和 ondestroy
外,Svelte v2 增加了两个新的 生命周期钩子 来响应状态变化:
export default {
function onstate({ changed, current, previous }: {
changed: any;
current: any;
previous: any;
}): void
onstate({ changed: any
changed, current: any
current, previous: any
previous }) {
// 这在 oncreate 之前触发,
// 并且在状态改变时触发
},
function onupdate({ changed, current, previous }: {
changed: any;
current: any;
previous: any;
}): void
onupdate({ changed: any
changed, current: any
current, previous: any
previous }) {
// 这在 oncreate 之后触发,
// 并且在 DOM 在状态改变后
// 更新时触发
}
};
你也可以以编程方式监听这些事件:
component.on('state', ({ changed: any
changed, current: any
current, previous: any
previous }) => {
// ...
});
component.observe
有了新的生命周期钩子,我们不再需要 component.observe(...)
方法:
// 之前
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 现在是 ${foo: any
foo}`);
});
}
};
// 之后
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 现在是 ${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
参数 — 相反,它总是返回整个状态对象:
// 之前
const const foo: any
foo = this.get('foo');
const const bar: any
bar = this.get('bar');
// 之后
const { const foo: any
foo, const bar: any
bar } = this.get();
这个改变最初可能看起来很烦人,但这是正确的选择:除其他事项外,随着我们在未来更充分地探索类型系统,它可能会更好地适应类型系统。
event_handler.destroy
如果你的应用有 自定义事件处理程序,它们必须返回一个带有 destroy
方法的对象,而_不是_ teardown
方法(这使事件处理程序与组件 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
选项。
我的应用坏了!救命!
希望这里涵盖了所有内容,对你来说更新应该比对我们来说更容易。但如果你发现 bug,或发现这里没有提到的事情,请访问 Discord 聊天室 或在 追踪器 上提出问题。