又一个框架来了!被称为要取代 React , vue ,Aangular !

点击上方“开发者技术前线”,选择“星标”

13:21 在看 真爱

又一个框架来了!被称为要取代 React , vue ,Aangular !_第1张图片

 
作者 | Mark Volkmann译者 | 王强  编辑 | 张之栋、王文婧
近年来,Web 框架层出不穷,作为主流 Web 框架之一的 Svelte,有着独特的优势。它不仅可以构建完整的 Web 应用程序,还可以创建自定义元素,并在其他框架实现的已有 Web 应用程序中使用。本文将对 Svelte 进行详细的介绍,并带领读者了解使用 Svelte 从头开始构建 Web 应用程序所需的基础知识。
Svelte 是什么?

Svelte 是 React、Vue 和 Angular 等 Web 框架的替代方案。与其同类产品一样,Svelte 可用于构建完整的 Web 应用程序。它还能用来创建自定义元素,这些自定义元素可以在使用其他框架实现的已有 Web 应用程序中使用。

Svelte 由 Rich Harris 开发,Rich Harris 曾在《卫报》工作,目前任职于《纽约时报》。Harris 先前创建的 Ractive Web 框架被《卫报》采用,并成为 Vue 的部分功能的灵感来源。Harris 还创建了 Rollup 模块打包器,它是 Webpack 和 Parcel 的替代品。

Svelte 尚未得到应有的重视。人们提到它时往往更关注它生成打包代码的能力,它打包出来的代码明显比竞争对手更小。但除此之外 Svelte 还简化了许多工作,包括定义组件、管理组件状态、管理应用程序状态以及添加动画等。

本文对 Svelte 进行了详尽的介绍,并提供了使用它从头开始构建 Web 应用程序所需的基础知识。

为什么要用 Svelte?

与其他 Web 框架创建的应用程序相比,Svelte 应用程序的包体积较小。它将应用程序代码编译到一个只包含少量框架代码的优化过的 JavaScript 文件来做到了这一点。

Svelte 是用 TypeScript 实现的 Web 应用程序编译器。它不是运行时库。例如,稍后介绍的 Todo 应用程序的包体积只有等效 React 应用程序的 13%。这两款应用程序的链接如下:

https://github.com/mvolkmann/svelte-todo

https://github.com/mvolkmann/react-todo

一项调查统计了现实世界中 Web 应用使用多种 Web 框架构建的相关数据。统计 显示,使用一些流行框架的应用经过 gzip 压缩后的大小 (KB)分别为:
  • Angular + ngrx:134
  • React + Redux:193
  • Vue:41.8
  • Svelte:9.7

调查报告:

https://www.freecodecamp.org/news/a-realworld-comparison-of-front-end-frameworks-with-benchmarks-2019-update-4be0d3c78075/

某些 Web 框架(包括 React 和 Vue)使用虚拟 DOM 来优化渲染更改。重新渲染组件时,框架会在内存中构建 DOM 的新版本,然后将其与以前的版本做对比,不一样的部分才会被应用到实际的 DOM 上。

尽管这比更新实际 DOM 中的所有内容要快,但构建虚拟 DOM 并将其与前一个 DOM 进行比较是需要花时间的。

Svelte 无需使用虚拟 DOM 就可以提供反应性。为了做到这一点,它会跟踪影响各个组件渲染的顶级组件变量的更改,并仅在检测到更改时才重新渲染 DOM 的这些部分。这样就能获得良好的性能表现。

Svelte 大大简化了组件和应用程序状态管理。相关功能包括上下文、存储和模块上下文,稍后将逐一详细介绍。

Svelte 为可访问性问题提供了运行时警告。例如,没有 alt 属性的元素会被标记出来。Svelte 当前不支持 TypeScript,但正在推进相关工作。Svelte Native 支持开发移动应用程序。它基于 NativeScript。

Svelte 会消失吗?

有人说一旦应用程序构建完毕,Svelte 就会消失。

Svelte 库主要由 node_modules/svelte 目录中的.js 文件定义。主要函数在 internal.js 中定义,目前大约有 1400 行代码。

其他库文件则针对特定功能,具体包括:
  • easing.js
  • motion.js
  • register.js
  • store.js
  • transition.js

输入 npm run build 会在 public 目录中生成文件,包括 bundle.js。应用程序使用的 Svelte 库函数将复制到 bundle.js 的顶部。后文展示的 Todo 应用程序中,这里大约是 500 行代码。

因此 Svelte 库代码不会消失,只是它与其他 Web 框架相比体积很小。

重要资源
下面是学习 Svelte 时需要查看的重要资源列表:
  • 重新思考反应性:https://svelte.dev/blog/svelte-3-rethinking-reactivity

这是 Rich Harris 在 You Gotta Love Frontend(YGLF)Code Camp 2019 上的演讲。它解释了 Svelte 3 背后的动力,并提供了一些简要介绍。
  • Svelte 主页:https://svelte.dev
  • Svelte 教程:https://svelte.dev/tutorial
  • Svelte API:https://svelte.dev/docs
  • Svelte 示例:https://svelte.dev/examples
  • 在线 Svelte REPL:https://svelte.dev/repl

想要尝试编写少量 Svelte 代码的话这个网站很有用。它还可以显示生成的代码并保存代码,以供分享和提交问题。
  • Svelte 博客:https://svelte.dev/blog
  • Discord 聊天室:https://discordapp.com/invite/yy75DKs
  • Svelte GitHub 存储库:https://github.com/sveltejs/svelte

入  门 
下面我们来一步步创建并运行一个 Svelte 应用程序。
  1. 从 https://nodejs.org 安装 Node.js。

这将安装 node、npm 和 npx 命令。
  1. npx degit sveltejs/template app-name

Rich Harris 创建了 degit 工具来简化项目框架。它会下载一个 git 仓库,默认为 master 分支。本例中“sveltejs”是用户名,“template”是存储库。第二个参数是要创建的目录名称。
  1. cd app-name
  2. npm install
  3. npm run dev

这将启动本地 HTTP 服务器并提供实时重载,这与 npm run start 是不一样的,后者忽略了实时重载。语法错误是在运行该命令的窗口中,而不是在浏览器中报告的。这是因为如果出现了错误,Svelte 不会生成该应用程序的新版本。
  1. 浏览localhost:5000

这一步会输出紫色的“Hello world!”。

现在你可以开始修改应用程序了。

初探package.json文件会发现两件事。首先是 Svelte 默认使用 Rollup 来打包模块。需要的话,可以将其更改为使用 Webpack 或 Parcel。其次是 Svelte 应用程序没有必需的运行时依赖项,只有 devDependencies。

最重要的起始文件包括:
public/index.html
src/main.js
src/App.svelte
这些文件使用 tab 缩进,但需要的话可以将 tab 替换为空格。
  1. 文件 public/index.html包含以下内容:



  
    
    
    Svelte app
    
    
    
  
  
    
  

请注意这里提取了两个 CSS 文件和一个 JavaScript 文件。
  • global.css包含可以影响任何组件的 CSS。
  • bundle.css由每个组件中的 CSS 生成。
  • bundle.js是由每个组件中的 JavaScript 和 HTML,以及应用程序中其他所有的 JavaScript 生成的。

  1. 文件src/main.js包含以下内容:

import App from './App.svelte';

const app = new App({
  target: document.body,
  props: {
    name: 'world'
  }
});

export default app;
这里会渲染 App 组件。target 属性指定应在何处渲染组件。对于大多数应用程序来说,这就是文档的主体。name prop 会传递给 App 组件。通常来说最顶层的组件不需要 props,此处的 props 属性可以删除。
  1. 文件src/App.svelte包含以下内容:





Hello {name}!

可以在使用该组件的文件中将导出的变量设置为 props。

大括号用于输出 JavaScript 表达式的值。这里称为 插值。稍后我们将看到,大括号也用于动态属性值。

定义组件 
流行的 Web 框架使用多种 JavaScript 容器来定义组件。
  • Angular 使用类。
  • React 使用函数或类。
  • Vue 使用对象字面量。

Svelte 不使用任何 JavaScript 容器。

Svelte 组件由包含 JavaScript 代码、CSS 和 HTML 的.svelte 文件定义。它们组合在一起形成组件定义,该定义将自动成为默认导出。

.svelte 文件可以在 src 目录下的任何位置。它们包含以下三部分,这三部分都是可选的。






请注意,每个部分都可以使用不同的注释语法。

组件名称

Svelte 组件定义未指定组件名称。其他框架中组件名称是在源文件中由类名称、函数名称或属性值提供的,这里不是这样,导入.svelte 文件时组件名称会被关联,并且必须以大写字母开头。小写名称保留给预定义元素,例如 HTML 和 SVG 提供的元素。

// 有些令人困惑
import AnyNameIWant from './some-name.svelte';

// 较清晰的写法
import SameName from './SameName.svelte';
共享数据

在 Svelte 组件之间共享数据有四种方法。

Props

它们将数据从父组件传递到子组件。

 上下文

祖先组件可以用它们来使数据用于后代组件。

存储

它们将数据存储在所有组件外,并使其对所有组件可用。

模块范围

它们将数据存储在组件模块中,并使数据可用于组件的所有实例。

这些内置方法非常有用,实际上你都不需要状态管理库。

Props

组件可以通过 props 接受输入。它们被指定为父组件渲染的组件元素上的属性。例如,父组件可以执行以下操作:




这里 name prop 的值是字面量字符串。

作为 JavaScript 表达式或非字符串字面量的 prop 值必须用大括号括起来,不能用引号。

src/Hello.svelte 中定义的子组件可以这样做:



Hello, {name}!

这里使用 export 关键字在组件的

上面的示例使用 bind 模拟双向数据绑定。这将在后文详细说明。

 样  式 

.svelte 文件的style标记中的样式将自动确定组件的范围。

Svelte 将生成的相同的 CSS 类名称(svelte-hash)添加到可能受这些 CSS 规则影响的组件的每个渲染元素中,从而实现了作用域。

全局样式应在 public/global.css 中定义。

与标准 CSS 一样,样式标记中的注释必须使用 / * */ 注释定界符。

"svelte3” ESLint 插件会对未使用的 CSS 选择器发出警告。

可以有条件地将 CSS 类添加到元素。在以下示例中,仅当 status 大于零时才添加 CSS 类 error。

0}>{result}
导入组件

组件可以在其

count = {count}

必须分配一个新值以触发此操作。将新元素推送到数组上不会触发它。可以使用以下方法:

myArr = myArr.concat(newValue);

// 另一种技巧
myArr.push(newValue);
myArr = myArr;
响应式声明

在 JavaScript 语句开头写一个名称,后面跟一个冒号,就会创建一个 标签语句。标签语句可以用作 break 和 continue 语句的目标。

有趣的是,在同一范围的多个语句中使用相同的标签名称在 JavaScript 中不是错误。当这个语法用在顶层语句(未嵌套在函数或块中)上且名称为美元符号时,Svelte 会将顶层语句视为 响应式声明

这是 Svelte 编译器以特殊方式处理有效的 JavaScript 语法的另一个例子。当这类语句引用的任何变量的值更改时,它们自己就会重复。这有点像 Vue 中的 计算属性。例如:

// average 的值一开始就被计算
// 如果 total 或 count 的值更改就重新计算。
$: average = total/count;

// count 的值输出在 devtools 控制面板中
// 当这个语句执行和每次更改时。
// 这方便了调试工作!
$: console.log('count =', count);

将 $: 应用于未声明变量的赋值时(比如上面的 average 赋值),不允许使用 let 关键字。$: 可以应用于一个块。例如:

$: {
  // 这里是要重复的语句
}

这也可以应用于多行语句,比如 if 语句。例如:

$: if (someCondition) {
  // 主体语句
}

如果条件或主体中引用的任何变量发生更改,上面的示例就会执行,但只有在条件为 true 时主体才执行。例如,如果条件包括对函数的调用,则如果主体中的任何引用发生更改就会调用它们。

Markup 中的逻辑
在 markup 中添加条件逻辑和迭代逻辑有三种常见方法。
  1. React 使用 JSX,其中逻辑由大括号中的 JavaScript 代码实现。
  2. Angular 和 Vue 支持特定于框架的逻辑属性。例如,Angular 支持 ngIf 和 ngFor,而 Vue 支持 v-if 和 v-for。
  3. Svelte 支持包装元素的类似 Mustache 的自定义语法。例如{#if}和{#each}。

 IF 语句

Svelte 的条件逻辑以{#if condition}开始。开头的 # 表示块的起始标记。用{/ if}标记结尾。开头的 / 表示块的结束标记。有条件渲染的 markup 位于这两者之间。它们之间可以包含的其他块标记有{:else if condition}和{:else}。开头的: 表示块的继续标记。例如:

{#if color === 'yellow'}
  
Nice color!
{:else if color === 'orange'}
That's okay too.
{:else}
Questionable choice.
{/if}

虽说这里的语法乍看起来似乎很奇怪,但它确实能有条件地渲染多个元素。Angular/Vue 中向元素添加特殊属性的方法需要指定一个公共父元素。

Each 语句

Svelte 的迭代从{#each iterable as element}开始。用{/each}标记结尾。每个元素要渲染的 markup 放在两者之间。一般来说可迭代的是数组,但任何可迭代的值都能用。{:else}之后的内容在可迭代内容为空时渲染。例如,假设变量 colors 设置为 ['red', 'green', 'blue']:


{#each colors as color}
  
{color}
{/each} {#each colors as color, index}
{index + 1}) {color}
{/each} {#each people as {name, age}}
{name} is {age} years old.
{:else}
There are no people.
{/each}

如果要添加、删除或修改列表中的项目,则应为每个元素提供唯一的标识符。这类似 React 和 Vue 中所需的 key prop。

在 Svelte 中,唯一标识符是 #each 语法的一部分,而不属于元素 prop。在以下示例中,每个 person 的唯一标识符是对应的 id 属性。

{#each people as person (person.id)}
  
{person.name} is {person.age} years old.
{/each}
Promise

Svelte 提供了 markup 语法来等待 promise 解析或拒绝。它可以根据 promise 的未完成、已解析或已拒绝的状态提供不同的输出。

以下示例假定组件具有返回 Promise 的 getData 函数。在:then 和:catch 之后可以使用任何变量名来接收解析或拒绝的值。

{#await getData()}
  
Waiting for data ...
{:then result}
result = {result}
{:catch error}
Error: {error.message}
{/await}

下一个示例在等待 Promise 解析时省略了要渲染的 markup。:catch 部分也可以省略。

{#await getData() then result}
  
result = {result}
{:catch error}
Error: {error.message}
{/await}
 插  槽 

插槽允许子内容传递到组件。接收组件可以决定是否渲染它,在何处渲染。请注意,空格算作子内容。接收组件可以标记使用渲染所有子内容的位置。这称为 默认插槽

它还可以为没有向插槽提供内容的父元素提供默认内容来渲染。例如,Thanks for nothing!

命名插槽允许父元素提供多组内容,接收组件可以针对这些内容决定是否渲染以及在何处渲染。父元素使用 slot 属性标识它们。子元素则定义它们在何处用带有匹配的 name 属性的 slot 元素来渲染。

下面是来自父元素的 HTML 示例,该 HTML 元素的目标是子元素 ShippingLabel 中的多个命名槽:


  
123 Some Street,
Somewhere, Some State 12345
Mark Volkmann

这是 ShippingLabel.svelte:



unknown
绑定表单元素

inputtextareaselect这样的表单元素可以绑定到变量上。这将模拟双向数据绑定。

除了提供当前值之外,双向数据绑定还提供事件处理,以便在用户更改表单元素值时更新变量。

对于类型为 number 或 range 的input元素,双向数据绑定会自动将值从字符串强制转换为数字。例如,考虑以下 HTML 表单:

又一个框架来了!被称为要取代 React , vue ,Aangular !_第2张图片

这是使用单个 Svelte 组件的实现。注意在多处使用了 bind:。





{#each flavors as flavor} {/each}
{#each seasons as season} {/each}