构建一个简单的 Chrome 扩展程序
功能需求
入门
让我们开始编码
我决定做我的第一个实验Chrome Extension
。我的同事提出了一个非常简单的实现想法,所以我决定尝试一下。
功能需求
创建一个Chrome Extension
将在页面左上角输出一个小的彩色方块,提醒您domain (i.e. .dev, .stage)
当前处于哪种类型的页面。这些domains
页面colors
将在 上进行管理Options Page
。
选项页面
及其environments
对应的color
应该在 上进行管理Options Page
,允许您添加/删除任意数量的条目。
活动标签
小方块应该只出现在domains
用户添加的匹配条目上Options Page
。
方块的背景颜色将反映当前条目。
入门
我最初是按照本教程开始的。
每个扩展都需要一个manifest.json
。有关选项的完整列表,请访问其官方文档。
下面是一个最简单的例子manifest.json
。
{
"name": "Environment Flag Example",
"version": "1.0",
"description": "Environment Flag Example Extension",
"manifest_version": 2,
"background": {},
"permissions": [],
"options_page": "",
"content_scripts": []
}
值得注意的设置
背景脚本
扩展程序是基于事件的程序,用于修改或增强 Chrome 浏览体验。事件是浏览器触发器,例如导航到新页面、移除书签或关闭标签页。扩展程序会在后台脚本中监控这些事件,然后根据指定的指令做出反应。
我们将使用它来向事件中background scripts
添加一个。event listener
onInstalled
这将允许我们在安装时运行代码extension
。我们将使用它event
为添加一些默认条目Options Page
。
{
"background": {
"scripts": ["background.js"],
"persistent": false
}
}
为什么persistent
标记为false?
如文档所述:
唯一需要保持后台脚本持续活跃的情况是扩展程序使用 chrome.webRequest API 来阻止或修改网络请求。webRequest API 与非持久性后台页面不兼容。
权限
要使用大多数 chrome.* API,您的扩展程序或应用必须在清单的“权限”字段中声明其意图。
例如,如果您想使用 Chrome 的存储 API,则必须请求权限storage
。
{
"permissions": ["storage"]
}
选项页面
此条目将告诉 Chromehtml
您想要使用哪个文件作为Options Page
您的Extension
。
{
"options_page": "options/options.html"
}
您可以通过点击Options
下拉菜单中的 来访问此页面Extension
。
内容脚本
内容脚本是在网页上下文中运行的文件。通过使用标准文档对象模型 (DOM),它们能够读取浏览器访问的网页的详细信息,对其进行更改,并将信息传递给其父扩展程序。
本质上,任何你想在给定页面上实际运行的脚本都需要利用这一点api
。在我们的例子中,我们将在页面的左上角注入一个彩色方块active tab
。
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content/content.js"]
}
]
我还建议观看有关内容脚本和孤立世界的视频,以便更好地了解幕后发生的事情。
我们还需要更新我们的权限以使用activeTab
:
{
"permissions": ["storage", "activeTab"]
}
完全的manifest.json
{
"name": "Environment Flag Example",
"version": "1.0",
"description": "Environment Flag Example Extension",
"manifest_version": 2,
"permissions": ["storage", "activeTab"],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"options_page": "options/options.html",
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content/content.js"]
}
]
}
让我们开始编码
整个代码库可以在我的github repo中找到。
额外奖励——对于纯粹主义者来说,我创建了一个没有依赖关系的分支。
安装
安装开发扩展已经有很好的文档记录,所以我就不再赘述了。
继续并遵循他们的官方文档。
背景脚本
我们应该做的第一件事是使用 chrome 的存储 API设置一些默认数据。
本教程需要了解的两种方法是:
chrome.storage.sync.set({ key: value }, function() {
console.log('Value is set to ' + value);
});
chrome.storage.sync.get(['key'], function(result) {
console.log('Value currently is ' + result.key);
});
second parameter
操作完成后,每个方法都会执行一次callback function
。storage
我们将利用它来Vue
更新内部状态。
让我们打开background.js
并添加一个扩展事件installed
:
// background.js
chrome.runtime.onInstalled.addListener(function() {
/**
* lets add a default domain
* for our options page
*/
chrome.storage.sync.set(
{
config: [
{
domain: 'docker',
color: '#2496ed',
},
],
},
null
);
}
在上面的代码中,我们执行以下操作:
storage object
向被叫添加新键config
- 对于以 结尾的域名,
entry
添加一个config
docker
选项页面
对于我的技术栈,我决定使用Bootstrap 4、Vue JS、Webpack和原生ES6 JavaScript。我选择这些是因为我对它们很熟悉,但你也可以自由选择你自己的。
出于本教程的目的,我不会对 Vue 进行太多解释,因为它是一个实现细节,并且不是构建扩展所必需的。
对于无依赖的实现,请查看此分支。
该options.html
页面非常简单:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<meta content="ie=edge" http-equiv="X-UA-Compatible" />
<title>Environment Flag Options</title>
<link
crossorigin="anonymous"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO"
rel="stylesheet"
/>
</head>
<body>
<main>
<div class="container py-5">
<div class="col-sm-8 offset-sm-2">
<div id="app"></div>
</div>
</div>
</main>
<script src="../dist/options.bundle.js"></script>
</body>
</html>
在继续之前,先看看选项文件夹Vue
。这是一个非常标准的应用程序。
让我们回顾一些值得注意的Vue
代码。Options.vue
大多数神奇的事情都是利用发生的chrome api
。
// options/Options.vue
{
data() {
return {
/**
* empty array to be used to store
* the chrome storage result
*/
config: [],
};
},
mounted() {
/**
* once the component mounts
* lets call the storage api
* and request our `config` key
*
* on our callback, lets call a method
* to set our internal state
*/
chrome.storage.sync.get(['config'], this.setConfig);
},
methods: {
setConfig(storage) {
/**
* set our internal state
* with the result from the
* chrome api call
*/
this.config = storage.config;
},
},
}
在上面的代码中,我们执行以下操作:
- 设置名为的键的内部状态
config
,并将其分配给空array
- 在
mounted()
方法中,我们请求config
密钥storage api
- 在
callback function
,我们调用一个方法this.setConfig
setConfig()
将我们的内部状态分配给从chrome api
然后我们有两种方法来改变chrome storage state
:
{
deleteEntry(index) {
/**
* remove the entry at a specific index
* from our internal state
*/
this.config.splice(index, 1);
/**
* update the chrome storage api
* with the new state
*/
chrome.storage.sync.set(
{
config: this.config,
},
null
);
},
addEntry(entry) {
/**
* add an entry to our internal state
*/
this.config.push(entry);
/**
* update the chrome storage api
* with the new state
*/
chrome.storage.sync.set(
{
config: this.config,
},
null
);
},
}
实现这些方法之后,最终的Options Page
效果是这样的:
我知道,没什么特别的……但这不是重点。出去玩玩吧!你会注意到我添加了一个edu
域名,如果你愿意的话,现在就添加吧。
内容脚本
现在我们有了一种Options Page
方法add / delete entries
,现在让我们实现将出现在有效域左上角的小方块。
为此,我们需要使用content script
之前讨论过的方法。让我们继续,打开content/content.js
文件。
// content/content.js
/**
* lets first request the `config` key from
* the chrome api storage
*/
chrome.storage.sync.get(['config'], ({ config }) => {
/**
* lets see if the `window.location.origin`
* matches any entry from our
* options page
*/
let match = config.find((entry) => {
let regex = RegExp(`${entry.domain}\/?$`);
return regex.test(window.location.origin);
});
/**
* if no match, don't do anything
*/
if (!match) return;
/**
* lets create the style attribute
* by building up an object
* then using join to combine it
*/
let node = document.createElement('div');
let nodeStyleProperties = {
'background-color': match.color,
height: '25px',
left: '5px',
opacity: 0.5,
'pointer-events': 'none',
position: 'fixed',
top: '5px',
width: '25px',
'z-index': '999999',
};
let nodeStyle = Object.entries(nodeStyleProperties)
.map(([key, value]) => {
return `${key}: ${value}`;
})
.join('; ');
/**
* apply the style to the node
* and a class flag (doesn't do anything)
*/
node.setAttribute('style', nodeStyle);
node.setAttribute('class', 'chrome-extension-environment-flag');
/**
* append the node to the document
*/
document.body.appendChild(node);
});
结论
现在,当我进入一个edu
域时,我会在左上角看到以下内容:
我希望本教程至少能让你对 Chrome 扩展程序感兴趣。我们只是粗略地介绍了一些内容。欢迎将我仓库中的任何代码用于任何用途。
文章来源:https://dev.to/michaeldscherr/building-a-simple-chrome-extension-1mal