打包第三方库有 4 种方法,这四种方法各有各的优缺点。

  1. 直接引入
    缺点:每次使用到需要手工导入。
  2. 插件引入
    如果使用 webpack.ProvidePlugin 插件引入的话,则不再需要你在模块手工引入。但没有全局变量,你在模块外是不能访问的。
  3. expose-loader 引入
    如果想在任何地方访问此变量,需要把此变量设置为环境变量 “window.isarray”,expose-loader 可以把模块添加到全局变量上。
  4. cdn 引入

我们用 lodash 包来讲解一下。

  1. yarn add lodash

直接引入

/** ./src/index.js **/
import lodash from 'lodash';
console.log(lodash.isArray, lodash.isArray([]));

我们打包一下看结果
image.png
我们发现 lodash 整个都被打包进了文件中。运行正常。
image.png

插件引入

我们改写下 入口文件,不用 import 导入,改为直接调用。

/** ./src/index.js **/
console.log(lodash.isArray, lodash.isArray([]));

很明显如果不做任何配置,打包必定会失败,如下:
image.png
image.png
lodash 没有被打包进来,运行也失败了。
现在我们做一下 webpack 配置,使用 webpack.ProvidePlugin 引入 loadsh。

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');

const htmlPlugins = ['index'].map(chunkName => {
  return new HtmlWebpackPlugin({
    filename: `${chunkName}.html`,
    inject: 'body',
    template: `./public/${chunkName}.html`
  })
});
module.exports = {
  devServer: {
    port: 8080,
    open: true,
    compress: true,
    static: './dist'
  },
  devtool: 'source-map',
  entry: {
    index: './src/index.js'
  },
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: '[name].[hash:8].js'
  },
  module: {

  },
  plugins: [
    new webpack.CleanPlugin(),
    ...htmlPlugins,
    new webpack.ProvidePlugin({
      lodash:'lodash'
    })
  ]
}

ProvidePlugin 接收的对象 key 为命名的变量名,value 为包的名字。
其他文件不变,我们看一下打包结果
image.png
image.png
我们发现 lodash 被打包进来了,而且运行成功。
但是有一个问题,通过 ProvidePlugin 导入的变量只是在每个模块加载的时候注入的,并没有在全局作用域中声明这个变量,如下:
image.png

expose-loader 引入

/** ./webpack.config.js **/
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');

const htmlPlugins = ['index'].map(chunkName => {
  return new HtmlWebpackPlugin({
    filename: `${chunkName}.html`,
    inject: 'body',
    template: `./public/${chunkName}.html`
  })
});
module.exports = {
  devServer: {
    port: 8080,
    open: true,
    compress: true,
    static: './dist'
  },
  devtool: 'source-map',
  entry: {
    index: './src/index.js'
  },
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: '[name].[hash:8].js'
  },
  module: {
    rules:[
      {
        test:/lodash/,
        use:[
          {
            loader: 'expose-loader',
            options: {
              exposes: {
                globalName: 'lodash',
                override: true
              }
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new webpack.CleanPlugin(),
    ...htmlPlugins,
  ]
}

我们使用 expose-loader 校验 lodash 路径,设置全局变量名为 lodash,是否覆盖选择是。
然后我们在 index 文件中引入,一次引入全局使用。

import lodash from 'lodash';
console.log(lodash.isArray, lodash.isArray([]));

然后看打包结果
image.png

正常打印结果,并且使用 window.lodash 能够获取到这个模块,说明 expose-loader 将它暴露到了全局环境。
通过 window.lodash 我们可以在任何地方使用这个包中的方法。

cdn 引入

我们现在 cdn 网站 找到 lodash 的开源 cdn 地址。

<script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.core.js"></script>

我们把它加到模版 html 中,

<!-- ./public/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script src="https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.core.js"></script>
</body>
</html>

然后设置 webpack

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');

const htmlPlugins = ['index'].map(chunkName => {
  return new HtmlWebpackPlugin({
    filename: `${chunkName}.html`,
    inject: 'body',
    template: `./public/${chunkName}.html`
  })
});
module.exports = {
  devServer: {
    port: 8080,
    open: true,
    compress: true,
    static: './dist'
  },
  devtool: 'source-map',
  externals:{
    lodash: '_'
  },
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: '[name].[hash:8].js'
  },
  module: {

  },
  plugins: [
    new webpack.CleanPlugin(),
    ...htmlPlugins,
    // new webpack.ProvidePlugin({
    //   lodash:'lodash'
    // })
  ]
}

如果你设置了 externals,key 是库的名字,值是全局变量名(如 _ 是 cdn 给全局变量赋的值),以后你再引入这个库的时候,直接从全局变量名上取值即可。
然后我们再次引入 lodash 包。

import lodash from 'lodash';
console.log(lodash.isArray, lodash.isArray([]));

image.png
打包后发现,在打包的逻辑中,lodash 直接从 cdn 中获取,而没有去打包 node_modules 中的 lodash。