likes
comments
collection
share

不再为 Node.js 模块引入 Polyfills

作者站长头像
站长
· 阅读数 16

2020-10-10 webpack 5.0.0 发布了,其中的一个变化是 Webpack 5 不再为 Node.js 模块引入 Polyfills 了。

在早期,webpack 的目的是为了让大多数的 Node.js 模块运行在浏览器中,但如今模块的格局已经发生了变化,现在许多模块主要是为前端而编写。webpack <= 4 的版本中提供了许多 Node.js 核心模块的 polyfills,一旦某个模块引用了任何一个核心模块(如 cypto 模块),webpack 就会自动引入 polyfills。

比如下面的代码:实现签名的一个函数。

const crypto = require('crypto');

// 将值通过私钥签名,由.分割原值和签名
function sign (val, secret) {
  return val + '.' + crypto
    .createHmac('sha256', secret)
    .update(val)
    .digest('base64')
    .replace(/\=+$/, '');
};

module.exports = sign;

代码中使用到了 crypto 模块,因为在浏览器环境下是没有 crypto 这个东西,所以要引入 polyfills 做兼容处理。保证在浏览器中可以正常运行。

引入 polyfills 导致的问题就是打包的体积变大了。(可以考虑不要使用 node 模块,其实 npm 上还有一些其它的模块,比如 crypto-js)

怎么防止打包的时候把 crypto 模块打进去了,其实我们可以在 package.json 里面配置 browser 字段,然后设置值 false 就可以。这样代码就只能在 node 环境下运行了。

{
  "name": "webpack4",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "browser": {
    "crypto": false
  },
  "scripts": {
    "webpck": "npx webpack"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "webpack": "^4.35.3",
    "webpack-cli": "^3.1.0"
  }
}

从 webpack 5 开始不再自动引入这些 polyfills,而会专注于前端模块兼容。目的是提高 web 平台的兼容性。因为 node 模块本身就不是为浏览器设计的。

1、所以尽量使用前端兼容的模块。比如 axios 这样的库,即可以在浏览器中运行,也可以在 node 环境中运行。

2、可以手动为 Node.js 核心模块添加 polyfill。错误提示会告诉你如何做。

比如下面的使用 webpack5 打包,报错信息是这样的。然后按照报错信息改。

不再为 Node.js 模块引入 Polyfills

let path = require('path')

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
    library: 'result'
  },
  resolve: {
    fallback: {
      "crypto": require.resolve("crypto-browserify"),
      "stream": require.resolve("stream-browserify")
    }
  },
}

3、在 package.json 中添加 browser 字段,使 node 模块与前端兼容。为浏览器提供其他的 dependencies 依赖。

下面 browser 字段的含义是在浏览器环境中把 crypto 模块替换为 crypto-js 模块。

{
  "name": "webpack4",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "browser": {
    "crypto": "crypto-js"
  },
  "scripts": {
    "webpck": "npx webpack"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "crypto-js": "^4.0.0",
    "webpack": "^4.35.3",
    "webpack-cli": "^3.1.0"
  }
}

总结:因为 webpack 打包默认是输出到浏览器端,如果我们在编写代码的时候引入了 node 的核心模块,此时 webpack5 就会抛出错误,提示我们需要配置 polyfills 。解决办法有三个,第一就是不要去使用 node 模块,而是替换成其他的前端模块。第二就是自己配置 polyfills,第三就不让 node 模块打包进行。

如果我们写的代码只运行在 node 环境,直接配置 taget 就可以。

let path = require('path')

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
    library: 'result'
  },
  target: 'node'
}

其实很少会用到 browser 字段。

这是 webpack4 升级到 webpack 5 的重大区别。如果你打包的时候发生报错,可以看看你是否引入了 node 模块导致的。