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) => 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 }) {
// 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
如果你的应用具有 自定义事件处理程序,则它们必须返回一个具有 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
选项即可。
我的应用坏了!救命!
希望这涵盖了所有内容,并且更新对你来说应该比对我们来说更容易。但如果你发现错误,或发现此处未提及的内容,请访问 Discord 聊天室 或在 跟踪器 上提出问题。