<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Svelte 原理淺析與評測

          共 4983字,需瀏覽 10分鐘

           ·

          2021-12-29 17:04

          術(shù)????

          簡介

          Svelte 是一個構(gòu)建 web 應(yīng)用程序的工具,與 React 和 Vue 等 JavaScript 框架類似,都懷揣著一顆讓構(gòu)建交互式用戶界面變得更容易的心。

          但是有一個關(guān)鍵的區(qū)別:Svelte 在 構(gòu)建/編譯階段 將你的應(yīng)用程序轉(zhuǎn)換為理想的 JavaScript 應(yīng)用,而不是在 運(yùn)行階段 解釋應(yīng)用程序的代碼。這意味著你不需要為框架所消耗的性能付出成本,并且在應(yīng)用程序首次加載時沒有額外損失。

          你可以使用 Svelte 構(gòu)建整個應(yīng)用程序,也可以逐步將其融合到現(xiàn)有的代碼中。你還可以將組件作為獨(dú)立的包(package)交付到任何地方,并且不會有傳統(tǒng)框架所帶來的額外開銷。

          特點

          代碼簡潔

          我們以一個簡單例子來說明,在輸入框中輸入內(nèi)容,然后在彈窗中顯示相關(guān)內(nèi)容。然后將svelte的代碼與react、vue作一下對比,可以很明顯的發(fā)現(xiàn),svelte要寫的代碼量遠(yuǎn)少于react和vue。

          • 使用svelte,代碼如下:




          type="text"?bind:value={animal}?/>

          彈出
          • react:
          import?React,?{?useState?}?from?'react';



          export?default?function?App()?{

          ??const?[animal,?setAnimal]?=?useState('dog');

          ??const?showModal?=?()?=>?{

          ????alert(`My?favorite?animal?is?${animal}`);

          ??};

          ??return?(

          ????<>

          ??????
          ????????type="text"

          ????????value={animal}

          ????????onChange={()?=>?{

          ??????????setAnimal(animal);

          ????????}}

          ??????/>

          ??????彈出

          ????

          ??);

          }
          • Vue





          無虛擬dom

          Svelte 能夠?qū)⒋a編譯成體積小、不依賴框架的普通js代碼,讓應(yīng)用程序無論是啟動還是運(yùn)行都很迅速。

          性能更好

          許多同學(xué)在學(xué)習(xí) react 或者 vue 時可能聽說過諸如“虛擬dom很快”之類的言論,所以看到這里就會疑惑,svelte 沒有虛擬dom,為什么反而更快呢?

          這其實是一個誤區(qū),react 和 vue 等框架實現(xiàn)虛擬 dom 的最主要的目的不是性能,而是為了掩蓋底層 dom 操作,讓用戶通過聲明式的、基于狀態(tài)驅(qū)動UI的方式去構(gòu)建我們的應(yīng)用程序,提高代碼的可維護(hù)性。

          另外 react 或者 vue 所說的虛擬 dom 的性能好,是指我們在沒有對頁面做特殊優(yōu)化的情況下,框架依然能夠提供不錯的性能保障。例如以下場景,我們每次從服務(wù)端接收數(shù)據(jù)后就重新渲染列表,如果我們通過普通dom操作不做特殊優(yōu)化,每次都重新渲染所有列表項,性能消耗比較高。而像react等框架會通過key對列表項做標(biāo)記,只對發(fā)生變化的列表項重新渲染,如此一來性能便提高了。

          思考上面這個場景,如果我們操作真實dom時也對列表項做標(biāo)記,只對發(fā)生變化的列表項重新渲染,省去了虛擬dom diff等環(huán)節(jié),那么性能是比虛擬dom還要高的。

          svelte便實現(xiàn)了這種優(yōu)化,通過將數(shù)據(jù)和真實dom的映射關(guān)系,在編譯的時候通過 ast 計算并保存起來,數(shù)據(jù)發(fā)生變動時直接更新dom,由于不依賴虛擬dom,初始化和更新時都都十分迅速。

          體積更小?

          我們都知道 react 和 vue 都是基于運(yùn)行時的框架,打包后除了用戶自己編寫的代碼之外,還有框架本身的 runtime。而 svelte 是通過靜態(tài)編譯減少框架運(yùn)行時的代碼量。

          https://www.npmtrends.com/react-vs-react-dom-vs-vue-vs-svelte[1]

          參照 npm trends,react、vue和svelte的 minzipped 體積分別為:42.2kb、22.9kb和1.6kb,足以看出 svelte 的短小精悍。

          但是上面這個單看框架的體積稍微有些片面,svelte 由于在編譯時將組件直接解釋為 js,所以相對來說組件編譯后的代碼量會比 vue 和 react 編譯后要大一些。假如有 n 個組件,svelte 每個組件編譯后個規(guī)模為 a,vue 或者 react 每個組件編譯后的規(guī)模為 b:

          • svelte:體積 = a * n
          • vue:體積 = S + b * n (S為框架運(yùn)行時的體積)

          在 a > b 的情況下,隨著 n 的數(shù)量的增多,svelte 項目在體積上并不會占據(jù)太大的優(yōu)勢。

          vue 對比

          Vue 方面,尤雨溪曾將 vue3 和 svelte 做了對比:https://github.com/yyx990803/vue-svelte-size-analysis[2]

          基于真實的 todomvc 場景構(gòu)建組件,編譯以后Svelte 的組件輸出大小是Vue的1.7倍,在 SSR 的情況下,這一比例會上升到2.1倍。在不開啟 SSR 的情況下,大概19個組件后就會抹平運(yùn)行時體積的大小差異,開啟 SSR 的情況下,大概 13 個組件后就會抹平差異。

          與 react 對比

          Jacek Schae 也曾將 svelte 和 react進(jìn)行對比,也是在組件數(shù)量達(dá)到一定的閾值之后, svelte 的體積優(yōu)勢就不再存在。


          可見,大型項目中使用 svelte 的體積問題還有待考究。

          真正的reactivity

          無需復(fù)雜的狀態(tài)管理庫,Svelte 為 JavaScript 自身添加反應(yīng)能力。后面的源碼解讀部分會講解 svelte 的響應(yīng)式實現(xiàn)。

          發(fā)展趨勢

          Svelte 是 Rich Harris[3] (rollup 作者),2016 年 svelte 開始開源, 2019 年開始引起較為廣泛的關(guān)注。

          Github 上 svelte[4] 現(xiàn)在是 49.9k star:

          Npm 上 svelte[5] 的周下載量大概在 15w 左右:


          雖然從 star 數(shù)和下載量來說離 react、vue 和 angular 還有較大差距,但是鑒于其出道比較晚也是可以理解。而且從框架的調(diào)研[6]來看,近兩年來其用戶滿意度和感興趣度都是高居第一,使用和知名度也是在急速上升的。


          總體來看,未來可期!

          源碼解讀

          svelte 的源碼由兩大部分組成,compiler 和 runtime。compiler 的作用是將 svelte 模版語法編譯為瀏覽器能夠識別的js SvelteComponent,而 runtime 則是在瀏覽器中幫助業(yè)務(wù)代碼運(yùn)作的運(yùn)行時函數(shù)。

          complier

          Svelte 如其介紹所說,在 complier 階段完成了大部分的工作,而 complier 又分為 parse 和 complie 兩部分:

          parse

          parse 會讀取 .svelte 文件的內(nèi)容進(jìn)行解析。

          • 對于 html 部分的內(nèi)容,會分為 tag、mustache、text 三種解析類型:

            • tag: tag 解析的內(nèi)容以 < 作為標(biāo)識,包括 HTMLElement、style標(biāo)簽、script標(biāo)簽以及用戶自定義的 svelte 組件以及 svelte 實現(xiàn)的一些特殊標(biāo)簽如 svelte:headsvelte:options 、svelte:window 以及svelte:body 等。
            • muscache:mustache 以 { 作為標(biāo)識,識別的內(nèi)容除了模板語法之外,還包括 svelte 的邏輯渲染(else……if、each)等語法、{``@html``}、{``@debug} 等。
            • text就是無語義的靜態(tài)文本

          最終parse會將.svelte 的內(nèi)容解析成含有 html 、css 、instance 、module 四部分的ast。

          Instance 是指 script 標(biāo)簽中響應(yīng)式的屬性和方法,module 是使用





          ??增加

          ??

          count?is:?{count}




          svelte編譯后的結(jié)果為:

          /*?App.svelte?generated?by?Svelte?v3.42.4?*/

          import?{

          ??SvelteComponent,

          ??append,

          ??detach,

          ??element,

          ??init,

          ??insert,

          ??listen,

          ??noop,

          ??safe_not_equal,

          ??set_data,

          ??space,

          ??text,

          }?from?'svelte/internal';



          function?create_fragment(ctx)?{

          ??let?div;

          ??let?button;

          ??let?t1;

          ??let?p;

          ??let?t2;

          ??let?t3;

          ??let?mounted;

          ??let?dispose;



          ??return?{

          ????c()?{

          ??????div?=?element('div');

          ??????button?=?element('button');

          ??????button.textContent?=?'增加';

          ??????t1?=?space();

          ??????p?=?element('p');

          ??????t2?=?text('count?is:?');

          ??????t3?=?text(/*count*/?ctx[0]);

          ????},

          ????m(target,?anchor)?{

          ??????insert(target,?div,?anchor);

          ??????append(div,?button);

          ??????append(div,?t1);

          ??????append(div,?p);

          ??????append(p,?t2);

          ??????append(p,?t3);



          ??????if?(!mounted)?{

          ????????dispose?=?listen(button,?'click',?/*addCount*/?ctx[1]);

          ????????mounted?=?true;

          ??????}

          ????},

          ????p(ctx,?[dirty])?{

          ??????if?(dirty?&?/*count*/?1)?set_data(t3,?/*count*/?ctx[0]);

          ????},

          ????i:?noop,

          ????o:?noop,

          ????d(detaching)?{

          ??????if?(detaching)?detach(div);

          ??????mounted?=?false;

          ??????dispose();

          ????},

          ??};

          }



          function?instance($$self,?$$props,?$$invalidate)?{

          ??let?count?=?0;



          ??const?addCount?=?()?=>?{

          ????$$invalidate(0,?(count?+=?1));

          ??};



          ??return?[count,?addCount];

          }



          class?App?extends?SvelteComponent?{

          ??constructor(options)?{

          ????super();

          ????init(this,?options,?instance,?create_fragment,?safe_not_equal,?{});

          ??}

          }



          export?default?App;

          我們看到編譯后的結(jié)果中,有一個 create_fragement 的方法和 instance 方法。

          另外還從 svelte/internal 引入了 append、detachelement 、insertlisten 等方法,從源碼[7]中可以知道都是一些很簡單的對原生 dom 操作的封裝。

          create_fragment

          create_fragment 是和每個組件生成 dom 相關(guān)的方法,里面定義了 c 、 m 、p 、i 、o 、d 等一系列內(nèi)置方法,從縮寫上不好理解,我們可以看下源碼[8]中其類型定義:

          export?interface?Fragment?{

          ??key:?string|null;

          ??first:?null;

          ??/*?create??*/?c:?()?=>?void;

          ??/*?claim???*/?l:?(nodes:?any)?=>?void;

          ??/*?hydrate?*/?h:?()?=>?void;

          ??/*?mount???*/?m:?(target:?HTMLElement,?anchor:?any)?=>?void;

          ??/*?update??*/?p:?(ctx:?any,?dirty:?any)?=>?void;

          ??/*?measure?*/?r:?()?=>?void;

          ??/*?fix?????*/?f:?()?=>?void;

          ??/*?animate?*/?a:?()?=>?void;

          ??/*?intro???*/?i:?(local:?any)?=>?void;

          ??/*?outro???*/?o:?(local:?any)?=>?void;

          ??/*?destroy?*/?d:?(detaching:?0|1)?=>?void;

          }
          • c(create):create 函數(shù)里會對一系列的 dom 節(jié)點進(jìn)行創(chuàng)建,并將它們保存在 create_fragment 的閉包中。
          • m(mount):mount 函數(shù)中會對根據(jù) dom 節(jié)點的層級關(guān)系,構(gòu)建 dom 樹,并將 dom 樹插入到頁面 target 節(jié)點下。同時還會將 dom 上相關(guān)的事件監(jiān)聽進(jìn)行綁定,然后將組件的 mounted 狀態(tài)置為 true。
          • p(update):組件的狀態(tài)發(fā)生改變時會觸發(fā) update 函數(shù),對 dom 中相應(yīng)的數(shù)據(jù)重新進(jìn)行更新渲染。
          • d(distroy):將 dom 掛載從頁面中移除,將組件的 mounted 狀態(tài)置為 false,同時移除一系列的事件監(jiān)聽。

          instance

          instance 方法中返回了包含組件實例中屬性和方法的數(shù)組,將相應(yīng)的數(shù)據(jù)綁定在組件實例的 $$.ctx 上,并且根據(jù)用戶定義的觸發(fā)屬性修改的方法去調(diào)用一個 $$invalidate方法,我們來看下$$invalidate這個方法干了什么:

          • $$.instance:
          instance(component,?options.props?||?{},?(i,?ret,?...rest)?=>?{

          ??const?value?=?rest.length???rest[0]?:?ret;

          ??if?($$.ctx?&&?not_equal($$.ctx[i],?($$.ctx[i]?=?value)))?{

          ????if?(!$$.skip_bound?&&?$$.bound[i])?$.bound[i](value?"i]?"i])?$.bound[i")?$.bound[i");

          ????if?(ready)?make_dirty(component,?i);

          ??}

          ??return?ret;

          });

          $$invalidate接收2個或更多參數(shù)。第一個參數(shù)是 i 是 屬性在 $$.ctx,第二個參數(shù) ret 是定義的屬性改變的邏輯函數(shù)。然后判斷屬性重新賦值后與之前的值是否相等,若不相等,則會調(diào)用 make_dirty 更新相關(guān)的 ui。

          臟檢測

          • make_dirty:
          function?make_dirty(component,?i)?{

          ??if?(component.$$.dirty[0]?===?-1)?{

          ????dirty_components.push(component);

          ????schedule_update();

          ????component.$$.dirty.fill(0);

          ??}

          ??component.$$.dirty[(i?/?31)?|?0]?|=?(1?<
          }

          每個組件的 $$ 屬性上有一個 dirty 數(shù)組,用于標(biāo)記 instance 中需要更新的屬性下標(biāo),當(dāng) dirty 第一項為 -1 時,表示這個組件當(dāng)前是干凈的,將其 push 到 dirty_components 中,然后執(zhí)行 schedule_update 方法。

          • schedule_update:

          schedule_update 中會異步去執(zhí)行 flush 函數(shù):

          export?function?schedule_update()?{

          ??if?(!update_scheduled)?{

          ????update_scheduled?=?true;

          ????resolved_promise.then(flush);

          ??}

          }
          • flush:

          flush 中對剛剛的 dirty_components 進(jìn)行遍歷,執(zhí)行 update 函數(shù).

          for?(let?i?=?0;?i?
          ??const?component?=?dirty_components[i];

          ??set_current_component(component);

          ??update(component.$$);

          }
          • Update

          update函數(shù)會調(diào)用組件 update 生命周期鉤子函數(shù),將 dirty 數(shù)組重新置為 -1,然后調(diào)用 fragment 的 p(update) 去更新ui。

          function?update($$)?{

          ??if?($$.fragment?!==?null)?{

          ????$$.update();

          ????run_all($$.before_update);

          ????const?dirty?=?$$.dirty;

          ????$$.dirty?=?[-1];

          ????$$.fragment?&&?$$.fragment.p($$.ctx,?dirty);



          ????$$.after_update.forEach(add_render_callback);

          ??}

          }
          • dirty 標(biāo)記

          回到上面的 make_dirty 方法,svelte 是通過如下操作對屬性進(jìn)行臟標(biāo)記的:

          component.$$.dirty[(i?/?31)?|?0]?|=?(1?<

          了解了位運(yùn)算,那我們看上面臟標(biāo)記的代碼,(i / 31) | 0 對每個 instance 返回的數(shù)組下標(biāo)除以 31 后和 0 做或運(yùn)算,即除 31 向下取整,(1 << (i % 31)) i 對 31 取余之后向左進(jìn)行移位操作。通過上述的兩步操作,可以了解到 dirty 數(shù)組存儲了一系列的 32 位整數(shù),通過這一操作,提高了內(nèi)存利用率,每個數(shù)組項可以存儲31個屬性是否需要更新。

          例如如下32位的整數(shù)43,對應(yīng)的32位二進(jìn)制為:

          Dirty = [43]

          43 -> 0000 0000 0000 0000 0000 0000 0010 1011

          二進(jìn)制中為1的位代表需要更新的 instance 中數(shù)組第幾項,即第1、2、4、6項屬性需要更新。

          整體流程

          周邊生態(tài)

          狀態(tài)管理

          Svelte 框架中自己實現(xiàn)了 store[9],無需安裝單獨(dú)的狀態(tài)管理庫。

          路由

          Svelte 官方目前沒有自己的路由,社區(qū)實現(xiàn)的路由庫:

          • svelte-routing[10],1.4k star,npm 周下載量 3.5k,和 react-router-dom 的使用方式很像
          • svelte-spa-router[11],886 star,npm 周下載量 3.5k,更類似于 vue-router 的使用方式

          SSR

          • sveltekit[12]

          目前官方主推的 ssr 框架,具備以下的特點:

          • 服務(wù)端渲染(SSR)
          • 路由
          • typescript支持
          • less, scss支持
          • serverless
          • vite打包
          • Sapper[13]

          sapper開發(fā)比較早,也是官方的 ssr 框架,但是 Rich Harris 在2020年10月的svelte峰會上表示:sapper永遠(yuǎn)不會發(fā)布1.0版本。也就是說 sapper 不會發(fā)布穩(wěn)定版甚至被放棄,而 svelte kit 則是它的繼任者。

          跨平臺

          Svelte 偏向于性能,目前在跨平臺方面還沒有進(jìn)行探究。

          • native

          svelte-native[14] (社區(qū)庫)

          • 小程序

          暫不支持

          • 桌面應(yīng)用

          可以 electron[15] 結(jié)合開發(fā)桌面應(yīng)用

          組件庫

          Svelte 現(xiàn)在組件庫數(shù)量尚可,但是都不夠完備,如 table 等復(fù)雜組件都沒有實現(xiàn)

          • svelte-material-ui[16] 1.8k star,扁平化設(shè)計,偏向 google 的 UI 設(shè)計系統(tǒng)。
          • carbon-components-svelte[17] 1k star,偏商務(wù),非主流,偏向 IBM 的開源設(shè)計系統(tǒng)。
          • smelte[18] 1k star,material 的另一種方案

          測試工具

          缺少官方的測試工具,社區(qū)單元測試庫:

          svelte-testing-library[19]

          總結(jié)來說,svelte 的周邊生態(tài)目前還不夠完備,但由于起步較晚可以理解。

          VSCode插件

          • Svelte for VS Code[20] 識別 svelte 語法,對 svelte 進(jìn)行語法高亮
          • Svelte 3 Snippets[21] 在 vscode 中通過簡寫快速生成 svelte 相關(guān)語法
          • Svelte Intellisense[22] svelte 組件 command + click 快速跳轉(zhuǎn)及 svelte 引入省略 .svelte 文件后綴

          Typescript

          支持

          CSS 預(yù)處理器

          支持 less、 scss 及 postcss

          使用 svelte 構(gòu)建 web component

          我們平臺組最近正在進(jìn)行 web component 組件庫開發(fā)的選型調(diào)研,svelte 也作為備選的框架之一。傳統(tǒng)的框架如 vue、react 如果想要開發(fā)web component,需要每個組件都打包一份體積龐大的運(yùn)行時,而 svelte 的運(yùn)行時會根據(jù)你的功能按需引入,所以十分適合 web component 的開發(fā)場景。

          配置

          下面是通過 svelte 開發(fā)一個簡單的 web component 的實例:

          1. 通過官方提供的腳手架創(chuàng)建一個組件
          npx?degit?sveltejs/component-template?custom-test-button
          1. 修改相關(guān)的文件配置:

          修改 package.json 包名稱

          {

          ??"name":?"CustomTestButton",

          ??"svelte":?"src/index.js",

          ??"module":?"dist/index.mjs",

          ??"main":?"dist/index.js",

          ??"scripts":?{

          ????"build":?"rollup?-c",

          ????"prepublishOnly":?"npm?run?build"

          ??},

          ??"devDependencies":?{

          ????"@rollup/plugin-node-resolve":?"^9.0.0",

          ????"rollup":?"^2.0.0",

          ????"rollup-plugin-svelte":?"^6.0.0",

          ????"svelte":?"^3.0.0"

          ??},

          ??"keywords":?[

          ????"svelte"

          ??],

          ??"files":?[

          ????"src",

          ????"dist"

          ??]

          }

          修改 rollup.config.js 文件的內(nèi)容:

          import?svelte?from?'rollup-plugin-svelte';

          import?resolve?from?'@rollup/plugin-node-resolve';

          import?pkg?from?'./package.json';



          const?name?=?pkg.name

          ??.replace(/^(@\S+/)?(svelte-)?(\S+)/,?'$3')

          ??.replace(/^\w/,?(m)?=>?m.toUpperCase())

          ??.replace(/-\w/g,?(m)?=>?m[1].toUpperCase());



          export?default?{

          ??input:?'src/index.js',

          ??output:?[

          ????{?file:?pkg.module,?format:?'es'?},

          ????{?file:?pkg.main,?format:?'umd',?name?},

          ??],

          ??plugins:?[svelte({?customElement:?true?}),?resolve()],

          };
          1. 增加組件內(nèi)容

          如下定義了一個組件內(nèi)容

          "custom-test-button"?/>







          ${type}`}>{value}




          1. 在項目目錄執(zhí)行 npm run build 將組件打包,假設(shè)打包后的文件為 index.js

          使用

          • Html 引入


          "en">

          ??

          ????"UTF-8"?/>

          ????"X-UA-Compatible"?content="IE=edge"?/>

          ????"viewport"?content="width=device-width,?initial-scale=1.0"?/>

          ????svelte?web?component

          ??

          ??

          ??????

          ????"測試按鈕"?type="danger">

          ??


          • vue 引入

          在 vue 的 html 中引入 index.js



          ????

          ????"app">


          ????

          ????

          ??

          然后在 vue 組件中使用


          • React 中引入

          同 vue,就不做過多介紹了

          總結(jié)

          整體來說,svelte 繼前端三大框架之后推陳出新,以一種新的思路實現(xiàn)了響應(yīng)式,由于起步時間不算很長,目前來說其生態(tài)還不夠完備, 在大型項目中的應(yīng)用目前也還有待考究,但是在一些簡單頁面如活動頁、靜態(tài)頁等場景感覺目前還是十分適合的,個人對其未來發(fā)展表示看好。

          由于其簡潔的語法以及與 vue 語法相似的特點,上手成本十分小,大家感興趣可以稍花一點點時間了解一下,豐富自己的武器庫。

          參考資料

          [1]

          https://www.npmtrends.com/react-vs-react-dom-vs-vue-vs-svelte: https://www.npmtrends.com/react-vs-react-dom-vs-vue-vs-svelte

          [2]

          https://github.com/yyx990803/vue-svelte-size-analysis: https://github.com/yyx990803/vue-svelte-size-analysis

          [3]

          Rich Harris: https://github.com/Rich-Harris

          [4]

          svelte: https://github.com/sveltejs/svelte

          [5]

          svelte: https://www.npmjs.com/package/svelte

          [6]

          調(diào)研: https://2020.stateofjs.com/en-US/technologies/front-end-frameworks/

          [7]

          源碼: https://github.com/sveltejs/svelte/blob/master/src/runtime/internal/dom.ts

          [8]

          源碼: https://github.com/sveltejs/svelte/blob/master/src/runtime/internal/Component.ts

          [9]

          store: https://github.com/sveltejs/svelte/blob/master/src/runtime/store/index.ts

          [10]

          svelte-routing: https://github.com/EmilTholin/svelte-routing

          [11]

          svelte-spa-router: https://github.com/italypaleale/svelte-spa-router

          [12]

          sveltekit: https://github.com/sveltejs/kit

          [13]

          Sapper: https://github.com/sveltejs/sapper

          [14]

          svelte-native: https://github.com/halfnelson/svelte-native

          [15]

          electron: https://github.com/electron/electron

          [16]

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

          [17]

          carbon-components-svelte: https://github.com/carbon-design-system/carbon-components-svelte

          [18]

          smelte: https://smeltejs.com/

          [19]

          svelte-testing-library: https://github.com/testing-library/svelte-testing-library

          [20]

          Svelte for VS Code: https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode

          [21]

          Svelte 3 Snippets: https://marketplace.visualstudio.com/items?itemName=fivethree.vscode-svelte-snippets

          [22]

          Svelte Intellisense: https://marketplace.visualstudio.com/items?itemName=ardenivanov.svelte-intellisense


          ???

          便內(nèi),^_^

          ?、、?~。

          關(guān)?ELab團(tuán)?~

          節(jié)跳,,負(fù)責(zé)節(jié)產(chǎn)發(fā)。

          產(chǎn)質(zhì)、發(fā)、創(chuàng)沿術(shù)業(yè),業(yè)獻(xiàn)經(jīng)。監(jiān)、、術(shù)、Serverless、、產(chǎn)設(shè)內(nèi)

          節(jié)/內(nèi):?CBP6BY9

          :?https://job.toutiao.com/s/8RWRqsn



          瀏覽 56
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  91精品国自产欧美在线观看 | 中文字幕亚洲日韩 | 一本久久精品一区二区 | 亲子乱伦一区二区 | 狠狠躁日日躁夜夜躁A片无码 |