👉 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ỗi | Nguyên nhân |
|---|---|
| Script không chạy | Thiếu scripting permission |
| Inject nhưng không thấy UI | Script bị inject trùng |
| Reload mất trạng thái | Không lưu domain |
| Popup không bấm được | Sai tab context |
So sánh nhanh các cách chạy script
| Cách | Khi nào dùng |
|---|---|
content_scripts | Luôn chạy |
| inject động | Theo user action |
| SPA observer | Web 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
