目錄結(jié)構(gòu)
- 說在前面
- 關(guān)于虛擬DOM
- 業(yè)內(nèi)大牛如何看待 svelte
- 關(guān)于UI
- svelte 與其他框架
- 開始打包
- 對比大小
- 性能考核
- 對比性能
- 使用svelte 模板
- 下載依賴
- 啟動項目
- 打開頁面
- 安裝ui
- 簡單了解語法
- 綁定數(shù)據(jù)
- 條件渲染
- 循環(huán)渲染
- 事件綁定
- 組件
- 傳值
- 動畫
- 生命周期
- 案例
- 注解
- 參考文獻(xiàn)
說在前面
svelte
不知道大家有沒有了解過逝她,最近一次偶然刷文章,看到一篇《都快2020年睬捶,你還沒聽說過SvelteJS?》 [0] 的文章黔宛,看了svelte與其他框架的對比
svelte 中文 doc [1]
對比各框架開發(fā)的項目的尺寸
[圖片上傳失敗...(image-7a421c-1607179090667)]
對比各項目的 Lighthouse 性能評分
[圖片上傳失敗...(image-b5004c-1607179090667)]
說實話屬實有點驚訝到我。 ** 真的假的擒贸?
于是我抱著試試就試試的心態(tài)來做個評測人
關(guān)于虛擬DOM
svelte 的作者 Rich Harris 在一篇名為《虛擬DOM純粹是開銷》 [2] 的文章中指出臀晃,為什么不使用虛擬dom
虛擬dom的三個步驟
- 兩個快照都包含一個元素。在兩種情況下都是
<div>
介劫,這意味著我們可以保留相同的DOM節(jié)點 - 我們列舉了新舊屬性上的所有屬性徽惋,
<div>
以查看是否需要更改,添加或刪除任何屬性座韵。在這兩種情況下险绘,我們都有一個屬性-aclassName
的值為"greeting"
- 下降到元素中,我們看到文本已更改,因此我們需要更新真實的DOM
而 svelte 直接跳過前面兩步宦棺,直接執(zhí)行第三步
if (changed.name) {
text.data = name;
}
這幾乎就是Svelte生成的更新代碼瓣距。與傳統(tǒng)的UI框架不同,Svelte是一種編譯器渺氧,它在構(gòu)建時就知道應(yīng)用程序中的情況如何變化旨涝,而不必等著在運行時進(jìn)行工作蹬屹。
業(yè)內(nèi)大牛如何看待 svelte
vue 的作者尤雨溪侣背,在知乎回答了個 《如何看待 svelte 這個前端框架?》 [3] 問題
“svelte 的核心思想在于 通過靜態(tài)編譯來減少框架運行時的代碼”
關(guān)于 UI
我覺著這兩個ui還是比較不錯的
Material UI
https://github.com/hperrin/svelte-material-ui
SVELTESTRAP
https://github.com/bestguy/sveltestrap
svelte 與其他框架
老嚴(yán)閑來的時候找到了 一個叫做 Realworld
存儲庫中有24種conduit實現(xiàn)As 慨默,也就是用來對比性能的以及大小的贩耐;
今天一起來對比一下 vue/react/svelte 這三個框架的
- 項目打包后壓縮包大小
- 項目網(wǎng)頁性能分析對比
vue:https://github.com/gothinkster/vue-realworld-example-app
react:https://github.com/gothinkster/react-redux-realworld-example-app
svelte:https://github.com/sveltejs/realworld
感興趣的同學(xué)也可以看看其他框架 https://github.com/gothinkster/realworld
開始打包
這三個項目,打包順序基本上在同一時間執(zhí)行 npm run build
厦取,svelte 直接在我我眨眼的一瞬間打完包 潮太,啪 很快啊 (有點夸張~幾秒鐘)
靜靜的等待vue和react同時打包完之后,我開始進(jìn)行統(tǒng)一壓縮靜態(tài)文件虾攻,壓縮格式為zip
對比大小
排名如下
- svelte — 89.9 KB
- vue — 483 KB
- react — 490 KB
果然差距還是確實如傳說一樣恐怖
性能考核
嚴(yán)老濕本次使用 Chrome 的 Lighthouse (谷歌網(wǎng)頁性能分析工具) 來對比性能評分
全局安裝 lighthouse
npm install -g lighthouse
執(zhí)行 (直接使用的對應(yīng)的線上地址)
lighthouse https://realworld.svelte.dev/
這是我在下面所測試的截屏
vue
react
svelte
對比性能
性能得分排名如下:
- svelte — 83
- react — 52
- vue — 32
svelte
也是不負(fù)眾望 穩(wěn)居第一
使用 svelte 模板
看到上面铡买,天天被逼著做性能優(yōu)化的同學(xué),激動起來了 ~ 那我們一起來簡單學(xué)習(xí)一下這個性能強(qiáng)悍的 svelte 吧
svelte 模板 [4] 霎箍,我們直接使用一個模板來開工
git clone https://github.com/sveltejs/template
&
cd template-master
下載依賴
yarn install
or
npm install
下載完成之后奇钞,我們看看目錄。老嚴(yán)的評價就是 簡潔
啟動項目
yarn dev
or
npm run dev
啟動完成之后
Your application is ready~! ?
- Local: http://localhost:5000
- Network: Add `--host` to expose
打開頁面
地址欄輸入 http://localhost:5000
我們可以看到這樣的一個頁面 hello world
安裝ui
這里我們使用 sveltestrap
npm install --save sveltestrap
npm install --save bootstrap
在 main.js 中引入
import 'bootstrap/dist/css/bootstrap.min.css';
在頁面中引入組件
<script>
import { Button } from "sveltestrap";
const handleClick = () => alert("I warned you!");
</script>
<Button color="danger" on:click={handleClick}>Do Not Press</Button>
簡單了解語法
在學(xué)習(xí)之前我覺得有必要簡單了解一下其語法
綁定數(shù)據(jù)
在 vue 中我們的變量需要寫在 data
中 漂坏,而 svelte
語法更加貼合原生
<!-- vue -->
data() {
return {
name: 'hhh',
};
}
<div>{{name}}</div>
svelte 綁定數(shù)據(jù) 景埃,svelte 動態(tài)綁定需要加上 {}
<script>
import { Button } from "sveltestrap";
// 定義數(shù)據(jù)
let name = "hhh";
let src = 'http://crazy-x-lovemysoul-x-vip.img.abc188.com/images/logo.png';
</script>
<!-- 綁定數(shù)據(jù) -->
<!-- 如果kv一致只用寫一個 -->
<img {src} alt="">
<Button>{name}</Button>
條件渲染
vue 中有條件渲染 v-if
v-else-if
v-else
,svelte 也有
<script>
let condition = 1;
</script>
{#if condition == 2}
<p>悲</p>
{:else if condition == 1}
<p>傷 </p>
{:else if condition == 0}
<p>日</p>
{:else}
<p>記</p>
{/if}
循環(huán)渲染
循環(huán)渲染列表
<script>
// 定義變量
let news = [
{ id: 1, title: '拜登呼吁必須停止把對手當(dāng)敵人' },
{ id: 2, title: '江蘇響水致78死爆炸案一審宣判' },
{ id: 3, title: '嫦娥五號將擇機(jī)實施月面軟著陸' }
];
</script>
<ul>
<!-- 有沒有點 ejs的感覺 -->
{#each news as {title}}
<li>
<a >
{title}
</a>
</li>
{/each}
</ul>
<style>
ul,li{
list-style: none;
}
a{
color: #ff3e00;
}
</style>
咋感覺有點像 ejs
循環(huán)渲染呢 [5]呢?
事件綁定
在 svelte
中方法直接寫函數(shù)定義函數(shù)即可使用
<script>
import { Button } from "sveltestrap";
// 定義數(shù)據(jù)
let name = "hhh",title = '標(biāo)題';
// 定義方法
const handleClick = () => {
name = "嚴(yán)老濕"
title = "老嚴(yán)帶你入坑 svelte"
};
</script>
<!-- on:click 綁定方法 {綁定動態(tài)值}-->
<Button color="danger" on:click={handleClick}>{name}</Button>
<h1>{title}</h1>
組件
組件是框架必不可少的一個功能
來看看 svelte 中如何創(chuàng)建一個組件吧
app.svelte
<script>
// 直接引入組件即可使用 無需注冊
import Child from './components/child.svelte'
let name = '我是你爹'
</script>
<div>
{name}
<Child></Child>
</div>
創(chuàng)建一個 child.svelte 頁面
<script>
let title = '我是你兒子'
</script>
<div>{title}</div>
那么組件有了,我們來看看組件傳值吧顶别!
傳值
app.svelte
<script>
import Child from './components/child.svelte'
let name = '我是你爹'
let childName = "狗剩"
</script>
<div>
{name}
<Child {childName}></Child>
</div>
child.svelte
<script>
export let childName;
</script>
<div>爹給我取的名字是 {childName}</div>
剛剛我們是簡單的單一傳值
接下來我們傳一個對象試試
app.svelte
<script>
import Child from './components/child.svelte'
let name = '我是你爹'
let aboutMe = {
name:'狗剩',
age:18,
gender:'man'
}
</script>
<div>
{name}
<!-- 通過... 展開 aboutMe -->
<Child {...aboutMe}></Child>
</div>
child.svelte
<script>
export let name,gender,age;
</script>
<div>我取的名字是 {name}</div>
<div>我取的年齡是 {age}</div>
<div>我取的性別是 {gender}</div>
那這多費勁吶谷徙?還需要一個個接收。有一話叫存在即合理
動畫
在官方 api 中提到 svelte 提供了一些動畫效果出來給大家使用
我們直接使用官方示例 淡入淡出動畫
<script>
import { fade } from 'svelte/transition';
let visible = true;
</script>
<label>
<input type="checkbox" bind:checked={visible}>
visible
</label>
{#if visible}
<p transition:fade>
Fades in and out
</p>
{/if}
生命周期
在 svelte 中的生命周期有onMount 驯绎、beforeUpdate 完慧、afterUpdate 、afterUpdate 下面老嚴(yán)依次給大家伙列出來
- onMount (掛載后)
該onMount
函數(shù)作為將component掛載到DOM后立即執(zhí)行的回調(diào)剩失。它必須在component初始化期間被調(diào)用(但不必位于component內(nèi)部屈尼;可以從外部模塊調(diào)用它)。
<script>
import { onMount } from 'svelte';
onMount(() => {
console.log('the component has mounted');
});
</script>
如果需要onMount
返回一個函數(shù)赴叹,則在卸載 component 時調(diào)用該函數(shù)鸿染。
<script>
import { onMount } from 'svelte';
onMount(() => {
const interval = setInterval(() => {
console.log('beep');
}, 1000);
return () => clearInterval(interval);
});
</script>
- beforeUpdate (更新前)
beforeUpdate
任何狀態(tài)更改后組件更新之前,回調(diào)函數(shù)會立即運行乞巧。第一次回調(diào)運行將在初始o(jì)nMount之前.
<script>
import { beforeUpdate } from 'svelte';
beforeUpdate(() => {
console.log('the component is about to update');
});
</script>
- afterUpdate (更新后)
afterUpdate
在組件更新后立即運行回調(diào)
<script>
import { afterUpdate } from 'svelte';
afterUpdate(() => {
console.log('the component just updated');
});
</script>
- onDestroy(銷毀后)
在組件卸載后運行回調(diào)涨椒。在onMount、beforeUpdate、afterUpdate和onDestroy中蚕冬,這是唯一一個在服務(wù)器端組件中運行的組件免猾。4
<script>
import { onDestroy } from 'svelte';
onDestroy(() => {
console.log('the component is being destroyed');
});
</script>
案例
老嚴(yán)逛著逛著 正好看到一個 官方示例的 to do list
本來還想著說帶大家做,那既然有現(xiàn)成的囤热,將就一下
沒有什么框架是寫一個 todolist 還學(xué)不會的 猎提,老嚴(yán)在代碼里面也加了一些代碼注釋
因為樣式代碼 太多,我們先上效果圖再看代碼
<script>
import { quintOut } from 'svelte/easing';
import { crossfade } from 'svelte/transition';
import { flip } from 'svelte/animate';
// 動畫
const [send, receive] = crossfade({
duration: d => Math.sqrt(d * 200),
fallback(node, params) {
const style = getComputedStyle(node);
const transform = style.transform === 'none' ? '' : style.transform;
return {
duration: 600,
easing: quintOut,
css: t => `
transform: ${transform} scale(${t});
opacity: ${t}
`
};
}
});
let uid = 1;
// 默認(rèn)數(shù)據(jù)
let todos = [
{ id: uid++, done: false, description: 'write some docs' },
{ id: uid++, done: false, description: 'start writing blog post' },
{ id: uid++, done: true, description: 'buy some milk' },
{ id: uid++, done: false, description: 'mow the lawn' },
{ id: uid++, done: false, description: 'feed the turtle' },
{ id: uid++, done: false, description: 'fix some bugs' },
];
// 新增待辦
function add(input) {
const todo = {
id: uid++,
done: false,
description: input.value
};
todos = [todo, ...todos];
input.value = '';
}
// 刪除方法
function remove(todo) {
todos = todos.filter(t => t !== todo);
}
// 選中方法
function mark(todo, done) {
todo.done = done;
remove(todo);
todos = todos.concat(todo);
}
</script>
<div class='board'>
<!-- 點擊回車執(zhí)行add -->
<input
placeholder="what needs to be done?"
on:keydown={e => e.which === 13 && add(e.target)}
>
<!-- 代辦 -->
<div class='left'>
<h2>todo</h2>
{#each todos.filter(t => !t.done) as todo (todo.id)}
<label
in:receive="{{key: todo.id}}"
out:send="{{key: todo.id}}"
animate:flip
>
<!-- 選中代表已做完 -->
<input type=checkbox on:change={() => mark(todo, true)}>
{todo.description}
<!-- 刪除 -->
<button on:click="{() => remove(todo)}">remove</button>
</label>
{/each}
</div>
<!-- 已完成 -->
<div class='right'>
<h2>done</h2>
{#each todos.filter(t => t.done) as todo (todo.id)}
<label
class="done"
in:receive="{{key: todo.id}}"
out:send="{{key: todo.id}}"
animate:flip
>
<!-- 修改狀態(tài)為代辦 -->
<input type=checkbox checked on:change={() => mark(todo, false)}>
{todo.description}
<!-- 刪除 -->
<button on:click="{() => remove(todo)}">remove</button>
</label>
{/each}
</div>
</div>
<style>
.board {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 1em;
max-width: 36em;
margin: 0 auto;
}
.board > input {
font-size: 1.4em;
grid-column: 1/3;
}
h2 {
font-size: 2em;
font-weight: 200;
user-select: none;
margin: 0 0 0.5em 0;
}
label {
position: relative;
line-height: 1.2;
padding: 0.5em 2.5em 0.5em 2em;
margin: 0 0 0.5em 0;
border-radius: 2px;
user-select: none;
border: 1px solid hsl(240, 8%, 70%);
background-color:hsl(240, 8%, 93%);
color: #333;
}
input[type="checkbox"] {
position: absolute;
left: 0.5em;
top: 0.6em;
margin: 0;
}
.done {
border: 1px solid hsl(240, 8%, 90%);
background-color:hsl(240, 8%, 98%);
}
button {
position: absolute;
top: 0;
right: 0.2em;
width: 2em;
height: 100%;
background: no-repeat 50% 50% url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23676778' d='M12,2C17.53,2 22,6.47 22,12C22,17.53 17.53,22 12,22C6.47,22 2,17.53 2,12C2,6.47 6.47,2 12,2M17,7H14.5L13.5,6H10.5L9.5,7H7V9H17V7M9,18H15A1,1 0 0,0 16,17V10H8V17A1,1 0 0,0 9,18Z'%3E%3C/path%3E%3C/svg%3E");
background-size: 1.4em 1.4em;
border: none;
opacity: 0;
transition: opacity 0.2s;
text-indent: -9999px;
cursor: pointer;
}
label:hover button {
opacity: 1;
}
</style>
當(dāng)你可以手動寫出這個todolist的時候,你就已經(jīng)出師了旁蔼,因為老嚴(yán)也就這點能耐 哈哈
最后代碼锨苏,我提交到了git 有需要的同學(xué)可以去下載噢
git clone git@github.com:CrazyMrYan/study-svelte.git
注解
[0] https://zhuanlan.zhihu.com/p/97825481
[2] https://svelte.dev/blog/virtual-dom-is-pure-overhead
[3] https://www.zhihu.com/question/53150351
[4] https://github.com/sveltejs/template
[5] https://ejs.bootcss.com/#docs