都快1202 年了,你還不知道 svelte?

目錄結(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的三個步驟

  1. 兩個快照都包含一個元素。在兩種情況下都是<div>介劫,這意味著我們可以保留相同的DOM節(jié)點
  2. 我們列舉了新舊屬性上的所有屬性徽惋,<div>以查看是否需要更改,添加或刪除任何屬性座韵。在這兩種情況下险绘,我們都有一個屬性-aclassName的值為"greeting"
  3. 下降到元素中,我們看到文本已更改,因此我們需要更新真實的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] 問題

image

“svelte 的核心思想在于 通過靜態(tài)編譯來減少框架運行時的代碼

關(guān)于 UI

我覺著這兩個ui還是比較不錯的

Material UI

https://sveltematerialui.com/

https://github.com/hperrin/svelte-material-ui

SVELTESTRAP

https://sveltestrap.js.org/

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

image

開始打包

這三個項目,打包順序基本上在同一時間執(zhí)行 npm run build厦取,svelte 直接在我我眨眼的一瞬間打完包 潮太,啪 很快啊 (有點夸張~幾秒鐘)

靜靜的等待vue和react同時打包完之后,我開始進(jìn)行統(tǒng)一壓縮靜態(tài)文件虾攻,壓縮格式為zip

對比大小

排名如下

  1. svelte — 89.9 KB
  2. vue — 483 KB
  3. react — 490 KB

果然差距還是確實如傳說一樣恐怖

性能考核

嚴(yán)老濕本次使用 Chrome 的 Lighthouse (谷歌網(wǎng)頁性能分析工具) 來對比性能評分

全局安裝 lighthouse

npm install -g lighthouse

執(zhí)行 (直接使用的對應(yīng)的線上地址)

lighthouse https://realworld.svelte.dev/

這是我在下面所測試的截屏

vue

image

react

image

svelte

image

對比性能

性能得分排名如下:

  1. svelte — 83
  2. react — 52
  3. 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)的評價就是 簡潔

image

啟動項目

yarn dev
or
npm run dev

啟動完成之后

  Your application is ready~! ?

  - Local:      http://localhost:5000
  - Network:    Add `--host` to expose

打開頁面

地址欄輸入 http://localhost:5000

image-20201130113222571

我們可以看到這樣的一個頁面 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>
image

簡單了解語法

在學(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>
image

條件渲染

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}
image

循環(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>
image

咋感覺有點像 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>
image

組件

組件是框架必不可少的一個功能

來看看 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>
image-20201205140934477

那么組件有了,我們來看看組件傳值吧顶别!

傳值

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>
image

剛剛我們是簡單的單一傳值

接下來我們傳一個對象試試

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>
image

那這多費勁吶谷徙?還需要一個個接收。有一話叫存在即合理

動畫

在官方 api 中提到 svelte 提供了一些動畫效果出來給大家使用

image

我們直接使用官方示例 淡入淡出動畫

<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}
image

生命周期

在 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)在代碼里面也加了一些代碼注釋

因為樣式代碼 太多,我們先上效果圖再看代碼

image
<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

[1] https://www.firecat.run/

[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

參考文獻(xiàn)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市棺聊,隨后出現(xiàn)的幾起案子伞租,更是在濱河造成了極大的恐慌,老刑警劉巖限佩,帶你破解...
    沈念sama閱讀 212,029評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件葵诈,死亡現(xiàn)場離奇詭異,居然都是意外死亡祟同,警方通過查閱死者的電腦和手機(jī)作喘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來晕城,“玉大人泞坦,你說我怎么就攤上這事」愠剑” “怎么了暇矫?”我有些...
    開封第一講書人閱讀 157,570評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長择吊。 經(jīng)常有香客問我李根,道長,這世上最難降的妖魔是什么几睛? 我笑而不...
    開封第一講書人閱讀 56,535評論 1 284
  • 正文 為了忘掉前任房轿,我火速辦了婚禮,結(jié)果婚禮上所森,老公的妹妹穿的比我還像新娘囱持。我一直安慰自己,他們只是感情好焕济,可當(dāng)我...
    茶點故事閱讀 65,650評論 6 386
  • 文/花漫 我一把揭開白布纷妆。 她就那樣靜靜地躺著,像睡著了一般晴弃。 火紅的嫁衣襯著肌膚如雪掩幢。 梳的紋絲不亂的頭發(fā)上逊拍,一...
    開封第一講書人閱讀 49,850評論 1 290
  • 那天,我揣著相機(jī)與錄音际邻,去河邊找鬼芯丧。 笑死,一個胖子當(dāng)著我的面吹牛世曾,可吹牛的內(nèi)容都是我干的缨恒。 我是一名探鬼主播,決...
    沈念sama閱讀 39,006評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼轮听,長吁一口氣:“原來是場噩夢啊……” “哼骗露!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蕊程,我...
    開封第一講書人閱讀 37,747評論 0 268
  • 序言:老撾萬榮一對情侶失蹤椒袍,失蹤者是張志新(化名)和其女友劉穎驼唱,沒想到半個月后藻茂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,207評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡玫恳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,536評論 2 327
  • 正文 我和宋清朗相戀三年辨赐,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片京办。...
    茶點故事閱讀 38,683評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡掀序,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出惭婿,到底是詐尸還是另有隱情不恭,我是刑警寧澤,帶...
    沈念sama閱讀 34,342評論 4 330
  • 正文 年R本政府宣布财饥,位于F島的核電站换吧,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏钥星。R本人自食惡果不足惜沾瓦,卻給世界環(huán)境...
    茶點故事閱讀 39,964評論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望谦炒。 院中可真熱鬧贯莺,春花似錦、人聲如沸宁改。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽还蹲。三九已至爹耗,卻和暖如春豁鲤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鲸沮。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評論 1 266
  • 我被黑心中介騙來泰國打工琳骡, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人讼溺。 一個月前我還...
    沈念sama閱讀 46,401評論 2 360
  • 正文 我出身青樓楣号,卻偏偏與公主長得像,于是被迫代替她去往敵國和親怒坯。 傳聞我的和親對象是個殘疾皇子炫狱,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,566評論 2 349

推薦閱讀更多精彩內(nèi)容