svtelte 是一款更小、更轻量的框架,比现在的三大框架都更轻量,但是应用不广泛。之中的利弊可以移步知乎等论坛
https://www.zhihu.com/question/53150351
Hello world
和vue 比较类似,他也有自己独特的 .svelte 后缀的文件
app.svelte
<h1>Hello world!</h1>
这个文件会输出对应的 html、js和css 代码,(简直是比vue还要精简)
动态绑定变量
<script>
let name = 'world';
</script>
<h1>Hello {name}!</h1>
用 {} 号将script 中的变量绑定到html中,和jsx语法类似
动态绑定属性
<script>
let src = 'tutorial/image.gif'
</script>
<img src={src}/>
// 或者简写
<img {src}/>
css 样式
通过<style> 中写样式
<style>
p {
color: purple;
font-family: 'Comic Sans MS', cursive;
font-size: 2em;
}
</style>
<p>This is a paragraph.</p>
引入组件
<script>中import 需要的组件 调用就可以了
<script>
import Nested from './Nested.svelte';
</script>
<p>This is a paragraph.</p>
<Nested/>
引入html 代码片段
通过@html 使用html代码片段,不要忘记之后加个空格哦
<script>
let string = `this string contains some <strong>HTML!!!</strong>`;
</script>
<p>{{@html string}</p>
svelte 开发工具插件
挂在到实际节点上
import App from './App.svelte';
const app = new App({
target: document.body,
props: {
// we'll learn about props later
answer: 42
}
});
响应式编程
通过on:click 绑定点击事件 修改变量之后{} 赋值
<script>
let count = 0;
function handleClick() {
count += 1;
}
</script>
<button on:click={handleClick}>
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>
计算属性
类似vue的computed ,通过$:声明一个变量
<script>
let count = 0;
$: doubled = count * 2;
function handleClick() {
count += 1;
}
</script>
<button on:click={handleClick}>
Clicked {doubled} {count === 1 ? 'time' : 'times'}
</button>
// 等价于 {count * 2}
监听变量
通过$: 之后执行代码,来监听变量,还可加条件监听,类似 react的useEffect 或vue的watch
// case 1
$: console.log(`the count is ${count}`);
// case 2
$: {
console.log(`the count is ${count}`);
alert(`I SAID THE COUNT IS ${count}`);
}
// case 3
$: if (count >= 10) {
alert(`count is dangerously high!`);
count = 9;
}
解决修改array或object 更新问题
数组push、pop、
shift
、unshift
和 splice
不会触发更新,解决办法就是重新 =
// case 1
function addNumber() {
numbers.push(numbers.length + 1);
numbers = numbers;
}
// case 2 (惯用套路)
function addNumber() {
numbers = [...numbers, numbers.length + 1];
}
操作属性也是可以触发更新的
function addNumber() {
numbers[numbers.length] = numbers.length + 1;
}
function addNumber() {
numbers.b += 1;
}
object 传递引用修改无效
const foo = obj.foo;
foo.bar = 'baz';
// 解决方法
obj = obj;
props 声明
通过export 对外声明组件变量
<script>
export let answer;
// 这样写 2 为anwer的默认值
export let answer = 2;
</script>
<p>The answer is {answer}</p>
通过三点表达式 简化传递属性
<Info {...pkg}/>
还可以使用$$props获取未声明传递的属性 类似react jsx写法
逻辑判断
if语句
{#if} 开始判断 {:else}{:else if}中间拼接 {/if}结束
<script>
let user = { loggedIn: false };
function toggle() {
user.loggedIn = !user.loggedIn;
}
</script>
{#if user.loggedIn}
<button on:click={toggle}>
Log out
</button>
{/if}
{#if !user.loggedIn}
<button on:click={toggle}>
Log in
</button>
{/if}
//或者
{#if user.loggedIn}
<button on:click={toggle}>
Log out
</button>
{:else}
<button on:click={toggle}>
Log in
</button>
{/if}
for 循环
cats 是item 、 i是索引值
{#each cats as cat, i}
<li><a target="_blank" href="https://www.youtube.com/watch?v={cat.id}">
{i + 1}: {cat.name}
</a></li>
{/each}
列表渲染key
id 作为key ,利于正确更新 。默认用thing 更新,会造成更新异常
{#each things as thing (thing.id)}
<Thing current={thing.color}/>
{/each}
await 代码块
利于promise 处理,用于网络请求
{#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: 绑定事件
<script>
let m = { x: 0, y: 0 };
function handleMousemove(event) {
m.x = event.clientX;
m.y = event.clientY;
}
</script>
<style>
div { width: 100%; height: 100%; }
</style>
<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>
事件绑定修饰符
- once : 绑定事件只执行一次
- preventDefault: 执行时 event.preventDefault()
- stopPropagation: 执行时 event.stopPropagation()
- passive : 提高scroll事件滚动流畅的效果
- capture : 事件捕获
- self: 事件发生对象为绑定对象触发。
on:click|once|capture={...} 链式 类似vue 语法.stop
<script>
function handleClick() {
alert('no more alerts')
}
</script>
<button on:click|once={handleClick}>
Click me
</button>
组件的自定义事件
通过createEventDispatcher 创建事件触发器 dispatch
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function sayHello() {
dispatch('message', {
text: 'Hello!'
});
}
</script>
注意自定义事件不会冒泡 、所以需要中间组件进行事件转发。通过event.detail 传递
<script>
import Inner from './Inner.svelte';
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function forward(event) {
dispatch('message', event.detail);
}
</script>
<Inner on:message={forward}/>
简写
<script>
import Inner from './Inner.svelte';
</script>
<Inner on:message/>
双向绑定
通过bind: 双向绑定进行绑定值
<script>
let name = 'world';
</script>
<input bind:value={name}>
<h1>Hello {name}!</h1>
<script>
let a = 1;
let b = 2;
</script>
<label>
// 自动处理
<input type=number bind:value={a} min=0 max=10>
<input type=range bind:value={a} min=0 max=10>
</label>
<label>
<input type=number bind:value={b} min=0 max=10>
<input type=range bind:value={b} min=0 max=10>
</label>
<p>{a} + {b} = {a + b}</p>
<label>
<input type=checkbox bind:checked={yes}>
Yes! Send me regular email spam
</label>
使用bind:group处理 单选按钮组或者多选按钮组
<script>
let scoops = 1;
let flavours = ['Mint choc chip'];
function join(flavours) {
if (flavours.length === 1) return flavours[0];
return `${flavours.slice(0, -1).join(', ')} and ${flavours[flavours.length - 1]}`;
}
</script>
<h2>Size</h2>
<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>
<h2>Flavours</h2>
<label>
<input type=checkbox bind:group={flavours} value="Cookies and cream">
Cookies and cream
</label>
<label>
<input type=checkbox bind:group={flavours} value="Mint choc chip">
Mint choc chip
</label>
<label>
<input type=checkbox bind:group={flavours} value="Raspberry ripple">
Raspberry ripple
</label>
{#if flavours.length === 0}
<p>Please select at least one flavour</p>
{:else if flavours.length > scoops}
<p>Can't order more flavours than scoops!</p>
{:else}
<p>
You ordered {scoops} {scoops === 1 ? 'scoop' : 'scoops'}
of {join(flavours)}
</p>
{/if}
<textarea bind:value={value}></textarea>
<select bind:value={selected} on:change="{() => answer = ''}">
<select multiple bind:value={flavours}>
{#each menu as flavour}
<option value={flavour}>
{flavour}
</option>
{/each}
</select>
动态绑定富文本代码片段
<div
contenteditable="true"
bind:innerHTML={html}
></div>
video 、audio标签绑定
- duration(只读):视频总时间
- buffered(只读):一个数组包含{start, end}对象
- seekable(只读):
- played(只读):
- seeking(只读):
- ended(只读):
其他四个 可以双向绑定的
- currentTime: 当前进度
- playbackRate:播放速度 设置1为正常速度
- paused : 暂停
- volume: 音量大小 0到1
<video
poster="https://sveltejs.github.io/assets/caminandes-llamigos.jpg"
src="https://sveltejs.github.io/assets/caminandes-llamigos.mp4"
on:mousemove={handleMousemove}
on:mousedown={handleMousedown}
bind:currentTime={time}
bind:duration
bind:paused
></video>
任何块状元素都有属性clientWidth
, clientHeight
, offsetWidth
和 offsetHeight
(都是只读)
<div bind:clientWidth={w} bind:clientHeight={h}>
<span style="font-size: {size}px">{text}</span>
</div>
通过bind:this 获取节点本身,必须要mounted后才能渲染到。 类似 react和vue 的ref
<canvas
bind:this={canvas}
width={32}
height={32}
></canvas>
绑定组件的自定义属性
<Keypad bind:value={pin} on:submit={handleSubmit}/>
生命周期
onMount 生命周期应用广泛你可以在这边,初始化发起http请求。
注意:在SSR环境中。除了onDestroy ,其他生命周期都不会触发.
要在组件初始化的时候,调用生命周期的钩子函数,不要在setTimeout中使用
onMount 如果返回一个方法,那个方法会在组件销毁时候调用。类似react 的useEffect
<script>
import { onMount } from 'svelte';
let photos = [];
onMount(async () => {
const res = await fetch(`https://jsonplaceholder.typicode.com/photos?_limit=20`);
photos = await res.json();
});
</script>
onDestory 组件销毁时候调用。比如说setInterval 。避免内存泄漏
<script>
import { onDestroy } from 'svelte';
let seconds = 0;
const interval = setInterval(() => seconds += 1, 1000);
onDestroy(() => clearInterval(interval));
</script>
可以封装一下类似react hooks 的工具
// utils.js
import { onDestroy } from 'svelte';
export function onInterval(callback, milliseconds) {
const interval = setInterval(callback, milliseconds);
onDestroy(() => {
clearInterval(interval);
});
}
// 在组件中使用
<script>
import { onInterval } from './utils.js';
let seconds = 0;
onInterval(() => seconds += 1, 1000);
</script>
beforeUpdate 和 afterUpdate 用于组件更新时候的hook(吐槽下: 官方的例子真的是简洁易懂)
<script>
import Eliza from 'elizabot';
import { beforeUpdate, afterUpdate } from 'svelte';
let div;
let autoscroll;
beforeUpdate(() => {
autoscroll = div && (div.offsetHeight + div.scrollTop) > (div.scrollHeight - 20);
});
afterUpdate(() => {
if(autoscroll) div.scrollTo(0,div.scrollHeight)
});
const eliza = new Eliza();
let comments = [
{ author: 'eliza', text: eliza.getInitial() }
];
function handleKeydown(event) {
if (event.which === 13) {
const text = event.target.value;
if (!text) return;
comments = comments.concat({
author: 'user',
text
});
event.target.value = '';
const reply = eliza.transform(text);
setTimeout(() => {
comments = comments.concat({
author: 'eliza',
text: '...',
placeholder: true
});
setTimeout(() => {
comments = comments.filter(comment => !comment.placeholder).concat({
author: 'eliza',
text: reply
});
}, 500 + Math.random() * 500);
}, 200 + Math.random() * 200);
}
}
</script>
<style>
.chat {
display: flex;
flex-direction: column;
height: 100%;
max-width: 320px;
}
.scrollable {
flex: 1 1 auto;
border-top: 1px solid #eee;
margin: 0 0 0.5em 0;
overflow-y: auto;
}
article {
margin: 0.5em 0;
}
.user {
text-align: right;
}
span {
padding: 0.5em 1em;
display: inline-block;
}
.eliza span {
background-color: #eee;
border-radius: 1em 1em 1em 0;
}
.user span {
background-color: #0074D9;
color: white;
border-radius: 1em 1em 0 1em;
word-break: break-all;
}
</style>
<div class="chat">
<h1>Eliza</h1>
<div class="scrollable" bind:this={div}>
{#each comments as comment}
<article class={comment.author}>
<span>{comment.text}</span>
</article>
{/each}
</div>
<input on:keydown={handleKeydown}>
</div>
tick 神奇的hooks ,不只再组件初始化的时候可以调用
<script>
import {tick} from 'svelte';
let text = `Select some text and hit the tab key to toggle uppercase`;
async function handleKeydown(event) {
if (event.which !== 9) return;
event.preventDefault();
const { selectionStart, selectionEnd, value } = this;
const selection = value.slice(selectionStart, selectionEnd);
const replacement = /[a-z]/.test(selection)
? selection.toUpperCase()
: selection.toLowerCase();
text = (
value.slice(0, selectionStart) +
replacement +
value.slice(selectionEnd)
);
await tick() // 再执行后面代码前,先把之前代码执行完成
this.selectionStart = selectionStart;
this.selectionEnd = selectionEnd;
}
</script>
<style>
textarea {
width: 100%;
height: 200px;
}
</style>
<textarea value={text} on:keydown={handleKeydown}></textarea>