赞
踩
前段时间接到公司任务。项目首次加载速度特别慢,首次加载要10多秒才能加载完。这个时间对于一个正常用户绝对是无法忍受的。于是开始网上查阅vue 相关速度优化资料。最终经过三天时间的摸索改造,通过使用CDN静态资源加载,重构路由模块代码,完成按需加载。最终达到生产环境首次加载平均速度1-2s 左右加载完成。4g 网络下3-4秒打开。这期间发现了很多坑,也学习到了很多东西,而且在网上找了很多资料发现都是复制黏贴的,千篇一律写的详细的也不是很多。决定更要整理一下。
项目之所以这么慢,也算是历史遗留问题了。因为来到这个公司的时候项目已经做了一部分,整个项目采用的 vue + vuex + vue-router + iview 技术栈。听同事说这个项目是从github 上找的 admin iview 下载下来改了改(那时候 admin iview 还没有收费)就直接用了(确实省事儿)。但是这个项目的配置坑还是挺多的。
这还是开启nginx 的gzip 压缩功能后的文件体积(没有开启gzip压缩的话体积会更大,具体的大小当时忘记截图了)。比较大的文件有5个:
app.css 这个css 文件是打包时抽取的所有公共样式文件
echarts.js 这个很熟悉eacharts 图表插件功能很强大,但是包也不小,所以打包的时候webpack 配置里给单独打包了一份出来
iview-area.js 省市县三级联动插件,因为所有的数据都一起打包到了里边,这个插件包也是很大
vendor.js 第三方插件打包后的文件
app.js 项目里自己写的所有代码的合集
而且项目中有很多的图片,当时记得整个项目打包完后,打成压缩包大概有 8.2M 。而且项目很大,做到现在大概有 80 多个页面。
然后先去看了下项目中的路由文件,当时的源代码的实现过程大概是这样的
通过 require.context() 方法动态来实现路由加载:
const routes = [
{ path: '*', redirect: '/index' }
];
importPages(require.context('./views', true, /\.vue$/,'lazy'))
function importPages (r) {
r.keys().forEach(key => {
routes.push({ path: (key.split('.'))[1], component: ()=>r(key)})
});
}
网上也有一些使用 require.context() 方法动态添加路由的文章比如:
戳这里查看
这个方法确实很方便,动态创建路由,不用每次去手动添加路由,根据路由规则直接写一个方法,然后去添加页面会自动读取到对应的文件。刚开始加入这个项目的时候看到这个方法我也在想确实挺方便,但是后来打包的时候每个路由对应的页面都不能单独拎出来自己生成一个文件,后来经过在网上查找相关资料,发现webpack 文档中有一段话:
英文不太好翻译过来大概意思就是: 这里的关键字是静态的。普通import语句不能在其他逻辑中动态使用或包含变量
webpack 官方链接
还有一段提示:
结论:import或者require里的路径必须是个静态的字符串,不然webpack识别不到,因为webpack打包的时候是静态分析,不是动态执行的,获取不到变量的值。因此我又花费几个小时去把路由的逻辑全部重构了一遍,全部静态引入页面,
代码如下:
{ path: '/', name: 'index', component: () => import(/* webpackChunkName: 'index' */ '@/views/index/index.vue'), meta: { unlimited: true, type: 'index' } }, { path: '/login', name: 'login', component: () => import(/* webpackChunkName: 'login' */ '@/views/common/login.vue'), meta: { unlimited: true } }, { path: '/register', name: 'register', component: () => import(/* webpackChunkName: 'register' */ '@/views/common/register.vue') },
这里的 /* webpackChunkName: ‘index’ */ 是这个文件打包以后生成的文件名,
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[name].[chunkhash].js')
},
webpack output 对象中的 chunkFilename 把原来的id 改成name 就会自动读取到 路由里的 webpackChunkName 对应的名字
然后重新打包发现每个路由都生成了对应的单独的js 文件,并且app.js 小了很多。然后谷歌浏览器的审查元素中观察,只有路由被加载了,head 标签中才会被插入对应的js 文件。
https://www.jb51.net/article/143925.htm
https://segmentfault.com/q/1010000015123542
前边app.js 文件包过大的问题解决了, 还有个 vendor.js 第三方资源包。
这个包其实就好搞了,因为这个包里主要打包的是第三方静态资源包,那我们就把这些资源包单独拿出来,通过CDN 引入页面,这个包自然就小了。
这里推荐两个CND 资源
https://www.bootcdn.cn/
https://unpkg.com/
经过使用测试发现,第一个网址上的CDN 资源加载要比第二个快,但是第一个网址上托管的CDN 并不是太多,有一些第三方插件包找不到。
第二个页面开头有一句话:unpkg是npm上所有内容的快速全球内容交付网络。也就是说所有的npm 包在这个网站上都能找到,使用方法也很简单,比如我们要找到vuex 的静态资源包:https://unpkg.com/vuex@3.1.1/ 在网址上输入package 资源名称 @ 加版本号 加 / 回车,就会自动跳出对应的静态资源,然后根据需求引入对应的文件即可。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title>document</title> <link rel="stylesheet" type="text/css" href="https://cdn.bootcss.com/iview/3.4.2/styles/iview.css"> </head> <body> <div id="app"></div> <!-- built files will be auto injected --> <script type="text/javascript" src="https://cdn.bootcss.com/vue/2.5.20/vue.min.js"></script> <script type="text/javascript" src="https://cdn.bootcss.com/iview/3.4.2/iview.min.js"></script> <script type="text/javascript" src="https://cdn.bootcss.com/echarts/4.2.1/echarts.min.js"></script> <script type="text/javascript" src="https://cdn.bootcss.com/axios/0.18.0/axios.min.js"></script> <script type="text/javascript" src="https://cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js"></script> <script type="text/javascript" src="https://cdn.bootcss.com/vuex/3.1.1/vuex.min.js"></script> <script type="text/javascript" src="https://unpkg.com/vuescroll@4.12.1/dist/vuescroll-native.min.js"></script> <script type="text/javascript" src="https://unpkg.com/iview-area@1.6.0/dist/iview-area.js" async="async"></script> </body> </html>
在webpack.base.conf.js 文件中添加如下代码
externals: {
'vue': 'Vue',
'vuex': 'Vuex',
'vue-router': 'VueRouter',
'iview': 'iView',
'iview-area': 'iviewArea',
'echarts': 'echarts',
'axios':'axios',
'vuescroll': 'Vuescroll'
},
output: {
......
}
externals 对象的作用官方给出的解释:
webpack 中的 externals 配置提供了不从 bundle 中引用依赖的方式。解决的是,所创建的 bundle 依赖于那些存在于用户环境(consumer environment)中的依赖。
怎么理解呢,意思是如果需要引用一个库,但是又不想让webpack打包(减少打包的时间),并且又不影响我们在程序中以CMD、AMD或者window/global全局等方式进行使用(一般都以import方式引用使用),那就可以通过配置externals。
然后在运行打包命令看下,vendor.js 文件包是不是比之前小了很多。
如果项目已经配置了nginx 并且开启了gzip 压缩,前端打包就没必要在开启了。
可以查看加载的资源中
Response Headers 下的content-encoding: 如果包含gizp 的话说明已经开启gzip 压缩。
前端开启的话先打开 config/index.js 文件:
然后先安装
npm install --save-dev compression-webpack-plugin
然后把productionGzip 改成true 就可以了
productionGzip: true
图片中的 productionSourceMap 如果为true 的话,也要改成false ,这个属性是用来控制打包的时候生成map 文件的。
它会将所有 required 的 *.css 模块抽取到分离的 CSS 文件。 所以你的样式将不会内联到 JS bundle,而是在一个单独的 CSS 文件。如果你的样式文件很大,这样会提速,因为 CSS bundle 和 JS bundle 是平行加载的
但是如果你的项目太大,抽取到的app.css 文件包太大的话单独加载这个文件也是很影响速度的,可以尝试关掉抽取css 功能
在webpack.prod.conf.js 文件中把allChunks 改成false 重新打包app.css 文件将会消失,样式会一起打包到js 文件中。
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css'),
// Setting the following option to `false` will not extract CSS from codesplit chunks.
// Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
// It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
// increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
// 把allChunks 改成false
allChunks: false,
}),
这里推荐一个很好的图片压缩网站,可以把项目中所有的图片用这个工具压缩一遍。
这期间我把现有项目配置升级到了vue-cli3,具体步骤这里不再多说,基本上和vue-cli2 的配置区别不大。因为vue-cli3 内部封装的是 webpack4, vue-cli2 中封装的是webpack3。 众所周知,webpack4 无论是从打包速度还是代码压缩拆包方面都比webpack3 优秀很多,所以才尝试了下,效果确实不错,webpack4 打包后 会生成 对应的单独的js 和css 文件,而且事实证明 webpack4 打包后的代码 比webpack3 打包后的代码在浏览器运行的更快,更流畅。如果是新开的项目,强烈建议使用vue-cli3 来创建。由于我们项目是 老项目,升级以后有好多配置不兼容 和eslint 规则不匹配问题,怕影响开发中项目的稳定性和代码规范性等问题,最终放弃了升级到vue-cli3。
http://www.west.cn/docs/39126.html
到此为止这篇博客就写完了,如果有写的不对的地方,或者其他同学有更好的方法欢迎指正交流。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。