背景
有时候我们需要在每个模块都使用一个模块,但是又不想在每个模块都引入一遍,这时就可以把模块暴露的全局。
例子
我们以 jquery 为例,将它暴露到每个模块。
先安装 jquery: yarn add jquery
不暴露时使用方法,我们必须在每个文件中通过 import 引入才能使用。如果没有 import,毫无意外会报错。
/** ./src/index.js **/
import $ from 'jquery';
console.log($);
eslint 配置
在暴露到全局时,我们需要先配置一下.eslintrc.js,因为eslint并不知道我们配置了全局变量,会导致变量为定义的错误,如下:
解决方法如下,在 globals 中添加 jquery 的’$’变量为 true,后续使用’$’时就不会报 eslint 错误了。
/** ./eslintrc.js **/
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
'airbnb-base',
],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
rules: {
"eol-last": "off",
"no-console": "off"
},
globals:{
'$':true
}
};
暴露的方法
使用 cdn
通过 cdn 可以直接在 window 中添加一个 $。
还记得我们最初配置打包参数时引入的 public 下 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>
<div id="app"></div>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
</body>
</html>
在11 行添加通过 script 标签引入 cdn 上的 js 文件。
接下来,我们在 index.js 文件中试一下,不用 import ,直接打印
/** ./src/index.js **/
console.log($);
console.log(window.$);
使用 ProvidePlugin
ProvidePlugin 插件被集成在 webpack 包中,通过这个插件可以在每个模块中注入 jquery。
但是注意的是,这边是在每个模块中注入,而不是真正意义上的暴露全局变量,并不会把模块挂到 window 上。
这种方式需要配置 webpack,如下:
/** ./webpack.config.js **/
let webpack = require('webpack');
module.exports = {
plugins:[
//...
new webpack.ProvidePlugin({
$:'jQuery'
})
]
}
例子还是刚才的例子
/** ./src/index.js **/
console.log($);
console.log(window.$);
打包后看结果
我们发现 “$” 还是能获取到的,但是 “window.$” 是获取不到的,说明他是单独注入每个模块,而不是把它声明到全局。
使用 expose-loader
最后一张是通过 expose-loader 的方式,在模块被第一次引入时暴露到全局。
expose-loader 有两种使用方式,详情见 expose,我这边简单介绍下。
- 内联
- 使用配置文件
内联
使用刚才的例子 ```javascript / ./src/index.js / / eslint-disable-next-line import/no-webpack-loader-syntax, import/no-unresolved / require(‘expose-loader?exposes=$!jquery’);
console.log($); console.log(window.$);
如例子所示 我们通过 require 引入,并且使用了特殊的语法<br />?exposes= 是后面加要声明的全局变量名<br />! 是后面加包名<br />而且因为我们配置了eslint,而eslint是不识别这种语法的,所以我们要添加:<br />/* eslint-disable-next-line import/no-webpack-loader-syntax, import/no-unresolved */<br />来忽略 eslint 检测。<br />接下来我们看打包运行结果:<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/1584826/1656045473827-8caa1776-1e3f-4005-84c1-a4d678b53462.png#clientId=u4d6efbca-2f56-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=301&id=u3b56d576&margin=%5Bobject%20Object%5D&name=image.png&originHeight=602&originWidth=2344&originalType=binary&ratio=1&rotation=0&showTitle=false&size=336173&status=done&style=none&taskId=ua4ca3f90-270a-4937-adac-ccbfffe4bac&title=&width=1172)<br />"$" 和 "window.$" 都能被获取所以暴露成功。
<a name="PXJsk"></a>
#### 使用配置文件
除了使用内联的方式,还可以使用配置文件的方式,效果一摸一样,我这边展示下配置方式。<br />在文件中引入
```javascript
require('jquery');
console.log($);
console.log(window.$);
在 webpack 配置文件中修改
/** ./webpack.config.js **/
//...
module.exports = {
module: {
rules: [
{
test:require.resolve('jquery'),
use:[
{
loader: 'expose-loader',
options: {
exposes:['$']
}
}
]
}
]
}
}
//...
打包结果
和之前一样,可以在全局访问”$”,并且被挂载到了 window 上。
剥离第三方库(externals)
有时候我们需要既可以在全局使用变量,又可以在本地模块引入,而且不会重复打包,这就需要用到 webpack 的 externals 属性。
首先我们来构造 暴露到全局的属性和本地引入的包。
这个需要使用到之前提过的 cdn 和 安装 jquery 包。我们就会有疑惑这不就重复了,没错就是重复了,所以这个方法我感觉很捞。不管,我们先实现一下:
现在 html 模版中,引入 cdn 文件,确保能全局调用。
<!-- ./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>
<div id="app"></div>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
</body>
</html>
然后安装 jquery npm 包
yarn add jquery
然后在 index 文件中实战使用一下
import $ from 'jquery';
console.log($);
console.log(window.$);
为了方便观察,我这边cdn引入的版本和npm安装的版本,jquery是不同的,
然后我们打包运行一下看结果:
很明显他把 jquery 也打包进去了,但是我们是通过 cdn 的,不需要把本地的包打进去。
再看页面:
我们发现 import 引入的 “$” 和 “window.$” 并不是一个东西,所以这很容易让人搞错。而且很难排查包版本的问题。
所以有两个问题:
- 重复打包
- 调用错乱
解决方法如下,我们在 webpack 中忽略 npm 中的包。
/** ./webpack.config.js **/
//...
module.exports = {
externals: {
'jquery':'$' // 外部的变量 不需要打包
}
}
//...
再执行打包
这时候就发现没有多余的打包了
再看页面
全局变量都被暴露出来,并且都是从 cdn 引入的。
总结一下,这个功能主要作用是将第三方库剥离出webpack的打包,提高打包效率。