Chrome 实现一个侧边栏其实很方便,因为侧边栏的框架 Chrome 已经帮我们实现好了,只需要配置权限,然后实现页面即可。
配置权限
在 manifest.json 文件中增加如下配置即可。permissions 添加 “sidePanel”,side_panel 设置默认的文件路径 "side-panel.html"。
{
...
"permissions": [
...
"sidePanel"
],
"background": {
"service_worker": "background.js"
},
"side_panel": {
"default_path": "side-panel.html"
}
}
当然 Chrome 也支持多个侧边栏,具体实现需求可以查阅官方文档。Chrome 支持侧边栏支持如下特性:字典侧边栏,全局侧边栏,多个侧边栏,打开侧边栏,针对特定网站的侧边栏。
side-panel.html 编码
侧边栏的代码可以很简单,然后通过 js 动态生成 DOM 即可
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>我的侧边栏</title>
</head>
<body>
<div class="projects">
</div>
<script src="side-panel.js"></script>
</body>
</html>
比如使用 document.querySelector('.projects').innerHTML = html; 直接在 DOM 元素中插入 HTMl 代码。或者 createElement DOM 元素,然后在其它元素后面 appendChild 等等。
点击插件图标按钮,直接打开侧边栏
// background.js
chrome.sidePanel
.setPanelBehavior({ openPanelOnActionClick:true })
.catch((error) => console.error(error));
跨域问题
Chrome 插件基本上不需要考虑跨域的问题。只需要在 manifest.json 文件中配置 host_permissions 权限即可。
{
...
"host_permissions": [
"https://api.example.com/*",
"https://another-api.example.com/*"
],
...
}
当前页面的脚本可以直接发起跨域请求。如果需要请求其它域名,则需要在 background.js 中使用 chrome.runtime.onMessage 监听消息,然后在回调函数中发起请求,最后通过 sendResponse 返回结果。
// background.js
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action==='fetchData') {
constinit= {
method,
headers,
credentials:'include', // include cookies
};
fetch(request.url, init)
.then((response) => response.json())
.then((data) => sendResponse({ data }))
.catch((error) => sendResponse({ error:error.message }));
returntrue; // 表示异步响应
}
});
实现一个测试侧边栏,获取百度的标题,展示在侧边栏中
manifest.json 配置
{
"manifest_version": 3,
"name": "测试侧边栏",
"version": "1.0",
"description": "一个简单的测试侧边栏插件",
"host_permissions": [
"https://*.baidu.com/*"
],
"background": {
"service_worker": "background.js"
},
"permissions": [
"sidePanel"
],
"side_panel": {
"default_path": "side-panel.html"
}
}
side-panel.html
<!DOCTYPE html>
<html lang="en"></html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>我的侧边栏</title>
</head>
<body>
<div class="projects">
</div>
<script src="side-panel.js"></script>
</body>
</html>
side-panel.js
// Example usage: Fetch Baidu title when the sidebar is loaded
document.addEventListener('DOMContentLoaded', () => {
chrome.runtime.sendMessage({ type:'FETCH', url:'https://www.baidu.com' }, (response) => {
if (response?.ok&&response.body) {
consttitleMatch=response.body.match(/<title>(.*?)<\/title>/);
consttitle=titleMatch?titleMatch[1] :'未找到标题';
document.querySelector('.projects').innerHTML=`<h1>${title}</h1>`;
} else {
console.error('Error fetching Baidu title:', response?.error);
document.querySelector('.projects').innerHTML=`<h1>加载标题失败</h1>`;
}
});
});
background.js
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (!message||!message.type) return;
if (message.type==='PING') {
sendResponse({ ok:true, ts: Date.now() });
}
// Handle cross-origin fetch with cookies from background
if (message?.type==='FETCH') {
const { url, method='GET', headers= {}, body } =message;
if (!url) {
sendResponse({ ok:false, error:'Missing url' });
return;
}
constinit= {
method,
headers,
credentials:'include', // include cookies
};
if (body!==undefined&&body!==null) {
init.body=typeofbody==='string'?body: JSON.stringify(body);
if (!init.headers||!('Content-Type'ininit.headers)) {
init.headers= { ...(init.headers|| {}), 'Content-Type':'application/json' };
}
}
fetch(url, init)
.then(async (res) => {
constcontentType=res.headers.get('content-type') ||'';
consttext=awaitres.text();
sendResponse({
ok:res.ok,
status:res.status,
statusText:res.statusText,
body:text,
contentType,
});
})
.catch((err) => {
sendResponse({ ok:false, error:err.message|| String(err) });
});
returntrue; // async
}
});
// Optional: Open side panel on action icon click
chrome.sidePanel
.setPanelBehavior({ openPanelOnActionClick:true })
.catch((error) => console.error(error));
夜雨聆风