Chrome Extension Part 4: Dynamic Script & Domain Control

3 min read

👉 Phần 4 sẽ hướng dẫn bạn cách inject script động và quản lý Chrome Extension theo domain, tránh chạy thừa script và cải thiện hiệu suất trình duyệt.

Mục tiêu của Phần 4

Sau bài này, bạn sẽ:

  • Hiểu inject script động là gì?
  • Biết dùng chrome.scripting.executeScript
  • Bật/tắt extension theo domain
  • Viết extension thân thiện hiệu năng
  • Có extension hoàn chỉnh dùng được ngay

Vì sao không nên luôn dùng content_scripts?

Ở Phần 2, chúng ta dùng:

"content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content.js"]
    }
  ]

Cách này:

  • ✅ Đơn giản
  • ❌ Chạy trên mọi trang, mọi lúc
  • ❌ Không kiểm soát theo domain
  • ❌ Lãng phí tài nguyên

👉 Với extension thật, inject động là lựa chọn tốt hơn.

Inject Script Động là gì?

Inject script động nghĩa là:

  • Script chỉ được chèn khi cần
  • Do background hoặc popup quyết định
  • Không khai báo trước trong content_scripts

Chrome cung cấp API:

chrome.scripting.executeScript()

📌 API này chỉ có trong Manifest V3.

Demo

🎯 Chức năng

  • Popup có nút Enable on this domain
  • Khi bật:
    • Inject script vào tab hiện tại
    • Hiển thị badge “Extension Active”
  • Trạng thái lưu theo domain
  • Reload trang vẫn giữ trạng thái

Cấu trúc thư mục

domain-toggle-extension/
├── manifest.json
├── background.js
├── popup.html
├── popup.js
└── injected.js

manifest.json

{
  "manifest_version": 3,
  "name": "Domain Toggle Extension",
  "version": "1.0",
  "description": "Inject script theo domain",
  "permissions": ["tabs", "storage", "scripting"],
  "background": {
    "service_worker": "background.js"
  },
  "action": {
    "default_popup": "popup.html"
  }
}

📌 Điểm mới:

  • scripting: bắt buộc để inject script
  • Không dùng content_scripts

injected.js – Script được inject vào trang

// injected.js
(function () {
  const ID = 'domain-toggle-badge';

  if (document.getElementById(ID)) return;

  const badge = document.createElement('div');
  badge.id = ID;
  badge.innerText = 'Extension Active';

  Object.assign(badge.style, {
    position: 'fixed',
    bottom: '16px',
    right: '16px',
    padding: '6px 10px',
    background: '#9333ea',
    color: '#fff',
    fontSize: '12px',
    borderRadius: '6px',
    zIndex: 9999
  });

  document.body.appendChild(badge);
})();

👉 Script này hoàn toàn độc lập, không cần Chrome API.

Background – Quyết định inject theo domain

chrome.runtime.onMessage.addListener((message, sender) => {
  if (message.type !== 'TOGGLE_DOMAIN') return;

  const { domain, tabId, enabled } = message;

  chrome.storage.local.get('domains', (data) => {
    const domains = data.domains || {};
    domains[domain] = enabled;

    chrome.storage.local.set({ domains });

    if (enabled) {
      chrome.scripting.executeScript({
        target: { tabId },
        files: ['injected.js']
      });
    }
  });
});

📌 Background:

  • Lưu trạng thái theo domain
  • Inject script ngay lập tức
  • Không reload tab

Popup – Điều khiển theo domain

popup.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <style>
    body { font-family: sans-serif; padding: 12px; }
    button { width: 100%; padding: 8px; }
  </style>
</head>
<body>
  <button id="toggle">Loading...</button>
  <script src="popup.js"></script>
</body>
</html>

popup.js

const btn = document.getElementById('toggle');

chrome.tabs.query({ active: true, currentWindow: true }, ([tab]) => {
  const url = new URL(tab.url);
  const domain = url.hostname;

  chrome.storage.local.get('domains', (data) => {
    const enabled = data.domains?.[domain] ?? false;
    updateButton(enabled);

    btn.onclick = () => {
      const newValue = !enabled;

      chrome.runtime.sendMessage({
        type: 'TOGGLE_DOMAIN',
        domain,
        tabId: tab.id,
        enabled: newValue
      });

      updateButton(newValue);
    };
  });
});

function updateButton(enabled) {
  btn.textContent = enabled
    ? 'Disable on this domain'
    : 'Enable on this domain';
}

Điều gì xảy ra khi reload trang?

👉 Vì:

  • Domain đã được lưu trong chrome.storage
  • Khi người dùng bật lại extension
  • Popup inject script lại

➡️ Extension giữ trạng thái đúng domain

(Ở Phần 5, chúng ta sẽ tự động inject khi tab load)

Best Practices khi inject động

  • Chỉ inject khi cần
  • Không dùng <all_urls> nếu không bắt buộc
  • Script inject phải độc lập
  • Logic quyết định nằm ở background
  • Domain-based extension = UX rất tốt

Những lỗi hay gặp

LỗiNguyên nhân
Script không chạyThiếu scripting permission
Inject nhưng không thấy UIScript bị inject trùng
Reload mất trạng tháiKhông lưu domain
Popup không bấm đượcSai tab context

So sánh nhanh các cách chạy script

CáchKhi nào dùng
content_scriptsLuôn chạy
inject độngTheo user action
SPA observerWeb hiện đại

Tóm tắt Phần 4

  • Inject script động là bước nâng cấp lớn
  • Background quyết định logic
  • Popup chỉ điều khiển
  • Domain-based extension là chuẩn production
Avatar photo

Leave a Reply

Your email address will not be published. Required fields are marked *