点击上方 前端Q,关注公众号
回复加群,加入前端Q技术交流群
作者 | Ty Hopp
译者 | 核子可乐
策划 | Tina
作者用了 1 个月时间开发了一款个人 RSS 阅读器,并选择了 Svelte 和 SvelteKit 作为 Web 客户端的工具。这个选择的主要目的是为了评估这些工具是否适合在大型项目中使用。作者发文分享了对于 Svelte 的一些思考,这篇文章引起了 Hacker News 上读者的关注,并且被顶到了首页。fenomas 和 illilarian 这两位用户谈到了他们对于过渡和动画 API 的看法。如果需要在 Svelte 管理的元素进入和离开 DOM 时对其进行动画处理,那么作者“吐槽”的这些 API 就非常有用。看来作者之前的抱怨不成立了。如果是你,你会把 Svelte 用到大型公开项目中吗?
以下是这篇“吐槽”原文,由 InfoQ 翻译。
过去一个月来,我开发了一款个人 RSS 阅读器。
在 Web 客户端这边,我选的是 Svelte 和 SvelteKit,主要是为了评估这些工具适不适合在大型项目里使用。
下面跟大家聊聊我自己对于 Svelte 的一点思考。
开篇总结
总的来说,我挺喜欢 Svelte 的使用体验。它的亮点在组件格式、内置 store 和事件调度程度 API。短板主要是响应式语句 ($)、await 块和内置的过渡与动画 API。
组件格式
Svelte 的组件格式最得我心。在编写.svelte 文件时,默认上下文跟浏览器是完全相同的,都是用 HTML。
以下片段来自 Svelte 文档(包括示例标记、JS 和 CSS),应该可以说明问题:
<script>
// logic goes here
function add(a, b) {
return a + b;
}
</script>
<!-- markup (zero or more items) goes here -->
<p>1 + 2 is: {add(1, 2)}</p>
<style>
/* styles go here */
p {
color: blue;
}
</style>
再来跟 React 对比一下,这里的默认上下文是 JavaScript,而 HTML 要通过 JSX 进行交错:
import '../some-styles.css'; // styles are imported into JS files
export function SomeComponent() {
// logic can go anywhere
function add(a, b) {
return a + b;
}
// markup is returned from JS functions
return <p className="some-class">{`1 + 2 is: ${add(1, 2)}`}</p>;
}
我没法斩钉截铁地告诉大家,这种方式就一定更好,比如“Svelte 的组件格式能让团队在构建组件时,比某某框架快多少倍。”这个我确实不敢说。
但我觉得组件格式确实是很多朋友喜爱 Svelte 的原因。这可能是因为浏览器也优先使用 HTML,所以用 Svelte 的话上下文切换较少,但我不确定是不是这样。总之,我个人非常喜欢。
内置 store
Svelte 为状态管理提供内置的 store 选项。
其实大家对于用户界面库 / 框架应该关注什么、没必要关注还有争议。而 Svelte 聪明的地方,就在于它承认状态管理可能会成为问题,而且提供了相应的解决方案。大家可以根据需要使用或者扩展。
更贴心的是,这个解决方案不像 React 上下文那样跟组件树紧密相关。
事件调度程序 API
Svelte 提供一个内置 API 可用于创建、分派和在父元素上侦听 CustomEvent。
在基于单向数据流概念构建的系统中,其实很难为 Web 事件建模。从本质上讲,Web 的事件模型会让数据向上流动。
Svelte 承认用户可能需要向树结构的上方发送数据,并提供一个使用 Web 平台原语的 API。我必须给它点个赞!
响应式语句
我发现 Svelte 的响应式语句有点让人摸不着头脑。
Svelte 的基本响应基于变量分配。通过文档中的以下示例,我已经弄明白了:
<script>
let count = 0;
function handleClick() {
// calling this function will trigger an
// update if the markup references `count`
count = count + 1;
}
</script>
这个很合理。
但接下来在引入响应式 $ 标签时,文档给出了以下示例:
<script>
export let title;
export let person;
// this will update `document.title` whenever
// the `title` prop changes
$: document.title = title;
$: {
console.log(`multiple statements can be combined`);
console.log(`the current title is ${title}`);
}
// this will update `name` when 'person' changes
$: ({ name } = person);
// don't do this. it will run before the previous line
let name2 = name;
</script>
考虑到这是文档里关于该主题的第一个示例,似乎显得太复杂了。但只要认真看下来,还是能理解其中逻辑的。
但 Svelte 文档又提到:请务必注意,响应块在统计时会通过简单的静态分析进行排序,所有编译器查看的都是分配给块本身、并在块内部使用的变量,而不在它们调用的任何函数当中。这意味着以下示例中,yDependent 不会随着 x 的更新而一同更新:
<script>
let x = 0;
let y = 0;
const setY = (value) => {
y = value;
};
$: yDependent = y;
$: setY(x);
</script>
这种“玄学”般的设计,让我在很多情况下都想不明白为什么组件无法更新。
最终我发现,确实很难明确认定 $ 标签是否起效。有时候我用起来一切正常,但有时候用起来就没有效果,非常诡异。
所以我决定离它远点。另一个类似的问题是访问 store 值,它跟 $ 的情况差不多,时灵时不灵。
正是 $ 标签阻止了我在大型项目中使用 Svelte。这是 Svelte 的核心部分,不可能彻底回避,而且我觉得由此引发错误的可能性很高、而且影响范围很大。
Await 块
Svelte 提供{#if ...} 和 {#each ...} 语法作为标记渲染的主要控制流方法。它还提供{#await ...},可以根据 Promise 的状态来决定渲染什么。
我喜欢这个设计思路,但在实践中总是以重构告终。在 Promise 被解决或拒绝之后,我总得再调整一下才能开始渲染,所以我可不打算每次运行服务时都用它。
而且该逻辑也不属于渲染代码中的内联。那它到底是怎么工作的?
把{#await ...}剔出来并放进<script>逻辑当中,之后在渲染时使用局部变量。
我觉得Svelte把{#await ...} 保留在核心中倒不是坏事,只是很遗憾文档中那种优雅的用例根本就没法稳定实现。
过渡和动画 API
我对 Svelte 的过渡和动画 API 最大的不满,在于它们应该由 CSS 负责,怎么成 Svelte 的事情了呢?
Svelte 提供一种优雅的方式,可以在带有<style> 标签的组件中使用CSS。那么,为什么不在CSS中实现过渡和动画?
也许我只是没有找到真正能用上这些API的用例,确实。但在找到合适的用例之前,我两袖清风是要质疑为什么非得把这些复杂的API塞进Svelte核心。
总结
好了,这就是我结合实际使用整理的 Svelte 体会!
我愿意在之后的个人项目里继续用它,但身为架构师,我可能不会把它用在公司的大型项目当中。
我对 SvelteKit 也有一些不满,但在构建了 SPA 之后也就没什么话说了,毕竟 SvelteKit 似乎主要关注服务器端渲染。
大家回见!
原文链接:
https://tyhopp.com/notes/thoughts-on-svelte
声明:本文为 InfoQ 翻译,未经许可禁止转载。
往期推荐
Echarts无法实现这个曲线图,那我手写一个
Echarts无法实现这个曲线图,那我手写一个
Echarts无法实现这个曲线图,那我手写一个
最后
欢迎加我微信,拉你进技术群,长期交流学习...
欢迎关注「前端Q」,认真学前端,做个专业的技术人...
点个在看支持我吧