使用Svelte開發Chrome Extension

語言: CN / TW / HK

一、背景

起因 最近Chrome瀏覽器升級到96大版本後,二維碼入口從位址列移動至二級選單。這對H5前端開發來說不太友好,每次需要頁面二維碼時都需要多點兩下(* ̄︿ ̄)。

因此萌生了開發一個二維碼Chrome Extension的想法(@ ̄ー ̄@)。

經過多方技術選型(React、原生、Vue、Svelte等),最終選擇Svelte,原因是

  • 語法簡單,心智負擔小
  • 執行時程式碼少,打包體積小
  • 響應式

d=====( ̄▽ ̄*),接下來就開始Svelte × Chrome Extension之旅。

二、建立&開發

2.1 專案建立

2.1.1 專案初始化

使用Svelte Kit新建專案npm`` init svelte@next qrcode-extension,目錄結構如下:

  • src:原始檔目錄

    • lib:元件庫等
    • routes:約定式路由檔案
    • app.html:入口模板檔案
  • static:靜態檔案目錄
  • svelte.config.js:svelte配置

初始化專案之後可以直接npm`` run dev啟動。

2.1.2 支援外掛開發

  1. manifest檔案

Extensions are built on web technologies such as HTML, JavaScript, and CSS.

—— Chrome開發文件

  1. Chrome外掛本質上是以manifest.json為入口規定的一系列前端資源集合,基於Chrome瀏覽器提供的API,實現各種功能。

因此在專案的靜態資原始檔目錄中新增manifest.json檔案:

{ "name": "QrCode", "description": "A simple qrcode extension powered by Svelte", "version": "1.0", "manifest_version": 3, "permissions": ["tabs", "downloads"], "action": { "default_popup": "index.html" }, "icons": { "16": "/images/qrcode-16.png", "32": "/images/qrcode-32.png", "48": "/images/qrcode-48.png", "128": "/images/qrcode-128.png" } }

幾個比較重要的欄位:

MV3檔案格式參考

  • manifest_version:manifest版本,之前為Manifest V2(MV2),Chrome推薦使用Manifest V3(MV3)
  • permissions:擴充套件要使用的瀏覽器許可權,大部分Chrome擴充套件API均有許可權依賴
  • action:定義外掛操作行為對應的頁面

    • default_popup:點選外掛圖示時的頁面
  • icons:外掛圖示

  • 新增chrome型別定義

  1. 安裝@types/chrome到devDependencies,並在tsconfig.json#compilerOptions#types中新增chrome型別。

2.2 功能開發

2.2.1 需求拆分

  1. 參考Chrome瀏覽器二維碼功能:

2.2.2 連結展示

  1. 需要獲取Chrome瀏覽器當前開啟的tab,查閱開發文件可知對應API為chrome.tabs,並在manifest.json#permissions新增tabs許可權宣告。

在首頁載入時,獲取當前tab的url,url展示到輸入框,並作為二維碼元件的輸入屬性。

``` async function getCurrentTab() { if (typeof chrome === 'undefined' || typeof chrome.tabs === 'undefined') { return { url: '' }; } let queryOptions = { active: true, currentWindow: true }; let [tab] = await chrome.tabs.query(queryOptions); return tab; }

import { onMount } from 'svelte';

let url = ''; // get current tab's url onMount(() => { (async () => { const tab = await getCurrentTab(); url = tab.url || ''; })(); }); ```

2.2.3 Svelte元件

二維碼元件程式碼定義在libs/QrCode.svelte中。

  1. 元件程式碼

Svelte與vue類似,提供單檔案元件。包括三部分:

  • <script></script>:js/ts業務邏輯
  • html:元件html模板
  • <style></style>:css樣式,編譯時自動做樣式隔離

元件詳細程式碼如下所示,使用qrcode庫生成二維碼。

```

{#if $dataUrl} qrcode {/if}

```

  1. 生命週期

  2. onMount:元件已掛載到DOM上(SSR時不執行)

  • beforeUpdate:元件狀態變更時立即執行,第一次會在onMount之前執行
  • afterUpdate:元件更新後
  • onDestroy:元件解除安裝(如onMount返回函式,則會執行)

  1. 輸入/輸出
  1. 元件中通過export宣告輸入屬性。

export let text; export let option: QRCodeToDataURLOptions = { type: 'image/png', margin: 2, width: 240 };

  1. 使用createEventDispatcher建立事件,當生成二維碼圖片base64時,觸發ready事件。

``` import { createEventDispatcher } from 'svelte';

const dispatch = createEventDispatcher();

dispatch('ready', { url }); ```

  1. 響應式
  1. 參考:svelte響應式程式碼塊
  1. 利用label語法宣告響應式邏輯,當輸入屬性text變化時更新二維碼內容。

$: { if (text) { toDataURL(text, option).then((url) => { dataUrl.set(url); dispatch('ready', { url }); }); } else { dataUrl.set(''); } }

2.2.4 二維碼下載

下載對應的Chrome API為chrome.downloads,同樣在manifest.json#permissions新增downloads許可權宣告。

監聽二維碼元件ready事件,並更新dataUrl。點選下載按鈕時觸發二維碼下載。

``` function downloadQrCode() { if (!dataUrl) { return; }

chrome.downloads?.download({ url: dataUrl, filename: getFilename(url), }); }

```

1.

三、除錯&構建

3.1 構建

Before you can deploy your SvelteKit app, you need to adapt it for your deployment target. Adapters are small plugins that take the built app as input and generate output for deployment.

—— Svelte Adapter

Svelte使用adapter轉換編譯產物,預設提供的adapter是@sveltejs/adapter-auto,需要配置打包目標平臺(Vercel、Cloudflare、Netlify)。一般使用@sveltejs/adapter-static打包靜態產物。

預設打包路徑為build,static資料夾下的靜態資源會打包到根路徑。

但用Chrome載入這個產物作為外掛會報錯,

ERROR Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-ri/Klr/GKqsTbCFK6rSYKj7VDIccXQJeipKxBmqg69g='), or a nonce ('nonce-...') is required to enable inline execution.

原因是違反CSP策略,index.html使用了內聯script指令碼,而這個檔案是打包時動態生成的,錯誤中提到的unsafe-inline關鍵字或nonce-等方法在MV3中均不支援。

解決方案 編譯後匹配所有html檔案中的內聯指令碼,將內聯指令碼內容寫入js檔案,並在html檔案中替換為sctipt標籤。自定義adapter完成產物轉換操作。

``` import type { Adapter } from '@sveltejs/kit'; import * as glob from 'fast-glob'; import { readFileSync, writeFileSync } from 'fs'; import { dirname, join } from 'path';

interface Props { /* dest for extension package dir / dest: string; }

function uuid() { return Math.random().toString(36).slice(2); }

function handleHtml(htmlPath, scriptTag) { const html = readFileSync(htmlPath).toString(); const matchReg = /]>([\s\S])/gm; const result = matchReg.exec(html);

return result && result[1] ? { html: html.replace(matchReg, scriptTag), script: result[1], } : null; }

export function extensionAdapter({ dest }: Props): Adapter { return { name: 'crx-adapter',

async adapt({ utils }) {
  utils.rimraf(dest);

  utils.copy_static_files(dest);
  utils.copy_client_files(dest);
  utils.rimraf(join(dest, '_app'));

  await utils.prerender({ all: true, dest: dest });

  const fileNames = await glob(join(dest, '**', '*.html'));
  for (const fileName of fileNames) {
    const dir = dirname(fileName);
    const scriptFileName = `start-${uuid()}.js`;
    const res = handleHtml(
      fileName,
      `<script type="module" src="/${scriptFileName}"></script>`,
    );

    if (res) {
      writeFileSync(fileName, res.html);
      writeFileSync(join(dir, scriptFileName), res.script);
    }
  }
},

}; } ```

3.2 除錯

產物打包好後,開啟Chrome瀏覽器,進入chrome://extensions/

  • 點選“載入已解壓的擴充套件程式”,選擇打包目錄以載入外掛
  • 當產物更新時,重新build並重新整理外掛即可

3.3 效果

最終效果如下,在網頁中點選外掛按鈕,即顯示對應二維碼。

對比一下:

| Chrome自帶 | qrcode-extension | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | | |

四、總結

本文主要工作如下:

  • 使用Svelte開發二維碼Chrome Extension
  • 自定義Svelte Adapter適配Chrome外掛安全策略