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插件教程终-使用vue来开发chrome插件】如果大家觉得chrome的开发模板在使用中. 有问题. 请前往github创建issue. 我会定期查看代码.

    推荐阅读