Svelte로 Chrome 확장 프로그램 개발

1. 배경

원인 최근 크롬 브라우저를 버전 96으로 업그레이드한 후 QR코드 항목이 주소창에서 보조메뉴로 이동됐다. 이것은 H5 프론트엔드 개발에 그다지 친화적이지 않고, 페이지 QR코드가 필요할 때마다 두 번 클릭해야 합니다(* ̄︿ ̄).

그래서 QR코드 Chrome Extension을 개발하자는 아이디어가 탄생했습니다(@ ̄ー ̄@).

여러 기술 선택(React, native, Vue, Svelte 등) 후에 마침내 Svelte 를 선택했습니다.

  • 간단한 문법, 낮은 정신적 부담
  • 더 적은 런타임 코드, 작은 패키지 크기
  • 반응형

d======( ̄▽ ̄*) 그런 다음 Svelte × Chrome 확장 프로그램의 여정을 시작하세요.

2. 생성 및 개발

2.1 프로젝트 생성

2.1.1 프로젝트 초기화

Svelte Kit 를 사용 npm`` init svelte@next qrcode-extension 하여 다음 디렉터리 구조 로 새 프로젝트 를 만듭니다.

  • src: 소스 파일 디렉토리

    • lib: 컴포넌트 라이브러리 등
    • routes: 기존 라우팅 파일
    • app.html: 항목 템플릿 파일
  • static: 정적 파일 디렉토리
  • svelte.config.js:단단한 구성

프로젝트를 초기화한 후 바로 시작할 수 있습니다 npm`` run dev .

2.1.2 플러그인 개발 지원

  1. 매니페스트 파일

확장은 HTML, JavaScript 및 CSS와 같은 웹 기술을 기반으로 합니다.

- 크롬 개발자 문서

  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:插件图标
  1. 添加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库生成二维码。

<script lang="ts">
  import { toDataURL } from 'qrcode';
  import type { QRCodeToDataURLOptions } from 'qrcode';
  import { writable } from 'svelte/store';
  import { createEventDispatcher } from 'svelte';

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

  const dataUrl = writable('');
  const dispatch = createEventDispatcher();
  
  // 响应式
  $: {
    if (text) {
      toDataURL(text, option).then((url) => {
        dataUrl.set(url);
        // 派发组件事件
        dispatch('ready', { url });
      });
    } else {
      dataUrl.set('');
    }
  }
</script>

<div class="qrcode">
{#if $dataUrl}
  <img src={$dataUrl} alt="qrcode">
{/if}
</div>

<style>
  .qrcode {
    width: 240px;
    height: 240px;
    border: 2px solid #e8eaed;
    border-radius: 10px;
    background: #f1f3f4;
  }

  img {
    width: 100%;
    height: auto;
    border-radius: 10px;
  }
</style>
复制代码
  1. 生命周期
  • 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),
  });
}

<button class="qrcode-download" on:click={downloadQrCode}>下载</button>
复制代码

三、调试&构建

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 콘텐츠 보안 정책 지시문 "script-src 'self'"를 위반했기 때문에 인라인 스크립트 실행을 거부했습니다. 인라인 실행을 활성화하려면 'unsafe-inline' 키워드, 해시('sha256-ri/Klr/GKqsTbCFK6rSYKj7VDIccXQJeipKxBmqg69g=') 또는 nonce('nonce-...')가 필요합니다.

그 이유는 CSP 정책을 위반하고 index.html인라인 스크립트 스크립트를 사용하며 이 파일은 패키징 과정에서 동적으로 생성되기 때문입니다.

솔루션 컴파일 후 html 파일의 모든 인라인 스크립트를 일치시키고 인라인 스크립트의 내용을 js 파일에 작성하고 html 파일의 sctipt 태그로 교체하십시오. 어댑터를 사용자 정의하여 제품 변환 작업을 완료하십시오.

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 = /<script\b[^>]*>([\s\S]*)</script>/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/를 입력합니다.

  • "압축되지 않은 확장 로드"를 클릭하고 플러그인을 로드할 패키지 디렉토리를 선택하십시오.
  • 제품이 업데이트되면 플러그인을 다시 빌드하고 새로 고칩니다.

3.3 효과

최종 효과는 다음과 같습니다. 웹 페이지에서 플러그인 버튼을 클릭하면 해당 QR 코드가 표시됩니다.

비교하다:

크롬과 함께 제공 qrcode 확장

4. 요약

본 논문의 주요 업무는 다음과 같다.

  • Svelte를 사용하여 QR 코드 Chrome 확장 프로그램 개발
  • Chrome 플러그인 보안 정책에 맞게 Svelte 어댑터 사용자 지정

рекомендация

отjuejin.im/post/7102391899918434341