Webpack--externals(外部扩展)详解
externals用法详解
在 《解决Vue项目打包后文件过大问题》一文中提到了借用Webpack-externals配置项,在打包时将依赖独立出来,本文详细讲解下externals的使用。
externals 配置项提供了阻止将某些 import 的包(package)打包到 bundle 中的功能,在运行时(runtime)再从外部获取这些扩展依赖(external dependencies)
externals用法:
module.exports={
configureWebpack:congig =>{
externals:{
key: value
}
}
}
语法说明:
-
key是第三方依赖库的名称,同package.json文件中的dependencies对象的key一样
-
value值可以是字符串、数组、对象。应该是第三方依赖编译打包后生成的js(要用CDN引入的js文件)文件,执行后赋值给window的全局变量名称。
那么,如何找这个全局变量呢?以element-ui为例:找到element-ui的CDN资源,如果是压缩后的文件,可以使用js在线格式化工具进行处理。格式化后代码如下:
function(e, t) {
"object" == typeof exports && "object" == typeof module ? //判断环境是否支持commonjs模块规范
module.exports = t(require("vue")) :
"function" == typeof define && define.amd ? //判断环境是否支持AMD模块规范
define("ELEMENT", ["vue"], t) :
"object" == typeof exports ? //判断环境是否支持CMD模块规范
exports.ELEMENT = t(require("vue")) :
e.ELEMENT = t(e.Vue)
} ("undefined" != typeof self ? self: this,function(e){
//省略...
});
从代码可以看出element-ui是用UMD模块规范输出的,所以
- 兼容commonjs模块规范(在node环境中使用)。
- 兼容AMD模块规范(用require.js引入使用)。
- 兼容CMD模块规范(用sea.js引入使用)。
代码中对各个环境做了判断,因为是用CDN在public/index.html引入,也就是浏览器环境,不符合上面各种环境,最后执行
e.ELEMENT = t(e.Vue)
其中e为window对象,那么赋值给window的全局变量名称是ELEMENT。
在vue.config.js中配置如下:
module.exports={
configureWebpack:{
externals: {
'element-ui': 'ELEMENT',
}
}
}
在public/index.html中引入
<body>
<div id="app"></div>
<script src="https://unpkg.com/element-ui@2.10.1/lib/index.js"></script>
</body>
运行后,发现报错了,如下图:
再看一下e.ELEMENT = t(e.Vue)
,发现还需要Vue,那么把Vue依赖包也提取出来。Vue编译打包生成js的文件,格式化后如下:
function(t, e) {
"object" == typeof exports && "undefined" != typeof module ?
module.exports = e() :
"function" == typeof define && define.amd ?
define(e) :
(t = t || self).Vue = e()
} (this,function(){
//省略...
})
在浏览器中最后执行(t = t || self).Vue = e(),其中t为this,this是window对象。那么赋值给window的全局变量名称是Vue。 在vue.config.js中配置如下:
module.exports={
configureWebpack:{
externals: {
'element-ui': 'ELEMENT',
'vue': 'Vue',
}
}
}
在public/index.html中引入
<body>
<div id="app"></div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.runtime.min.js"></script>
<script src="https://unpkg.com/element-ui@2.10.1/lib/index.js"></script>
</body>
那么,如何判断外部资源是否成功引入了呢?在不好辨别插件赋值给window的全局变量名称是什么时候,又该如何处理呢?以xlsx为例:
var XLSX = {};
function make_xlsx_lib(e){
//省略...
}
if (typeof exports !== "undefined") make_xlsx_lib(exports);
else if (typeof module !== "undefined" && module.exports) make_xlsx_lib(module.exports);
else if (typeof define === "function" && define.amd) define(function() {
if (!XLSX.version) make_xlsx_lib(XLSX);
return XLSX
});
else make_xlsx_lib(XLSX);
var XLS = XLSX,
从上面的代码中不好判断赋值给window的全局变量名称是什么了,先猜测用的是XLSX
,在public/index.html中引入:
<body>
<div id="app"></div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.runtime.min.js"></script>
<script src="https://unpkg.com/element-ui@2.10.1/lib/index.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/xlsx/0.16.1/xlsx.min.js"></script>
</body>
把public/index.html这个文件丢到浏览器打开,然后在控制台输入window.XLSX
,看看有没有值。输出如下:window.XLSX
输出有值,不为undefined,说明赋值给window的全局变量名称是XLSX
。那么在vue.config.js中配置如下:
module.exports={
configureWebpack:{
externals: {
'element-ui': 'ELEMENT',
'vue': 'Vue',
'xlsx': 'XLSX'
}
}
}
再次执行npm run dev
,刷新页面无报错。
用externals提取第三方依赖包后,代码原先引入依赖的地方,要不要去改动呢?比如main.js中:
import ElementUI from 'element-ui';
Vue.use(ElementUI);
正常是不需要改动的。如果把<script src="https://unpkg.com/element-ui@2.10.1/lib/index.js"></script>
放在app.js后面引入:
<body>
<div id="app"></div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.runtime.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/xlsx/0.16.1/xlsx.min.js"></script>
</body>
<script src="https://unpkg.com/element-ui@2.10.1/lib/index.js"></script>
编译打包后,dist/index.html生成内容如下:
此时刷新浏览器,报错如下:
删除main.js中的引用,删除后,刷新浏览器,无报错。
import ElementUI from 'element-ui';
Vue.use(ElementUI);
前面提到element-ui的CDN链接放在app.js后面引入,main.js里面的代码都会编译打包到app.js中,在执行app.js时会遇到element-ui
。但是,由于element-ui的CDN链接是放在app.js后面加载执行的,这时ElementUI是不存在的,所以会报错。
所以只要保证element-ui的CDN链接放在app.js之前加载,就不会报错。换句话来说,只要在main.js中需要引入的依赖,其CDN链接都要放在app.js之前加载,这样就不需要去改变原来引入依赖的代码。
但是,app.js是入口文件,放在app.js之前加载,会不会阻塞app.js的加载和执行呢?其实并有没有影响,因为app.js已经用link标签做了预加载。js文件的加载是并发的,谁先加载完先执行谁。
我们的最终目的是减少http请求资源大小,用externals提取第三方依赖包时,提取的过细将会增加http请求数量。
更多推荐
所有评论(0)