记录-实现封装webpack
功能介绍:
只需安装一个包,即可开箱即用优化过的webpack配置,并且不用管webpack内部各种依赖包之间的 版本依赖关系 及 版本升级问题 (所有的跟webpack打包相关的,都封装在里面)
- (类似vue-cli-service命令)
@vue/cli-service
安装了一个名为vue-cli-service
的命令。你可以在 npm scripts 中以vue-cli-service
、或者从终端中以./node_modules/.bin/vue-cli-service
访问这个命令。 如:{
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build"
}
}
- (类似vue-cli-service命令)
除了有内置优化好的webpack配置外,还支持用户自定义webpack配置,只需写一个webpack.config.js就行,格式和webpack官方标准一样就行
本文目录大纲
- 封装webpack是什么意思?
- 封装webpack有什么好处?
- 如何封装webpack?
- 难点1:需要一个命令平台
- 难点2:需要在封装后的包内部,去执行webpack及webpack-dev-serve命令
- 难点3:如何往webpack的配置文件内去传参?(配置文件是webpack调用的,没法正常传参)
- 难点4:需要去读取用户写的webpack配置,然后覆盖默认的配置
以下将按照上面的大纲来介绍
1. 封装webpack是什么意思?
首先我们看看未封装前和封装后的样子
package.json
// 封装前
{
...
"devDependencies": {
"webpack": "^4.9.1",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.11.2",
"babel-loader": "^8.2.2",
"css-loader": "^2.1.1",
"file-loader": "^3.0.1",
"less-loader": "^5.0.0",
"postcss-loader": "^3.0.0",
"sass-loader": "^7.1.0",
"style-loader": "^0.23.1",
"vue-loader": "^15.7.2",
"url-loader": "^1.1.2",
// 还有一些webpack的 loader 和 plugin, 省略
}
}
// 封装后 只需加载一个包
{
...
"devDependencies": {
"@myCompany/fe": "^1.0.68",
}
}
封装的意思,其实就是,把所有和webpack有关的依赖,都封装在一个包里面,用webpack的能力只需要下载这一个包就行了。 接下来介绍好处
2. 封装webpack有什么好处?
为什么要封装呢?封装有什么好处?
好处是:
背景:在内部有10+个独立的项目,每个项目都有自己的webpack配置,并且很多都比较接近,项目是类似的,其实可以把webpack统一起来,封装成一个包。
这样的话,可以统一用上最优的配置(含各种性能优化配置),而且只需要一个包发版本,所有10+项目都可以更新到最新的配置。可以做到前端项目统一高质量优化。 (往下看,也能支持个性化配置)
并且用户不用管webpack内部各种依赖包之间的 版本依赖关系 及 版本升级问题,我内部统一处理
3. 如何封装webpack?
- 难点1:需要一个命令平台
- 难点2:需要在封装后的包内部,去执行webpack及webpack-dev-serve命令
- 难点3:如何往webpack的配置文件内去传参?(配置文件是webpack调用的,没法正常传参)
- 难点4:需要去读取用户写的自定义webpack配置,然后覆盖内部默认的配置
难点1:需要一个命令平台
我们之前已经有工具平台了 内部前端工具平台搭建,
只需要在工具平台的代码内 多增加一个配置项就是可以了,比如 fe run
,在细化一点可以是如下
fe run dev 类似 npm run dev
fe run 类似 npm run build
webpack相关的依赖也放在这个工具平台上,后续直接在这个包内执行webpack命令
- 此处有个很关键的细节
// package.json
{
...
"dependencies": {
... 工具平台上 所有的包要放在这里面(含webpack 及 各种依赖),不能放在"devDependencies": {} 这里面,否则后续会拿不到依赖
},
}
难点2:需要在封装后的包内部,去执行webpack及webpack-dev-serve命令
执行命令,比如 webpack xx,需要用到spawn
- spawn里面需要配置cwd执行路径,否则会出错
const { spawn } = require('child_process') // 执行命令行 如: lnpm i
// 开启子进程来执行命令: 例如npm install
const runCommand = (obj) => {
const { command, args=[], fn, cwd } = obj
// console.log('-----', cwd)
const runner = spawn(command, args, {
stdio: 'inherit', // (继承父进程的stdio输出) 输出 npm install 的信息
cwd: cwd || null // 执行环境path
})
runner.on('close', function (code) {
if (fn) {
fn(code)
}
})
}
* 此处有个细节:spawn里面要配置 cwd
如果不配置,默认会在 执行路径 去执行命令
比如当前项目是QWER,我们想对QWER这个项目打包,现在在QWER的目录内
xxxMacBook-Pro QWER % pwd
/Users/xxx/Desktop/project/QWER
如果不在spawn里面配置cwd,那么就会在QWER目录内执行webpack xx。显然不是我们想要到,我们要在@myCompany/fe目录内执行webpack xx 命令
此处还有问题,如果用spawn去执行webpack命令会报错,即使在spawn中设置了cwd,定位到了@myCompany/fe目录内,也还是会报错。
- 具体原因是node_modules依赖的阻隔性,不能跨依赖去调用 命令。 而且用户环境可能全局安装了webpack,然后版本不同,可能也会影响到最终的结果
所以只能用另一种办法:用npm,开发者的环境内肯定有npm(node自带的),也不受版本和环境影响,用npm run xx 去触发,如下
let isDev = false // 默认 mode: production, fe run
if (process.argv.pop() === 'dev') { // fe run dev 会执行到这里面去, mode dev
isDev = true
}
runCommand({
command: 'npm', // 要执行的命令是 npm
args: ['run', isDev ? 'dev' : 'build'], // npm run dev 或 npm run build
cwd: path.resolve(__dirname, '../../') // 在工具平台的路径内执行
})
// 最终是由npm run dev 或 build 来触发, 命令行内:需要告诉webpack读取默认配置文件 webpack.config.js 用 --config
"dev": "webpack-dev-server --open --hot --env.NODE_ENV=development --progress --mode=development --config ./bin/webpackConfig/webpack.config.js"
"build": "webpack --inline --env.NODE_ENV=production --progress --mode=production --hide-modules --config ./bin/webpackConfig/webpack.config.js"
此时已经可以 fe run dev 和 fe run 已经可以执行webpack打包了,配置文件用的 默认配置好的。 接下来,需要做配置化,让用户可以覆盖默认配置
难点3:如何往webpack的配置文件内去传参?(配置文件是webpack调用的,没法正常传参)
背景:webpack的配置文件内,entry需要拿到对应项目的path,output也需要path。这个path肯定是动态的
问题难点:webpack配置文件是webpack调用的,没法正常传参
解法:通过一个中介来拿参数,中介是:“文件”。用读文件的方式的来拿到参数
- 用户执行
fe cli
来打包的时候,可以拿到当前的执行路径pwd,然后把这个路径写到 本地文件 ,webpack的配置文件是webpack.config.js是js文件,可以去加代码 读本地文件(例如:const CONFIG = require('./obj.js')
),获得路径参数。- 需要的话,也可以通过这种方式拿到其他参数
难点4:需要去读取用户写的自定义webpack配置,然后覆盖内部默认的配置
核心是用webpack-merge
先给一个使用示例,用户的项目的根目录内,需要写一个配置文件:比如:fe.config.js
// fe.config.js
module.exports = {
webpack: {
... // 此处可以写webpack配置,格式和标准格式一致,最终会通过webpack-merge这个包,合并配置
// 比如:
entry: 'xx'
output: {
path: 'xx'
},
plugins: []
}
}
读取配置文件,const getFeConfig = require('打包项目的fe.config.js的path')
,需要拿到 fe.config.js的path
,参考难点3
码字不易,点赞鼓励!