15000字大章带你一步一步使用Vue3开发chrome浏览器插件 | 掘金年度征文

之前写过一篇 chrome 浏览器插件开发的文章 全方面手把手从0到1带你开发谷歌浏览器插件 ,但是不是 vue/react 这种第三方框架的,是原生和 jquery 混合的,但是那种开发前端方式比较麻烦,所以下面是用 vue 来开发插件。

文章有点长,建议先收藏再一步步来进行实操

github地址:https://github.com/18055975947/my-vue3-plugin

码云地址:https://gitee.com/guoqiankun/my-vue3-plugin

一、创建 Vue 项目

使用 vue-cli 创建 vue3.x 版本的 vue 项目 vue create my-vue3-plugin 如果在创建项目的时候报错,报错内容如下:

1
2
3
4
5
6
7
8
9
10
11
error Couldn't find package "postcss-normalize-string@^4.0.2" required by "cssnano-preset-default@^4.0.0" on the "npm" registry.
Error: Couldn't find package "@vue/cli-overlay@^4.5.9" required by "@vue/cli-service@~4.5.0" on the "npm" registry.
at MessageError.ExtendableBuiltin (/usr/local/lib/node_modules/yarn/lib/cli.js:243:66)
at new MessageError (/usr/local/lib/node_modules/yarn/lib/cli.js:272:123)
at PackageRequest.<anonymous> (/usr/local/lib/node_modules/yarn/lib/cli.js:38988:17)
at Generator.throw (<anonymous>)
at step (/usr/local/lib/node_modules/yarn/lib/cli.js:92:30)
at /usr/local/lib/node_modules/yarn/lib/cli.js:105:13
at process._tickCallback (internal/process/next_tick.js:68:7)
ERROR command failed: yarn

可以参考 使用 vue-cli 创建 vue3.x 版本项目报错 文章来处理 此时文件目录为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.
├── README.md
├── babel.config.js
├── package.json
├── public
│ ├── favicon.ico
│ └── index.html
├── src
│ ├── App.vue
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ └── HelloWorld.vue
│ └── main.js
└── yarn.lock

二、修改项目

因为我们要开发 chrome 插件项目,而这种生成的 vue 项目里面的文件夹和文件很多我们不需要,所以我们需要处理下:

  1. 在根目录下创建 vue.config.jsvue 配置文件;
  2. src 文件夹下面的 app.vue、components 文件夹删除
  3. assets 文件中创建 images 文件夹,并在 images 文件夹里面添加自己插件的 icon
  4. 删除根目录下的 public 文件夹
  5. src 文件夹下 创建 background、content、plugins、popup、utils 文件夹
  6. background 文件夹下创建 main.js
  7. content 文件夹下创建 components 文件夹和 main.jscomponents 文件夹下创建 app.vue
  8. plugins 文件夹下创建 inject.js、manifest.json 文件
  9. popup 文件夹下创建 components 文件夹 main.jsindex.htmlcomponents 文件夹下创建 app.vue

步骤解析:

  • vue.config.jsvue 项目的打包、运行、等的配置文件,我们需要生成插件项目,这个文件需要创建并且自行配置
  • 删除多余的文件,我们插件里面目前只有 需要一个 popup 页面,不需要 外部的 app.vue 和 组件
  • 自己的插件 icon,按照 16 * 16、48 * 48、128 * 128 三个尺寸
  • 不需要 public 文件夹里面的 index.html
  • 创建我们插件需要的 background.js、content.js、popup页面、插件配置
  • 创建 background.js 文件
  • 创建 content.js 文件
  • 创建 popup.js、popup.html 文件

此时文件目录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
.
├── README.md
├── babel.config.js
├── package.json
├── src
│ ├── assets
│ │ ├── images
│ │ │ ├── icon128.png
│ │ │ ├── icon16.png
│ │ │ └── icon48.png
│ │ └── logo.png
│ ├── background
│ │ └── main.js
│ ├── content
│ │ ├── components
│ │ │ └── app.vue
│ │ └── main.js
│ ├── main.js
│ ├── plugins
│ │ ├── inject.js
│ │ └── manifest.json
│ ├── popup
│ │ ├── components
│ │ │ └── app.vue
│ │ ├── index.html
│ │ └── main.js
│ └── utils
├── vue.config.js
└── yarn.lock

三、配置项目

1、plugins/manifest.json 文件配置

先配置 manifest.json 文件,在按照此文件配置 vue.config.js 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
{
"manifest_version": 2,
"name": "my-vue3-plugin",
"description": "基于vue3.x版本的chrome插件",
"version": "1.0.0",
"browser_action": {
"default_title": "my-vue3-plugin",
"default_icon": "assets/images/icon48.png",
"default_popup": "popup.html"
},
"permissions": [],
"background": {
"scripts": ["js/background.js"]
},
"icons": {
"16": "assets/images/icon16.png",
"48": "assets/images/icon48.png",
"128": "assets/images/icon128.png"
},
"content_scripts": [
{
"matches": ["https://*.taobao.com/*"],
"css": ["css/content.css"],
"js": ["js/content.js"],
"run_at": "document_idle"
}
],
"web_accessible_resources": ["js/inject.js"]
}

解析:

  1. browser_action 中的 default_popup 配置为 和 manifest.json 文件一级的 popup.html
  2. browser_action 中的 default_icon 配置为 assets/images/icon48.png
  3. background 配置为 js/background.js
  4. icons 文件进行 项目的配置
  5. content_scripts 配置对应的 js、css、和 matches
  6. web_accessible_resources 配置网页内置 js/inject.js

2、配置 vue.config.js 文件

通过上面的 manifest.json 文件可以看出,我们需要配置 js 文件夹,css 文件夹,popup.html 文件,background.js 文件,inject.js 文件,content.js 文件,content.css 文件;

1. 添加 copy-webpack-plugin 模块,用于复制文件

我们需要把 plugins 文件夹下的文件复制到打包之后的 dist 文件中

安装:

1
2
yarn add copy-webpack-plugin@6.0.2 --dev

2. 文件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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"
}
}
}

3. 解析:

  1. copyFiles 是复制文件的字段
  2. pages 是配置多页面的文件字段
  3. configureWebpack 来配置 content.jsbackground.js 文件
  4. css 配置 content.css 文件

3、popup 文件夹修改

从上面的配置我们知道了,popup 文件夹是用来生成 browser_actionpopup.html 文件的,所以此时我们来写入 popup 文件夹

1. popup/index.html

popup 文件夹下的 index.html 文件,因为这个是 html 文件,我们就只需要按照 vue create 生成的项目中的 public 文件夹下的 index.html 文件内容拷贝过来即可,顺便把 favicon 删除,把 title 修改下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>my-vue-chrome-plugin</title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

2. popup/main.js

这个是 vue 项目的入口配置文件,就按照 src 下面的 main.js 复制过来即可,别忘了改下 大小写

1
2
3
4
5
import { createApp } from 'vue'
import app from './components/app.vue'

createApp(app).mount('#app')

3. popup/components/app.vue

此文件就是正常的 vue 文件,按照平时写 vue 项目开发即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div class="popup_page">
this is popup page
<div class="popup_page_main">
this is popup page main
</div>
</div>
</template>

<script>
export default {

}
</script>

<style></style>

4、content 文件夹修改

content 文件夹下是对应 chrome 插件的 content.js,这个可以在嵌入页面里面渲染页面,我们也可以用 vue 开发

1. content/components/app.vue

正常的 vue 开发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div class="content_page">
content_page
<div class="content_page_main">
content_page_main
</div>
</div>
</template>

<script>
export default {

}
</script>

<style>

</style>

2. content/main.js

main.js 这个文件是比较重要的,是通过这个文件引入 vue 组件以及使用 vue 开发 content 页面的,所以这个页面,需要在插件嵌入的页面,增加一个 dom 元素,并把这个插件的 content 页面,渲染进去。

1
2
3
4
5
6
7
8
9
10
11
12
13
import { createApp } from 'vue'
import app from './components/app.vue'

joinContent(app)

function joinContent (element) {
const div = document.createElement('div')
div.id = 'joinContentApp'
document.body.appendChild(div)
console.log(div)
createApp(element).mount('#joinContentApp')
}

解析:

  1. 引入 vue3createApp
  2. 引入 app 组件
  3. 创建一个 idjoinContentAppdom 元素,把此元素插入 body 中,并把应用实例挂载到此 dom

5、background 文件夹

此文件夹是对应的 background.js 文件,可以只写一个简单的日志打印即可

1
2
console.log('this is background main.js')

6、yarn run build 打包

此时,先进行 run build 打包,如果你报错了,是 eslint 报错,可以进行在 .eslintrc.js 文件中进行配置,添加一些我常用的 eslint 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
module.exports = {
root: true,
env: {
node: true
},
extends: [
'plugin:vue/vue3-essential',
'@vue/standard'
],
parserOptions: {
parser: 'babel-eslint'
},
rules: {
"generator-star-spacing": "off",
"object-curly-spacing": "off",
"no-var": "error",
"semi": 0,
"eol-last": "off",
"no-tabs": "off",
"indent": "off",
"quote-props": 0,
"no-mixed-spaces-and-tabs": "off",
"no-trailing-spaces": "off",
"arrow-parens": 0,
"spaced-comment": "off",
"space-before-function-paren": "off",
"no-empty": "off",
"no-else-return": "off",
"no-unused-vars": [2, {"vars": "all", "args": "after-used"}],
"no-console": "off",
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
}
}

然后在进行打包

此时的文件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
.
├── README.md
├── babel.config.js
├── dist
│ ├── assets
│ │ ├── images
│ │ │ ├── icon128.png
│ │ │ ├── icon16.png
│ │ │ └── icon48.png
│ │ └── logo.png
│ ├── js
│ │ ├── background.js
│ │ ├── chunk-vendors.fa86ccee.js
│ │ ├── content.js
│ │ ├── inject.js
│ │ └── popup.js
│ ├── manifest.json
│ └── popup.html
├── package.json
├── src
│ ├── assets
│ │ ├── images
│ │ │ ├── icon128.png
│ │ │ ├── icon16.png
│ │ │ └── icon48.png
│ │ └── logo.png
│ ├── background
│ │ └── main.js
│ ├── content
│ │ ├── components
│ │ │ └── app.vue
│ │ └── main.js
│ ├── main.js
│ ├── plugins
│ │ ├── inject.js
│ │ └── manifest.json
│ ├── popup
│ │ ├── components
│ │ │ └── app.vue
│ │ ├── index.html
│ │ └── main.js
│ └── utils
├── vue.config.js
└── yarn.lock

此时我们可以看到 dist 文件夹下已经按照我们需要的内容进行打包了,但是没有 css 文件夹那是因为我们没有写入 css

7、引入 less

我们写页面少不了使用 css,现在都是使用预处理器,我比较倾向于 less,所以我使用 less、less-loader

1. 引入 less less-loader

1
2
yarn add less less-loader --dev

2. 修改 app.vue 文件

然后我们在 content/components/app.vuepopup/components/app.vue 文件中写入 css 样式

content/components/app.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<template>
<div class="content_page">
content_page
<div class="content_page_main">
content_page_main
</div>
</div>
</template>

<script>
export default {

}
</script>

<style lang="less" scoped>
.content_page{
color: red;
position: fixed;
z-index: 100001;
right: 10px;
bottom: 10px;
.content_page_main{
color: green;
}
}
</style>

popup/components/app.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<template>
<div class="popup_page">
this is popup page
<div class="popup_page_main">
this is popup page main
</div>
</div>
</template>

<script>
export default {

}
</script>

<style lang="less" scoped>
.popup_page{
color: red;
.popup_page_main{
color: green;
}
}
</style>

3. yarn run build 打包

此时 tree dist 查看 dist 文件夹内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
dist
├── assets
│ ├── images
│ │ ├── icon128.png
│ │ ├── icon16.png
│ │ └── icon48.png
│ └── logo.png
├── css
│ ├── content.css
│ └── popup.css
├── js
│ ├── background.js
│ ├── chunk-vendors.4f73d0d4.js
│ ├── content.js
│ ├── inject.js
│ └── popup.js
├── manifest.json
└── popup.html

我们可以看到,我们通过 vue.config.js 文件配置的内容都已经生成到 dist 文件夹中了

四、导入插件项目

1、在谷歌拓展程序中打开我们的插件

点击 加载已解压的拓展程序,选择我们的 dist 文件夹,此时我们的插件就被引入进来了

![插件](15000字大章带你一步一步使用Vue3开发chrome浏览器插件 掘金年度征文.image/7d9444b4f858469b9c9c62fcb6d52b77tplv-k3u1fbpfcp-watermark.awebp)

2、打开 淘宝首页

1. 为什么打开淘宝首页呢?

因为我们在 manifest.json 中的 content_scripts 中配置 "matches": ["https://*.taobao.com/*"]

2. 点击右上角的插件 icon

![popup](15000字大章带你一步一步使用Vue3开发chrome浏览器插件 掘金年度征文.image/1803339a7dd9420582a654e4ddb75c93tplv-k3u1fbpfcp-watermark.awebp)

我们可以看到我们的 popup 页面已经我们给它写的样式

3、我们的 content 文件呢?

我们在 content 文件中配置的 main.jsapp.vue 也写入了样式,也挂载到 dom 实例上了,但是为什么没有渲染,也没有打印

1
2
3
4
5
6
7
8
function joinContent (element) {
const div = document.createElement('div')
div.id = 'joinContentApp'
document.body.appendChild(div)
console.log(div)
createApp(element).mount('#joinContentApp')
}

我们在 js 文件中有个 console.log 日志输入,但是可以看到淘宝页面的控制台并没有输入

1. 为什么没有输出

因为我们是用的 vue 开发项目,在 main.js 中是用的 vue 开发,所以我们得引入 vue 文件,得在 content 中引入 vue 才可以

2. 解决方法,引入 vue

我们可以看到 dist 文件夹下面有一个 chunk-vendors.4f73d0d4.js,这个就是 vue 打包之后的文件,我们先在 dist 中的 manifest.json 文件先把它引入进来先看下

dist/manifest.json 文件下的 content_scripts 字段

1
2
3
4
5
6
7
8
9
"content_scripts": [
{
"matches": ["https://*.taobao.com/*"],
"css": ["css/content.css"],
"js": ["js/chunk-vendors.4f73d0d4.js", "js/content.js"],
"run_at": "document_idle"
}
],

此时,拓展程序页面刷新插件,并刷新淘宝首页,可以看到

![控制台输出](15000字大章带你一步一步使用Vue3开发chrome浏览器插件 掘金年度征文.image/c4fd576ab93b4aac94c9d1de8735f3aftplv-k3u1fbpfcp-watermark.awebp) content

此时可以看到我们的 content 文件已经输出了。

4、background.js 文件

还记得我们在 background 文件夹下中的 main.js 写入日志输出吗?

1
2
console.log('this is background main.js')

我们打开拓展程序,找到我们的插件,点击 背景页 按钮

![在这里插入图片描述](15000字大章带你一步一步使用Vue3开发chrome浏览器插件 掘金年度征文.image/62c2dcefa41b4933967355d8dc38d6fatplv-k3u1fbpfcp-watermark.awebp)

此时,背景页的控制台就出来,我们可以看到我们的日志输出,好像并没有输出我们的日志???

1. 原因

此问题的原因和上面的 content 文件的原因是一致的,也是没有引入 vue 文件

2. 解决,引入 vue

dist/manifest.jsonbackground 字段

1
2
3
4
"background": {
"scripts": ["js/chunk-vendors.4f73d0d4.js", "js/background.js"]
},

此时,刷新插件,可以看到日志输出

![background日志输出](15000字大章带你一步一步使用Vue3开发chrome浏览器插件 掘金年度征文.image/82f92426d33d4d40a6dd5d073a5204b2tplv-k3u1fbpfcp-watermark.awebp)

5、引入 inject 文件

1. 首先我们在 plugins/inject.js 文件中输出日志

1
2
console.log('this is my inject.js')

2. 然后在 content/main.js 文件中引入 inject.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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')
}

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)
})
}

3. yarn run build 打包

此时打包可以发现报错了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
yarn run v1.22.10
$ vue-cli-service build

⠼ Building for production...

ERROR Failed to compile with 1 error 上午11:12:48

error in ./src/content/main.js

Module Error (from ./node_modules/thread-loader/dist/cjs.js):

/Users/guoqiankun/work/chromePlugin/my-vue3-plugin/src/content/main.js
21:16 error 'chrome' is not defined no-undef

1 problem (1 error, 0 warnings)


ERROR Build failed with errors.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

error 'chrome' is not defined no-undef

我们在上面插入 jnject 文件使用的 chrome 未定义,那我们就定义一下;

4. 修改 .eslintrc.js 文件

1
2
3
4
5
6
7
8
root: true,
globals: {
chrome: true,
},
env: {
node: true
},

增加一个 globals 字段,里面 chrome: true

然后在进行 yarn run build 打包

然后我们可以看到 dist/manifest.json 文件中的 content_scriptsbackground/scripts 已经没有引入 vue 了,所以我们不能在 dist 文件夹中修改,我们要在 plugins/manifest.json 文件中修改

但是我们可以看到,我们每次打包生成的 chunk-vendors.js 会跟一个 hash,因为我们此时没有修改别的文件,所以 hash 后缀没有变化,但是如果我们改了内容之后在 yarn run build 呢?此时 hash 就会变化,总不能在改一次 manifest.json 再打一次包吧…

6、修改 vue.config.js 文件,让打包时生成的 chunk-vendors.js 不带 hash

1. 配置 chainWebpack 字段

配置 chainWebpack 字段,对 config 内容进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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()
}
}
}

2. 修改 plugin/manifest.json 文件

在此文件中引入 chunk-vendors.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
plugin/manifest.json
"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_scripts": [
{
"matches": ["https://*.taobao.com/*"],
"css": ["css/content.css"],
"js": ["js/chunk-vendors.js", "js/content.js"],
"run_at": "document_idle"
}
],

3. yarn run build 打包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
dist
├── assets
│ ├── images
│ │ ├── icon128.png
│ │ ├── icon16.png
│ │ └── icon48.png
│ └── logo.png
├── css
│ ├── content.css
│ └── popup.css
├── js
│ ├── background.js
│ ├── chunk-vendors.js
│ ├── content.js
│ ├── inject.js
│ └── popup.js
├── manifest.json
└── popup.html

4. 刷新插件,刷新页面

属性插件,刷新页面,之后可以看到

![console](15000字大章带你一步一步使用Vue3开发chrome浏览器插件 掘金年度征文.image/7b145c39c6514f8a8e56ab5c5653e513tplv-k3u1fbpfcp-watermark.awebp)

![inject.js](15000字大章带你一步一步使用Vue3开发chrome浏览器插件 掘金年度征文.image/8c87698ecb7244939db5a38fa50b5a17tplv-k3u1fbpfcp-watermark.awebp)

五、热加载

此时我们的 vue 开发插件项目已经基本上可以了,剩下的就是按照需求开发插件页面,按照需求添加 manifest.json 字段即可,但是我们不能每一次想看样式就打个包,然后属性插件,刷新页面看下,这样也可以,但是我们是开发,这样效率比较低,我不服…

所以我们需要添加一下热加载

1、热加载

utils 文件夹下创建 hotReload.js 文件

写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// 加载文件

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;

2、引入

bckground/main.js 中 引入

1
2
3
4
5
import hotReload from '@/utils/hotReload'

hotReload()
console.log('this is background main.js')

3、修改 package.json 中的 scripts

1. 增加一个 watch 用来监听打包

1
2
3
4
5
6
7
"scripts": {
"watch": "vue-cli-service build --watch",
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},

2. 此时运行 yarn run watch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
yarn run v1.22.10
$ vue-cli-service build --watch

⠙ Building for development...

DONE Compiled successfully in 3956ms 上午11:39:00

File Size Gzipped

dist/js/chunk-vendors.js 668.79 KiB 122.48 KiB
dist/js/content.js 26.47 KiB 3.71 KiB
dist/js/popup.js 26.23 KiB 3.55 KiB
dist/js/background.js 15.57 KiB 3.30 KiB
dist/js/inject.js 0.04 KiB 0.05 KiB
dist/css/content.css 0.18 KiB 0.14 KiB
dist/css/popup.css 0.11 KiB 0.09 KiB

Images and other types of assets omitted.

DONE Build complete. Watching for changes...

可以看到一直在监听改变

3. 然后我们刷新插件和页面

发现有一个报错

1
2
Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' blob: filesystem:".

![报错](15000字大章带你一步一步使用Vue3开发chrome浏览器插件 掘金年度征文.image/07f43ebfa4dc4034b8d55b97b38d89edtplv-k3u1fbpfcp-watermark.awebp)

4. 按照错误解决问题:

plugins/manifest.json 中添加:

1
2
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",

5. 重新运行 yarn run bild

6. 清除插件错误、刷新插件、淘宝页面

4、修改 content/components/app.vue 文件

1
2
3
4
5
6
7
8
9
10
11
12
<template>
<div class="content_page">
content_page
<div class="content_page_main">
content_page_main
</div>
<div class="content_page_footer">
content_page_footer
</div>
</div>
</template>

保存

然后发现插件自动刷新、浏览器页面自动刷新。

此时浏览器页面右下角我们新加的内容就展示在上面了。

![在这里插入图片描述](15000字大章带你一步一步使用Vue3开发chrome浏览器插件 掘金年度征文.image/5abb076de80e4444881116188bcf21f3tplv-k3u1fbpfcp-watermark.awebp)

六、总结

  1. 使用 vue 开发插件,要先想一下我们要做成什么样
  2. 针对性的修改对应的文件,按照我们的需求去配置
  3. 遇到问题先想一下哪一步的问题,为什么出现,可以自己先想想,最后在寻求帮助
  4. 完结🎉🎉🎉

七、源码地址

github地址:https://github.com/18055975947/my-vue3-plugin

码云地址:https://gitee.com/guoqiankun/my-vue3-plugin

参考资料