打包第三方库有 4 种方法,这四种方法各有各的优缺点。
- 直接引入
缺点:每次使用到需要手工导入。 - 插件引入
如果使用 webpack.ProvidePlugin 插件引入的话,则不再需要你在模块手工引入。但没有全局变量,你在模块外是不能访问的。 - expose-loader 引入
如果想在任何地方访问此变量,需要把此变量设置为环境变量 “window.isarray”,expose-loader 可以把模块添加到全局变量上。 - cdn 引入
我们用 lodash 包来讲解一下。
yarn add lodash
直接引入
/** ./src/index.js **/
import lodash from 'lodash';
console.log(lodash.isArray, lodash.isArray([]));
我们打包一下看结果
我们发现 lodash 整个都被打包进了文件中。运行正常。
插件引入
我们改写下 入口文件,不用 import 导入,改为直接调用。
/** ./src/index.js **/
console.log(lodash.isArray, lodash.isArray([]));
很明显如果不做任何配置,打包必定会失败,如下:
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 为包的名字。
其他文件不变,我们看一下打包结果
我们发现 lodash 被打包进来了,而且运行成功。
但是有一个问题,通过 ProvidePlugin 导入的变量只是在每个模块加载的时候注入的,并没有在全局作用域中声明这个变量,如下:
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([]));
然后看打包结果
正常打印结果,并且使用 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([]));
打包后发现,在打包的逻辑中,lodash 直接从 cdn 中获取,而没有去打包 node_modules 中的 lodash。