北京时间 2024年10月20日凌晨12点45分
,在持续了 11
个月,发布了 272
个预览版后,Svelte 终于发布了 v5.0.0
正式版。
在发布后的 6小时内
,接近 400
的点赞人数,在所有开源项目中都是首屈一指的,可见大家对这次大版本发布的期待和关注。
为了照顾一下还不了解 Svelte 的读者,笔者对 Svelte 简单科普一下。
Svelte 的作者 Rich Harris
首先介绍一下 Svelte 背后的作者,叫做 Rich Harris
,是一位非常有影响力的 JavaScript 开发者和开源贡献者,最初是一名记者和数据可视化专家,曾在英国卫报 (The Guardian) 和纽约时报 (The New York Times) 工作。
Svelte 的想法在 2016年
感恩节期间形成,他受到编译器概念的启发,开始开发 Svelte 作为一种 “无框架的框架”
。
2022年,Rich Harris 加入 Vercel公司
,全职开发 Svelte,这一举动使 Svelte 的开发更加稳定和持续。
Svelte 的特点
其次,则是 Svelte 为何有如此大的魔力,让这么多人为其趋之若鹜呢?
以下是 Svelte 的主要特点:
- Svelte 是一个编译时框架,而不是运行时框架。
- 更小的 bundle size,因为框架代码不需要在运行时加载。
- 更快的运行速度,因为大部分工作在编译时完成。
- Svelte 使用简洁直观的语法来定义组件和行为。
- 降低学习曲线,特别是对于已经熟悉 HTML、CSS 和 JavaScript 的开发者。
- 提高开发效率,减少样板代码。
- Svelte 的反应性系统基于简单的赋值操作。
- 无需使用特殊的 API 或方法来触发更新。
- 代码更加直观和易于理解。
- Svelte 直接操作 DOM,而不使用虚拟 DOM。
- 潜在的性能提升,特别是在小型到中型应用中。
- 减少内存使用。
- Svelte 提供了内置的动画和过渡系统。
- 易于创建流畅的用户界面。
- 无需引入额外的库来处理动画。
- Svelte 默认提供组件级的样式封装。
- 避免样式冲突,无需额外的 CSS-in-JS 解决方案。
- 提高样式管理的可维护性。
- 通过 SvelteKit,Svelte 原生支持服务器端渲染。
- 改善首次加载性能和搜索引擎优化 (SEO)。
- 提供更好的用户体验,特别是在网络连接较慢的情况下。
- Svelte 生成的代码非常轻量。
- 适合性能敏感的应用,如移动 web 应用。
- 减少加载时间,提高用户体验。
- Svelte 官方支持 TypeScript。
- 提供类型安全,减少运行时错误。
- 改善开发体验,特别是在大型项目中。
- Svelte 可以轻松集成到现有项目中。
- 允许渐进式采用,无需一次性重写整个应用。
- 适合 both 小型组件和大型应用开发。
- Svelte 的设计理念是使前端开发更加简单和直观。
- 新开发者可以快速上手。
- 减少团队培训时间和成本。
- 尽管相对较新,Svelte 已经拥有一个快速增长的社区。
- 持续的改进和更新。
- 丰富的资源和第三方库。
特点不少,主要是照顾一下第一次了解 Svelte 的读者,可以对其进行一次更全面的认识。
那么对于笔者来说呢,主要是一个特点吸引了我:无运行时
。
也就是说,使用 Svelte 编译后的代码,不像 Vue 或 React 一样,编译后还有个运行时在里面。
也就是说,我们去看戏,必须得自己带凳子。
Svelte 不一样,直接坐地上,简单方便。
当然,具体的细节,肯定不是这个比喻这么简单,读者可以去查看官网,了解一下更全面的内容。
Svelte v5 版本更新内容
那么本次更新日志,虽然字不多,但是涉及到了 600 多个 issue 的处理,可以说是 字少事大
了。
- 更好的表现。
- 带有符文的更精细的反应系统。
- 具有片段和事件属性的更具表现力的模板语法。
- 原生 TypeScript 支持。
- 并与以前的语法向后兼容!
总结起来一句话:更好,更快,更强
。
来看看本次更新后的语法
Runes - 符文
本次最重要的更新,当 Runes 莫属,这是关于变量定义和效果管理的内容。
Svelte v4 的响应式写法:
<script>
let count = 0;
</script>
<button onclick={() => count++}>
clicks: {count}
</button>
Svelte v5 的响应式写法:
<script>
let count = $state(0);
</script>
<button onclick={() => count++}>
clicks: {count}
</button>
哈哈,是不是新语法有一种 Vue 的既视感?正所谓,万法归一,殊途同归。
那么不仅如此,类似这样的语法,大概有这么些:
$state
$state.raw
$state.snapshot
$derived
$derived.by
$effect
$effect.pre
$effect.tracking
$effect.root
$props
$bindable
$inspect
$host
这不仅让我回想起了 10 年前写 PHP 的时候,满屏幕的 $
的日子。。。
不过还是那句话,习惯就好,我喜欢新语法,喜欢 $
。
Snippets - 片段
片段的主要作用,就是让你少写重复代码。
笔者看到这个,只感觉两行感动的热泪流了下来 😭。
Vue 中的类似功能是这样的:
<script setup>
import { createReusableTemplate } from '@vueuse/core';
const [DefineTemplate, ReuseTemplate] = createReusableTemplate();
</script>
<template>
<DefineTemplate>
<!-- something complex -->
</DefineTemplate>
<dialog v-if="showInDialog">
<ReuseTemplate />
</dialog>
<div v-else>
<ReuseTemplate />
</div>
</template>
Svelte v4 的写法:
{#each images as image}
{#if image.href}
<a href={image.href}>
<figure>
<img
src={image.src}
alt={image.caption}
width={image.width}
height={image.height}
/>
<figcaption>{image.caption}</figcaption>
</figure>
</a>
{:else}
<figure>
<img
src={image.src}
alt={image.caption}
width={image.width}
height={image.height}
/>
<figcaption>{image.caption}</figcaption>
</figure>
{/if}
{/each}
Svelte v5 的写法:
{#snippet figure(image)}
<figure>
<img
src={image.src}
alt={image.caption}
width={image.width}
height={image.height}
/>
<figcaption>{image.caption}</figcaption>
</figure>
{/snippet}
{#each images as image}
{#if image.href}
<a href={image.href}>
{@render figure(image)}
</a>
{:else}
{@render figure(image)}
{/if}
{/each}
代码是人在写,人在看,电脑只是执行一串二进制。
写法上的提升,对于编程体验的影响是非常大的,代码应该优先服务于人,其次才服务于机器。
这种编程体验的改变,不亚于钓鱼佬钓到一条重达 2 两的大鲫鱼的快感。
Event handlers - 事件处理
Svelte v4 单事件的写法
<script>
let count = $state(0);
</script>
<button on:click={() => count++}>
clicks: {count}
</button>
Svelte v5 单事件的写法
<script>
let count = $state(0);
</script>
<button onclick={() => count++}>
clicks: {count}
</button>
Svelte v4 多事件的写法
<button on:click={one} on:click={two}>
...
</button>
Svelte v5 多事件的写法
<button
onclick={(e) => {
one(e);
two(e);
}}
>
...
</button>
Svelte v4 事件修饰符的写法
<button on:click|once|preventDefault={handler}>...</button>
Svelte v5 事件修饰符的写法
<script>
function once(fn) {
return function (event) {
if (fn) fn.call(this, event);
fn = null;
};
}
function preventDefault(fn) {
return function (event) {
event.preventDefault();
fn.call(this, event);
};
}
</script>
<button onclick={once(preventDefault(handler))}>...</button>
事件上面的写法,给我的感觉就是,返璞归真了。
说实话,我喜欢原来的写法。
Imports - 导入
flushSync
<script>
import { flushSync } from 'svelte';
let count = $state(0);
let element;
function onclick() {
flushSync(() => (count += 1));
// without `flushSync`, the DOM would be updated in the future
console.log(element.textContent === String(count));
}
</script>
<span bind:this={element}>{count}</span>
<button {onclick}>update</button>
mount
import { mount } from 'svelte';
import App from './App.svelte';
const app = mount(App, {
target: document.querySelector('#app'),
props: { some: 'property' }
});
hydrate
import { hydrate } from 'svelte';
import App from './App.svelte';
const app = hydrate(App, {
target: document.querySelector('#app'),
props: { some: 'property' }
});
摘录了几个代码片段,全部更新内容预览在这 https://svelte-5-preview.vercel.app