svelte 上手初体验
开发前准备工作
安装插件:Svelte for VS Code
安装 demo 项目 npm create vite@latest my-app -- --template svelte
或者使用 svelteKit
来安装项目 npx create svelte@latest my-app
。
svelte 的特点
- 不使用虚拟DOM
- 重编译 轻运行时
- 更少的code
- 无障碍友好(对于一些不规范的标签写法,会有提示)
- 可作为单独的组件开发,嵌入其他的技术栈的项目中。
svelte 生命周期
声明周期对于每个应用/组件来说都十分重要,各个框架总体来说也大同小异。
onMount
组件初始化的生命周期,在此处进行异步数据的获取。此外,如果 onMount
函数返回一个函数,这个函数会在组件销毁时调用。
<script>
import { onMount } from 'svelte';
let photos = [];
onMount(async () => {
const res = await fetch(`/tutorial/api/album`);
photos = await res.json();
});
</script>
onDestroy
顾名思义,用来组件销毁时进行逻辑处理,如清除定时器,解除监听事件之类的。
<script>
import { onDestroy } from 'svelte';
let counter = 0;
const interval = setInterval(() => counter += 1, 1000);
onDestroy(() => clearInterval(interval));
</script>
beforeUpdate & afterUpdate
DOM 更新前后分别触发,值得注意的是,初始化的时候,beforeUpdate
会先于 onMount
执行。
beforeUpdate(() => {
autoscroll = div && (div.offsetHeight + div.scrollTop) > (div.scrollHeight - 20);
});
afterUpdate(() => {
if (autoscroll) div.scrollTo(0, div.scrollHeight);
});
上手开发
基础语法:
- 数据绑定/属性绑定:使用
{}
符号包裹。 - 样式:在
.svelte
文件里面声明style
标签。这种方式与 vue 类似,不同的是它是只作用于组件,相当于scoped
- 渲染
html
:{@html }
- 事件绑定:
on:click={}
- 双向绑定:
<input bind:value={inputValue} bind:checked={isChecked}/>
, 对于单选和多选框的双向绑定,使用bind:group={selectValue}
来实现。 - 计算属性:
$:
。 不知道该怎么表达,就用了vue
里面的相等概念。 - 组件传值 props:
export let childProps = 'inital value';
。这样就表示声明了一个childProps
的值,在引用组件的时候可以通过childProps={}
来给组件传值。其中默认赋值不是必须的。 - 事件装饰器:内置的用来约束 DOM 绑定事件的装饰器,用法与 vue 类似,写法有点区别 -
<div on:click|once={handleClick}></div>
。框架提供的的装饰器有:preventDefault
stopPropagation
passive
capture
nonpassive
once
self
trusted
入口文件
入口文件引入主组件,和公共样式。
import App from './App.svelte'
const app = new App({
target: document.getElementById('app')
})
export default app
.svelte
文件
这个文件整体和 .vue
差不多,都有 style
标签和 script
标签,不同的是不需要 template
标签来包裹。
模板语法
条件判断
条件判断的写法还算中规中矩,如果有用过模板语法或者PHP之类的,应该不陌生,很好上手。
{#if user.loggedIn}
<button on:click={toggle}>
Log out
</button>
{:else}
<button on:click={toggle}>
Log in
</button>
{/if}
循环
循环的写法就略微有些怪异了,用了一个 as
关键字,我第一次写的时候就搞错了,习惯性就打出来一个 in
- 。-
<script>
let cats = [
{ id: 'J---aiyznGQ', name: 'Keyboard Cat' },
{ id: 'z_AbfPXTKms', name: 'Maru' },
{ id: 'OUtn3pvWmpg', name: 'Henri The Existential Cat' }
];
let name = 'caaaaaat'
</script>
<ul>
{#each cats as cat}
<li><a target="_blank" href="https://www.youtube.com/watch?v={cat.id}">
{cat.name}
</a></li>
{/each}
</ul>
除此之外,也可以用解构的方式,将数组的对象属性拍平。不过需要注意的是,如果数组内对象的属性和组件的其他响应式值重名时,在循环内部是获取不到这个重名的响应式值的。所以尽量不要重名哦,不然会造成使用上的困扰。
<ul>
<script>
let cats = [
{ id: 'J---aiyznGQ', name: 'Keyboard Cat' },
{ id: 'z_AbfPXTKms', name: 'Maru' },
{ id: 'OUtn3pvWmpg', name: 'Henri The Existential Cat' }
];
let name = 'caaaaaat'
</script>
<!-- open each block -->
{name}
{#each cats as {name, id}}
<li><a target="_blank" href="https://www.youtube.com/watch?v={id}">
{name}
</a></li>
<!-- close each block -->
{/each}
</ul>
还有一点值得注意的是,使用数组方法操作数组时,为了保证正确的响应,需要加上key。声明的方式是 for things as thing (thing.id)
, 括号内的就是 key。
异步模块
这个模块很方便很好用,对于异步方法获取到的 data ,可以实现在模板中对 pending 状态和 resolve/ reject 状态区分展示。而在 vue 的写法中,我们通常的做法是:定义一个 loading 的数据,再进行 if/else
的判断。而在 svelte
里面就可以不需要单独的定义冗余字段。
<script>
async function getRandomNumber() {
const res = await fetch(`/tutorial/random-number`);
const text = await res.text();
if (res.ok) {
return text;
} else {
throw new Error(text);
}
}
let promise = getRandomNumber();
function handleClick() {
promise = getRandomNumber();
}
</script>
<button on:click={handleClick}>
generate random number
</button>
{#await promise}
<p>loading。。。</p>
{:then number}
<p>{number}</p>
{:catch error}
<p>{error.message}</p>
{/await}
响应式
虽然说 Svelte 是普通的变量声明来实现响应式,但是对于对象和数组的响应式,相对来说比较怪异哈,具体表现为:
- 通过改变数组的方法响应式不会触发响应式。
- 直接修改对象的某个属性值也不会触发响应式。
- 总之:需要响应式的值必须要出现在等式的左边,才会触发更新。
组件
组件写法
组件为一个单个的 .svelte
文件,通过 export
可以导出 props
。
// Counter.svelte
<script lang="ts">
let count: number = 0
const increment = () => {
count += 1
}
export let childProps;
</script>
<button on:click={increment}>
count is {count}
</button>
<p>{childProps}</p>
组件引入
引入组件,需要注意的是组件必须为大写开头,不然会被编译为普通的 html
标签。
// app.svelte
<script lang="ts">
import svelteLogo from './assets/svelte.svg'
import Child from './lib/Child.svelte'
</script>
<main>
<div class="card">
<Counter childProps={2333}/>
</div>
</main>
组件事件
组件的事件通过 dispatch
触发,父组件通过 on:eventName
来接收事件。
<script>
import {createEventDispatcher} from 'svelte'
const dispatch = createEventDispatcher();
function sayHello() {
dispatch('message', {
text: 'hello world!'
})
}
</script>
<button on:click={sayHello}>
Click to say hello
</button>
如果不指定监听的回调函数,则事件会被透传到上一级组件,可以用来做对下下级组件的事件监听。