Svelte入門——Web Components實現跨框架元件複用

葡萄城技術團隊發表於2021-11-24

Svelte 是構建 Web 應用程式的一種新方法,推出後一直不溫不火,沒有繼Angular、React和VUE成為第四大框架,但也沒有失去熱度,無人問津。造成這種情況很重要的一個原因是,Svelte 的核心思想在於【通過靜態編譯減少框架執行時的程式碼量】,它可以像React和VUE一樣開發,但卻沒有虛擬DOM。,使它可以Svelte可以將程式碼編譯為體積小、不依賴於框架的JS程式碼。

看起來滿滿優點,但因為過於靈活,導致大家無法寫出高度一致的業務程式碼,以上優點並沒有在實際的大專案中得到很好的體現。

Svelte 這款框架並不完美,卻又沒有在殘酷的市場競爭中死掉,是因為它擁有一本特殊祕籍,一些使它成為其他框架無法替代的一員的功能。。

而對於 Svelte 來說,這本祕籍的名字就叫做——Web Components。

在多團隊協同完成的大專案中,各個團隊可能使用不同的框架版本,甚至不同的框架,這讓不同專案之間的元件複用變得困難。"write one,run anywhere"就是一句空話。這種情況下Svelte就變成了溝通跨越框架鴻溝的橋樑,使用Svelte開發的無框架依賴的Web Components,可以在各個框架間複用。同時,Svelte的開發方式也不像寫pure js那樣繁瑣。

下面以SpreadJS整合為例,介紹如何用Svelte開發一款spread-sheets Web Component供其他頁面複用。

  1. 建立Svelte template工程。
    svelte 官方提供了template 工程,只要clone或者下載專案即可。

https://github.com/sveltejs/component-template

npx degit sveltejs/component-template my-new-component
cd my-new-component
npm install # or yarn
  1. 修改 rollup.config.js,新增 customElement: true 配置,輸出為web component元件。

新增後的rollup.config.js如下。

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. 更新 src/Component.svelte,建立spread-sheets元件。
<svelte:options tag="spread-sheets" />
<script>
    import { createEventDispatcher, onMount } from 'svelte';
    // Event handling
    const dispatch = createEventDispatcher();
    export let value ="";
    $: valueChanged(value);
    function valueChanged(newValue) {
      console.log("value changed", newValue);
      if(spread){
        let sheet = spread.getActiveSheet();
        sheet.setValue(0, 0, value);
      }
    }

    let spreadHost;
    let spread;
    function dispatchEvent(name, e) {
      // dispatch(name, e);
      const event = new CustomEvent(name, {
        detail: e,
        bubbles: true,
        cancelable: true,
        composed: true, // this line allows the event to leave the Shadow DOM
      });
      // console.log(event)
      spreadHost.dispatchEvent(event);
    }
      onMount(() => {
          spread = new GC.Spread.Sheets.Workbook(spreadHost);
          let sheet = spread.getActiveSheet();
          sheet.setValue(0, 0, value);
          spread.bind(GC.Spread.Sheets.Events.ValueChanged, function(s, e){
            e.evnetName = "ValueChanged";
            dispatchEvent("changed", e);
          });
          spread.bind(GC.Spread.Sheets.Events.RangeChanged, function(s, e){
            e.evnetName = "RangeChanged";
            dispatchEvent("changed", e);
          });
      });



  </script>
  <style>
      
  </style>
  <div bind:this={ spreadHost} style="width: 100%; height:100%"></div>

這樣我們的自定義元件就建立好了,只需要呼叫npm run build,就能編譯出spread-sheets 元件了。

  1. 在頁面引用元件。
    建立index.html頁面,並引用編譯好的js檔案。同時引入spreadjs相關資源。
    直接使用spread-sheets標籤新增SpreadJS。
<!doctype html>
<html lang="en">
  <head>
    <meta name="spreadjs culture" content="zh-cn" />
    <meta charset="utf-8" />
    <title>My Counter</title>
    <base href="/">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <link rel="stylesheet" type="text/css" href="https://demo.grapecity.com.cn/spreadjs/SpreadJSTutorial/zh/purejs/node_modules/@grapecity/spread-sheets/styles/gc.spread.sheets.excel2013white.css">
  </head>
  <body>
    <!-- <spread-sheets-designer></spread-sheets-designer> -->
    <button onclick="getJSON()">GetJSON</button>
    <spread-sheets value="123" style="display:block; width: 80%; height: 400px;"></spread-sheets>


    <script src="https://demo.grapecity.com.cn/SpreadJS/WebDesigner/lib/spreadjs/scripts/gc.spread.sheets.all.14.1.3.min.js" type="text/javascript"></script>

    <script type="text/javascript" src="/dist/index.js"></script>
    <script type="text/javascript">

      document.querySelector("spread-sheets").addEventListener("changed", function(){
        console.log(arguments)
      })

      window.onload = function(){
        document.querySelector("spread-sheets").setAttribute("value", "234");
      }

    </script>
  </body>
</html>

新增後效果如下圖。

總結

雖然看起來Web Component完美解決了元件之間的複用問題,但是用Svelte 開發的Web Component也存在一些限制:比如,只能傳遞string 屬性;繫結的attribute是單向繫結,想要獲取元件內部更新值,需要繫結event獲取。

如果大家對Svelte 有更多興趣,歡迎留言交流~

相關文章