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

目錄結構

  • 說在前面
  • 關于虛擬DOM
  • 業(yè)內大牛如何看待 svelte
  • 關于UI
  • svelte 與其他框架
    • 開始打包
    • 對比大小
    • 性能考核
    • 對比性能
  • 使用svelte 模板
  • 下載依賴
  • 啟動項目
  • 打開頁面
  • 安裝ui
  • 簡單了解語法
    • 綁定數據
    • 條件渲染
    • 循環(huán)渲染
    • 事件綁定
    • 組件
      • 傳值
    • 動畫
    • 生命周期
  • 案例
  • 注解
  • 參考文獻

說在前面

svelte 不知道大家有沒有了解過,最近一次偶然刷文章,看到一篇《都快2020年,你還沒聽說過SvelteJS?》 [0] 的文章,看了svelte與其他框架的對比

svelte 中文 doc [1]

對比各框架開發(fā)的項目的尺寸

[圖片上傳失敗...(image-7a421c-1607179090667)]

對比各項目的 Lighthouse 性能評分

[圖片上傳失敗...(image-b5004c-1607179090667)]

說實話屬實有點驚訝到我。 ** 真的假的?

于是我抱著試試就試試的心態(tài)來做個評測人

關于虛擬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是一種編譯器,它在構建時就知道應用程序中的情況如何變化,而不必等著在運行時進行工作。

業(yè)內大牛如何看待 svelte

vue 的作者尤雨溪,在知乎回答了個 《如何看待 svelte 這個前端框架?》 [3] 問題

image

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

關于 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 與其他框架

老嚴閑來的時候找到了 一個叫做 Realworld 存儲庫中有24種conduit實現As ,也就是用來對比性能的以及大小的;

今天一起來對比一下 vue/react/svelte 這三個框架的

  • 項目打包后壓縮包大小
  • 項目網頁性能分析對比

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

感興趣的同學也可以看看其他框架 https://github.com/gothinkster/realworld

image

開始打包

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

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

對比大小

排名如下

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

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

性能考核

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

全局安裝 lighthouse

npm install -g lighthouse

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

lighthouse https://realworld.svelte.dev/

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

vue

image

react

image

svelte

image

對比性能

性能得分排名如下:

  1. svelte — 83
  2. react — 52
  3. vue — 32

svelte 也是不負眾望 穩(wěn)居第一

使用 svelte 模板

看到上面,天天被逼著做性能優(yōu)化的同學,激動起來了 ~ 那我們一起來簡單學習一下這個性能強悍的 svelte 吧

svelte 模板 [4] ,我們直接使用一個模板來開工

git clone https://github.com/sveltejs/template
&
cd template-master

下載依賴

yarn install 
or 
npm install

下載完成之后,我們看看目錄。老嚴的評價就是 簡潔

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

簡單了解語法

在學習之前我覺得有必要簡單了解一下其語法

綁定數據

在 vue 中我們的變量需要寫在 data 中 ,而 svelte 語法更加貼合原生

<!-- vue -->
data() {
    return {
        name: 'hhh',
    };
}

<div>{{name}}</div>

svelte 綁定數據 ,svelte 動態(tài)綁定需要加上 {}

<script>
    import { Button } from "sveltestrap";
    // 定義數據
    let name = "hhh";
    let src = 'http://crazy-x-lovemysoul-x-vip.img.abc188.com/images/logo.png';
</script>
<!-- 綁定數據 -->
<!-- 如果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: '拜登呼吁必須停止把對手當敵人' },
        { id: 2, title: '江蘇響水致78死爆炸案一審宣判' },
        { id: 3, title: '嫦娥五號將擇機實施月面軟著陸' }
    ];
</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 中方法直接寫函數定義函數即可使用

<script>
    import { Button } from "sveltestrap";
    // 定義數據
    let name = "hhh",title = '標題';
    // 定義方法
    const handleClick = () => {
        name = "嚴老濕"
        title = "老嚴帶你入坑 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 下面老嚴依次給大家伙列出來

  • onMount (掛載后)

onMount函數作為將component掛載到DOM后立即執(zhí)行的回調。它必須在component初始化期間被調用(但不必位于component內部;可以從外部模塊調用它)。

<script>
    import { onMount } from 'svelte';

    onMount(() => {
        console.log('the component has mounted');
    });
</script>

如果需要onMount返回一個函數,則在卸載 component 時調用該函數。

<script>
    import { onMount } from 'svelte';

    onMount(() => {
        const interval = setInterval(() => {
            console.log('beep');
        }, 1000);

        return () => clearInterval(interval);
    });
</script>
  • beforeUpdate (更新前)

beforeUpdate任何狀態(tài)更改后組件更新之前,回調函數會立即運行。第一次回調運行將在初始onMount之前.

<script>
    import { beforeUpdate } from 'svelte';

    beforeUpdate(() => {
        console.log('the component is about to update');
    });
</script>
  • afterUpdate (更新后)

afterUpdate在組件更新后立即運行回調

<script>
    import { afterUpdate } from 'svelte';

    afterUpdate(() => {
        console.log('the component just updated');
    });
</script>
  • onDestroy(銷毀后)

在組件卸載后運行回調。在onMount、beforeUpdate、afterUpdate和onDestroy中,這是唯一一個在服務器端組件中運行的組件。4

<script>
    import { onDestroy } from 'svelte';

    onDestroy(() => {
        console.log('the component is being destroyed');
    });
</script>

案例

老嚴逛著逛著 正好看到一個 官方示例的 to do list 本來還想著說帶大家做,那既然有現成的,將就一下

沒有什么框架是寫一個 todolist 還學不會的 ,老嚴在代碼里面也加了一些代碼注釋

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

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;
    // 默認數據
    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>

當你可以手動寫出這個todolist的時候,你就已經出師了,因為老嚴也就這點能耐 哈哈

最后代碼,我提交到了git 有需要的同學可以去下載噢

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

參考文獻

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容