jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

Build Chrome Extensions · Part 11 — Pro Tooling & Build (Vite + CRXJS + TS + React)

Why plain files stop scaling, and how to set up a modern extension project with Vite, CRXJS, TypeScript and React — HMR, bundling, npm packages, and a typed manifest. With an interactive build-pipeline visualizer.

Mọi thứ đến giờ dùng file .js thuần bạn tải trực tiếp. Tuyệt để học, nhưng ngừng mở rộng ngay khi bạn muốn TypeScript, React, npm package, hay lặp nhanh. Phần này thiết lập bộ công cụ chuyên nghiệp.

Chạy build và bật dev mode bên dưới để xem source thành dist/ tải được:


1. Vì sao cần bước build?

Nhớ CSP của MV3 từ Phần 9: không code từ xa, không script inline. Vậy để dùng npm package bạn phải đóng gói nó vào file cục bộ. Bước build còn cho bạn TypeScript, JSX, minify, và hot reload.

CRXJS là plugin Vite làm riêng cho extension. Nó hiểu manifest.json, đóng gói mọi entry point, viết lại đường dẫn, và cho bạn HMR cho popup/optionsauto-reload cho content script.


2. Dựng project

npm create vite@latest my-extension -- --template react-ts
cd my-extension
npm install
npm install -D @crxjs/vite-plugin@beta

Phiên bản đổi nhanh — xem tài liệu CRXJS cho lệnh cài hiện tại và tương thích Vite.


3. Manifest như config có kiểu

Thay vì JSON sửa tay, định nghĩa manifest trong TypeScript và có autocomplete cùng kiểm tra kiểu:

// manifest.config.ts
import { defineManifest } from "@crxjs/vite-plugin";

export default defineManifest({
  manifest_version: 3,
  name: "My Extension",
  version: "1.0.0",
  action: { default_popup: "src/popup/index.html" },
  background: { service_worker: "src/background.ts", type: "module" },
  content_scripts: [
    { matches: ["https://*/*"], js: ["src/content.ts"] },
  ],
  permissions: ["storage", "activeTab"],
});
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { crx } from "@crxjs/vite-plugin";
import manifest from "./manifest.config";

export default defineConfig({
  plugins: [react(), crx({ manifest })],
});

Trỏ entry point vào file source; CRXJS phát đường dẫn băm cuối cùng vào dist/manifest.json.


4. Vòng lặp dev

npm run dev      # starts Vite, writes dist/ and watches

Tải dist/ một lần như extension chưa đóng gói (Phần 1). Bây giờ:

  • Sửa popup/options → HMR cập nhật tức thì, giữ state.
  • Sửa content script → CRXJS tự tải lại các tab liên quan.
  • Sửa service worker → nó tự tải lại.

Không còn click “reload extension” thủ công cho đa số thay đổi. Bấm nút “Save an edit” trong trình trực quan để xem đường HMR sáng lên.


5. Gắn kiểu cho chrome.*

Cài types chính thức để chrome.* có kiểu đầy đủ:

npm install -D @types/chrome

Giờ chrome.storage.local.get trả kết quả có kiểu, và bạn bắt lỗi lúc biên dịch. Kết hợp với wrapper storage có kiểu từ Phần 7 để an toàn đầu-cuối.


6. Cấu trúc project hợp lý

my-extension/
├─ manifest.config.ts      # typed manifest
├─ vite.config.ts
├─ src/
│  ├─ background.ts         # service worker
│  ├─ content.ts            # content script
│  ├─ popup/                # React popup
│  │  ├─ index.html
│  │  └─ Popup.tsx
│  ├─ options/              # React options page
│  ├─ lib/storage.ts        # typed storage wrapper (Part 7)
│  └─ lib/messages.ts       # shared message types
└─ dist/                    # build output — this is what you load/zip

Chia sẻ kiểu hình-dạng-tin-nhắn và helper storage trong lib/ để mọi ngữ cảnh đồng thuận cùng hợp đồng.


7. Build cho production

npm run build    # minified, tree-shaken dist/

dist/ xuất ra chính là thứ bạn nén và tải lên Chrome Web Store (Phần 12). CRXJS lo code-splitting và phát manifest.json production tự động.


8. Bài tập

1. Bạn import { z } from "zod" trong popup và nó chạy sau khi bundle, nhưng <script src="https://cdn.../zod"> thì không. Vì sao cần bundle?

Lời giải

CSP của MV3 cấm code từ xa; bundler nội tuyến package vào file cục bộ bạn ship.

2. manifest.config.ts nên tham chiếu entry point nào — src/ hay dist/?

Lời giải

File src/. CRXJS chuyển đổi chúng và ghi đường dẫn dist/ băm đúng vào manifest.json phát ra.

3. Sau npm run build, bạn tải chính xác cái gì lên Chrome Web Store?

Lời giải

Một zip của thư mục dist/ — kết quả đã build, minify, không phải source.

Nâng cao:trong trình trực quan, chạy dev mode rồi “Save an edit” và để ý chỉ popup hot-reload trong khi phần còn lại của dist/ giữ nguyên — đó là HMR.


Điểm chính

  • Bước build là bắt buộc để dùng npm package dưới CSP của MV3.
  • Vite + CRXJS đóng gói entry point, viết lại đường dẫn manifest, và cho HMR/auto-reload.
  • Định nghĩa manifest như config có kiểu; thêm @types/chrome.
  • Chia sẻ kiểu tin nhắn và helper storage trong lib/.
  • Bạn ship dist/ đã build, không bao giờ source.

Tiếp theo

Phần 12 — Phát hành, tự cập nhật, đa trình duyệt & capstone: đóng gói và tải lên Chrome Web Store, quy trình duyệt, đánh phiên bản và tự cập nhật, port sang Firefox/Edge, và một extension capstone gắn cả series lại.