记录-实现封装webpack

1,853 阅读6分钟

记录-实现封装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 访问这个命令。 如:
      {
        "scripts": {
          "serve": "vue-cli-service serve",
          "build": "vue-cli-service build"
        }
      }
      
  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

// 封装前
{
  ...
  "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. 难点1:需要一个命令平台
  2. 难点2:需要在封装后的包内部,去执行webpack及webpack-dev-serve命令
  3. 难点3:如何往webpack的配置文件内去传参?(配置文件是webpack调用的,没法正常传参)
  4. 难点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


码字不易,点赞鼓励!