Svelte

0x01 概述

(1)关于

  • 官网:

  • 介绍:

    • 开源的 JavaScript 框架
    • 增强型网络应用程序(Cybernetically enhanced web apps)
    • 在编译时加载框架,无需运行时(runtime)
  • 特点:

    • 减少代码量
    • 无虚拟 DOM
    • 提供响应式
  • 优点:

    • 内置动画
    • 专注于业务
    • CSS 模块化
  • 适用情况:

    • 构建和响应速度要求较高
    • 连接性较差
    • 交互效果需求大
  • 对比:

    Svelte React Vue
    应用性能 最快 最慢 中等
    构建方法 脚本编译器 DOM 虚拟 DOM
    平均应用大小(Kb) 15 193 71
    学习曲线 容易 较容易 较容易

(2)创建项目

需要 NodeJS、npm、git 等环境

  1. 使用命令 npm create vite@latest 启动 Vite 创建项目
  2. 输入项目名称,如 svelte-app
  3. 选择 Svelte 框架(framework)
  4. 选择 JavaScript 变体(variant)
  5. 使用 cd 命令进入项目目录并使用命令 npm install 安装依赖
  6. 使用命令 npm run dev 启动项目

(3)目录结构

  • node_modules:NodeJS 依赖目录

  • public:公共资源

  • src:代码资源文件

    • App.svelte:主应用页面文件

      <script>
      </script>
      
      <main>Hello world!</main>
      
      <style>
      </style>
      
    • main.js:入口文件

      import App from './App.svelte'
      
      const app = new App({
        target: document.getElementById('app'),
      })
      
      export default app
      
    • vite-env.d.ts:Vite 环境声明文件

  • index.html:页面文件

  • jsconfig.json:配置文件

  • package.json:NodeJS 包配置文件

  • svelte.config.js:Svelte 配置文件

  • vite.config.js:Vite 配置文件

0x02 基础语法

(1)数据渲染

  • 把在 <script> 标签中,使用 let 声明的变量渲染到页面中

  • 可以通过 {@html } 将 HTML 字符串渲染为 HTML 文档

  • 举例:

    <script>
      let src = "/vite.svg"
      let text = "Lorem ipsum.";
      let string = "<h1 style='color: red'>Hello World</h1>";
    </script>
    
    <main>
      <img src={src} alt="Svelte logo" />
      <p>Lower: {text.toLowerCase()}</p>
      <p>Upper: {text.toUpperCase()}</p>
      {@html string}
    </main>
    

(2)双向绑定

  • 方法:

    1. 通过 let 声明响应式变量
    2. 通过 bind: 进行绑定
  • 举例:

    <script>
      let text = "";
      let checkbox = false;
      let radioBook = "";
      let checkboxBooks = [];
      let selectBook = "";
    </script>
    
    <main>
      <p>输入内容:{text}</p>
      <input type="text" bind:value={text} />
    
      <p>单选框组:{radioBook}</p>
      <input type="radio" bind:group={radioBook} name="books" value="Book A" />
      <input type="radio" bind:group={radioBook} name="books" value="Book B" />
      <input type="radio" bind:group={radioBook} name="books" value="Book C" />
    
      <p>多选框:{checkbox}</p>
      <input type="checkbox" bind:checked={checkbox} />
    
      <p>多选框组:{checkboxBooks}</p>
      <input type="checkbox" bind:group={checkboxBooks} name="books" value="Book A" />
      <input type="checkbox" bind:group={checkboxBooks} name="books" value="Book B" />
      <input type="checkbox" bind:group={checkboxBooks} name="books" value="Book C" />
    
      <p>选择框:{selectBook}</p>
      <select bind:value={selectBook} on:click={(e) => console.log(e.target.value)}>
        <option value="Book A">Book A</option>
        <option value="Book B">Book B</option>
        <option value="Book C">Book C</option>
      </select>
    </main>
    

(3)样式

  • Svelte 支持通过 style: 定义元素内联样式

  • 举例:

    <script>
    </script>
    
    <main>
      <p style:color="red">Svelte 样式</p>
    
      <p style="color: blue">HTML 内联样式</p>
    
      <p class="style">Style 标签定义样式</p>
    </main>
    
    <style>
      .style {
        color: green;
      }
    </style>
    
  • Svelte 支持使用 Less、Sass 等 CSS 预处理语言开发样式

(4)事件

  • Svelte 支持通过 on: 监听动作并触发事件

    • 语法:on:事件名|修饰符={方法}

    • 修饰符可选,取值包括:

      修饰符 含义 修饰符 含义
      capture 捕获阶段处理事件 nonpassive 显式设置 passive: false
      once 只能执行一次 passive 提高滚动性能
      preventDefault 停止默认事件 self 仅在事件对象 event.target 是元素本身时触发
      stopPropagaion 停止事件冒泡 trusted 仅在 event.isTrusted === true 时触发
  • Svelte 也支持使用 $: 进行监听变量改变(反应性

  • 举例:

    <script>
      let count = 0;
      const onMouseover = () => {
        console.log("mouse over");
      };
    
      $: console.log("count", count);
    </script>
    
    <main>
      <button on:click|once={() => alert("Clicked")}>Click</button>
      <button on:click={() => count++}>Count: {count}</button>
      <div
        style="width: 100px; height: 100px; background-color: black"
        on:mouseover={onMouseover}
      />
    </main>
    
  • 通过 <svelte:body> 可以对页面的 <body> 绑定事件,如:

    <main style="height: 100vh;"></main>
    <svelte:body on:click={() => alert("Hello!")} />
    

(5)响应式数组/对象

  1. 声明一个数组,通过监听数组变化计算元素和,并渲染到页面展示

    <script>
      let arr = [1, 2, 3];
      $: sum = arr.reduce((sum, cur) => (sum += cur), 0);
    </script>
    
    <main>
      <p>{arr.join(" + ")} = {sum}</p>
      <button
        on:click={() => {
          arr.push(4);
        }}>Add 4</button
      >
    </main>
    

    此时,点击按钮后页面并未改变,需要通过浅/深拷贝解决

  2. click 事件中对 arr 进行浅拷贝

    <script>
      let arr = [1, 2, 3];
      $: sum = arr.reduce((sum, cur) => (sum += cur), 0);
    </script>
    
    <main>
      <p>{arr.join(" + ")} = {sum}</p>
      <button
        on:click={() => {
          arr.push(4);
          arr = [...arr];
        }}>Add 4</button
      >
    </main>
    

0x03 渲染控制

(1)条件渲染

  • 语法:

    {#if condition1}
    {:else if condition2}
    {:else}
    {/if}
    
  • 举例:

    <script>
      let flag = true;
    </script>
    
    <main>
      <button on:click={() => flag = !flag}>Change</button>
      {#if flag}
        <div>A</div>
      {:else}
        <div>B</div>
      {/if}
    </main>
    

(2)列表渲染

  • 语法:

    {#each array as item, index (key)}
    {/each}
    
  • 举例:

    <script>
      let students = [
        { id: 201, name: "John" },
        { id: 321, name: "Jane" },
        { id: 666, name: "Jim" },
      ];
    </script>
    
    <table>
      <tr>
        <th>序号</th>
        <th>学号</th>
        <th>姓名</th>
      </tr>
      {#each students as item, index (item.id)}
        <tr>
          <td align="center">{index}</td>
          <td align="center">{item.id}</td>
          <td align="center">{item.name}</td>
        </tr>
      {/each}
    </table>
    

(3)异步渲染

  • 语法:

    {#await async}
    {:then response}
    {/await}
    
  • 举例:

    <script>
      const sleep = async (duration) =>
        new Promise((resolve) => setTimeout(() => resolve("Done"), duration));
    </script>
    
    <main>
      {#await sleep(3000)}
        Loading...
      {:then res}
        {res}
      {/await}
    </main>
    

0x04 组件化开发

(1)创建组件

  1. 在 src 目录下创建 lib 作为组件文件目录,并在其中新建 Parent.svelte

    <script></script>
    
    <main>Parent</main>
    
    <style></style>
    
  2. 在 App.svelte 中导入并使用该组件

    <script>
      import Parent from "./lib/Parent.svelte";
    </script>
    
    <main>
      <Parent />
    </main>
    

(2)插槽

a. 匿名插槽

<slot /> 用于定义插槽

  1. 在 lib 目录下新建 Child.svelte

    <script></script>
    
    <main>Child</main>
    
    <style></style>
    
  2. 在 Parent.svelte 中使用匿名插槽

    <main>
      Parent
      <slot />
    </main>
    
  3. 在 App.svelte 的 Parent 组件中使用 Child 组件

    <script>
      import Child from "./lib/Child.svelte";
      import Parent from "./lib/Parent.svelte";
    </script>
    
    <main>
      <Parent>
        <Child />
      </Parent>
    </main>
    

b. 具名插槽

<slot /> 的属性 name 用于命名

  1. 在 Parent.svelte 中为插槽命名

    <main>
      Parent
      <slot name="slot1" />
      <slot name="slot2" />
    </main>
    
  2. 在 App.svelte 中通过名称使用不同的插槽

    <main>
      <Parent>
        <p slot="slot1">1: <Child /></p>
        <p slot="slot2">2: <Child /></p>
      </Parent>
    </main>
    

(3)数据传递

a. 父传子

通过组件属性来传值

  1. 在子组件 Child.svelte 中声明并暴露变量

    <script>
      export let msg = "";
    </script>
    
    <main>Child: {msg}</main>
    
  2. 在 App.svelte 中使用组件并通过属性传值

    <main>
      <Parent>
        <Child msg="Hello from Parent" />
      </Parent>
    </main>
    

b. 子传父

通过 createEventDispatcher 方法实现

  1. 在子组件 Child.svelte 中导入 createEventDispatcher 方法并添加按钮来发送数据

    <script>
      import { createEventDispatcher } from "svelte";
    
      const dispatch = createEventDispatcher();
      const onSend = () => dispatch("msg", "Hello from Child")
    </script>
    
    <main>
      Child
      <button on:click={onSend}>send</button>
    </main>
    
    • dispatch 的第一个参数是名称,第二个参数是数据
  2. 在父组件 App.svelte 中通过 on: 监听事件并获取、渲染数据

    <script>
      import Child from "./lib/Child.svelte";
      import Parent from "./lib/Parent.svelte";
    
      let msg = ""
    </script>
    
    <main>
      <p>Message: {msg}</p>
      <Parent>
        <Child on:msg={e => msg = e.detail} />
      </Parent>
    </main>
    

c. 上下文

通过 setContext 发送数据,getContext 接收数据

  1. 在 Parent.svelte 发送数据

    <script>
      import { setContext } from "svelte";
    
      setContext("msg", "Message from Parent");
    </script>
    
    <main>
      Parent
      <slot />
    </main>
    
  2. 在 Child.svelte 接收数据

    <script>
      import { getContext } from "svelte";
    
      let msg = getContext("msg") || "";
    </script>
    
    <main>
      Child: {msg}
    </main>
    

0x05 特性语法

(1)生命周期

  • Svelte 有四个生命周期方法

    • onMount:挂载时
    • beforeUpdate:更新前
    • afterUpate:更新后
    • onDestory:卸载时
  • 举例:

    <script>
      import { afterUpdate, beforeUpdate, onDestroy, onMount } from "svelte";
    
      onMount(() => console.log("app mount"));
      beforeUpdate(() => console.log("before update"));
      afterUpdate(() => console.log("after update"));
      onDestroy(() => console.log("app destroy"));
    
      let msg = "";
    </script>
    
    <main>
      <h1>{msg}</h1>
      <input type="text" bind:value={msg} />
    </main>
    

(2)全局状态管理

  1. 在 src 目录下新建 store.js

    import { writable } from "svelte/store";
    
    export const count = writable(0);
    
  2. 在 App.svelte 中使用并修改全局状态

    <script>
      import { count } from "./store";
    
      let countValue;
      count.subscribe((value) => {
        countValue = value;
      });
    </script>
    
    <main>
      {countValue}
      <button
        on:click={() => {
          count.update((value) => {
            value += 1;
            return value;
          });
        }}
      >
        +1
      </button>
    </main>
    

(3)动画

a. tweened 补间动画

举例:

<script>
  import { cubicOut } from "svelte/easing";
  import { tweened } from "svelte/motion";

  const process = tweened(0, {
    duration: 500,
    easing: cubicOut,
  });
</script>

<main>
  <progress value={$process} />
  <button on:click={() => process.set(0)}>0%</button>
  <button on:click={() => process.set(0.5)}>50%</button>
  <button on:click={() => process.set(1)}>100%</button>
</main>

b. spirng 弹性动画

举例:

<script>
  import { spring } from "svelte/motion";

  const process = spring(0, {
    stiffness: 0.1, // 刚度
    damping: 0.25, // 阻尼
  });
</script>

<main>
  <progress value={$process} />
  <button on:click={() => process.set(0)}>0%</button>
  <button on:click={() => process.set(0.5)}>50%</button>
  <button on:click={() => process.set(1)}>100%</button>
</main>

c. 显隐动画

举例:

<script>
  import { fade, fly } from "svelte/transition";

  let show = false;
</script>

<main>
  <button on:click={() => (show = !show)}>Toggle</button>
  {#if show}
    <p transition:fade>淡入淡出</p>
    <p transition:fly={{ y: 200, duration: 2000 }}>飞入飞出</p>
    <p in:fly={{ y: 200, duration: 2000 }} out:fade>飞入淡出</p>
  {/if}
</main>

案例:待办列表

  • App.svelte

    <script>
      import Item from "./lib/Item.svelte";
    
      let todoList = [
        {
          id: 1,
          content: "吃饭",
        },
        {
          id: 2,
          content: "睡觉",
        },
        {
          id: 3,
          content: "打豆豆",
        },
      ];
      let total = todoList.length;
      let newContent = "";
      const addItem = () => {
        total++;
        todoList = [
          ...todoList,
          {
            id: todoList.length + 1,
            content: newContent,
          },
        ];
        newContent = "";
      };
    </script>
    
    <main>
      <h3>待办列表</h3>
      <ul>
        {#each todoList as item, index (item.id)}
          <li>
            <Item content={item.content} on:send={(e) => total--} />
          </li>
        {/each}
      </ul>
      <p>总待办数:{todoList.length},未完成待办数:{total}</p>
      <div>
        <input type="text" bind:value={newContent} />
        <button on:click={addItem}>添加</button>
      </div>
    </main>
    
    <style>
      main {
        width: 100%;
        height: 100%;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
      }
      ul {
        list-style: none;
        padding: 0;
      }
    </style>
    
  • lib\Item.svelte

    <script>
      import { createEventDispatcher } from "svelte";
    
      let isChecked = false;
      export let content = "";
    
      const dispatch = createEventDispatcher();
      const onSend = () => {
        dispatch("send", undefined);
      };
      $: isChecked ? onSend() : null;
    </script>
    
    {#if isChecked}
      <input type="checkbox" bind:checked={isChecked} disabled />
      <span style="color: gray; text-decoration: line-through">{content}</span>
    {:else}
      <input type="checkbox" bind:checked={isChecked} />
      <span>{content}</span>
    {/if}
    
    <style>
    </style>
    

详细使用方法查看官方教程:https://www.svelte.cn/tutorial

-End-

posted @ 2024-09-10 17:27  SRIGT  阅读(33)  评论(0编辑  收藏  举报