如何通过webpack定义全局模块

不知道大家在开发中有没有遇到这种情况,就是在很多模块里都要用到某一个库,于是在这些模块里都要不厌其烦地import一遍。页面少也还好,当项目庞大到一定程度,就有些讨厌了。有没有办法不要每次使用前都import,而是有个全局的引入,在其他任何地方直接使用就行了呢?有,webpack可以帮我们,它的内置插件ProvidePlugin就是为这个而生的。

1. 用法

在webpack配置文件中配置 webpack.ProvidePlugin 插件。
语法:

1
2
3
new webpack.ProvidePlugin({
identifier: 'modulename'
})

如果像上面一样,直接指定模块名,确保你的模块就在当前目录下,或者是通过npm、yarn安装的。因为这么写,默认模块路径为当前目录./**node_modules,webpack只会从这两个目录去加载模块(先找当前目录,找不到找node_modules目录)。

另外如果是导入ES6modules,需要指定模块的默认导出属性:

1
2
3
new webpack.ProvidePlugin({
identifier: ['modulename','propertyname']
})

你也可以自己指定模块的完整绝对路径。这样你的模块可以放在工程的任何目录:

1
2
3
new webpack.ProvidePlugin({
identifier: path.resolve(__dirname, somepath ,'modulename')
})

例如我在项目中要用到jQuery, Vue, mili(项目中的自己开发的工具模块),这几个模块在很多地方都要使用,我就可以把他们申明在 ProvidePlugin 中:

1
2
3
4
5
6
7
8
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
'window.$': 'jquery',
Vue: ['vue/dist/vue.esm.js', 'default'],
mili: path.resolve(__dirname, 'src', 'mili')
})
]

2. 原理

ProvidePlugin是webpack内置的插件,在里面申明过的模块变量名,都可以在工程中任何模块中直接独立使用。因为webpack编译后,在代码中遇到独立出现的这些全局变量名时,会去自动加载它对应的模块,并把这个模块导出的内容(或者导出的某个指定的属性)赋给这个模块变量名。

3. 应用

当我在ProvidePlugin中申明过全局模块变量后,在其他模块,我可以直接使用它们,而不用在头部import了。
例如就上面例子中我申明了$, window.$, Vue, mili 四个全局模块变量,我就可以直接这么使用了:

1
2
3
4
5
6
7
// any module file
// free to use $, window.$, Vue, mili,without importing them.

let {Message} = mili;
let $toot = $('#root');
let $ele = window.$('#container');
Vue.prototype.$bus = new Vue;

番外

除了webpack.ProvidePlugin,还有其他方法实现全局模块变量,当作番外简单介绍一下吧。

1. html引用

我们还可以通过在index.html模版中通过script标签引入全局模块。这个模块的路径可以是CDN地址,也可以是本地路径。
然后在每个模块中就都能使用这个模块了。

引用CDN

1
2
3
4
//index.html file
<head>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
</head>

引用本地

前提是这个文件在项目发布的时候服务器上要有,所以记得在webpack上配置 copy-webpack-plugin 插件。

1
2
3
4
//index.html file
<head>
<script src="/vendor/jquery_3.4.1.js"></script>
</head>

这样在其他模块都可以直接使用window.$$了。

1
2
console.log(window.$);
console.log($);

2. expose-loader

expose-loader 虽然是一个比较老的包,但是依然可用。它的作用是给全局对象window添加模块。

先安装:npm i -S expose-loader

可以内联使用

在入口文件导入全局模块:

1
require('expose-loader?$!jquery');

或则

1
import $ from 'expose-loader?$!jquery';

也可以在webpack中配置

1
2
3
4
5
6
7
8
9
10
11
module: {
rulers: [
{
test: require.resolve('jquery'),
use: {
loader: 'expose-loader',
options: '$'
}
}
]
}

不论使用哪种方式,最后在任何模块都可以通过window.$来使用jquery了:

1
console.log(window.$);


GOODLUCK!