深入Craco/Webpack升级:3. 自定义webpack corejs polyfill plugin

96 阅读2分钟

〇、上篇文章

juejin.cn/post/743637…

一、Corejs当前实现

在项目中经常会看到这种corejs代码,用来作为polyfill,使代码中的新特性能够兼容低版本的浏览器

// IE10
import 'core-js/es/map';
import 'core-js/es/set';
// IE11, IE10
import 'core-js/es/object/assign';

但是我们接手的是一个古老的项目,引入了类似上面代码块的大量的polyfill代码,许多polyfill实际上已经不再需要了,并且有些同事又爱用新的js语法来写代码,这里又缺失了一些polyfill。经常出现在browserlist中的浏览器版本被qa抓到出现了白屏现象。

所以我们需要实现一个动态的corejs插件,目标是通过package.json中的browserlist字段,获取当前需要支持的浏览器环境,生成polyfill的内容并insert到我们的入口js的顶部

二、自定义的corejs插件

我们引入core-js-builder这个库来根据browserlist字段生成polyfill code 代码如下

// polyfill-loader.js
const builder = require('core-js-builder')
const { loadConfig } = require("browserslist")
const path = require('path')

const name = 'CorejsPolyfillPlugin'
function CorejsPolyfillPlugin() {
  let entries;
  this.hooks.entryOption.tap(name, (context, entry) => {
    entries = Object.values(entry).reduce((result, entryItem) => {
      if (entryItem.import) {
        return result.concat(entryItem.import) // 支持多入口打包
      }
      return result
    }, [])
  })

  this.hooks.beforeCompile.tap(name, (params) => {
    // inject loader
    params.normalModuleFactory.hooks.afterResolve.tap(name, (data) => {
    // 注意:此处代码为webpack5版本。webpack4 版本中回调的直接就是createData, webpack5又包了一层
      const { createData } = data; 
      if (entries.includes(createData.resource)) {
        createData.loaders.push({ loader: __filename });
      }
    });
  });
}

function PolyFillLoader(content, map, meta) {
  const cb = this.async()

  builder({
    modules: ['core-js/actual'],
    targets: loadConfig({path: path.resolve('../')}), //package.json的相对路径
    format: 'esm'
  }).then(corejs => {
    cb(null, corejs + content, map, meta)
  });
}

PolyFillLoader.CorejsPolyfillPlugin = CorejsPolyfillPlugin
module.exports = PolyFillLoader;

简述上述代码:

由于项目是个多入口打包的react项目,我们需要先找出所有entries, 通过传入package.json中browserlist字段,让core-js-builder生成形如下面的代码(就是代码中的corejs回调参数)

 "/**\n * core-js 3.35.0\n * © 2014-2023 Denis Pushkarev (zloirock.ru)\n * license: https://github.com/zloirock/core-js/blob/v3.35.0/LICENSE\n * source: https://github.com/zloirock/core-js\n */\nimport 'core-js/modules/es.symbol.js';\nimport 'core-js/modules/es.symbol.description.js';\nimport 'core-js/modules/es.symbol.async-iterator.js';\nimport 'core-js/modules/es.symbol.has-instance.js';\nimport 'core-js/modules/es.symbol.is-concat-spreadable.js';"

三、引用自定义的corejs插件

const { CorejsPolyfillPlugin } = require('./polyfill-loader')
...
// webpack的plugin数组中push这个自定义plugin
plugins.push(CorejsPolyfillPlugin)