Previously, we have learned about the communication between pages Authority and a series of operations You have mastered the basic skills to develop chrome extensions The rest is that you need to use some APIs. You can develop them by looking at the api documentation For the contemporary front end We only use native js and html to write our code In this way, we can't withdraw our value Here we are based on some previous codes To handle our modern framework vue to develop our plug - ins
Technology stack
The technology stack we need to use here is as follows
scss webpack5 vue3 vue-router4 vant3
We use the latest version here Is to shuai
Planning the packaged directory structure
First, we have to figure out what kind of directory we need to package We are based on the previous example We define the following directory
background.js manifest.json assets content.js option.html option.js popup.html popup.js devtool.html devtool.js devtool/panel.html devtool/panel.js devtool/sidebar.html devtool/sidebar.js
This is our final catalog after packing After we have a general structure, we begin to deal with our code
Initial directory
After we plan the directory, we need to deal with our project path Create a directory at will Then use the following command
npm init
If there is no node Please install by yourself I won't describe it too much here After execution There's a bunch of input protocols and so on After all operations are completed We have our package json. The most important thing is to initialize the JSON file It's convenient for us to operate by ourselves First install our foundation webpack.
npm install webpack --save-dev
After completion We need to plan our project catalogue
Planning project directory
first. Our outermost layer must be some global configuration and directory For example, Babel config. js. For example, package JSON these are in the outermost layer Our core code is placed under the src directory There is a dedicated storage place for packaged file code We must have one at the entrance to chrome Put it under src With the above basic things We can get our approximate file directory My file directory is as follows
build // webpack packaging configuration dist // Packaged files node_modules // npm module public // Base document src // Core code src/entry // chrome entry to all pages src/routers // We define our own routing src/views // All pages of chrome src/content.js // content script entry src/background.js // service worker entry utils // Tool class
webpack
First, we define the entry when we package the webpack Because we have a development environment and a generation environment We have the following three files under the build folder
base.js // Basic configuration file, etc dev.js // Configuration in development mode prod.js // Configuration in production environment
first. We need to define our base js. There are several points in it first. We have multiple entry files And src/entry So we have to define our entrance as follows
/* global module, require, __dirname */ const path = require('path') const utils = require('../utils/util') // Export default configuration information Leave the rest alone module.exports = { entry: utils.findEntry(), output: { path: path.resolve(path.dirname(__dirname), 'dist'), filename: '[name].js', publicPath: './', clean: true, libraryTarget: 'umd' } } // utils/util /*global require, __dirname, module */ const path = require('path') const fs = require('fs') function findEntry() { const entry_path = path.dirname(__dirname) + '/src/entry' const modules = [] const entries = {} const dirs = fs.readdirSync(entry_path) dirs.forEach(function(item) { const full_path = path.join(entry_path, item) const stat = fs.statSync(full_path) if (stat.isDirectory()) { modules.push(full_path) } }) // Get all the entry files under all the folders if (modules.length <= 0) { return {} } modules.map(function(item) { const entry = fs.statSync(item + '/main.js') if (!entry.isFile()) { return } const info = path.parse(item + '/main.js') const dirname = info.dir.split('/').pop() entries[dirname] = item + '/main.js' }) return entries } module.exports = { findEntry: findEntry, }
After definition Our enrty entrance looks like this
{ "popup": "src/entry/popup/main.js", // Absolute path }
After having this It's like we have a main JS entry At the same time We need to increase the configuration of the output of webpack In addition We also want to tell webpack which files can be processed In base JS add the following code
resolve: { // Can handle js,json,vue files extensions: [ '.js', '.vue', '.json' ] },
In addition We also have to deal with the format of some documents For example vue
Introduce vue
Execute the command
npm install vue@next vue-router@next --save npm install vue-loader@next
In build / base JS tells how to handle vue files
const { VueLoaderPlugin } = require('vue-loader') // The following configurations are added during export module: { rules: [ // Specifies that the vue file is parsed using vue loader { test: /\.vue$/, loader: 'vue-loader' } ] }, plugins: [ new VueLoaderPlugin() ]
So we can deal with our vue
Introducing babel
We need babel for Caesar This is because the compatibility of various browsers with js is not particularly good So we need to deal with compatibility Execute the command first
npm install @babel/preset-env babel-loader babel-plugin-import core-js -D
Then in build / base Module. JS The following configuration is added to rules
{ test: /\.js$/, loader: 'babel-loader' }
Enables loading of babel
Introducing css/scss
In css You can use style loader to package css directly But we need to use scss So We need to introduce sass loader Also handle compatible postcss So Execute the following command
npm install -D css-loader node-sass sass-loader postcss-loader
Then the same as babel In build / base Add the following configuration to the rules in JS
{ test: /\.(css|scss|sass)$/, use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'] }
This increases the processing of css
Introducing vant
Direct use
npm i vant@3
Then because we used babel Direct use of on - demand loading Create babel in the following path config. js. Add the following code
// babel.config.js /* global module */ const presets = [] const plugins = [ [ 'import', { 'libraryName': 'vant', 'libraryDirectory': 'es', 'style': true } ] ] module.exports = { plugins, presets }
The reference is complete
Handling special inlet
We used utils The findEntry function handles entries But for our special For example, sidebar and panel This is used for the entrance of the console Need to be in devtools JS So We will devtools JS is packaged separately content, so modify our findEntry function as follows
function findEntry() { const app_path = path.dirname(__dirname) const entry_path = path.dirname(__dirname) + '/src/entry' const modules = [] const entries = {} const dirs = fs.readdirSync(entry_path) dirs.forEach(function(item) { const full_path = path.join(entry_path, item) const stat = fs.statSync(full_path) if (stat.isDirectory()) { modules.push(full_path) } }) // Get all the entry files under all the folders if (modules.length <= 0) { return {} } modules.map(function(item) { const entry = fs.statSync(item + '/main.js') if (!entry.isFile()) { return } const info = path.parse(item + '/main.js') let dirname = info.dir.split('/').pop() if (['panel', 'sidebar'].indexOf(dirname) > -1 ) { dirname = 'devtools/' + dirname } entries[dirname] = item + '/main.js' }) if (fs.statSync(app_path + '/src/content.js').isFile()) { entries['content'] = app_path + '/src/content.js' } if (fs.statSync(app_path + '/src/background.js').isFile()) { entries['background'] = app_path + '/src/background.js' } if (fs.statSync(app_path + '/src/devtools.js').isFile()) { entries['devtools'] = app_path + '/src/devtools.js' } return entries }
At the same time Processed content background. devtools. These have to deal with special panel s and sidebar s Just put it directly in the entries But this alone will not work We also need to package the html code We use html-webpack-plugin Direct installation
npm i html-webpack-plugin -D
At the same time, in build / base JS module Add the following code to the plugins of the exports object
// [ new VueLoaderPlugin(), ].concat(utils.genHtmlPlugins()) // In utils genHtmlPlugins // Here we need to do some special processing to package Package the corresponding sidebar and panel The main thing is to inject code function genHtmlPlugins() { const entires = findEntry() const plugins = [] const template = path.dirname(__dirname) + '/public/extension.html' const modules = Object.keys(entires) // There's a problem here It needs to be changed again for (var index in modules) { const module_name = modules[index] const name = module_name.split('/').pop() if (['content', 'background'].indexOf(module_name) > -1) { continue } // Package the corresponding module to the specified directory The rest will not be packed const filename = module_name + '.html' plugins.push(new HtmlWebpackPlugin({ // publicPath: './devtools', title: name, template: template, name: name, filename: filename, chunks: [module_name], inject: 'body', minify: { removeComments: true // Automatically delete comments } })) } return plugins }
So we can package the corresponding code
Copy public files
After we package the basic code, we also need to copy the public files in public Directly use copy webpack plugin Execute the command first
npm i copy-webpack-plugin -D
Use as follows
plugins: [ // . vue file packaging new VueLoaderPlugin(), // Copy the original file directly new CopyWebpackPlugin({ patterns: [ // Set Src / manifest JSON is copied directly { from: path.resolve(path.dirname(__dirname), 'src/manifest.json'), to: 'manifest.json' }, { from: path.dirname(__dirname) + '/public', filter: async(file_path) => { const app_path = path.dirname(__dirname) if (file_path.indexOf('extension.html') > 0) { return false } if (file_path.indexOf('devtools.html') > 0) { // If src/entry/panel and src/entry/sidebar are not present I won't copy it if (!fs.statSync(app_path + '/src/entry/panel').isDirectory() && !fs.statSync(app_path + '/src/entry/sidebar').isDirectory()) { return false } } return true } } ] }) ].concat(utils.genHtmlPlugins())
So Just pack it
additional
But some are not particularly perfect It will be generated after packaging LISENCE.txt this kind of file Use the terser webpack plugin directly Install first
npm i terser-webpack-plugin -D
Then use as follows
const TerserPlugin = require('terser-webpack-plugin') // In module Added in exports object optimization: { minimizer: [new TerserPlugin({ extractComments: false })] }
In addition If you directly treat the dist folder as an extension Will report an error directly This is because if webpack is dev development mode JS is loaded in the form of eval It is not allowed in chrome So we need special treatment Here are my build/dev.js and build/prod.js
// dev.js const { merge } = require('webpack-merge') var base_config = require('./base') // Deal with it again module.exports = merge(base_config, { mode: 'production', // Specify the entrance watch: true }) // prod.js const { merge } = require('webpack-merge') var base_config = require('./base') // Deal with it again module.exports = merge(base_config, { mode: 'production' })
production is used here for packaging This completes the configuration However, the basic entry code and eslint are not provided These are my words Please check source code.
last
The basic is over For everyone, you should have started The rest is handled by documents For example, I don't know the api You need to look up chrome api.
// source code https://github.com/AdolphGithub/crx-template // api documentation https://developer.chrome.com/docs/extensions/reference/
If you think the chrome development template is in use something the matter. Please go to github to create issue I will check the code regularly