使用Vue3+Element Plus开发Chrome插件
引言初始Vue项目的创建Element-Plus组件的安装及导入其他文件配置.eslintrc.js文件vue.config.js文件package.jsonsrc/文件夹assets/background/main.jscontent/components/app.vue文件content/main.js文件plugins/inject.jsplugins/manifest.json文件popup/components/app.vue文件popup/index.html
成果检验热更新设置2022-8-17使用的vue环境
引言
本人Vue3小白一枚,目前正在研究基于Vue3的Chrome浏览器插件的使用,经过不断尝试和整合各路大佬方法,现将自己尝试有效的项目框架搭建流程进行总结。在此非常感谢大佬们的分享。 本文主要参考文章——15000字大章带你一步一步使用Vue3开发chrome浏览器插件
初始Vue项目的创建
控制台中输入vue create XXX创建新的vue工程项目在创建工程过程中选择Manually select features手动配置模块选中babel、linter、router后进行下一步,其中router项目若无需求可不选对于vue.js选择3.x版本Router模式选择history选择ESLint with error prevention only,不选其他的,不然代码规范对后续开发产生很多麻烦随后选择Lint on save,如果有Git也可选择下面那项选择In dedicated config files将eslint、babel等文件分别单独存放完成初始Vue项目的创建
Element-Plus组件的安装及导入
控制台输入npm install element-plus --save向Vue项目安装element-plus组件项目结构可参考大佬的文章所给出的结构,我的项目最终产出的目录结构为:
├── dist
│ ├── assets
│ │ ├── images
│ │ │ ├── icon128.png
│ │ │ ├── icon16.png
│ │ │ └── icon48.png
│ │ └── logo.png
│ ├── js
│ │ ├── background.js
│ │ ├── chunk-vendors.js
│ │ ├── content.js
│ │ ├── inject.js
│ │ └── popup.js
│ ├── manifest.json
│ └── popup.html
├── src
│ ├── assets
│ │ ├── images
│ │ │ ├── icon128.png
│ │ │ ├── icon16.png
│ │ │ └── icon48.png
│ │ └── logo.png
│ ├── background
│ │ └── main.js
│ ├── content
│ │ ├── components
│ │ │ └── app.vue
│ │ └── main.js
│ ├── plugins
│ │ ├── inject.js
│ │ └── manifest.json
│ ├── popup
│ │ ├── components
│ │ │ └── app.vue
│ │ ├── index.html
│ │ └── main.js
│ ├── router
│ │ └── index.js
│ └── utils
│ └── hotReload.js
├── .browserslistrc
├── .eslintrc.js
├── .gitignore
├── babel.config.js
├── jsconfig.json
├── package-lock.json
├── package.json
├── README.md
└── vue.config.js
其中dist文件夹为执行npm run build指令后生成的,其他均应在执行npm run build前准备就绪(如果开发的插件不涉及路由等操作,则删除src文件夹下的router/index.js文件夹及文件)在src/popup/main.js中添加element-plus插件,代码如下:
import { createApp } from 'vue'
import App from './components/app.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
const app = createApp(App)
app.use(ElementPlus, {locale: zhCn})
app.mount('#app')
至此,项目的创建和element-plus组件的导入完成
其他文件配置
.eslintrc.js文件
主要是在globals中添加chrome: true从而让chrome属性在全局的.js文件中为可用状态复制以下内容到eslintrc.js文件中
module.exports = {
root: true,
globals: {
chrome: true,
},
env: {
node: true
},
'extends': [
'plugin:vue/vue3-essential',
'eslint:recommended'
],
parserOptions: {
parser: '@babel/eslint-parser'
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
}
}
vue.config.js文件
vue.config.js作为项目配置文件,对打包生成的dist文件夹以及src文件夹下的各文件起到关键作用复制以下代码到vue.config.js文件中
const CopyWebpackPlugin = require("copy-webpack-plugin");
const path = require("path");
// 复制文件到指定目录
const copyFiles = [
{
from: path.resolve("src/plugins/manifest.json"),
to: `${path.resolve("dist")}/manifest.json`
},
{
from: path.resolve("src/assets"),
to: path.resolve("dist/assets")
},
{
from: path.resolve("src/plugins/inject.js"),
to: path.resolve("dist/js")
}
];
// 复制插件
const plugins = [
new CopyWebpackPlugin({
patterns: copyFiles
})
];
// 页面文件
const pages = {};
// 配置 popup.html 页面
const chromeName = ["popup"];
chromeName.forEach(name => {
pages[name] = {
entry: `src/${name}/main.js`,
template: `src/${name}/index.html`,
filename: `${name}.html`
};
});
module.exports = {
pages,
productionSourceMap: false,
// 配置 content.js background.js
configureWebpack: {
entry: {
content: "./src/content/main.js",
background: "./src/background/main.js"
},
output: {
filename: "js/[name].js"
},
plugins
},
// 配置 content.css
css: {
extract: {
filename: "css/[name].css"
}
},
chainWebpack: config => {
if (process.env.NODE_ENV === 'production') {
config.output.filename('js/[name].js').end()
config.output.chunkFilename('js/[name].js').end()
}
}
}
package.json
在 "scripts"字段中添加 “watch”: “vue-cli-service build --watch”,来增加NPM脚本中对插件的热监控功能
"scripts": {
"watch": "vue-cli-service build --watch",
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
src/文件夹
src文件夹下主要针对manifest.json文件、popup中的文件、content中的文件进行更改
assets/
assets文件中用于存放图标,注意images文件夹内的三张图片要是同一个图片的不同的名称,例如:
background/main.js
background作为Chrome插件的后台,随着浏览器的启动而启动,可将全局性的、一直运行的功能放在此处考虑到仅仅做一个demo,此处此处打印一串字作为测试,将以下内容复制到main.js中
console.log('this is background')
content/components/app.vue文件
添加以下内容到app.vue文件作为植入到浏览器中的HTML文件(不植入无法获取网页中的DOM)注意:此时的.vue文件尚未具有注入HTML中的能力
this is the content
.head{
color: red;
position: fixed;
z-index: 100001;
right: 10px;
bottom: 10px;
}
content/main.js文件
添加以下内容到main.js文件中,在浏览器当前Tab中创建一个< div >标签并将app.vue文件加载到标签中以完成HTML的植入的操作当app.vue成功植入到HTML中时,我们便可以通过chrome自带的功能猥琐欲为了
import { createApp } from 'vue'
import app from './components/app.vue'
joinContent(app)
injectJsInsert()
function joinContent (element) {
const div = document.createElement('div')
div.id = 'joinContentApp'
document.body.appendChild(div)
console.log(div)
createApp(element).mount('#joinContentApp')
}
//chrome的API接口,用于传输或监听数据信号
chrome.extension.onRequest.addListener(
function (request) {
if (request.popAction == "Test") {
console.log("test")
}
}
);
function injectJsInsert () {
document.addEventListener('readystatechange', () => {
const injectPath = 'js/inject.js'
const script = document.createElement('script')
script.setAttribute('type', 'text/javascript')
script.src = chrome.extension.getURL(injectPath)
document.body.appendChild(script)
})
}
plugins/inject.js
inject.js文件主要负责分析网页的DOM,拿到大图资源链接,并翻页,直到获取足够数量的图片可以在此文件内输入以下内容,最终结果可在浏览器窗口的控制台中打印出来
console.log('this is inject')
plugins/manifest.json文件
manifest.json文件的重要性再此不多做赘述了,文件中各个字段的意义和功能请见其他大佬的博客——Chrome插件manifest.json文件详解复制以下内容到manifest.json中以完成Chrome插件的配置
{
"manifest_version": 2,
"name": "test",
"description": "Vue3的Chrome插件",
"version": "1.0.0",
"browser_action": {
"default_title": "plugin-base-vue3",
"default_icon": "assets/images/icon48.png",
"default_popup": "popup.html"
},
"permissions": [],
"background": {
"scripts": ["js/chunk-vendors.js", "js/background.js"]
},
"icons": {
"16": "assets/images/icon16.png",
"48": "assets/images/icon48.png",
"128": "assets/images/icon128.png"
},
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"css": ["css/content.css"],
"js": ["js/chunk-vendors.js", "js/content.js"],
"run_at": "document_idle"
}
],
"web_accessible_resources": ["js/inject.js"]
}
popup/components/app.vue文件
添加以下内容到app.vue文件中作为浏览器插件的前端显示窗口
测试按钮
const test = () => {
chrome.tabs.getSelected(null, function(tab){
chrome.tabs.sendRequest(tab.id, { popAction: "Test"});
});
}
.wrapper{
width: 100px;
height: 100px;
background-color: aquamarine;
}
popup/index.html
index.html文件作为.vue文件的载体,其内容使用新建项目时的初始内容即可
test
We're sorry but doesn't work properly without JavaScript enabled. Please enable it to continue.
上述文件内容填写完成后在控制台执行npm run build指令以获得dist文件,使用Chrome加载dist文件夹以加载插件
成果检验
在加载插件的窗口中,点击查看视图旁的背景页可以看到来自background/main.js文件的打印内容 打开任意一个页面可以看到右下角出现了this is the content-script字样,这说明我们的content里的app.vue文件已经成功植入到网页的html文件中了打开插件,出现了带有Element-plus控件元素的按钮,说明Element已经成功引入打开控制台(F12)可以看到植入到html页面中的< div >元素被打印了出来,同时来自plugins/inject.js的内容也被打印了出来点击插件中的按钮在控制台中出现了test字样,这说明content中的js文件已经接收到了来自popup中js传来的信息(由于.vue内带有script的编写区域,因此chrome的数据传输功能可直接在.vue文件中进行) 恭喜你!你已经成功搭建了基于Vue3+Element-plus的Chrome框架
热更新设置
如果不采用热更新,开发过程中将会不断经历"更改->打包->加载->再更改"的痛苦历程,为了提高开发效率,在utils/hotReload.js文件中添加以下内容:
// 加载文件
const filesInDirectory = dir =>
new Promise(resolve =>
dir.createReader().readEntries(entries => {
Promise.all(
entries
.filter(e => e.name[0] !== '.')
.map(e =>
e.isDirectory ? filesInDirectory(e) : new Promise(resolve => e.file(resolve))
)
)
.then(files => [].concat(...files))
.then(resolve);
})
);
// 遍历插件目录,读取文件信息,组合文件名称和修改时间成数据
const timestampForFilesInDirectory = dir =>
filesInDirectory(dir).then(files =>
files.map(f => f.name + f.lastModifiedDate).join()
);
// 刷新当前活动页
const reload = () => {
window.chrome.tabs.query({
active: true,
currentWindow: true
},
tabs => {
// NB: see https://github.com/xpl/crx-hotreload/issues/5
if (tabs[0]) {
window.chrome.tabs.reload(tabs[0].id);
}
// 强制刷新页面
window.chrome.runtime.reload();
}
);
};
// 观察文件改动
const watchChanges = (dir, lastTimestamp) => {
timestampForFilesInDirectory(dir).then(timestamp => {
// 文件没有改动则循环监听watchChanges方法
if (!lastTimestamp || lastTimestamp === timestamp) {
setTimeout(() => watchChanges(dir, timestamp), 1000); // retry after 1s
} else {
// 强制刷新页面
reload();
}
});
};
const hotReload = () => {
window.chrome.management.getSelf(self => {
if (self.installType === 'development') {
// 获取插件目录,监听文件变化
window.chrome.runtime.getPackageDirectoryEntry(dir => watchChanges(dir));
}
});
};
export default hotReload;
同时修改background/main.js为:
import hotReload from '@/utils/hotReload'
hotReload()
console.log('this is background')
在package.json中的scripts字段中添加"watch": "vue-cli-service build --watch",来向NPM脚本中添加热更新快捷操作
2022-8-17使用的vue环境
本文创建于2022-8-17日,采用的package.json为:
{
"name": "plugin",
"version": "0.1.0",
"private": true,
"scripts": {
"watch": "vue-cli-service build --watch",
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.8.3",
"element-plus": "^2.2.13",
"vue": "^3.2.13",
"vue-router": "^4.0.3"
},
"devDependencies": {
"@babel/core": "^7.12.16",
"@babel/eslint-parser": "^7.12.16",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-plugin-router": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3",
"less": "^4.1.3",
"less-loader": "^11.0.0"
}
}
通常来说,即使不采用本文的各模块版本,按照本文提供的方法一步一步执行也可以创建出Chrome插件的框架本文所讲述内容,作者从项目创建开始一步一步执行,亲测有效,所涉及各项功能均无异常———————————————————————————————————————————2022.12.15 00:10:00更新——最近看到评论区的小伙伴们在按照文章步骤实现的过程中出现了各种各样的报错,于是我按照文章内容再次搭建了一遍,确认按照步骤搭建chrome插件是没问题的,为了方便大家能够更加简单方便的使用本文提供的方法,我已将代码上传至git代码仓库,有需要的小伙伴们可以自行【前往下载】。代码仓库地址 —— https://gitee.com/XiaoYueDeCode/vue3-chrome-plugin-demo下载完成的代码,需要在本地使用npm i和npm install element-plus --save指令完成所需环境的布置。环境布置完成后就可以愉快的开始你的创作啦。大半夜更新代码属实不易,希望小伙伴们能够关注收藏,多多支持,谢谢大家啦~~
|