乐趣区

关于chrome:chrome插件教程终使用vue来开发chrome插件

后面,咱们曾经理解了各页面之间的通信. 权限等一系列的操作. 曾经把握了根本能够开发 chrome 扩大的技能. 剩下的就是须要用到某些 api 查看一下 api 文档就能开发了. 对于当代前端来说. 咱们仅仅用原生的 js 和 html 来写咱们的代码. 这样就提现不出咱们的价值了. 这里咱们就依据之前的一些代码. 来解决咱们的古代框架 vue 来开发咱们的插件.

技术栈

这里咱们所须要应用的技术栈如下

scss
webpack5
vue3
vue-router4
vant3

这里咱们应用的都是最新的版本. 就是要 shuai

布局打包后的目录构造

首先,咱们得想明确咱们须要打包成什么样的目录. 咱们依据之前的示例. 咱们定义出如下的目录

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

这是咱们打包过后最终的目录. 有了一个大略的构造过后,咱们就开始来解决咱们的代码.

初始目录

咱们布局好目录过后, 就须要解决咱们的我的项目门路了. 随便建设一个目录. 而后应用如下命令.

npm init

如果没有 node. 请自行装置. 我这里就不做过多的形容了. 执行过后. 就有一堆输出协定之类的货色. 所有操作结束过后. 就有了咱们的 package.json. 最次要的就是初始化这个 json 文件. 咱们好不便本人操作. 首先装置咱们的根底. webpack.

npm install webpack --save-dev

实现过后. 咱们就须要对咱们的我的项目目录得有所布局了.

布局我的项目目录

首先. 咱们的最外层必定都的是一些全局型的配置和目录. 例如 babel.config.js. 例如 package.json 这些都在最外层. 咱们的外围代码都放到 src 目录上面. 有一个专门用于打包文件代码存储的中央. 咱们 chrome 的入口必定也得有一个. 放到 src 上面. 有了以上根本的货色. 就能失去咱们大略的文件目录是什么样子的了. 我的文件目录如下.

build // webpack 打包配置
dist // 打包过后的文件
node_modules // npm 模块
public // 根底文件
src // 外围代码
src/entry // chrome 所有页面的入口
src/routers // 咱们本人定义的路由.
src/views // chrome 的所有页面
src/content.js // content script 入口
src/background.js // service worker 入口
utils // 工具类 

webpack

首先咱们的定义咱们 webpack 打包时的入口. 因为咱们有开发时候的环境和生成环境. 咱们咱们 build 文件夹上面就有以下 3 个文件

base.js // 根底配置文件等
dev.js // 开发模式下的配置
prod.js // 生产环境时的配置.

首先. 咱们要定义咱们的 base.js. 这外面有几个点. 首先. 咱们的入口文件是有多个. 及 src/entry 上面则有多个. 所以咱们得定义咱们的入口. 如下

/* global module, require, __dirname */
const path = require('path')
const utils = require('../utils/util')
// 导出默认的配置信息. 其余的先不论.
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)
    }
  })

  // 获取所有的文件夹上面所有的入口文件.
  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,}

定义完过后. 咱们的 enrty 入口大略就是这样的样子.

{"popup": "src/entry/popup/main.js", // 绝对路径}

有了这个过后. 就相当于咱们有了一个 main.js 的入口. 同时. 咱们须要减少 webpack 的输入的配置. 此外. 咱们还要告知 webpack 能解决哪些文件. 在 base.js 外面退出上面的代码.

  resolve: {
    // 能够解决 js,json,vue 文件
    extensions: ['.js', '.vue', '.json']
  },

另外. 咱们还得对一些文件依照怎么的格局进行解决. 比方说.vue

引入 vue

执行一下命令

npm install vue@next vue-router@next --save
npm install vue-loader@next

在 build/base.js 告知怎么解决 vue 文件.

const {VueLoaderPlugin} = require('vue-loader')

// 导出时减少如下配置
module: {
    rules: [
      // 指定 vue 文件应用 vue-loader 来进行解析.
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      }
    ]
},
plugins: [new VueLoaderPlugin()
]

这样就能解决咱们的 vue.

引入 babel

为撒咱们须要 babel. 是因为各个浏览器对 js 的兼容性都不是特地好. 所以咱们须要对兼容性进行解决. 先执行命令

npm install @babel/preset-env babel-loader babel-plugin-import core-js -D

而后在 build/base.js 中 module.rules 外面减少如下配置

{
        test: /\.js$/,
        loader: 'babel-loader'
}

启用对 babel 的加载.

引入 css/scss

在 css 中. 能够应用 style-loader 来间接打包 css. 然而咱们要应用 scss. 所以. 咱们就须要引入 sass-loader. 同时还要解决兼容 postcss. 所以. 执行如下命令

npm install -D css-loader node-sass sass-loader postcss-loader 

而后同 babel 一样. 在 build/base.js 中的 rules 减少如下配置

{test: /\.(css|scss|sass)$/,
        use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader']
}

这样就减少了对 css 的解决.

引入 vant

间接应用

npm i vant@3

而后因为咱们应用了 babel. 间接应用按需加载形式. 在跟门路创立 babel.config.js. 退出如下代码.

// babel.config.js
/* global module */
const presets = []
const plugins = [
  [
    'import',
    {
      'libraryName': 'vant',
      'libraryDirectory': 'es',
      'style': true
    }
  ]
]

module.exports = {
  plugins,
  presets
}

即可实现援用.

解决非凡入口

后面咱们应用了 utils.findEntry 函数来解决入口. 然而对于咱们非凡的. 比方 sidebar 和 panel. 这种用于控制台的入口. 须要在 devtools.js 外面进行创立. 所以. 咱们就将 devtools.js 给独自打包. 跟 content, 所以批改咱们的 findEntry 函数. 如下

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

  // 获取所有的文件夹上面所有的入口文件.
  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
}

同时. 解决了 content. background.devtools. 这几个还得解决非凡的 panel 和 sidebar. 就间接放到 entries 外面就能够. 然而仅仅这样是不行的. 咱们还须要将 html 代码给打包解决. 这咱们应用了 html-webpack-plugin. 间接装置

npm i html-webpack-plugin -D

同时在 build/base.js 的 module.exports 对象的 plugins 外面退出如下代码.

// 
[new VueLoaderPlugin(),
].concat(utils.genHtmlPlugins())
// 在 utils.genHtmlPlugins
// 这里要做一些非凡解决能力进行打包. 对对应的 sidebar, panel 来进行打包. 次要是要注入代码才能够.
function genHtmlPlugins() {const entires = findEntry()
  const plugins = []
  const template = path.dirname(__dirname) + '/public/extension.html'
  const modules = Object.keys(entires)

  // 这里有问题. 须要从新改变一下就能够了.
  for (var index in modules) {const module_name = modules[index]
    const name = module_name.split('/').pop()

    if (['content', 'background'].indexOf(module_name) > -1) {continue}

    // 打包对应的模块到指定的目录. 其余就不打包了.
    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 // 主动删除正文}
    }))
  }

  return plugins
}

这样咱们就能打包好对应的代码了.

复制公共文件

咱们打包好根本代码过后, 还须要将 public 外面的公共文件进行复制. 间接应用 copy-webpack-plugin. 先执行命令

npm i copy-webpack-plugin -D

依照如下形式应用

plugins: [
    // .vue 文件打包
    new VueLoaderPlugin(),
    // 间接将原始文件复制过来.
    new CopyWebpackPlugin({
      patterns: [
        // 将 src/manifest.json 间接复制过来.
        {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) {
            // 如果不存在 src/entry/panel 和 src/entry/sidebar. 就不复制了.
            if (!fs.statSync(app_path + '/src/entry/panel').isDirectory() && !fs.statSync(app_path + '/src/entry/sidebar').isDirectory()) {return false}
          }

          return true
        } }
      ]
    })
  ].concat(utils.genHtmlPlugins())

这样. 打包就好了.

额定

不过有一些不是特地完满. 打包后会生成.LISENCE.txt 这种文件. 间接应用 terser-webpack-plugin 插件. 先装置

npm i terser-webpack-plugin -D

而后依照如下应用

const TerserPlugin = require('terser-webpack-plugin')

// 在 module.exports 对象中减少
optimization: {
    minimizer: [new TerserPlugin({extractComments: false})]
 }

另外. 打包好的货色如果间接将 dist 文件夹当成扩大来解决的话. 会间接报错. 这是因为 webpack 如果是 dev 开发模式. 会以 eval 这种模式来加载 js. 在 chrome 外面是不容许的. 所以咱们须要做非凡解决. 这里是我的 build/dev.js 和 build/prod.js

// dev.js
const {merge} = require('webpack-merge')
var base_config = require('./base')

// 重新处理一下.
module.exports = merge(base_config, {
  mode: 'production',
  // 指定入口.
  watch: true
})

// prod.js
const {merge} = require('webpack-merge')
var base_config = require('./base')

// 重新处理一下.
module.exports = merge(base_config, {mode: 'production'})

这里都应用 production 来进行打包. 这样就配置好了. 不过根底的入口代码,eslint 等没提供. 这些的话. 请查看源代码.

最初

根底的也就完了. 对于大家来应该曾经入门. 剩下的就是靠文档来解决了. 例如不晓得 api 的. 就须要去查找一下 chrome 的 api.

// 源代码
https://github.com/AdolphGithub/crx-template
// api 文档
https://developer.chrome.com/docs/extensions/reference/

如果大家感觉 chrome 的开发模板在应用中. 有问题. 请返回 github 创立 issue. 我会定期查看代码.

退出移动版