有些时候,一些 npm 包并没有按照规范的方式打包,或者本身 npm 包比较老旧,这时候我们需要修改一些配置,使得 Webpack 正确的处理这些包。

resolve.alias

有些 npm 包不怎么规范,package.jsonmain 字段没有指向正确的文件。或者我们想让 Webpack 加载其他版本的构建结果。此时我们需要通过 resolve.alias 来重新指定模块路径。

const config = {
  resolve: {
    alias: {
      demo: path.resolve(__dirname, 'node_modules/demo/dist/demo.js'),
    },
  },
};
1
2
3
4
5
6
7

上面的配置告诉 Webpack 当遇到 demo 开头的模块时,使用指定的路径来寻找。我们还可以将 demo 配置成一个正则来匹配。

比如,我们可以通过 resolve.alias 来加载 React 已经构建好的 .min.js 版本,放弃一些 propTypes 校验,来减小构建大小。

在 loader 中,我们可以通过 resolveLoader.alias 实现同样的效果。

resolve.modules

我们可以通过 resolve.modules 来修改 Webpack 查找模块的地址。比如,默认情况下,Webpack 只会在 node_modules 目录下查找模块,我们通过如下配置

const config = { resolve: { modules: ['demo', 'node_modules'] } };
1

使得 Webpack 优先从 demo 目录下查找,找不到再去 node_modules 目录下查找模块。

在大项目中,如果我们想自定义查找模块的地址,这个配置项非常有用。

resolve.extensions

默认情况下,Webpack 只会解析 .js.json.mjs 文件,我们可以通过 resolve.extensions 来修改。

const config = { resolve: { extensions: ['.js', '.jsx'] } };
1

这个例子使得 Webpack 可以解析 .jsx 文件。

resolve.plugins

默认情况下,Webpack 会使用目录下的 index 文件来作为模块的返回,resolve.plugins 可以帮助我们对这一行为进行定制。directory-named-webpack-pluginopen in new window 就是一个很好的例子,通过这个插件,我们可以将 import foo from "./foo"; 变成 import foo from "./foo/foo.js";

babel-plugin-module-resolveropen in new window 这个插件功能类似,只是为 Babel 提供服务。

不处理某些包

一些第三方包,比如 JQuery 通常都是通过 CDN 部署,页面直接引用即可。因此,在 Webpack 配置中我们需要将这一类的包标识为 external

const config = { externals: { jquery: 'jquery' } };
1

有些时候我们为了防止 CDN 出现问题,我们还需要做一些防备手段,当从 CDN 加载失败以后,可以从本服务器进行加载。

<script src="//ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script>
  window.jQuery ||
    document.write('<script src="js/jquery-3.1.1.min.js"><\/script>');
</script>
1
2
3
4
5

从 Webpack 5 开始,支持设置 externalsTypeopen in new window 字段,来自定义加载模块的方式。比如设置为 "promise",则会异步加载,设置为 "import" 则会使用 import() 的方式加载。我们还可以针对每一个包做自定义,比如我们可以配置异步加载 JQuery, ["jquery", "promise"]

处理全局变量

有一些包会使用全局变量,比如 JQuery 中会使用 $。Webpack 提供了一些处理这些全局变量的方法。

注入全局变量

imports-loaderopen in new window 可以帮助我们将全局变量注入到我们的模块中。在下面的例子中,Webpack 会为每一个模块都注入 import $ from 'jquery';

const config = {
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: 'imports-loader',
        options: {
          imports: ['default jquery #39;],
        },
      },
    ],
  },
};
1
2
3
4
5
6
7
8
9
10
11
12
13

解析全局变量

Webapck 的 ProvidePlugin 插件可以帮助 Webpack 正确的处理模块中的全局变量。

const config = {
  plugins: [new webpack.ProvidePlugin({ $: 'jquery' })],
};
1
2
3

暴露全局变量

有时候我们需要将模块内的一些变量暴露到全局中给其他模块使用。expose-loaderopen in new window 可以实现这个功能。

const config = {
  test: require.resolve('react'),
  loader: 'expose-loader',
  options: {
    exposes: ['React'],
  },
};
1
2
3
4
5
6
7

如果你需要在暴露全局变量的时候执行一些逻辑,则可以使用 script-loaderopen in new window

删除未使用的模块

有些模块会携带很多我们不需要的信息。比如 moment 这个包,会带有很多 locale 文件,这会增加我们的构建大小。我们可以使用 Webpack 的IgnorePlugin 这个插件来忽略这些文件。

const config = {
  plugins: [
    new webpack.IgnorePlugin({
      resourceRegExp: /^\.\/locale$/,
      contextRegExp: /moment$/,
    }),
  ],
};
1
2
3
4
5
6
7
8

如果需要加载某一个特殊的 locale 文件,则可以使用 ContextReplacementPlugin 插件。

const config = {
  plugins: [
    new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /de|fi/),
  ],
};
1
2
3
4
5

处理预构建的包

Webpack 在处理一些依赖包的时候,如果使用了预构建(已经构建完毕并压缩过等)的版本,则会报下面这样的错误。

WARNING in ../~/jasmine-promises/dist/jasmine-promises.js
Critical dependencies:
1:113-120 This seems to be a pre-built javascript file. Though this is possible, it's not recommended. Try to require the original source to get better results.
 @ ../~/jasmine-promises/dist/jasmine-promises.js 1:113-120
1
2
3
4

通常情况下,我们有两种方法处理。一种是将解析包的路径指向包的源代码版本。另一种是使用 module.noParse 来跳过处理这个包。

const config = {
  module: { noParse: /node_modules\/demo\/index.js/ },
};
1
2
3

总结

我们在构建的时候可能会碰到各种各样因为依赖包导致的构建问题。Webpack 提供了一些处理这些依赖包的方法,我们可以根据需要使用。

通过 Webpack 我们还可以使用其他包中的全局变量,也可以将自己模块内的信息以全局变量的形式暴露出去。

关注微信公众号,获取最新推送~