sveltejs文档地址 svelte.dev
中文文档地址 www.sveltejs.cn/
开始
创建一个应用,文档参考 www.sveltejs.cn/blog/svelte…
npx degit sveltejs/template svelte-app
cd svelte-app
上面如果创建失败,可以将sveltejs的模板通过git下载到本地
git clone https://github./comsveltejs/template.git
cd template
npm install
如果要使用typescript,执行下面
# 如果要使用typescript,执行下面
node scripts/setupTypeScript.js
简介
添加数据
声明一个变量,在页面中使用花括号就可以将变量的内容显示到页面上(跟React很像)
<script lang="ts">
let name: string = 'world'
setTimeout(() => {
name = 'shibin'
}, 2000)
</script>
<main>
<h1>Hello {name}!</h1>
</main>
在页面上显示的html
<h1>Hello world!</h1>
添加样式
在组件中添加一个<style>标签,<style>标签中的样式只在改组件内有效(类似vue中的<style>标签中加上scoped)
<script lang="ts">
let name: string = 'world'
</script>
<main>
<h1>Hello {name}!</h1>
<p>
Visit the <a href="https://svelte.dev/tutorial">Svelte tutorial</a> to learn
how to build Svelte apps.
</p>
</main>
<style>
main {
text-align: center;
padding: 1em;
max-width: 240px;
margin: 0 auto;
}
h1 {
color: #ff3e00;
text-transform: uppercase;
font-size: 4em;
font-weight: 100;
}
@media (min-width: 640px) {
main {
max-width: none;
}
}
</style>
嵌套组件
在 <script> 中导入组件就可以使用了
<script lang="ts">
import Hello from './Hello.svelte'
</script>
<main>
<Hello/>
</main>
html标签
通常,字符串以纯文本形式插入,如果要插入html代码片段,可以使用{@html ...}实现
<p>{@html string}</p>
创建一个应用
Svelte提供了Rollup和Webpack的插件,可以根据需要进行配置
- rollup-plugin-svelte
- svelte-loader 其他插件 也可以直接使用上面(开始)下载的模板
项目配置好,编译器会将每个组件转化为常规的JavaScript类,接下来只需导入它并用 new 实例化即可
import App from './App.svelte'
const app = new App({
target: document.body,
props: {
name: 'world'
}
})
export default app
赋值
Reactivity
Svelte能够让 DOM 与你的应用程序状态保持同步,你只需要改变数据,Svelte 会自动更新 DOM
<script lang="ts">
import Hello from './Hello.svelte'
let count: number = 0
const handleClick = () => {
count += 1
}
</script>
<button on:click={handleClick}>Clicked {count} times</button>
声明
当组件的某个部分需要其他部分计算得出时(例如Vue中的计算属性),Svelte提供了reactive declarations
<script lang="ts">
let count = 0
$: doubled = count * 2
</script>
在页面中使用{count * 2}也能达到效果,但当需要引用多次时,使用这种声明会更好
语句
语句也可以通过计算
下面代码,当count值发生改变时,就会执行语句
<script lang="ts">
let count = 0
$: console.log(`the count is ${count}`);
</script>
将一组语句组合成一个代码块
<script lang="ts">
let count = 0
$: {
console.log(`the count is ${count}`)
alert(`I SAID THE COUNT IS ${count}`)
}
</script>
甚至可以将 $: 放在 if 代码块前面。下面代码当count>=10时,就是执行if里的代码块
<script lang="ts">
let count = 0
$: if (count >= 10) {
alert(`count is dangerously high!`)
count = 9
}
</script>
更新数组和对象
Svelte的数据更新是由赋值语句触发的,所以使用数组的push、slice、pop等不会触发自动更新。
下面代码不会更新
<script lang="ts">
let arr:number[] = [1,2,3]
function addNumber(){
arr.push(arr.length + 1)
}
</script>
<main>
{arr}
<button on:click={addNumber}>addNumber</button>
</main>
解决的办法是重新赋值
<script lang="ts">
import Hello from './Hello.svelte'
let arr:number[] = [1,2,3]
function addNumber(){
arr.push(arr.length + 1)
arr = arr
}
</script>
还有一种更惯用的方法
function addNumber() {
arr = [...arr, arr.length + 1]
}
赋值给数组和对象的 属性(properties)与对值本身进行赋值的方式相同。
function addNumber() {
arr[arr.length] = arr.length + 1
}
一个简单的经验法则是:被更新的变量的名称必须出现在赋值语句的左侧
const foo = obj.foo;
// 不会触发obj.foo.bar 的引用
foo.bar = 'baz';
// 会触发obj.foo.bar 的引用
// obj.foo.bar = 'baz';
就不会更新对 obj.foo.bar 的引用,除非使用 obj = obj 方式。
Props
props用于父组件向子组件中传递数据
<Hello name="world"/>
子组件可以通过export 关键字来获取父组件传递的数据
<script lang="ts">
export let name: string
</script>
<h1>Hello {name}!</h1>
为props设置默认值
export let name: string = 'Shibin'
如果你的组件含有有一个对象属性,可以利用...对象解构语法将数据传递给子组件
<Hello {...obj}/>
逻辑
条件渲染
if
{#if user.loggedIn}
<button on:click={toggle}>
Log out
</button>
{/if}
{#if !user.loggedIn}
<button on:click={toggle}>
Log in
</button>
{/if}
else
{#if user.loggedIn}
<button on:click={toggle}>
Log out
</button>
{:else}
<button on:click={toggle}>
Log in
</button>
{/if}
else if
{#if x > 10}
<p>{x} is greater than 10</p>
{:else if 5 > x}
<p>{x} is less than 5</p>
{:else}
<p>{x} is between 5 and 10</p>
{/if}
列表渲染
<script lang="ts">
let arr: number[] = [1, 2, 3]
</script>
<main>
<ul>
{#each arr as number}
<li>{number}</li>
{/each}
</ul>
</main>
你也可以将index 作为第二个参数(key),类似于:
{#each arr as number, index}
<li>{index} {number}</li>
{/each}
一般来说,当你修改each 块中的值时,它将会在 尾端 进行添加或删除条目,并更新所有变化, 这可能不是你想要的效果。
为此,我们为 each 块指定一个唯一标识符,作为 key 值:
{#each things as thing (thing.id)}
<Thing current={thing.color}/>
{/each}
(thing.id) 告诉 Svelte 什么地方需要改变。
Await
使用await在标签中处理异步数据
{#await promise}
<p>...waiting</p>
{:then number}
<p>The number is {number}</p>
{:catch error}
<p style="color: red">{error.message}</p>
{/await}
如果在请求完成之前不想程序执行任何操作,也可以忽略第一个块。
{#await promise then value}
<p>the value is {value}</p>
{/await}
事件
在元素中使用on: directive可以为元素添加所任何事件
<div on:mousemove={handleMousemove}>
The mouse position is {m.x} x {m.y}
</div>
也可以声明内联事件处理函数
<div on:mousemove="{e => m = { x: e.clientX, y: e.clientY }}">
The mouse position is {m.x} x {m.y}
</div>
事件修饰符
在Svelte中,DOM 事件处理程序具有额外的 修饰符
<button on:click|once={handleClick}>
Click me
</button>
Svelte中事件修饰符有
- preventDefault 调用event.preventDefault() ,在运行处理程序之前调用,组织浏览器的默认行为
- stopPropagation 调用 event.stopPropagation(), 防止事件冒泡
- passive 优化了对 touch/wheel 事件的滚动表现(Svelte 会在合适的地方自动添加滚动条)。
- capture 在 capture 阶段而不是bubbling 阶段触发事件处理程序
- once 运行一次事件处理程序后将其删除
- self 仅当 event.target 是其本身时才执行
组合使用:on:click|once|capture={...}
组件事件
组件可以向父组件发送自定义事件
<script lang="ts">
import {createEventDispatcher} from 'svelte'
export let name: string = 'wd';
const dispatch = createEventDispatcher()
function sayHello(){
dispatch('message',{
text: 'hello'
})
}
</script>
<h1>Hello {name}!</h1>
<button on:click={sayHello}>Say hello</button>
父组件接收自定义事件
<script>
function handleMessage(e) {
alert(e.detail.text)
}
</script>
<Hello on:message={handleMessage}/>
注意, 组件事件不会冒泡,如果需要多层传递,则需要转发(自定义事件一层层传递)
事件转发也可以应用到DOM事件。
<button>
Click me
</button>
<FancyButton on:click={handleClick}/>
绑定
表单绑定
<script lang="ts">
let name: string = 'shibin'
</script>
<div>
<input bind:value={name}>
{name}
</div>
number
Svelte中可以绑定input到值,当type为number、range时,无需转换,默认就是number类型的
<script>
let a = 1;
let b = 2;
</script>
<label>
<input type=number value={a} min=0 max=10>
<input type=range value={a} min=0 max=10>
</label>
<label>
<input type=number value={b} min=0 max=10>
<input type=range value={b} min=0 max=10>
</label>
<p>{a} + {b} = {a + b}</p>
单选和多选
<script lang="ts">
let yes: string = true
</script>
<input type=checkbox bind:checked={yes}>
{yes}
<label>
多个复选框
<script lang="ts">
let menu = [
'Cookies and cream',
'Mint choc chip',
'Raspberry ripple'
];
let flavours = ['Mint choc chip'];
</script>
<h2>Flavours</h2>
{#each menu as flavour}
<label>
<input type=checkbox bind:group={flavours} value={flavour}>
{flavour}
</label>
{/each}
过个单选框
<script lang="ts">
let scoops = 1
</script>
<label>
<input type=radio bind:group={scoops} value={1}>
One scoop
</label>
<label>
<input type=radio bind:group={scoops} value={2}>
Two scoops
</label>
<label>
<input type=radio bind:group={scoops} value={3}>
Three scoops
</label>
textarea
同样适用bind:value进行绑定
<textarea bind:value={value}></textarea>
值与变量名相同,我们也可以使用简写形式
<textarea bind:value></textarea>
select
同样适用bind:value进行绑定
<select bind:value={selected} >
选择框含有一个名为 multiple 的属性,在这种情况下,它将会被设置为数组而不是单值(vue类似)。
Contenteditable绑定
支持 contenteditable="true"属性的标签,可以使用 textContent 与 innerHTML 属性的绑定:
<div
contenteditable="true"
bind:innerHTML={html}
></div>
this绑定
this可以绑定到任何标签 (或组件) 并允许你获取对渲染标签的引用
<script lang="ts">
import { onMount } from "svelte"
let canvas
onMount(() => {
const ctx = canvas.getContext('2d')
//
})
</script>
<canvas bind:this={canvas} width={32} height={32} />
组件绑定
正如可以绑定到DOM元素的属性一样,你也可以将组件的属性绑定。例如,我们能绑定位于<Keypad>组件内的 value 属性,就如同一个表单标签一般:
<Keypad bind:value={pin} on:submit={handleSubmit}/>
生命周期
每个组件都有一个生命周期,从创建时开始,到销毁时结束
onMount
onMount 第一次渲染到 DOM 之后运行
<script lang="ts">
import { onMount } from "svelte"
let canvas
onMount(() => {
console.log(canvas)
})
</script>
<canvas bind:this={canvas}></canvas>
onDestroy
onDestroy 组件销毁时执行
<script lang="ts">
import { onMount, onDestroy } from "svelte";
let canvas;
let timer;
onMount(() => {
console.log(canvas);
timer = setInterval(() => {
console.log(new Date());
}, 2000);
});
onDestroy(() => {
clearInterval(timer);
});
</script>
<canvas bind:this={canvas} />
beforeUpdate 和 afterUpdate
- beforeUpdate 函数实现在DOM渲染完成前执行
- afterUpdate 渲染完成后执行
trick
在Svelte中每当组件状态发生改变时,DOM不会立即更新,反而会会等待下一个 microtask 以查看是否还有其他变化的状态或组件需要应用更新。这样做避免了浏览器做无用功,使之更高效。
import { tick } from 'svelte';
...
// 等待dom更新
await tick();
// dom更新后的操作
this.selectionStart = selectionStart;
this.selectionEnd = selectionEnd;
...
Store
使用跟vue、react类似,
// store/index.ts
import { writable } from 'svelte/store'
export const count = writable(0)
// 订阅
const unsubscribe = count.subscribe(value => {
count_value = value;
})
function increment() {
// 触发更新
count.update(n => n + 1)
}
// 重置
function reset() {
count.set(0)
}
下面是一个例子
<script lang="ts">
import {count} from './store'
import Hello from './Hello.svelte'
let count_value
const unsubscribe = count.subscribe(value => {
count_value = value
})
</script>
<h1>The count is {count_value}</h1>
<Hello/>
<script lang="ts">
import {count} from './store'
export let name: string = 'wd';
let count_value;
const unsubscribe = count.subscribe(value => {
count_value = value;
});
function increment() {
count.update(n => n + 1);
}
function reset() {
count.set(0);
}
</script>
<h1>Hello {name}!</h1>
{count_value}
<button on:click={increment}>+</button>
<button on:click={reset}>reset</button>
自动订阅
调用subscribe后,当组件被销毁时,不会自动销毁调阅函数,需要手动调用unsubscribe去销毁
import { onDestroy } from 'svelte';
import { count } from './stores.js';
import Incrementer from './Incrementer.svelte';
import Decrementer from './Decrementer.svelte';
import Resetter from './Resetter.svelte';
let count_value;
const unsubscribe = count.subscribe(value => {
count_value = value;
});
onDestroy(unsubscribe);
上面的,当组件有很多需要手动销毁时很麻烦,可以使用$ 来引用store中的值
<script>
import { count } from './stores.js';
import Incrementer from './Incrementer.svelte';
import Decrementer from './Decrementer.svelte';
import Resetter from './Resetter.svelte';
console.log('count:' +$count)
</script>
<h1>The count is {$count}</h1>
自定义store
只要一个对象正确的使用 subscribe ,它就是可以称之为store
例如, 这个 count store 在前面的例子中包含 increment、 decrement 和 reset组件,以防止暴露 set 和update方法
function createCount() {
const { subscribe, set, update } = writable(0);
return {
subscribe,
increment: () => update(n => n + 1),
decrement: () => update(n => n - 1),
reset: () => set(0)
};
}
绑定store
<script lang="ts">
import {count} from './store'
</script>
<input type="number" bind:value={$count}>
<Hello/>