阿里、蚂蚁、晟腾、中科加禾精彩分享 AI 基础设施洞见,现购票可享受 9 折优惠 |AICon 了解详情
写点什么

被称为“三大框架”替代方案,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:583798

评论

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

11. 数据类型 - 字典

茶桁

Python 数据类型 字典

国产 etl engine 监控面板 为管理者掌握平台运行情况,决策执行方案提供即时数据支撑

weigeonlyyou

数据库 数据交换 物联网 ETL 云数据迁移

买量业务福音:自有App运行小游戏

FinFish

小游戏 App运行小游戏 游戏买量

JSP的内置对象

java易二三

编程 程序员 计算机

北航基于openEuler构建工业机器人操作系统,打造“开箱即用”的机器人基础软件平台

openEuler

Linux 操作系统 机器人 嵌入式 openEuler

研发提测前测试到底能做些什么 | 京东云技术团队

京东科技开发者

测试 测试开发 企业号 8 月 PK 榜

spring多数据源动态切换的实现原理及读写分离的应用 | 京东云技术团队

京东科技开发者

spring 读写分离 企业号 8 月 PK 榜 AbstractRouting 动态切换

质量指标如何发挥作用?构建质量能力是关键

BY林子

质量指标 质量度量 质量能力

从 Java 11 迁移到 Java 17 的注意事项

高端章鱼哥

Java JVM java17

实现静态资源访问的几种方法

疯狂紫萧

前端 CDN

苹果Mac软件Topaz Video AI激活安装 智能视频增强软件

晴雯哥

Java日记——使用强大的Elastisearch搜索引擎

java易二三

Java 编程 程序员 计算机

产业共振 智能涌现!8月16日飞桨携手生态伙伴带来大模型企业应用创新实践

飞桨PaddlePaddle

人工智能 百度飞桨 百度AI 文心大模型

选择FastAPI或Flask:Python Web开发框架的终极指南

Apifox

Python flask 框架 web开发 FastApi

OpenSSL 3.0.0 设计(二)|Core 和 Provider 设计

铜锁开源密码库

开源 开发者 算法 信息安全 密码学

GPT-4助力数据分析:提升效率与洞察力的未来关键技术 | 京东云技术团队

京东科技开发者

数据分析 ChatGPT4 企业号 8 月 PK 榜

商品推荐系统浅析 | 京东云技术团队

京东科技开发者

算法 推荐系统 推荐算法 企业号 8 月 PK 榜 搜索推荐系统

头像生成App 欢迎试用 | AIGC

AIWeker

人工智能 AIGC Stable Diffusion

让数据说话,瓴羊Quick BI报表工具助企业洞察市场趋势

流量猫猫头

SSM框架——使用MyBatis Generator自动创建代码

java易二三

编程 程序员 mybatis 计算机

12. 数据类型 - 集合详解

茶桁

Python 集合 数据类型

大模型AI人才培养研习会,上海、武汉站同期招募!

飞桨PaddlePaddle

人工智能 百度飞桨 文心大模型

哪些项目管理工具领跑2023年中国市场?全新榜单揭秘

PingCode

项目管理 项目管理软件

bi数据分析系统:从数据中发现商机

巷子

持续赋能 AI 应用,和鲸 ModelWhale 入选“2023 数字生态500强”优秀案例&解决方案榜单

ModelWhale

人工智能 大模型 数字生态 AIGC

MES和ERP的区别

优秀

ERP mes mes和erp区别

快手出席中国多媒体大会:产学研合作共赢,迎接AGI时代新机遇

Geek老T

多媒体 AGI 智能媒体生产

数据分析看板助力企业实现数据价值最大化

夏日星河

Java中final、finally和finalize的区别

java易二三

Java 程序员 计算机 final

透过源码,捋清楚循环依赖到底是如何解决的!

江南一点雨

Java spring

Camtasia 2023 for Mac(视频录制和剪辑软件) v2023.1.2中文特别版

mac

Camtasia2023 苹果mac 屏幕录制软件 视频编辑软件

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