Svelte.js 快速入门

淮城一只猫 编程技术 阅读量 0 评论量 0

Cap1 前言

我为什么要使用它,因为它目前对我来说很适合开发小而快速迭代的项目,而且极致精简。采用尤雨溪解释:Svelte 的核心思想在于『通过静态编译减少框架运行时的代码量』。 这就意味着 Svelte 在浏览器运行中不会存在所谓的 runtime 。至于更多特色请看尤雨溪知乎回答:如何看待 svelte 这个前端框架? 不过这边我不做对此评价,毕竟只有合适自己的工具才是好工具。

学习前提有良好的 HTMLJS 基础,如果会 Vue 上手更快。

Cap2 初始化

npx degit sveltejs/template my-svelte-project
 
cd my-svelte-project
 
npm install
npm run dev

或者下载 Svelte 模板进行编写,修改 App.svelte 文件:

<script>
    export let name = 'World';
</script>
<section>
    <h1 class="hello">Hello {name}!</h1>
</section>
<style>
.hello {
    color: red;
}
</style>

上述是它的一个简单的例子,风格看起来和 Vue 很相似。

Cap3 模板语法

Svelte 的模板语法使用 {} 来表达。

标签

Svelte 标签风格和 Vue.js 一样,小写标签代表常规的 HTML 标签,单词首字母大写代表 Svelte 引进来的组件。

Attributes 和 props

风格依然和 Vue.js 一样,但引号他可以忽略不写:

<div class="foo">
    <button disabled="{!disabled}">按钮切换</button>
    <input disabled={!disabled}/>
</div>
<div class=div></div>
表达式
<h1>Hello {name}!</h1>
<p>{a} + {b} = {a + b}.</p>
条件渲染
{#if porridge.temperature > 28}
    <p>太热了</p>
{:else if 12 > porridge.temperature}
    <p>太冷了</p>
{:else}
    <p>刚刚好</p>
{/if}
列表渲染

语法如下:

<script>
  export const items = [{ message: "Foo" }, { message: "Bar" }];
</script>
<section>
  <ul>
    {#each items as item}
      <li>{item.message}</li>
    {/each}
  </ul>
</section>
<!-- 渲染效果
<section>
    <ul>
        <li>Foo</li>
        <li>Bar</li>
    </ul>
</section>
-->

如果需要索引值可以修改:

<script>
  export const items = [{ message: "Foo" }, { message: "Bar" }];
</script>
<section>
  <ul>
    {#each items as item, index}
      <li>{index} - {item.message}</li>
    {/each}
  </ul>
</section>

为了保证区分列表数据的唯一性,需要加入 key 值来区分:

<section>
  <ul>
    {#each items as item, index (item.id)}
      <li>{index} - {item.message}</li>
    {/each}
  </ul>
</section>

还支持在语法块中进行解构赋值剩余参数

{#each items as { id, name, qty }, i (id)}
    <li>{i + 1}: {name} x {qty}</li>
{/each}
{#each objects as { id, ...rest }}
    <li><span>{id}</span><MyComponent {...rest}/></li>
{/each}
{#each items as [id, ...rest]}
    <li><span>{id}</span><MyComponent values={rest}/></li>
{/each}

如果循环为空的时候使用 {:else} 来表示循环块的占位符:

{#each todos as todo}
    <p>{todo.text}</p>
{:else}
    <p>没有数据了!</p>
{/each}
{#await ...}

这个用于请求接口业务比较多,请求接口周期分成三大块:请求中,请求成功和请求失败:

<script>
  let promise = getRandomNumber();
  async function getRandomNumber() {
    const res = await fetch(
      `https://api.imjad.cn/hitokoto/?charset=utf-8&length=50&encode=json`
    );
    const text = await res.text();
    if (res.ok) {
      return JSON.parse(text);
    } else {
      throw new Error(text);
    }
  }
</script>

<section>
  {#await promise}
    <p>正在等待接口响应...</p>
  {:then value}
    <p>获取到数据: {value.hitokoto}</p>
  {:catch error}
    <p>获取失败: {error.message}</p>
  {/await}
</section>

catch 可以选择性忽略,当然最简洁的写法如下:

{#await promise then value}
    <p>获取数据: {value}</p>
{/await}
{@html ...}

这个标签和 Vue.js 中的 v-html 一样,渲染 html 内容:

<script>
  let html = `<h3 style="color: #eee">Hello Wold</h3>`
</script>
<section>
  {@html html}
</section>

值得注意的是,@html 渲染的标签必须是有效的 html 内容。

{@debug ...}

该标签用于替代 console.log ,当指定的变量名改变时候,他会在控制台记录,并且在 devtools 开启状态下给该变量进行断点测试,方便数据测试。

语法:

{@debug var1, var2, ..., varN}
<script>
  let html = `<h3 style="color: #eee">Hello Wold</h3>`
</script>
<section>
  {@debug html}
  {@html html}
</section>

Cap4 指令元素

这里的指令元素和 Vue 一样,使用上面也差不多,但在 Svelte 上它更显然灵活些。

on:eventname

语法:

on:eventname={handler}
on:eventname|modifiers={handler}
<script>
    let count = 0;
    function handleClick(event) {
        count += 1;
    }
</script>
<button on:click={handleClick}>
    数量: {count}
</button>

同样支持内联声明函数:

<script>
    let count = 0;
</script>
<button on:click="{() => count += 1}">
    count: {count}
</button>

修饰符用法:

<form on:submit|preventDefault={handleSubmit}>
</form>
bind:property
bind:property={variable}

用于数据绑定:

<input bind:value={name}>
<textarea bind:value={text}></textarea>
<input type="checkbox" bind:checked={yes}>

如果绑定的属性名相同可以简写如下:

<!-- 俩者效果相同 -->
<input bind:value={value}>
<input bind:value>

可以支持原生的属性绑定。

bind:this

这个属性和 Vue.jsref 一样:

<script>
  import { onMount } from "svelte";
  let getHelloWorldDom;
  onMount(() => {
    console.log(getHelloWorldDom);
  });
</script>
 
<div bind:this={getHelloWorldDom}>hello world</div>
class:name

动态绑定 class 属性:

<div class="{active ? 'active' : ''}">...</div>
<div class:active={active}>...</div>
<div class:active>...</div>
<div class:active class:inactive={!active} class:isAdmin>...</div>
use:action

用于创建原生调用的函数,支持销毁方法等。

use:action={parameters}
action = (node: HTMLElement, parameters: any) => {
    update?: (parameters: any) => void,
    destroy?: () => void
}
<script>
  function foo(node) {
    // 已经在 dom 挂载
    console.log("挂载成功!");
    return {
      destroy() {
        // 已经在 dom 销毁
        console.log("销毁成功!");
      }
    };
  }
</script>
 
<div use:foo />

同样支持更新动作器:

<script>
  let bar = false;
  function foo(node) {
    // 已经在 dom 挂载
    console.log("挂载成功!");
    return {
      update(bar) {
        // 当 bar 更新的时候触发
        console.log(`bar 更新了`);
      },
      destroy() {
        // 已经在 dom 销毁
        console.log("销毁成功!");
      }
    };
  }
  function handleClick() {
    bar = true;
  }
</script>
 
<div use:foo={bar} />
<button on:click={handleClick}>点击</button>
transition:fn

这边同样请参考 Vuetransition 组件。

transition:fn
transition:fn={params}
transition:fn|local
transition:fn|local={params}
transition = (node: HTMLElement, params: any) => {
    delay?: number,
    duration?: number,
    easing?: (t: number) => number,
    css?: (t: number, u: number) => string,
    tick?: (t: number, u: number) => void
}
{#if visible}
    <div transition:fade>
        fades in and out
    </div>
{/if}

值得注意的是 transition 支持双向转换,意味着将原生平滑反转。

use:action 一样支持参数传递:

{#if visible}
    <div transition:fade="{{ duration: 2000 }}">
        flies in, fades out over two seconds
    </div>
{/if}

关于更多动画请参考文档说明,这边不再描述。

Cap5 组件指令

on:eventname

组件事件可以使用 DOM 转发事件和 createEventDispatcher

<SomeComponent on:whatever={handler}/>
<SomeComponent on:whatever/>
bind:property

可以将绑定的值传递给组件:

<Keypad bind:value={pin}/>
bind:this

同样支持 bind:this 传递,可以进行组件交互:

<ShoppingCart bind:this={cart}/>
<button on:click={() => cart.empty()}>
    Empty shopping cart
</button>

Cap6 插槽

同样还支持 <solt> 组件,是的,依然和 Vue.js 一样。

支持匿名插槽和具名插槽:

<!-- App.svelte -->
<Widget>
    <h1 slot="header">Hello</h1>
    <p slot="footer">Copyright (c) 2019 Svelte Industries</p>
</Widget>
<!-- Widget.svelte -->
<div>
    <slot name="header">No header was provided</slot>
    <p>Some content between header and footer</p>
    <slot name="footer"></slot>
</div>

Cap7 组件递归

<svelte:self> 允许组件以递归形式遍历自己,请注意判断表达式,免得出现无限递归。

<script>
  export let count = 30;
</script>
{#if count > 0}
  <p>counting down... {count}</p>
  <svelte:self count={count - 1} />
{:else}
  <p>lift-off!</p>
{/if}

Cap8 动态渲染

<svelte:component> 用于动态渲染组件,当属性更改时,该组件被销毁并且重新创建,如果属性是错误的,则不会渲染任何组件。

<!--App.svelte-->
<script>
    import RedThing from './RedThing.svelte';
    import GreenThing from './GreenThing.svelte';
    import BlueThing from './BlueThing.svelte';
    const options = [
        { color: '红',   component: RedThing   },
        { color: '绿', component: GreenThing },
        { color: '蓝',  component: BlueThing  },
    ];
    let selected = options[0];
</script>
<select bind:value={selected}>
    {#each options as option}
        <option value={option}>{option.color}</option>
    {/each}
</select>
<svelte:component this={selected.component}/>
 
<!--BlueThing.svelte-->
<style>
    strong { color: blue; }
</style>
<strong>蓝色</strong>
 
<!--GreenThing.svelte-->
<style>
    strong { color: green; }
</style>
<strong>绿色</strong>
 
<!--RedThing.svelte-->
<style>
    strong { color: red; }
</style>
<strong>红色</strong>

Cap9 侦听 window 对象

<svelte:window on:event={handler}/>
<svelte:window bind:prop={value}/>

允许将事件侦听到 window 对象里。

<script>
    function handleKeydown(event) {
        alert(`pressed the ${event.key} key`);
    }
</script>
<svelte:window on:keydown={handleKeydown}/>
<svelte:window bind:scrollY={y}/>

支持绑定的值:

  • innerWidth

  • innerHeight

  • outerWidth

  • outerHeight

  • scrollX

  • scrollY

  • online — window.navigator.onLine 的别名

Cap10 侦听 body 对象

<svelte:body on:event={handler}/>

<svelte:window/> 一样,侦听 document.body 事件。

<svelte:body
    on:mouseenter={handleMouseenter}
    on:mouseleave={handleMouseleave}
/>

Cap11 head

<svelte:head>...</svelte:head>

可以添加一些元素到 <head> 里,在服务器渲染,这边渲染主要的 html 内容,来保良好的 seo

<svelte:head>
    <link rel="stylesheet" href="tutorial/dark-theme.css">
</svelte:head>

Cap12 编译器配置

<svelte:options option={value}/>

可以配置每个组件的编译的条件:

<svelte:options tag="my-custom-element"/>

可用属性如下:

  • immutable={true} - you never use mutable data, so the compiler can do simple referential equality checks to determine if values have changed
  • immutable={false} - the default. Svelte will be more conservative about whether or not mutable objects have changed
  • accessors={true} - adds getters and setters for the component's props
  • accessors={false} - the default
  • namespace="..." - the namespace where this component will be used, most commonly "svg"
  • tag="..." - the name to use when compiling this component as a custom element

Cap13 Svelte 运行

生命周期

Vue.js 一样,它也包含生命周期:

  • onMount - DOM 加载好后执行的回调。

  • beforeUpdate - 组件更新前的回调。

  • afterUpdate - 组件更新后立即执行的回调。

  • onDestroy - 组件销毁的运行回调。

tick
promise: Promise = tick()

Returns a promise that resolves once any pending state changes have been applied, or in the next microtask if there are none.

<script>
    import { beforeUpdate, tick } from 'svelte';
    beforeUpdate(async () => {
        console.log('the component is about to update');
        await tick();
        console.log('the component just updated');
    });
</script>
setContext

因为Svelte不包含 this,利用该属性可以将上下文对象与当前组件关联,可以使用 getContext 获取上下文信息提供给组件。和生命周期函数一样,必须在组件初始化期间调用它。

<script>
    import { setContext } from 'svelte';
    setContext('answer', 42);
</script>
getContext
context: any = getContext(key: any)

检索指定的组件上下文 key ,需要在组件初始化期间调用。

createEventDispatcher

创建一个自定义组件事件,语法如下:

dispatch: ((name: string, detail?: any) => void) = createEventDispatcher();

创建的事件不会被冒泡,也无法使用 event.preventDefault() 取消。

<script>
    import { createEventDispatcher } from 'svelte';
    const dispatch = createEventDispatcher();
</script>
 
<button on:click="{() => dispatch('notify', '详细信息')}">火宅事件</button>
<script>
    function callbackFunction(event) {
        console.log(`收到通知,详细如下: ${event.detail}`)
    }
</script>
<Child on:notify="{callbackFunction}"/>
如何看待 svelte 这个前端框架? - 知乎
看了下。十分精简。思路也很独特。
Svelte API docs
Cybernetically enhanced web apps
喵~