I’ve written an article about chrome plugin development, which takes you from zero to one, but it’s not a third-party framework like Vue/React, it’s a mixture of native and jquery, but it’s a bit tricky to develop the front end, so here’s how to use Vue to develop the plugin.
The article is a little long, it is recommended to collect and then step by step to carry out practical operation
Making address: https://github.com/18055975947/my-vue3-plugin
Yards cloud address: https://gitee.com/guoqiankun/my-vue3-plugin
Create a Vue project
Vue create my-vue3-plugin vue create my-vue3-plugin vue create my-vue3-plugin vue create my-vue3-plugin
Error Couldn't find package "postcsS-Normalize-string @^4.0.2" Required by "cssnano-preset-default@^4.0.0" on the" NPM" registry. Error: I Couldn't find package "@vue/cli-overlay@^4.5.9" required by "@vue/cli-service@~4.5.0" on the "NPM" registrie. 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: yarnCopy the code
You can refer to the vue3. X project error article using vue-cli to handle the file directory:
│ ├─ public │ ├── favicon.ico │ ├─ index.html │ ├─ SRC │ ├─ app.vue │ ├── assets │ ├─ logo.png │ ├── components │ ├── HelloWorld. Vue │ ├─ mainCopy the code
Ii. Modification items
Because we are developing the Chrome plugin project, this generated vue project has many folders and files that we do not need, so we need to deal with:
- Create it in the root directory
vue.config.js
的vue
Configuration file; - the
src
Under the folderApp. Vue, components
Folder deletion - in
assets
Create in fileimages
Folder and inimages
Folder to add your own plug-inicon
- Example Delete files in the root directory
public
folder - in
src
Create a folderBackground, content, plugins, popup, utils
folder - in
background
Create a foldermain.js
- in
content
Create a foldercomponents
The folder andmain.js
.components
Create a folderapp.vue
- in
plugins
Create a folderInject. Js, the manifest. Json
file - in
popup
Create a foldercomponents
foldermain.js
和index.html
.components
Create a folderapp.vue
Step 2:
vue.config.js
是vue
The package, run, etc. configuration file for the project that we need to generate the plug-in project. This file needs to be created and configured- Delete redundant files, we only need one in the plugin at the moment
popup
Pages don’t need to be externalapp.vue
And the component - Your own plug-in
icon
According to the16 * 16, 48 * 48, 128 * 128
Three dimension - Don’t need
public
Inside the folderindex.html
- Create what our plug-in needs
Background. js, content.js, popup page, plug-in configuration
等 - create
background.js
file - create
content.js
file - create
Popup. Js, popup. HTML
file
At this point the file directory:
. ├ ─ ─ the README. Md ├ ─ ─ Babel. Config. Js ├ ─ ─ package. The json ├ ─ ─ the SRC │ ├ ─ ─ assets │ │ ├ ─ ─ images │ │ │ ├ ─ ─ icon128. PNG │ │ │ ├ ─ ─ PNG │ ├─ ├─ ch.png │ ├─ ├─ ch.png │ ├─ ├─ class.js │ ├─ content │ ├─ components │ │ ├─ ch.htm │ └ ─ ─ app. Vue │ │ └ ─ ─ the main, js │ ├ ─ ─ the main, js │ ├ ─ ─ the plugins │ │ ├ ─ ─ inject. Js │ │ └ ─ ─ the manifest. Json │ ├ ─ ─ popup │ │ ├ ─ ─ │ │ ├── index.html │ │ ├─ main.js │ ├─ utils ├─ vue.config.js │ ├─ yarn.htmCopy the code
3. Configure items
1.plugins/manifest.json
Configuration file
First, configure the manifest.json file, and then configure the vue.config.js file according to the manifest.json file
{
"manifest_version": 2."name": "my-vue3-plugin"."description": "Chrome plugin based on vue3. X version"."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"]}Copy the code
Resolution:
browser_action
In thedefault_popup
Configured to andmanifest.json
File-levelpopup.html
browser_action
In thedefault_icon
Configured toassets/images/icon48.png
background
Configured tojs/background.js
icons
File to configure the projectcontent_scripts
Configure the correspondingJs, CSS, and matches
web_accessible_resources
Configure the built-in web pagejs/inject.js
2, configuration,vue.config.js
file
We need to configure the js folder, CSS folder, popup. HTML file, background.js file, inject. Js file, content.js file, etc. The CSS file;
Add 1.copy-webpack-plugin
Module for copying files
We need to copy the files from the plugins folder into the packaged dist file
Installation:
Yarn add [email protected] - devCopy the code
2. File content
const CopyWebpackPlugin = require("copy-webpack-plugin");
const path = require("path");
// Copy the file to the specified directory
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")}];// Copy the plug-in
const plugins = [
new CopyWebpackPlugin({
patterns: copyFiles
})
];
// Page file
const pages = {};
// Configure the popup.html page
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.// Configure content.js background.js
configureWebpack: {
entry: {
content: "./src/content/main.js".background: "./src/background/main.js"
},
output: {
filename: "js/[name].js"
},
plugins
},
/ / configuration content. the CSS
css: {
extract: {
filename: "css/[name].css"}}}Copy the code
3. The resolution:
copyFiles
Is the field of the copy filepages
Is a file field that configures multiple pagesconfigureWebpack
To configure thecontent.js
,background.js
filecss
configurationcontent.css
file
3,popup
Folder modification
As we know from the configuration above, the popup folder is used to generate the popup. HTML file for browser_action, so let’s write to the popup folder
1. popup/index.html
Because this is an HTML file, we just need to copy the contents of the index.html file in the public folder of the project generated by vue Create, and remove the Favicon by the way. Let’s change the title
<! DOCTYPEhtml>
<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>
Copy the code
2. popup/main.js
This is the entry configuration file for the vue project, just copy it from main.js under SRC, don’t forget to change the case
import { createApp } from 'vue'
import app from './components/app.vue'
createApp(app).mount('#app')
Copy the code
3. popup/components/app.vue
This file is a normal VUE file, according to the usual write vUE project development
<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>
Copy the code
4,content
Folder modification
Under the Content folder is the corresponding Content.js for the Chrome plugin, which can render the page inside the embedded page, or we can develop it with Vue
1. content/components/app.vue
Normal VUE development
<template>
<div class="content_page">
content_page
<div class="content_page_main">
content_page_main
</div>
</div>
</template>
<script>
export default{}</script>
<style>
</style>
Copy the code
2. content/main.js
Main. js is an important file, which is used to introduce vue components and develop content pages with Vue. Therefore, for this page, we need to add a DOM element to the embedded page of the plug-in and render the content page of the plug-in.
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')}Copy the code
Resolution:
- The introduction of
vue3
的createApp
- The introduction of
app
component - To create a
id
为joinContentApp
的dom
Element, insert this elementbody
And mount the application instance theredom
上
5,background
folder
This folder is the corresponding background.js file, which can be written as a simple log print
console.log('this is background main.js')
Copy the code
6,yarn run build
packaging
If you have an esLint error, you can configure it in.eslintrc.js and add some of my usual ESLint configurations
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'}}Copy the code
And then we’re packing
The contents of the file at this time
. ├ ─ ─ the README. Md ├ ─ ─ Babel. Config. Js ├ ─ ─ dist │ ├ ─ ─ assets │ │ ├ ─ ─ images │ │ │ ├ ─ ─ icon128. PNG │ │ │ ├ ─ ─ icon16. PNG │ │ │ └ ─ ─ icon48. PNG │ │ └ ─ ─ logo. The PNG │ ├ ─ ─ js │ │ ├ ─ ─ background. Js │ │ ├ ─ ─ the chunk - vendors. Fa86ccee. Js │ │ ├ ─ ─ content. js │ │ ├ ─ ─ inject. Js │ │ └ ─ ─ popup. Js │ ├ ─ ─ the manifest. Json │ └ ─ ─ popup. The HTML ├ ─ ─ package. The json ├ ─ ─ the SRC │ ├ ─ ─ assets │ │ ├ ─ ─ Images │ │ │ ├ ─ ─ icon128. PNG │ │ │ ├ ─ ─ icon16. PNG │ │ │ └ ─ ─ icon48. PNG │ │ └ ─ ─ logo. The PNG │ ├ ─ ─ background │ │ └ ─ ─ main. Js │ ├ ─ ─ the content │ │ ├ ─ ─ components │ │ │ └ ─ ─ app. Vue │ │ └ ─ ─ the main, js │ ├ ─ ─ the main, js │ ├ ─ ─ the plugins │ │ ├ ─ ─ inject. Js │ │ └ ─ ─ the manifest. Json │ ├ ─ ─ popup │ │ ├ ─ ─ components │ │ │ └ ─ ─ app. Vue │ │ ├ ─ ─ index. The HTML │ │ └ ─ ─ the main, js │ └ ─ ─ utils ├ ─ ─ Vue. Config. Js └ ─ ─ yarn. The lockCopy the code
At this point we can see that the dist folder is already packaged as we want it to be, but there’s no CSS folder because we didn’t write CSS
7, introducingless
I prefer less, so I use less and less-loader
1. The introduction ofless less-loader
yarn add less less-loader --dev
Copy the code
2. Modifyapp.vue
file
Then we are in the content/components/app. Vue and popup/components/app. Vue file write CSS styles
content/components/app.vue
<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>
Copy the code
popup/components/app.vue
<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>
Copy the code
3. yarn run build
packaging
At this point tree dist looks at the contents of the dist folder
Dist ├ ─ ─ assets │ ├ ─ ─ images │ │ ├ ─ ─ icon128. PNG │ │ ├ ─ ─ icon16. PNG │ │ └ ─ ─ icon48. PNG │ └ ─ ─ logo. The PNG ├ ─ ─ CSS │ ├ ─ ─ Content. CSS │ └ ─ ─ popup. CSS ├ ─ ─ js │ ├ ─ ─ background. Js │ ├ ─ ─ the chunk - vendors. 4 f73d0d4. Js │ ├ ─ ─ content. js │ ├ ─ ─ inject. Js │ ├── popup.js ├── manifestCopy the code
As you can see, the contents we configured through the vue.config.js file have been generated into the dist folder
Iv. Import the plug-in project
1. Open our plugin in Google Extensions
Click Load the unzipped extension, select our dist folder, and our plug-in is introduced
2, open theTaobao homepage
1. Why open the home page of Taobao?
Because we configure “matches” in content_scripts in manifest.json: [“https://*.taobao.com/*”]
2. Click the plugin in the upper right cornericon
We can see ourpopup
The page has the style we gave it
3. Ourscontent
The file?
The main.js and app.vue we configured in the content file are also styled and mounted to the DOM instance, but why is it not rendered or printed
function joinContent (element) {
const div = document.createElement('div')
div.id = 'joinContentApp'
document.body.appendChild(div)
console.log(div)
createApp(element).mount('#joinContentApp')}Copy the code
We have a console.log log input in the JS file, but you can see that the console of the Taobao page has no input
1. Why no output
Since we use vue to develop the project, we use vue to develop main.js, so we need to introduce vue file, we need to introduce vue in the content
2. Solution, introductionvue
You can see under the dist folder there’s a change-vendors.4f73d0d4.js, and that’s what the vue package is, so let’s introduce it first in the manifest.json file in dist and take a look at it first
The content_scripts field in the dist/manifest.json file
"content_scripts": [{"matches": ["https://*.taobao.com/*"]."css": ["css/content.css"]."js": ["js/chunk-vendors.4f73d0d4.js"."js/content.js"]."run_at": "document_idle"}].Copy the code
At this time, expand the program page to refresh the plug-in, and refresh the Home page of Taobao, you can see
And then you can see ourcontent
The file has been printed.
4,background.js
file
Remember when we wrote the log output to main.js in the Background folder?
console.log('this is background main.js')
Copy the code
Let’s open the extension, find our plugin, and click the Background page button
At this point, the background page console appears, and we can see our log output.
Reason 1.
The reason for this problem is the same as for the content file above, which is that the vue file is not introduced
2. Solve, introducevue
Background field in dist/manifest.json
"background": {
"scripts": ["js/chunk-vendors.4f73d0d4.js"."js/background.js"]},Copy the code
At this point, refresh the plug-in and you can see the log output
5, the introduction ofinject
file
1. First we are inplugins/inject.js
Logs are generated in the file
console.log('this is my inject.js')
Copy the code
2. Then incontent/main.js
Import into fileinject.js
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)
})
}
Copy the code
3. yarn run build
packaging
At this point, the package can find that an error is reported
yarn run v1.22.10
$ vue-cli-serviceBuild ⠼ Buildingfor production...
ERROR Failed to compile with 1The error in the morning11: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.
Copy the code
error 'chrome' is not defined no-undef
Chrome doesn’t define what we’re going to insert the JNject file with, so let’s define it;
4. Modify.eslintrc.js
file
root: true.globals: {
chrome: true,},env: {
node: true
},
Copy the code
Add a globals field to chrome: true
Then package the yarn run build
Then we can see that content_scripts and background/scripts in dist/manifest.json are no longer imported into vUE, so we can’t change them in dist folder. We’ll do this in the plugins/manifest.json file
But as you can see, every time we package the chunk-pdF.js we generate, it will follow a hash, and since we are not modifying any other files at this time, the hash suffix will not change, but what if we change the content after the yarn run build? You can’t change the manifest. Json file and type it again.
6, modify,vue.config.js
File to be generated when packagingchunk-vendors.js
Don’t takehash
1. The configurationchainWebpack
field
Configure the chainWebpack field to process the Config content
module.exports = {
pages,
productionSourceMap: false.// Configure content.js background.js
configureWebpack: {
entry: {
content: "./src/content/main.js".background: "./src/background/main.js"
},
output: {
filename: "js/[name].js"
},
plugins
},
/ / configuration content. the 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()
}
}
}
Copy the code
2. Modifyplugin/manifest.json
file
Introduce chunk-pds.js into this file
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"}].Copy the code
3. yarn run build
packaging
Dist ├ ─ ─ assets │ ├ ─ ─ images │ │ ├ ─ ─ icon128. PNG │ │ ├ ─ ─ icon16. PNG │ │ └ ─ ─ icon48. PNG │ └ ─ ─ logo. The PNG ├ ─ ─ CSS │ ├ ─ ─ ├── ─ vendors. Js │ ├── Content.js │ ├─ Base.js │ ├─ Content.js │ ├─ base.js │ ├─ Content.js │ ├─ base.js │ ├─ html.pdf Popup.js ├── manifest. Json ├─ popup.htmlCopy the code
4. Refresh the plug-in and refresh the page
Properties plug-in, refresh the page, then you can see
5. Thermal loading
At this time, our VUE development plug-in project is basically ready, the rest is to develop the plug-in page according to the requirements, and add the manifest. Json field according to the requirements. However, we can’t just type a package every time we want to see the style, then attribute the plug-in, and refresh the page to have a look. This efficiency is relatively low, I refuse to accept…
So we need to add hot loading
1. Thermal loading
Create the hotreload.js file under the utils folder
write
// Load the file
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);
})
);
// Iterate through the plug-in directory, read the file information, and combine the file name and modification time into data
const timestampForFilesInDirectory = dir= >
filesInDirectory(dir).then(files= >
files.map(f= > f.name + f.lastModifiedDate).join()
);
// Refresh the current active page
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);
}
// Force the page refresh
window.chrome.runtime.reload(); }); };// Observe file changes
const watchChanges = (dir, lastTimestamp) = > {
timestampForFilesInDirectory(dir).then(timestamp= > {
// Loop to the watchChanges method if the file has not changed
if(! lastTimestamp || lastTimestamp === timestamp) {setTimeout(() = > watchChanges(dir, timestamp), 1000); // retry after 1s
} else {
// Force the page refreshreload(); }}); };const hotReload = () = > {
window.chrome.management.getSelf(self= > {
if (self.installType === 'development') {
// Get the plugin directory and listen for file changes
window.chrome.runtime.getPackageDirectoryEntry(dir= >watchChanges(dir)); }}); };export default hotReload;
Copy the code
2, the introduction of
Introduced in bckground/main.js
import hotReload from '@/utils/hotReload'
hotReload()
console.log('this is background main.js')
Copy the code
3, modify,package.json
In thescripts
1. Add onewatch
Used to listen to packages
"scripts": {
"watch": "vue-cli-service build --watch"."serve": "vue-cli-service serve"."build": "vue-cli-service build"."lint": "vue-cli-service lint"
},
Copy the code
2. Run the commandyarn run watch
yarn run v1.22.10
$ vue-cli-service build --watch
⠙ Building for development...
DONE Compiled successfully in 3956On the morning of ms11: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...
Copy the code
You can see that you’re listening for changes
3. Then we refresh the plugin and page
An error was found
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:".
Copy the code
4. Fix the problem according to the error:
In plugins/manifest.json add:
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'".Copy the code
5. Run the command againyarn run bild
6. Clear the plug-in error, refresh the plug-in and Taobao page
4, modify,content/components/app.vue
file
<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>
Copy the code
save
Then you find that the plug-in automatically refreshes and the browser page automatically refreshes.
At this point, in the bottom right corner of the browser page, our new content is displayed at the top.
Six, summarized
- use
vue
To develop plug-ins, we need to think about what we want to do - Modify corresponding files and configure according to our requirements
- When you encounter a problem, you should first think about which step of the problem and why it appears. You can think about it first and finally ask for help
- End 🎉 🎉 🎉
Seven, source address
Making address: https://github.com/18055975947/my-vue3-plugin
Yards cloud address: https://gitee.com/guoqiankun/my-vue3-plugin
The resources
- vue3 API
- Vue – cli configuration
Denver annual essay | 2020 technical way with me The campaign is under way…