阿里云「飞天发布时刻」2024来啦!新产品、新特性、新能力、新方案,等你来探~ 了解详情
写点什么

被称为“三大框架”替代方案,Svelte 如何简化 Web 开发工作(下)

  • 2019-10-10
  • 本文字数:10510 字

    阅读完需:约 34 分钟

被称为“三大框架”替代方案,Svelte 如何简化 Web 开发工作(下)

Web 框架层出不穷,作为主流 Web 框架之一的 Svelte,有着独特的优势。它不仅可以构建完整的 Web 应用程序,还可以创建自定义元素,并在其他框架实现的已有 Web 应用程序中使用。本文将对 Svelte 进行详细的介绍,并带领读者了解使用 Svelte 从头开始构建 Web 应用程序所需的基础知识。您可点击这里回顾该文章上篇。

存储

存储(Stores)在所有组件外部保持应用程序状态。它们是使用 props 或上下文来使数据在组件中可用的替代方法。


对于应该对所有组件可用的存储,请在 src/stores.js 之类的文件中定义并导出它们,并在需要时从该文件导入它们。


对于应该仅对给定组件的后代可用的存储,请在这个组件中定义它们,然后使用 props 或上下文将它们传递给后代。


Svelte 提供三种存储。


  1. 可写存储——这是唯一可以由组件修改的存储。

  2. 可读存储——这些存储处理它们自己的数据。

  3. 派生存储——这些存储从其他存储的当前值派生数据。


这些存储都有一个 subscribe 方法,该方法返回一个可调用的函数来 unsubscribe。


也可以创建自定义存储。它们唯一的限制是成为具有正确实现的 subscribe 方法的对象。示例见

可写存储

要创建可写存储,请调用 svelte/store 包中定义的 writable 函数。然后传递初始值,还可以传递一个带有 set 函数的函数。如果传入了后者,它可以异步确定存储的值。例如,它可以调用 REST 服务,并将返回的值传递给 set。在第一个组件订阅存储之前不会调用此函数。


除了 subscribe 方法外,可写存储还有以下方法:


  • set(newValue)


这将为商店设置一个新值。


  • update(fn)


这将基于当前值更新存储值。fn 是一个传递当前值并返回新值的函数。


下面是仅使用初始值定义可写存储的示例。


// 在stores.js内import {writable} from 'svelte/store'; // 初始值是空数组。export const dogStore = writable([]);
复制代码


这是一个使用函数确定值来定义可写存储的示例。


// 在stores.js内import {writable} from 'svelte/store'; export const dogStore = writable(initialValue, async set => {  // 订阅计数由0到1时调用。  // 计算初始值并传递给set函数。  const res = await fetch('/dogs');  const dogs = await res.json();  set(dogs);   return () => {    // 订阅计数归零时调用。  };});
复制代码


可以将表单元素的值绑定到可写存储。当用户更改表单元素值时将更新存储。


<input bind:value={$someStore} />
复制代码


存储名称上的 $前缀接下来会解释。

可读存储

要创建可读存储,请调用 svelte/store 包中定义的 readable 函数。


与可写存储一样,这里要为它传递一个初始值,还可以传递一个带有 set 函数的函数。


例如:


import {readable} from 'svelte/store'; export const dogStore = readable(  [], // 初始值。  set => {    const res = await fetch('/dogs');    const dogs = await res.json();    set(dogs);    // 这里可以返回一个清理函数。  });
复制代码


set 函数可以使用 setInterval 来连续更改值。

使用存储

要开始使用存储,请选择下面一种方式来访问​​它:


  1. 作为 prop 接受它。

  2. 从上下文中获取它。

  3. 从.js 文件导入(适用于全局范围)。


有两种方法从存储中获取值:


  1. 在其上调用 subscribe 方法(有些冗长)。

  2. 使用自动订阅的捷径(通常是首选)。


下面是使用 subscribe 方法的示例。


<script>  import {onDestroy} from 'svelte';  import {dogStore} from './stores.js';  let dogs;  const unsubscribe = dogStore.subscribe(value => (dogs = value));  onDestroy(unsubscribe);</script> <!-- 在HTML中使用dogs。 -->
复制代码


下面是使用自动订阅的示例。


名称以 $开头的所有变量都必须放入存储。通过这种方法,组件在首次使用时会自动订阅存储,而被销毁时会自动取消订阅。


<script>  import {dogStore} from './stores.js';</script> <!-- 在HTML中使用 $dogStore。 -->
复制代码


下面是更改可写存储的示例。


订阅存储的组件将看到更改。


<script>  import {dogStore} from './stores.js';  import Child from './Child.svelte';   const dog = $dogStore;   function changeDog() {    // 方法 #1 - 创建新对象    //dogStore.set({age: 2, breed: 'GSP', name: 'Oscar'});     // 方法 #2 - 调整并复用对象    dog.age = 2;    dog.breed = 'GSP';    dog.name = 'Oscar';    dogStore.set(dog);  }</script> <h1>Store Demo</h1><Child /><button on:click={changeDog}>Change Dog</button>
复制代码


下面是一个使用 HTML 中的 $引用从存储中获取更改的示例。


<script>  import {dogStore} from './stores.js';</script> <div>  {$dogStore.name} is a {$dogStore.breed} that is {$dogStore.age} years old.</div>
复制代码


下面的代码效果同上,但是使用 JavaScript 代码从存储中获取数据。


<script>  import {dogStore} from './stores.js';   // 这里需要Parens才能知道开放的大括号不是块的开头。  $: ({age, breed, name} = $dogStore);</script><div>{name} is a {breed} that is {age} years old.</div>
复制代码

模块上下文(module context)

想要只在组件源文件中运行一次 JavaScript 代码,而不是为创建的每个组件实例都运行一次代码,请将代码包含在指定模块上下文的 script 标记中。


<script context="module">  ...</script>
复制代码


如果 script 标记未指定其上下文,则它为实例上下文


两种 script 标记(实例和模块上下文)都可以出现在组件源文件中。


两种上下文中都可以导出值。无法指定默认导出,因为组件本身会自动成为默认导出。


模块上下文可以声明变量并定义函数。这些可以在组件所有实例的实例上下文中访问,但它们不是响应式的。组件更改时不会重新渲染。这样就能在所有实例之间共享数据。


实例上下文变量和函数在模块上下文中不可访问。


请注意,不需要将不访问组件状态的函数移至模块上下文,因为(根据 Svelte API 文档)“ Svelte 将从组件定义中提升所有不依赖本地状态的函数。” 但将函数放在模块上下文中的一个目的是从外部导出和调用它们。

批处理 DOM 更新

可以更改顶级组件变量的值来使组件状态无效。


根据 Svelte 文档,“当你使 Svelte 中的组件状态无效时,它不会立即更新 DOM。相反,它会等到下一个微任务才查看是否需要应用其他任何更改(包括在其他组件中)。这样做避免了不必要的工作,并使浏览器可以更有效地对事物进行批处理。”


tick 函数“返回一个 promise,该 promise 将在任何未决状态更改应用于 DOM 时立即解析(如果没有未决状态更改,则立即解析)。”


应用 DOM 更新后,可以使用此方法进行其他状态更改。


<script>  import {tick} from 'svelte';  ...  // 做一些状态更改。   // 下面的内容预防tick调用后的批量更新。 await tick();   // DOM更新后做更多状态更改。  ...</script>
复制代码


调用 await tick()在测试中也很有用,可以在测试效果之前等待更改被处理。

动画

Svelte 提供了许多功能用来轻松将动画添加到元素。以下是它提供的一些函数和过渡效果值。


  • svelte/animate 包提供了 flip 函数。

  • svelte/motion 包提供 spring 和 tweened 函数。

  • svelte/transition 包提供了 crossfade 函数及过渡值 draw(用于 SVG 元素)、fade、fly、scale 和 slide。

  • 另请参见 svelte/easing 包,这个包提供了控制动画随时间变化的速率的缓动函数。


下面是一个基本的动画示例,其中有一个列表项在装载时淡入并在销毁时淡出。


<script>  import {fade} from 'svelte/transition';</script> <li transition:fade>  <!-- 一些内容 --></li>
复制代码


可以创建自定义动画。示例见


组件可以侦听事件,以了解过渡何时开始和结束。需要侦听的事件有:


  • introstart 和 introend

  • outrostart 和 outroend

特殊元素

Svelte 支持几种特殊元素,其形式为<svelte:name props>。总结如下。


<svelte:component this = {expression} optionalProps>
复制代码


它将渲染 expression 指定的组件。如果 expression 是虚值则不渲染任何内容。可选的 props 会被传递到要渲染的组件。


<svelte:self props>
复制代码


它允许组件渲染其自身的实例。它支持递归组件,这是必需的,因为组件无法导入自身。


<svelte:window on:eventName={handler}>
复制代码


它将注册一个由 DOM window 对象调度给定事件时要调用的函数。resize 事件就是一个例子。


<svelte:window bind:propertyName={variable}>
复制代码


它会将变量绑定到 window 属性。一个例子是 innerWidth。


<svelte:body on:eventName={handler}>
复制代码


当 DOM body 元素调度给定事件时,此方法注册一个要调用的函数。例子包括 mouseEnter 和 mouseLeave。


<svelte:head>elements</svelte:head>
复制代码


它会将元素插入 DOM 文档的 head 元素中。例子包括插入 link 和 script 标记。


<svelte:options option={value} />
复制代码


它位于.svelte 文件的顶部,而不是 script 标记内部。它指定了编译器选项,包括:


immutable
复制代码


它意味着 props 将被视为不可变的,从而提供了优化。


默认值为 false。不可变意味着父组件将为对象 props 创建新对象,而不是修改现有对象的属性。这使 Svelte 可以通过对比对象引用(而不是对象属性)来确定 prop 是否已更改。


当此选项设置为 true 时,如果父组件修改了子组件的对象属性,则子组件将不会检测到更改并且不会重新渲染。


accessors
复制代码


它为组件 props 添加了 getter 和 setter 方法。默认为 false。将 Svelte 组件编译为非 Svelte 应用程序中使用的自定义元素时,这个方法很有用。


namespace="value"
复制代码


它指定了组件的命名空间。一种用途是为 SVG 组件指定命名空间 svg。


tag="value"
复制代码


它指定将 Svelte 组件编译为自定义元素时要使用的名称。它允许 Svelte 组件用作非 Svelte 应用程序中的自定义元素。

调试

当给定变量更改时,使用 @debug 中断,并在 devtools 控制台中输出它们的值。


将其放置在 HTML 部分的顶部,不要放在script标记内部。


例如:


{@debug var1, var2, var3}
复制代码


被监视的变量可以具有任何类型的值,包括对象。


要在所有状态更改时中断,请省略变量名称。


{@debug}
复制代码

ESLINT

ESLint 称自己为“针对 JavaScript 和 JSX 的可插入式 linting 实用程序”。它可以报告许多语法错误和潜在的运行时错误。它还可以报告与指定编码准则的差异。


要在 Svelte 项目中安装 ESLint 所需的全部内容,请输入 npm install -D name,其中 name 为:


  • eslint

  • eslint-plugin-svelte3


创建具有以下内容的.eslintrc.json 文件:


{  "env": {    "browser": true,    "es6": true,    "jest": true  },  "extends": ["eslint:recommended", "plugin:import/recommended"],  "globals": {    "cy": "readonly"  },  "overrides": [    {      "files": ["**/*.svelte"],      "processor": ["svelte3/svelte3"]    }  ],  "parserOptions": {    "ecmaVersion": 2019,    "sourceType": "module"  },  "plugins": ["svelte3"],  "rules": {    "no-console": "off"   }}
复制代码


将以下 npm 脚本添加到 package.json:


"lint": "eslint --fix --quiet src --ext .js,.svelte",
复制代码


要运行 ESLint,请输入 npm run lint。


有关针对 Svelte 的 ESLint 选项的更多信息,请参见文档

Prettier

Prettier 称自己为“经过优化的 JavaScript 格式化程序”。它支持多种语言和语言功能,包括 ES2017、TypeScript、JSON、HTML、CSS、LESS、SCSS、JSX、Vue 和 Markdown。


要在 Svelte 项目中安装 Prettier 所需的全部内容,请输入 npm install -D name,其中 name 为:


  • prettier

  • prettier-plugin-svelte


Svelte ESLint 插件强制按scriptstyle和 HTML 的顺序执行。


将以下 npm 脚本添加到 package.json:


"format": "prettier --write '{public,src}/**/*.{css,html,js,svelte}'",
复制代码


要运行 Prettier,请输入 npm run format。

ToDo 应用

下面来看一个简单的 Todo 应用程序的实现,正好过一遍最重要的那些 Svelte 概念。代码链接在GitHub上。



要添加新的待办事项时,需要在输入框中输入待办事项的文本,然后按“添加”按钮或 Enter 键。


要将待办事项在已完成和未完成的状态之间切换,需要单击其左侧的复选框。请注意,顶部附近的“remaining”文本显示当前未检查的待办事项数和待办事项总数。


要删除待办事项,需要单击其右侧的“Delete”按钮。


要存档所有已检查的待办事项,需要单击“Archive Completed”按钮。但这个版本的应用并不会真的存储它们,其实它们都被删掉了。


下面是文件 src/main.js,它在文档主体中渲染 TodoList 组件来启动应用程序。


import TodoList from './TodoList.svelte'; const app = new TodoList({target: document.body}); export default app;
复制代码


下面是文件 src/Todo.svelte 中 Todo 组件的代码。


它是一个包含以下内容的列表项:


  • 待办事项文本

  • 一个复选框

  • 一个“Delete”按钮


它需要一个名为“todo”的 prop 来保存待办事项的文本。


切换复选框后,它将调度一个“toggleDone”事件。


按下“Delete”按钮时,它将调度一个“删除”事件。


<script>  import {createEventDispatcher} from 'svelte';  const dispatch = createEventDispatcher();  export let todo; // 唯一的prop</script> <style>  /* 在todo的文本上画一条线标记其完成状态。 */  .done-true {    color: gray;    text-decoration: line-through;  }  li {    margin-top: 5px;  }</style> <li>  <input    type="checkbox"    checked={todo.done}    on:change={() => dispatch('toggleDone')}  />  <span class={'done-' + todo.done}>{todo.text}</span>  <button on:click={() => dispatch('delete')}>Delete</button></li>
复制代码


下面是文件 src/TodoList.svelte 中 TodoList 组件的代码。


看到这里,关于 Svelte 你已经很熟悉了,所以这些代码应该很容易看懂。


<script>  import Todo from './Todo.svelte';   let lastId = 0;   // 创建一个todo对象。  const createTodo = (text, done = false) => ({id: ++lastId, text, done});   let todoText = '';   // 应用程序初始有两个todo项目。  let todos = [    createTodo('learn Svelte', true),    createTodo('build a Svelte app')  ];   let uncompletedCount = 0;   // 这是"响应式声明"。  // 它保证未完成的代码在todo数组修改时被更新。  $: uncompletedCount = todos.filter(t => !t.done).length;   // 这是另一个"响应式声明"。  // 保证当uncompletedCount或todo数组更改时状态随时更新  $: status = `${uncompletedCount} of ${todos.length} remaining`;   // 创建并添加一个新的todo.  function addTodo() {    // 回想这里为何必须使用concat代替push。    todos = todos.concat(createTodo(todoText));    todoText = ''; // 清空input  }   // 删除全部标记为完成的todo。  const archiveCompleted = () => (todos = todos.filter(t => !t.done));   // 删除特定todo。  const deleteTodo = todoId => (todos = todos.filter(t => t.id !== todoId));   // 改变给定todo的状态。  function toggleDone(todo) {    const {id} = todo;    todos = todos.map(t => (t.id === id ? {...t, done: !t.done} : t));  }</script> <style>  button {    margin-left: 10px;  }   /* 从加点(·)列表中移除点。 */  ul.unstyled {    list-style: none;    margin-left: 0;    padding-left: 0;  }</style> <div>  <h2>To Do List</h2>  <div>    {status}    <button on:click={archiveCompleted}>Archive Completed</button>  </div>  <br />  <!-- 我们不想真的提交表单。       使用表单使得按下enter键时触发"Add"按钮。 -->  <form on:submit|preventDefault>    <input      type="text"      size="30"      autofocus      placeholder="enter new todo here"      bind:value={todoText}    />    <button disabled={!todoText} on:click={addTodo}>      Add    </button>  </form>  <ul class="unstyled">    {#each todos as todo}      <Todo        todo={todo}        on:delete={() => deleteTodo(todo.id)}        on:toggleDone={() => toggleDone(todo)}      />    {/each}  </ul></div>
复制代码

单元测试

Svelte 组件的单元测试可以使用 Jest 实现。另外建议使用“Svelte 测试库”。它与 Jest 协作,可以简化 Svelte 组件的单元测试编写。


本文不会深入探究这些测试工具的细节,但下面提供了测试代码示例。要了解这些工具的更多信息,请访问https://jestjs.io/https://testing-library.com/


要安装所需的全部内容,请输入 npm install -D name,其中 name 为:


  • @babel/core

  • @babel/preset-env

  • @testing-library/svelte

  • babel-jest

  • jest

  • jest-transform-svelte


使用以下内容创建文件 babel.config.js:


module.exports = {  presets: [    [      '@babel/preset-env',      {        targets: {          node: 'current'        }      }    ]  ]};
复制代码


如果未按上面所示设置 targets.node,则在运行测试时将显示错误消息“regenerator-runtime not found”。


使用以下内容创建文件 jest.config.js:


module.exports = {  transform: {    '^.+  .js$': 'babel-jest',    '^.+  .svelte$': 'jest-transform-svelte'  },  moduleFileExtensions: ['js', 'svelte'],  bail: false,  verbose: true};
复制代码


将 bail 设置为 false 意味着 Jest 在某个测试失败时不应退出测试套件。


将 verbose 设置为 true 会使 Jest 显示每个测试的结果,而不只是各个测试套件的结果摘要。


将以下 npm 脚本添加到 package.json:


"test": "jest --watch src",
复制代码


要运行单元测试,请输入 npm test。


以下是用于测试文件 src/Todo.spec.js 中 Todo 组件的代码:


import {cleanup, render} from '@testing-library/svelte'; import Todo from './Todo.svelte'; describe('Todo', () => {  const text = 'buy milk';  const todo = {text};   // 卸载之前测试中挂载的所有组件。  afterEach(cleanup);   test('should render', () => {    const {getByText} = render(Todo, {props: {todo}});    const checkbox = document.querySelector('input[type="checkbox"]');    expect(checkbox).not.toBeNull(); // 找到复选框    expect(getByText(text)); // 找到todo文本    expect(getByText('Delete')); // 找到Delete按钮  });   // 测试事件在checkbox状态更改或"Delete"按钮按下时是否fired没有捷径。    // 它们由TodoList.spec.js的测试覆盖。});
复制代码


以下是用于测试文件 src/TodoList.spec.js 中 TodoList 组件的代码:


import {tick} from 'svelte';import {cleanup, fireEvent, render, wait} from '@testing-library/svelte'; import TodoList from './TodoList.svelte'; describe('TodoList', () => {  const PREDEFINED_TODOS = 2;   afterEach(cleanup);   // 它被下面的很多测试函数使用。  function expectTodoCount(count) {    return wait(() => {      // 每个todo有一个<li>根元素。      const lis = document.querySelectorAll('li');      expect(lis.length).toBe(count);    });  }   test('should render', async () => {    const {getByText} = render(TodoList);    expect(getByText('To Do List'));    expect(getByText('1 of 2 remaining'));    expect(getByText('Archive Completed')); // 按钮    await expectTodoCount(PREDEFINED_TODOS);  });   test('should add a todo', async () => {    const {getByTestId, getByText} = render(TodoList);     const input = getByTestId('todo-input');    const value = 'buy milk';    fireEvent.input(input, {target: {value}});    fireEvent.click(getByText('Add'));     await expectTodoCount(PREDEFINED_TODOS + 1);    expect(getByText(value));  });   test('should archive completed', async () => {    const {getByText} = render(TodoList);    fireEvent.click(getByText('Archive Completed'));    await expectTodoCount(PREDEFINED_TODOS - 1);    expect(getByText('1 of 1 remaining'));  });   test('should delete a todo', async () => {    const {getAllByText, getByText} = render(TodoList);    const text = 'learn Svelte'; // 第一个todo    expect(getByText(text));     const deleteBtns = getAllByText('Delete');    fireEvent.click(deleteBtns[0]); // 删除第一个todo    await expectTodoCount(PREDEFINED_TODOS - 1);  });   test('should toggle a todo', async () => {    const {container, getByText} = render(TodoList);    const checkboxes = container.querySelectorAll('input[type="checkbox"]');     fireEvent.click(checkboxes[1]); // 第二个todo    await tick();    expect(getByText('0 of 2 remaining'));     fireEvent.click(checkboxes[0]); // 第一个todo    await tick();    expect(getByText('1 of 2 remaining'));  });});
复制代码

端到端测试

可以使用 Cypress 来实现 Svelte 应用程序的端到端测试。本文不会深入探究 Cypress 的细节,但是下面提供了测试代码示例。


要了解有关 Cypress 的更多信息,请访问https://www.cypress.io/


要安装 Cypress,请输入 npm install -D cypress。


将以下 npm 脚本添加到 package.json:


"cy:open": "cypress open","cy:run": "cypress run",
复制代码


要以交互方式启动 Cypress 测试工具,请输入 npm run cy:open。如果尚不存在 cypress 目录,它还会创建一个带有以下子目录的目录:


fixtures
复制代码


这个目录可以保存测试使用的数据。数据通常在导入到测试的.json 文件中。


integration
复制代码


你的测试文件在此目录的顶部或子目录中。


plugins
复制代码


此目录扩展了 Cypress 的功能。示例请参见https://github.com/bahmutov/cypress-svelte-unit-test。在运行每个规范文件之前,Cypress 会自动在该目录的 index.js 文件中运行代码。


screenshots
复制代码


这个目录存放屏幕截图,截图通过调用 cy.screenshot()生成。这在调试测试时很有用。


support
复制代码


此处的文件会添加自定义的 Cypress 命令,使它们在测试中可用。在运行每个规范文件之前,Cypress 会自动在该目录的 index.js 文件中运行代码。


这些目录中装有示例文件,所有示例文件都可以删除。


在 cypress/integration 目录下创建带有.spec.js 扩展名的测试文件。


要运行端到端测试:


  1. 使用 npm run dev 启动应用程序服务器

  2. 输入 npm run cy:open

  3. 按下 Cypress 工具右上角的“Run all specs”按钮


这将打开一个浏览器窗口,在其中运行所有测试。


完成测试后,关闭此浏览器窗口和 Cypress 工具。


以下是文件 cypress/integration/TodoList.spec.js 中 Todo 应用程序的端到端测试代码。


const baseUrl = 'http://localhost:5000/'; describe('Todo app', () => {  it('should add todo', () => {    cy.visit(baseUrl);    cy.contains('1 of 2 remaining');    // "Add"按钮应被禁用,直到文本输入后才解除。    cy.contains('Add')      .as('addBtn')      .should('be.disabled');     // 输入todo文本。    const todoText = 'buy milk';    cy.get('[data-testid=todo-input]')      .as('todoInput')      .type(todoText);     cy.get('@addBtn').should('not.be.disabled');    cy.get('@addBtn').click();     cy.get('@todoInput').should('have.value', '');    cy.get('@addBtn').should('be.disabled');    cy.contains(todoText);    cy.contains('2 of 3 remaining');  });   it('should toggle done', () => {    cy.visit(baseUrl);    cy.contains('1 of 2 remaining');     // 找到第一个checkbox并打勾。    cy.get('input[type=checkbox]')      .first()      .as('cb1')      .click();    cy.contains('2 of 2 remaining');     // 转换同一个checkbox。    cy.get('@cb1').check();    cy.contains('1 of 2 remaining');  });   it('should delete todo', () => {    cy.visit(baseUrl);    cy.contains('1 of 2 remaining');     const todoText = 'learn Svelte'; // 第一个todo    cy.contains('ul', todoText);     // 点击第一个"Delete"按钮。    cy.contains('Delete').click();    cy.contains('ul', todoText).should('not.exist');    cy.contains('1 of 1 remaining');  });   it('should archive completed', () => {    cy.visit(baseUrl);     const todoText = 'learn Svelte'; // 第一个todo    cy.contains('ul', todoText);     // 点击"Archive Completed"按钮。    cy.contains('Archive Completed').click();    cy.contains('ul', todoText).should('not.exist');    cy.contains('1 of 1 remaining');  });});
复制代码


要重新运行测试,请单击浏览器窗口顶部附近的圆形箭头按钮。


为了更好地调试,请在应用程序代码中添加 console.log 调用,然后在运行测试的浏览器窗口中打开 devtools 控制台。


将更改保存到应用程序源文件或测试文件时,测试将自动重新运行。


要以命令行模式启动 Cypress 测试工具,请输入 npm run cy:run。这将在终端窗口中输出测试结果,记录测试运行的视频,并输出视频的文件路径。双击视频文件即可观看。

相关工具

以下是推荐读者研究的 Svelte 相关工具。



这是“由 Svelte 支持的应用程序框架”。Sapper 是一名士兵,负责诸如修建和维修道路和桥梁,铺设和清除地雷等任务。Sapper 类似 Next 和 Gatsby。它提供路由、服务端渲染和代码拆分功能。



这是一个社区推动的项目,支持使用 Svelte 实现原生移动应用程序。它基于 nativescript-vue。



这是 Three.js 3D 图形库的 Svelte 版本,正在开发中。


总结

到这里就结束了!Svelte 是当前流行的 React、Vue 和 Angular 框架的很好的替代品。它有许多好处,包括较小的包体积、简单的组件定义、方便的状态管理以及无需虚拟 DOM 的反应性。


非常感谢 Charles Sharp 和 Kristin Kroeger 审阅本文!


原文链接


https://objectcomputing.com/resources/publications/sett/july-2019-web-dev-simplified-with-svelte


2019-10-10 17:583800

评论

发布
暂无评论
发现更多内容

火遍全网的AI给老照片上色,这里有一份详细教程!

博文视点Broadview

发布一个轻量级的 Elasticsearch 压测工具 - Loadgen

极限实验室

elasticsearch 极限实验室 loadgen 压测工具 esrally

虚拟主机、WordPress 主机和云主机之间的区别

海拥(haiyong.site)

6月月更

融云一图看懂:社死的“谭某某”,霸屏的信息安全

融云 RongCloud

Django基础-1

zyf

django 6月月更

博睿数据荣获优炫软件产品兼容互认证书和海量数据兼容互认证书

博睿数据

智能运维 博睿数据 产品兼容 数据兼容

Kubernetes容器网络及Flannel插件详解

巨子嘉

云原生

服务器运维环境安全体系(上篇)

融云 RongCloud

网络安全

在映客的虚拟KTV里唱了一首“爱你”

ZEGO即构

音视频 虚拟KTV 线上K歌

架构实战营模块 7 作业

热猫

Microsoft  Office  MSDT 代码执行漏洞

郑州埃文科技

网络安全 漏洞分析 漏洞缓解

Electron框架XSS TO RCE简析

美创科技

漏洞

Java中的阻塞队列

急需上岸的小谢

6月月更

大数据培训Hive到Spark离线计算实践

@零度

spark hive 大数据开发

【直播回顾】Hello HarmonyOS进阶课程第五课——原子化服务

HarmonyOS开发者

HarmonyOS

专为云原生、微服务架构而设计的链路追踪工具 【SkyWalking介绍及搭建】

掂掂三生有幸

云原生 Skywalking ,docker 微服务框架 6月月更

互联网拓扑是怎样构成的?又代表了什么?

郑州埃文科技

互联网拓扑 网络空间地图

web前端培训如何在 H5 网页中实现扫码功能

@零度

前端开发

聚焦Arbitrum开发热点技术,一探以太坊的L2未来

TinTinLand

场景驱动的特征计算方式OpenMLDB,高效实现“现算先用”

第四范式开发者社区

人工智能 机器学习 数据库 开源 实时计算

PHP 对接微信公众号订阅消息详细教程

CRMEB

王者荣耀商城异地多活架构设计

Dean.Zhang

实时特征计算平台架构方法论和基于 OpenMLDB 的实践

第四范式开发者社区

机器学习 数据库 AI 特征平台 特征工程

IC 首届全球黑客松奖金高达600 万美元,助力开发者勇闯 Web 3!

TinTinLand

区块链

变量 var const let 的区别

大熊G

JavaScript 前端 前端教程 6月月更

技术分享| 如何部署安装分布式序列号生成器系统

anyRTC开发者

分布式 后端 音视频 服务器 Tinyid

好声音不同凡响,泥炭耳机618重磅新品来袭

江湖老铁

阿里云架构师梁旭:MES on 云盒,助力客户快速构建数字工厂

阿里云弹性计算

最佳实践 数字化转型 制造业 mes 云盒

DFINITY 明星项目盘点,区块链热门赛道一览

TinTinLand

区块链

私有化IM即时通讯怎样在保障企业安全下提高效率?

WorkPlus

首评 | 阿里云顺利完成国内首个云原生安全成熟度评估

阿里巴巴云原生

阿里云 云原生

被称为“三大框架”替代方案,Svelte 如何简化 Web 开发工作(下)_大前端_Mark Volkmann_InfoQ精选文章