Svelte
0x01 概述
(1)关于
-
官网:
-
介绍:
- 开源的 JavaScript 框架
- 增强型网络应用程序(Cybernetically enhanced web apps)
- 在编译时加载框架,无需运行时(runtime)
-
特点:
- 减少代码量
- 无虚拟 DOM
- 提供响应式
-
优点:
- 内置动画
- 专注于业务
- CSS 模块化
-
适用情况:
- 构建和响应速度要求较高
- 连接性较差
- 交互效果需求大
-
对比:
Svelte React Vue 应用性能 最快 最慢 中等 构建方法 脚本编译器 DOM 虚拟 DOM 平均应用大小(Kb) 15 193 71 学习曲线 容易 较容易 较容易
(2)创建项目
需要 NodeJS、npm、git 等环境
- 使用命令
npm create vite@latest
启动 Vite 创建项目 - 输入项目名称,如 svelte-app
- 选择 Svelte 框架(framework)
- 选择 JavaScript 变体(variant)
- 使用
cd
命令进入项目目录并使用命令npm install
安装依赖 - 使用命令
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)双向绑定
-
方法:
- 通过
let
声明响应式变量 - 通过
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)响应式数组/对象
-
声明一个数组,通过监听数组变化计算元素和,并渲染到页面展示
<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>
此时,点击按钮后页面并未改变,需要通过浅/深拷贝解决
-
在
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)创建组件
-
在 src 目录下创建 lib 作为组件文件目录,并在其中新建 Parent.svelte
<script></script> <main>Parent</main> <style></style>
-
在 App.svelte 中导入并使用该组件
<script> import Parent from "./lib/Parent.svelte"; </script> <main> <Parent /> </main>
(2)插槽
a. 匿名插槽
<slot />
用于定义插槽
-
在 lib 目录下新建 Child.svelte
<script></script> <main>Child</main> <style></style>
-
在 Parent.svelte 中使用匿名插槽
<main> Parent <slot /> </main>
-
在 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
用于命名
-
在 Parent.svelte 中为插槽命名
<main> Parent <slot name="slot1" /> <slot name="slot2" /> </main>
-
在 App.svelte 中通过名称使用不同的插槽
<main> <Parent> <p slot="slot1">1: <Child /></p> <p slot="slot2">2: <Child /></p> </Parent> </main>
(3)数据传递
a. 父传子
通过组件属性来传值
-
在子组件 Child.svelte 中声明并暴露变量
<script> export let msg = ""; </script> <main>Child: {msg}</main>
-
在 App.svelte 中使用组件并通过属性传值
<main> <Parent> <Child msg="Hello from Parent" /> </Parent> </main>
b. 子传父
通过
createEventDispatcher
方法实现
-
在子组件 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
的第一个参数是名称,第二个参数是数据
-
在父组件 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
接收数据
-
在 Parent.svelte 发送数据
<script> import { setContext } from "svelte"; setContext("msg", "Message from Parent"); </script> <main> Parent <slot /> </main>
-
在 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)全局状态管理
-
在 src 目录下新建 store.js
import { writable } from "svelte/store"; export const count = writable(0);
-
在 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-