webpack构建太费时,升级架构又太危险,于是写了个构建提速脚本

95 阅读2分钟

前言

随着业务发展,公司后台系统越来越多页面,开发构建速度从10s飙升到1、2分钟,甚至有的机器因为内存问题,项目跑不动!这怎么能忍!于是乎,写个插件

webpack插件的基本结构

通过翻阅webpack文档得知,插件就是一个具有apply函数的类,如下

class Plugin() {
    constructor() {}
    apply(compiler) {
        // 做些事情
    }
}

写插件需要关注的几个核心点

  • compiler

    webpack整个生命周期的对象,有自己的hooks

  • compilation

    构建的产物,有自己的hooks

  • 各种hook

    hook,指在特定时期会执行的钩子函数

ps:因为了解不深,只能补充一些我自己的概念,如有需要可自行查阅官方文档,有更仔细的说明

怎么减少构建速度?

我的思路是,在开发阶段,比如开发A需求,我们通常只需要关注A需求的模块(具体为路由,页面),那除了A的这些模块,是不是可以选择性地不构建它呢?答案是:可以的

于是我们插件的开发思路可以划分为以下几个步骤

  • 定义一个apply函数的插件类
  • 在apply函数中来控制构建哪些模块
  • 过滤完成,减少了不必要的构建,从而减少了构建时间

具体代码

1、监听模块创建时的hook

  • normalModuleFactory:import形式的模块的工厂函数
  • contextModuleFactory:require.context形式的模块的工厂函数

工厂函数也有自己的hook

  • beforeResolve:解析模块具体内容之前的钩子,可以通过返回false来阻止解析
apply(compiler) {
    compiler.hooks.normalModuleFactory.tap(this.name, (nmf) => {
      nmf.hooks.beforeResolve.tap(this.name, this.checkIgnore)
    })
    compiler.hooks.contextModuleFactory.tap(this.name, cmf => {
      cmf.hooks.beforeResolve.tap(this.name, this.checkIgnore)
    })
    compiler.hooks.done.tap(this.name, () => {
      // 构建结束做些什么
    })
}

2、判断模块路径是否满足过滤规则,是则停止解析

回调函数checkIgnore接受解析的一些上下文信息,比如

  • request:请求路径
  • context:请求的上下文

举个例子,我们有个a文件

// a.js
import './b.js'

当解析到b模块的时候,context就是a的文件路径,request就是'./b.js'

checkIgnore(resolveData) {
    if (!resolveData) {
        return resolveData
    }
    const { request, context } = resolveData
    
    if (request.indexOf('./b.js') !== -1) {
        // 停止解析这个模块
        return false
    }
    return resolveData
  }

3、结合自己项目的实际场景,通过配置文件来控制构建

我这边的做法是

  • 新增加构建dev命令
  • 生成config,根据config的数据来选择性构建哪些页面,哪些路由
  • 完成构建

结尾

webpack真的太太太太难了,虽然脚本最后成功投入在开发环境使用了,但是期间也踩了不少坑,比如解析文件路径出错,模块找不到导致运行不起来等问题,但也是在不断踩坑中,加强了对webpack的一些实际理解,有一说一,webpack万物皆为模块的想法真的牛逼!