The end of the chrome plug-in tutorial - using vue to develop chrome plug-ins

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

Keywords: Javascript Vue.js chrome vant chrome-extension

Added by bhi0308 on Tue, 18 Jan 2022 17:50:59 +0200