后面,咱们曾经理解了各页面之间的通信. 权限等一系列的操作. 曾经把握了根本能够开发chrome扩大的技能. 剩下的就是须要用到某些api查看一下api文档就能开发了.对于当代前端来说. 咱们仅仅用原生的js和html来写咱们的代码. 这样就提现不出咱们的价值了. 这里咱们就依据之前的一些代码. 来解决咱们的古代框架vue来开发咱们的插件.
技术栈
这里咱们所须要应用的技术栈如下
scsswebpack5vue3vue-router4vant3
这里咱们应用的都是最新的版本. 就是要shuai
布局打包后的目录构造
首先,咱们得想明确咱们须要打包成什么样的目录. 咱们依据之前的示例. 咱们定义出如下的目录
background.jsmanifest.jsonassetscontent.jsoption.htmloption.jspopup.htmlpopup.jsdevtool.htmldevtool.jsdevtool/panel.htmldevtool/panel.jsdevtool/sidebar.htmldevtool/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 --savenpm 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.jsconst { merge } = require('webpack-merge')var base_config = require('./base')// 重新处理一下.module.exports = merge(base_config, { mode: 'production', // 指定入口. watch: true})// prod.jsconst { 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. 我会定期查看代码.