前言
最近在公司的一个项目中使用到了Webpack5, 然而在使用某个npm包的时候,出现了Buffer is not defined
这个问题,原因很明显了,因为浏览器运行时没有Buffer这个API,所以需要为浏览器引入Buffer Polyfill.
Webpack5
这种在Webpack项目下使用NodeJS包的场景应该是很常见的,为什么之前没有遇到过这个问题。因为Webpack5之前的版本,Webpack会自动为我们引入NodeJS API Polyfill, 然而Webpack5之后,官方认为自动引入Polyfill会导致bundle体积过大,并且大多数情况下这些自动引入的Polyfill是没有用到的。所以Webpack5需要我们手动引入Polyfill
配置Webpack5
安装Buffer Polyfill
我们需要先安装兼容浏览器环境的Buffer实现。这里使用的是buffer这个npm包
npm install -D buffer
配置 Fallback
Webpack resolve.fallback指定了当我们的目标构建环境中不存在对应的包时,将使用fallback的值作为回退。这里我们使用安装的buffer包作为fallback
{
resolve: {
fallback: {
buffer: require.resolve('buffer/'),
},
},
}
这里使用了'buffer/', 这样可以明确告诉NodeJS模块查询算法,使用buffer npm包,而不是NodeJS自带的buffer模块
配置ProvidePlugin
Webpack ProvidePlugin可以注入一个模块或者值,作为全局变量。我们之后可以直接使用被注入的全局变量,无需import/require
{
plugins: [
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'], // ['包名', '包中的值']
})
]
}
上面的配置相当于
const Buffer = require('buffer').Buffer;
完整配置
{
resolve: {
fallback: {
buffer: require.resolve('buffer/'),
},
},
plugins: [
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
})
]
}
package.json browser字段
经过上面的配置,正常情况下就可以解决Buffer is not defined
这个问题了。然而我使用的公司内部的某个npm包却报出了新的问题Cannot read properties of undefined(reading 'allocUnsafe')
, 其它依赖buffer的包则一切正常。
经过排查,和这个包的package.json browser字段有关。
// package.json
{
"browser": {
"buffer": false
}
}
按照非npm官方规范(https://github.com/defunctzombie/package-browser-field-spec),browser可以有三种用途
main字段的替代。npm官方文档中介绍的用途,值为string类型,用于指定浏览器环境的包入口
"browser": "./browser/specific/main.js"
替代指定文件。左侧为你需要替换的模块或文件名称,右侧为替换项
"browser": { "module-a": "./shims/module-a.js", "./server/only.js": "./shims/client-only.js" }
忽略模块。设置false可以阻止模块或文件被打包到包中
"browser": { "module-a": false }
以上配置会导致a为undefined
const a = require('module-a');
解决方法也很简单,让包的维护者把package.json中的browser字段删除即可。至此,所有报错全部解决,可以愉快地使用Buffer API了
结语
Webpack5不再自动为我们引入NodeJS Polyfill, 我们可以安装兼容浏览器环境的npm包,然后通过配置resolve.fallback以及providePlugin插件来手动引入;如果配置完成后,某些包还是有问题,可以尝试检查对应包中的package.json browser字段