??四年前端教你使用externals解决chunk-vendors.js过大问题
红尘炼心 2020/6/14 11:25:41
本文用项目例子是用Vue Cli3搭建的Vue项目
如果小伙伴有做过首屏加载时间优化,应该会遇到chunk-vendors.js这个文件,巨大无比,加载时间超长,是首屏加载时间过长的罪魁祸首之一。
下面通过一个实际的项目来演示,先通过插件webpack-bundle-analyzer来可视化地查看chunk-vendors.js这个文件里面地内容。
npm install webpack-bundle-analyzer --save-dev 复制代码
在vue.config.js中引入这插件
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; module.exports={ configureWebpack:config =>{ return { plugins:[ new BundleAnalyzerPlugin() ] } } } 复制代码

在上面的分析图中我们可以看到,最大的两个js文件,里面都是一些第三方依赖包,那么只要把这些依赖包提取出来,就可以解决chunk-vendors.js过大的问题。下面就教你使用externals
来提取这些依赖包,其实应该说用externals
来防止这些依赖包被打包。
externals
的值是个对象,
module.exports={ configureWebpack:congig =>{ externals:{ key: value } } } 复制代码
其中key是第三方依赖库的名称,同package.json文件中的dependencies
对象的key一样。下图红框所示

说起externals,网上已经很多教程了,但是讲解都不是很详细,特别是对value值的讲解。如果value值不正确导致的报错,也不知道怎么去排查和修复。
按我的理解value值应该是第三方依赖编译打包后生成的js文件,然后js文件执行后赋值给window的全局变量名称。
那怎么找这个全局变量名称呢,教你一个方法。
上面所说的js文件就是要用CDN引入的js文件。那么可以通过浏览器打开CDN链接。由于代码是压缩过,找个在线js格式化把代码处理一下,就可以阅读代码了。
例如要提取element-ui这个依赖包。其CDN链接是unpkg.com/element-ui@…,格式化后代码如下,是个自执行函数。
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文件的CDN链接是cdn.bootcdn.net/ajax/libs/v…, 格式化后代码如下,是个自执行函数。
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://unpkg.com/element-ui@2.10.1/lib/index.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.runtime.min.js"></script> </body> 复制代码
重新npm run dev
后刷新页面,发现还是报错。

回到public/index.html,vue在element-ui后面引入,难怪会报错,vue一定要在element-ui之前引入,修改一下
<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> 复制代码
刷新页面,没有报错了。
这样讲解是不是很清晰了,知道value值是从哪里来的。遇到错误知道排查和修复。
接下来继续把vue-router依赖和xlsx依赖提取了
vue-router编译打包生成js文件的CDN链接是cdn.bootcdn.net/ajax/libs/v…, 格式化后代码如下。
var t, e; t = this, e = function(){ //省略... }, "object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define(e) : (t = t || self).VueRouter = e(); 复制代码
在浏览器中最后执行((t = t || self).VueRouter = e()
,其中t为this,this是window对象。那么赋值给window的全局变量名称是VueRouter
。
在vue.config.js中配置如下
module.exports={ configureWebpack:{ externals: { 'element-ui': 'ELEMENT', 'vue': 'Vue', 'vue-router': 'VueRouter', } } } 复制代码
在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://cdn.bootcdn.net/ajax/libs/vue-router/3.2.0/vue-router.min.js"></script> <script src="https://unpkg.com/element-ui@2.10.1/lib/index.js"></script> </body> 复制代码
重新npm run dev
后刷新页面,正常无报错,继续提取xlsx依赖包。
xlsx编译打包生成js文件的CDN链接是cdn.bootcdn.net/ajax/libs/x…, 格式化后代码如下。
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://cdn.bootcdn.net/ajax/libs/vue-router/3.2.0/vue-router.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
,看看有没有值。

有值不为undefined
,说明赋值给window的全局变量名称是XLSX
。那么在vue.config.js中配置如下
module.exports={ configureWebpack:{ externals: { 'element-ui': 'ELEMENT', 'vue': 'Vue', 'vue-router': 'VueRouter', '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后面引入。
在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://cdn.bootcdn.net/ajax/libs/vue-router/3.2.0/vue-router.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

<script src="https://unpkg.com/element-ui@2.10.1/lib/index.js"></script>
已经放在app.js后面引入。
刷新浏览器,就发现报错

只要你把main.js中去掉这段代码就行。
import ElementUI from 'element-ui'; Vue.use(ElementUI); 复制代码
刷新浏览器,无报错。
网上很多教程都讲到要把这段代码去掉,不然会报错,其实错误原因根本不在这里。
前面提到element-ui的CDN链接放在app.js后面引入,但是main.js里面代码都会编译打包到app.js中,执行app.js会遇到
import ElementUI from 'element-ui'; Vue.use(ElementUI); 复制代码
由于你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文件的加载是并发,谁先加载完先执行谁。

用externals提取完第三依赖包后,执行npm run build
,分析图如下

和之前对比,两个大文件的一个已经消失,剩下一个只有81.42KB,和之前的1003.44KB相比是不是小了很多。再看里面的内容,之前的elemen-ui、jquery.js、vue.runtime.esm.js、xlsx.js这些依赖包都消失。优化成效明显。
用externals提取第三方依赖包时,需切记中庸之道。虽然我们的最终目的是减少http请求资源大小,但是过犹不及,提取的过细将会增加http请求数量。

关于找一找教程网
本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。
本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。
[??四年前端教你使用externals解决chunk-vendors.js过大问题]http://www.zyiz.net/tech/detail-139472.html
- 2023-03-22接口思维:如何使用 Context API 构建灵活、可维护的 React 组件-icode9专业技术文章分享
- 2023-03-22掌握 ReactJS Hooks:现代 Web 开发综合指南-icode9专业技术文章分享
- 2023-03-12热点面试题:Vue2、3 生命周期及作用?
- 2023-03-06【备战春招】第19天 新版 Node.js+Express+Koa2 开发Web Server博客 10-13
- 2023-02-14【备战春招】第6天 6-14 Vue3如何实现响应式
- 2023-02-14【备战春招】第六天+vue复习
- 2023-02-14【备战春招】第6天 新版 Node.js+Express+Koa2 开发Web Server博客 8-8~8-9
- 2023-02-13【老卫拆书】009期:Vue+Node肩挑全栈!《Node.js+Express+MongoDB+Vue.js全栈开发实战》开箱
- 2023-02-13【备战春招】第5天 React零基础入门到实战,完成企业级项目简书网站开发——React基础篇
- 2023-02-13【备战春招】第5天 新版 Node.js+Express+Koa2 开发Web Server博客 8-6