Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

记一次错误的 webpack 问题解决方案 #108

Open
yubaoquan opened this issue Apr 19, 2024 · 0 comments
Open

记一次错误的 webpack 问题解决方案 #108

yubaoquan opened this issue Apr 19, 2024 · 0 comments

Comments

@yubaoquan
Copy link
Owner

需求背景

工程中有多个目录, 每个目录的 index.js 是一个打包入口. webpack 的打包配置集中在一个公共的目录中. 在编译样式文件时需要根据每个目录入口的一个 xx.json 文件中的某个 flag 决定是否要在 postcss-loader 的配置中添加某个plugin

错误思路

首先想到的是写一个 webpack plugin, 在 compilation.buildModule 钩子中根据文件的引用路径找到入口 index.js 继而找到 xx.json, 这样根据 json 中的 flag 就可以修改 loader 中的配置

但是 webpack 是多线程打包的, 当编译多个文件时, 修改打包配置钩子会执行多次, 后执行的钩子会把前面钩子改动的配置改掉. 导致编译时的配置可能是错误的. 当然可以通过把 parallelism 参数改为 1 来强制 webpack 一次只编译一个文件. 但是这样做会给后续的维护埋下性能问题

自定义的 webpack plugin 的代码

(这段代码只是展示一些查询依赖的思路, 不要在 webpack plugin 中修改打包配置)

/** 找到物料的 index.js */
const findRootModule = (moduleGraph, module) => {
  let current = module
  let parent = moduleGraph.getIssuer(current)
  while (parent) {
    current = parent
    parent = moduleGraph.getIssuer(current)
  }
  return current
}

/** 获取物料元数据 */
const findMetadata = (moduleGraph, module) => {
  const rootModule = findRootModule(moduleGraph, module)
  const filePath = rootModule.resource
  const metaFilePath = filePath.replace(/index\.js$/, 'meta.json')
  return require(metaFilePath)
}

/** 移除 postcss-pxtorem */
const removePx2Rem = (postcssLoader) => {
  const { plugins } = postcssLoader.options
  const pxtoremPluginIndex = plugins.findIndex(
    (plugin) => plugin.postcssPlugin === 'postcss-pxtorem',
  )
  if (pxtoremPluginIndex !== -1) {
    plugins.splice(pxtoremPluginIndex, 1)
  }
}

class DynamicRemPlugin {
  constructor(options) {
    this.options = options
  }

  // eslint-disable-next-line class-methods-use-this
  apply(compiler) {
    compiler.hooks.compilation.tap('DynamicRemPlugin', (compilation) => {
      compilation.hooks.buildModule.tap('DynamicRemPlugin', (module) => {
        const postcssLoader = module.loaders?.find((loader) =>
          loader.loader.includes('postcss-loader'),
        )

        if (!postcssLoader) return

        const metadata = findMetadata(compilation.moduleGraph, module)
        if (!metadata.basic?.platform?.includes('H5')) {
          removePx2Rem(postcssLoader)
        }
      })
    })
  }
}

module.exports = DynamicRemPlugin

目前方案

经过查找得知, postcss-loader 的 postcssOptions 可以是一个函数, 函数的入参是当前编译环境的上下文. 这样就可以把之前在钩子中写的逻辑转移到 postcssOptions 中. 这样就不会有配置修改的顺序问题

注意

postcssOptions 作为函数传递的特性在低版本(v3) postcss-loader 中不支持, 目前已知 v7 是支持这个特性的

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant