2023年了,国内前端领域基本被Vue
、React
占领市场,近几年似乎前端技术栈的迭代更新缓慢了下来。
当然近几年也出现了像svelte
、solid.js
等一些新兴的前端框架,这些框架有很多创新的点,比如svelte相比于vue,react最大的不同,没有使用虚拟dom,将程序运行时的一系列处理移到了编译阶段,这样理论上就可以大大提高程序运行时的性能。
虽然现在国内svelte
不温不火,除了它自身存在的一些问题,主要还是生态不够完善。但是它在国外是非常火的,说不定哪天这团火就烧到国內了。
本文通过和vue语法逐一对比的方式,可以让熟悉vue的同学快速上手svelte~
安装svelte项目:
npx degit sveltejs/template my-svelte-project
cd my-svelte-project
npm install
npm run dev
vue页面都是以.vue
结尾,而svelte页面是以.svelte
结尾。(吐槽:框架名起的拗口就算了,文件后缀还不能简化,这估计也是在国内流行不起来的一个原因)。
在svelte组件内可以直接写html内容,不像vue需要写在template
模版中。
样式写在标签中,自带 scope(样式隔离) 效果。
vue的插值表达式是用双大括号包裹的,而svelte是用一个大括号{}
包裹.
<script>
let name = 'world';
</script>
<h1>Hello {name}!</h1>
直接用{}包裹就行,如果需要拼接,可以用引号包裹,或者用模版字符串也是可以的。
ps:如果变量名和属性名一致,甚至可以简写,例如src={src}
可以简写为{src}
。如下:
<script>
let src = 'https://p3-passport.byteimg.com/img/user-avatar/f51713bb5df34d682e00c40d5078b95e~180x180.awebp';
let name = '前端阿彬的头像';
</script>
<img src={src} alt="">
<img {src} alt="图片描述:{name}">
<img {src} alt={`${src} : ${name}`}>
vue里使用v-html
语法糖向指定节点中渲染包含html结构的内容,即设置元素的innerHTML
在svelte里语法是使用特殊标记{@html ...}
<script>
let string = `学如逆水行舟,不进则退!`;
</script>
<p>{@html string}</p>
在vue里,v-bind
一般用来动态绑定值,v-model
用来双向绑定数据。
而到了svelte里,双向绑定是用bind:
标记。svelte没有类似于vue的v-bind
动态绑定这一说,统统用{}
插值表达式就能实现。
<script>
let name = 'world';
</script>
<input bind:value={name}>
<h1>Hello {name}!</h1>
svelte的双向绑定有个很方便的用法,就是绑定多个值,语法为bind:group
。例如在实现复选框组时,可以用bind:group
绑定数据,绑定的值会形成一个数组,如下:
let list = [
'小红',
'小美',
'阿彬'
];
let peoples = [阿彬'];
{#each list as people}
<label>
<input type=checkbox bind:group={peoples} value={flavour}>
{people}
</label>
{/each}
上面代码很简单就能实现一个复选框组合,值绑定给了peoples
,是个数组,多个值时格式为['阿彬','小美']
。
vue绑定事件用v-on
,即语法糖@
。
在svelte里绑定事件语法为on:事件名
,如下:
function handleClick(){}
<button on:click={handleClick}>
同样,svelte也有事件修饰符,语法为on:click|once={handleClick}
,可以同时使用多个修饰符,例如on:click|once|capture={handleClick}
svelte的事件修饰符有:
preventDefault
:调用event.preventDefault()
,阻止默认事件;stopPropagation
:调用 event.stopPropagation()
, 阻止冒泡;passive
:优化了对 touch/wheel 事件的滚动表现(Svelte 会在合适的地方自动添加滚动条);capture
:在捕获阶段而不是冒泡阶段触发事件处理程序;once
:事件只能执行一次。self
:仅当 event.target 是其本身时才执行。svelte的$: 代码块
类似于vue的 computed计算属性 和 watch状态监听 的结合体
。
它既能用来声明计算属性,同时可以监听数据变化执行代码块,类似于vue3的watchEffect,它不需要指定要监听的数据,只要在代码块中就会监听。
计算属性举例:
let name = '阿彬'
$: level = name + '是个前端程序员'
<p>{ level }</p>
监听数据变化:需要用{}
包裹代码块
let val
$: {
console.log(`你输入的内容是: ${val}`);
alert(`你输入的内容是: ${val}`);
}
<input bind:value={val}>
以上代码,只要在输入框输入内容,就会弹窗提示
使用
$:
监听数据变化时,可以在{}
代码块前使用if
语句,是不是很巧妙,能减少很多情况下的代码量。
$: if (count >= 10) {
alert(`count is dangerously high!`);
count = 9;
}
svelt里的v-if
条件语句语法有点像uniapp的条件编译。由首尾两个标记包裹需要根据条件控制显隐的dom代码块,语法如下:
字符
#
始终表示块开始标记。字符/
始终表示块结束标记。:
中的字符表示{:else}
块连续标记。svelte的逻辑语句都是用开始标记
#
和结束标记/
包裹。 逻辑语句包括if else
条件编译、each 遍历渲染
、await块
,也就是本文7、8、9点。
{#if 条件}
代码块
{/if}
举例:
{#if user.loggedIn}
<button on:click={toggle}>
Log out
</button>
{/if}
{#if !user.loggedIn}
<button on:click={toggle}>
Log in
</button>
{/if}
if条件语句中的else、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}
vue的v-for语法为v-for=(item,index) in list) :key="index"
。
svelte遍历渲染使用的是 each
块:
下面例子中,类比vue,cat就是遍历的item:
<script>
let cats = [
{ id: '1', name: 'Keyboard Cat' },
{ id: '2', name: 'Maru' },
{ id: '3', name: 'Henri The Existential Cat' }
];
</script>
<ul>
{#each cats as cat (cat.id)}
<li><a target="_blank" href="https://www.youtube.com/watch?v={cat.id}">
{cat.name}
</a></li>
{/each}
</ul>
怎么像vue一样,增加索引当第二个参数呢,写法如下,用,
逗号分隔就行:
{#each cats as cat,i}
<li><a target="_blank" href="https://www.youtube.com/watch?v={cat.id}">
{cat.name}
</a></li>
{/each}
v-for里的key
唯一标识符,到了svelte里,只要在each块后括号说明key是什么就行,如下,id作为key
{#each cats as cat,i (cat.id)}
<li><a target="_blank" href="https://www.youtube.com/watch?v={cat.id}">
{cat.name}
</a></li>
{/each}
如果在开发一个用户个人主页时,在数据加载时需要显示骨架屏,加载成功正常显示页面,加载失败显示错误提示。
像这种依赖于异步请求决定页面的不同显示逻辑的场景,就可以使用 svelte 的 await
标记代码块。使用方法如下:
<script>
let promise = getRandomNumber();
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);
}
}
</script>
{#await promise}
<p>...waiting</p>
{:then number}
<p>The number is {number}</p>
{:catch error}
<p style="color: red">{error.message}</p>
{/await}
可以看到,可以很容易显示一个三种不同状态下的页面显示,甚至不需要声明什么变量来承接请求得到的数据,是不是很巧妙
如果你不需要判断请求错误的情况,则可以忽略 catch
块。如果在请求完成之前不想程序执行任何操作,也可以忽略第一个块。如下:
{#await promise then value}
<p>the value is {value}</p>
{/await}
svelte引入使用组件类似vue3,引入就可以直接使用,不需要注册。同时要注意组件名要大写,和html标签区分开。
<script>
import Child from './Nested.svelte';
</script>
<Child/>
vue中父组件向子组件传参,常规方式是在子组件用 props
声明可以接收的自定义属性。
而svelte则不同,它是在子组件中用
export
声明需要接收的props参数。
export
导出时赋值就是设置默认值。
注意: 与vue不同,svelte子组件接收的props数据是可以在子组件里直接修改的!
//父组件
import Child from './Child.svelte';
<Child content="父组件内容"/>
//子组件
<script>
export let content;
</script>
<p>The answer is {answer}</p>
传递多个参数: 如果你的组件含有一个对象属性,可以利用...
扩展运算符一次性绑定,如下:
const pkg = {
name: 'svelte',
version: 3,
speed: 'blazing',
website: 'https://svelte.dev'
};
<Info {...pkg}/>
//相当于
<Info name={pkg.name} version={pkg.version} speed={pkg.speed} website={pkg.website}/>
子组件自定义事件,即实现子组件调用父组件方法。
在子组件内需要使用createEventDispatcher
在首次实例化组件时,定义一个dispatch
进行连接,进而把组件实例化。具体使用如下:
//父组件
<script>
import Inner from './Inner.svelte';
function handleMessage(event) {
alert(event.detail.text);
}
</script>
<Inner on:message={handleMessage}/>
//子组件
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function sayHello() {
dispatch('message', {
text: 'Hello!'
});
}
</script>
<button on:click={sayHello}>
Click to say hello
</button>
如果想为组件注册dom事件,只需要在子组件内部需要触发这个事件的地方加上on:事件名
就行,例如:
<button on:click>
Click me
</button>
这样就可以在使用这个子组件时绑定on:click
事件了。
this
可以绑定到任何标签 (或组件) 并允许你获取对渲染标签的引用。 例如,我们对 标签进行绑定:
<canvas
bind:this={canvas}
width={32}
height={32}
></canvas>
注意,svelte也是有生命周期的,在onMount生命周期前,组件还没有挂载完毕,canvas
的值这时候是undefined
,所以如果要操作dom引用,需要在 onMount
生命周期后。
svelte的生命周期只有四个,分别是:
onMount
:页面渲染完成后;onDestroy
:组件销毁前;onDestroy
:DOM渲染完成前执行;onDestroy
:在异步数据加载完成后执行;PS:tick
函数不同于其他生命周期函数,因为你可以随时调用它。它类似于vue的nextTick
,每当组件pending状态
变化便会立即体现到DOM中 (除非没有pending状态
变化)。
由于 Svelte 的响应式是由赋值语句触发的,因此使用数组的诸如 push
和 splice
之类的方法就不会触发自动更新视图。
解决该问题的一种方法是多执行一个赋值的语句:
function addNumber() {
numbers.push(numbers.length + 1);
numbers = numbers;
}
但还有一个更惯用的解决方案:
function addNumber() {
numbers = [...numbers, numbers.length + 1];
}
你可以使用类似的模式来替换 pop
、shift
、unshift
和 splice
方法。
另外,需要注意只有被更新的变量名称出现在赋值语句的左侧,才能在变量变化时更新其视图。例如下面这种情况视图是不会修改的:
<script>
const obj = {
foo: {
name: '阿彬'
}
}
const foo = obj.foo;
const handleClick = () => {
foo.name = '小美' //因为obj没出现在赋值表达式左侧,所以下面的p标签内容不会变化
}
</script>
<p>{ JSON.stringify(obj) }</p>
//在输入内容时,上面的p标签显示不会变化
<input bind:value={ foo.name }>
//点击时,上面的p标签显示也不会变化
<button on:click={handleClick}>测试</button>
svelte的使用还是很简单的,如果你会vue,相信你花5分钟读完这篇文章,就可以上手开发svelte了
我是喜欢归纳总结前端相关知识的前端阿彬,尽力持续输出原创优质文章,欢迎点赞关注
往期文章:
# ♀️css魔法:伪元素content ➕ css函数
# 玩转css逐帧动画,纯css让哥哥动起来
# 2023 前端 SEO 无死角解读
# 我给自己搭建的前端导航网站,你们都别用
# 2023 最新最细 vite+vue3+ts 多页面项目架构,建议收藏备用!
# 浅谈 强制缓存/协商缓存 怎么用?
# 2023 前端性能优化清单