记录-实现封装webpack

功能介绍:

  1. 只需安装一个包,即可开箱即用优化过的webpack配置,并且不用管webpack内部各种依赖包之间的 版本依赖关系 及 版本升级问题 (所有的跟webpack打包相关的,都封装在里面)

    • (类似vue-cli-service命令)@vue/cli-service 安装了一个名为 vue-cli-service 的命令。你可以在 npm scripts 中以 vue-cli-service、或者从终端中以 ./node_modules/.bin/vue-cli-service 访问这个命令。 如:
      1. {
      2. "scripts": {
      3. "serve": "vue-cli-service serve",
      4. "build": "vue-cli-service build"
      5. }
      6. }
  2. 除了有内置优化好的webpack配置外,还支持用户自定义webpack配置,只需写一个webpack.config.js就行,格式和webpack官方标准一样就行

本文目录大纲

  1. 封装webpack是什么意思?
  2. 封装webpack有什么好处?
  3. 如何封装webpack?
    1. 难点1:需要一个命令平台
    2. 难点2:需要在封装后的包内部,去执行webpack及webpack-dev-serve命令
    3. 难点3:如何往webpack的配置文件内去传参?(配置文件是webpack调用的,没法正常传参)
    4. 难点4:需要去读取用户写的webpack配置,然后覆盖默认的配置

以下将按照上面的大纲来介绍

1. 封装webpack是什么意思?

首先我们看看未封装前和封装后的样子

package.json

  1. // 封装前
  2. {
  3. ...
  4. "devDependencies": {
  5. "webpack": "^4.9.1",
  6. "webpack-cli": "^3.3.10",
  7. "webpack-dev-server": "^3.11.2",
  8. "babel-loader": "^8.2.2",
  9. "css-loader": "^2.1.1",
  10. "file-loader": "^3.0.1",
  11. "less-loader": "^5.0.0",
  12. "postcss-loader": "^3.0.0",
  13. "sass-loader": "^7.1.0",
  14. "style-loader": "^0.23.1",
  15. "vue-loader": "^15.7.2",
  16. "url-loader": "^1.1.2",
  17. // 还有一些webpack loader plugin 省略
  18. }
  19. }
  20. // 封装后 只需加载一个包
  21. {
  22. ...
  23. "devDependencies": {
  24. "@myCompany/fe": "^1.0.68",
  25. }
  26. }

封装的意思,其实就是,把所有和webpack有关的依赖,都封装在一个包里面,用webpack的能力只需要下载这一个包就行了。 接下来介绍好处

2. 封装webpack有什么好处?

为什么要封装呢?封装有什么好处?

好处是:

背景:在内部有10+个独立的项目,每个项目都有自己的webpack配置,并且很多都比较接近,项目是类似的,其实可以把webpack统一起来,封装成一个包。

这样的话,可以统一用上最优的配置(含各种性能优化配置),而且只需要一个包发版本,所有10+项目都可以更新到最新的配置。可以做到前端项目统一高质量优化。 (往下看,也能支持个性化配置)

并且用户不用管webpack内部各种依赖包之间的 版本依赖关系 及 版本升级问题,我内部统一处理

3. 如何封装webpack?

  1. 难点1:需要一个命令平台
  2. 难点2:需要在封装后的包内部,去执行webpack及webpack-dev-serve命令
  3. 难点3:如何往webpack的配置文件内去传参?(配置文件是webpack调用的,没法正常传参)
  4. 难点4:需要去读取用户写的自定义webpack配置,然后覆盖内部默认的配置

难点1:需要一个命令平台

我们之前已经有工具平台了 内部前端工具平台搭建
只需要在工具平台的代码内 多增加一个配置项就是可以了,比如 fe run,在细化一点可以是如下

  1. fe run dev 类似 npm run dev
  2. fe run 类似 npm run build

webpack相关的依赖也放在这个工具平台上,后续直接在这个包内执行webpack命令

  • 此处有个很关键的细节
    1. // package.json
    2. {
    3. ...
    4. "dependencies": {
    5. ... 工具平台上 所有的包要放在这里面(含webpack 各种依赖),不能放在"devDependencies": {} 这里面,否则后续会拿不到依赖
    6. },
    7. }

难点2:需要在封装后的包内部,去执行webpack及webpack-dev-serve命令

执行命令,比如 webpack xx,需要用到spawn

  • spawn里面需要配置cwd执行路径,否则会出错
  1. const { spawn } = require('child_process') // 执行命令行 如: lnpm i
  2. // 开启子进程来执行命令: 例如npm install
  3. const runCommand = (obj) => {
  4. const { command, args=[], fn, cwd } = obj
  5. // console.log('-----', cwd)
  6. const runner = spawn(command, args, {
  7. stdio: 'inherit', // (继承父进程的stdio输出) 输出 npm install 的信息
  8. cwd: cwd || null // 执行环境path
  9. })
  10. runner.on('close', function (code) {
  11. if (fn) {
  12. fn(code)
  13. }
  14. })
  15. }
  16. * 此处有个细节:spawn里面要配置 cwd
  17. 如果不配置,默认会在 执行路径 去执行命令
  18. 比如当前项目是QWER,我们想对QWER这个项目打包,现在在QWER的目录内
  19. xxxMacBook-Pro QWER % pwd
  20. /Users/xxx/Desktop/project/QWER
  21. 如果不在spawn里面配置cwd,那么就会在QWER目录内执行webpack xx。显然不是我们想要到,我们要在@myCompany/fe目录内执行webpack xx 命令

此处还有问题,如果用spawn去执行webpack命令会报错,即使在spawn中设置了cwd,定位到了@myCompany/fe目录内,也还是会报错。

  • 具体原因是node_modules依赖的阻隔性,不能跨依赖去调用 命令。 而且用户环境可能全局安装了webpack,然后版本不同,可能也会影响到最终的结果

所以只能用另一种办法:用npm,开发者的环境内肯定有npm(node自带的),也不受版本和环境影响,用npm run xx 去触发,如下

  1. let isDev = false // 默认 mode: production, fe run
  2. if (process.argv.pop() === 'dev') { // fe run dev 会执行到这里面去, mode dev
  3. isDev = true
  4. }
  5. runCommand({
  6. command: 'npm', // 要执行的命令是 npm
  7. args: ['run', isDev ? 'dev' : 'build'], // npm run dev 或 npm run build
  8. cwd: path.resolve(__dirname, '../../') // 在工具平台的路径内执行
  9. })
  10. // 最终是由npm run dev 或 build 来触发, 命令行内:需要告诉webpack读取默认配置文件 webpack.config.js 用 --config
  11. "dev": "webpack-dev-server --open --hot --env.NODE_ENV=development --progress --mode=development --config ./bin/webpackConfig/webpack.config.js"
  12. "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

  1. // fe.config.js
  2. module.exports = {
  3. webpack: {
  4. ... // 此处可以写webpack配置,格式和标准格式一致,最终会通过webpack-merge这个包,合并配置
  5. // 比如:
  6. entry: 'xx'
  7. output: {
  8. path: 'xx'
  9. },
  10. plugins: []
  11. }
  12. }

读取配置文件,const getFeConfig = require('打包项目的fe.config.js的path'),需要拿到 fe.config.js的path,参考难点3


码字不易,点赞鼓励!