首屏加载慢原因:Vue只有第一次会加载页面, 以后的每次页面切换,只需要进行组件替换。因为Vue 是SPA,所以首页第一次加载时会把所有的组件以及组件相关的资源全都加载了,造成网站首页打开速度变慢的问题 (打包构建时 js包会很大)*
1、路由懒加载
懒加载:把代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或即将引用另外一些新的代码块。
通过import()
引用的子模块会被单独分离出来,打包成一个单独的文件(打包出来的文件被称为chunk )依照webpack
原本的打包规则打包项目,我们就无法确定子模块在打包出来的哪个JS文件中,而且子模块的代码会和其他代码混合在同一个文件中。这样就无法进行懒加载操作。所以,要实现懒加载,就得保证懒加载的子模块代码单独打包在一个文件中。
懒加载(按需加载)原理分两步:
将需要进行懒加载的子模块打包成独立的文件children chunk
借助函数来实现延迟执行子模块的加载代码
实现方法:
// 在进入路由后才会加载这个 chunk 文件component: () => import('@/views/emOrderHandle/eeOrderTxzSocTwoHandle') // 每个组件打包成一个js文件
// 指定了相同的webpackChunkName,会合并打包成一个js文件。 把组件按组分块() => import(/* webpackChunkName: "orderHandleRoute" */ './UserDetails.vue')() => import(/* webpackChunkName: "orderHandleRoute" */ './eeOrderTxzSocTwoHandle.vue')
2、CDN链接&externals
默认的打包规则会将开发环境中npm
引入的依赖打入项目公共模块集合中 即chunk-vendors.js
在页面首次加载时会被下载。通过减小公共依赖包体积达到优化首屏速度的目的
externals
的作用在于防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部(CDN方式)获取这些扩展依赖,我们可以在bootcdn
网站上找到相关包的CDN链接
// externals 排除不需要打包的依赖包module.exports = {// vue/cli 中在此处管理 webpack 配置configureWebpack: (config) => {externals: {'vue': 'Vue','vue-router': 'VueRouter','vuex': 'Vuex''element-ui': 'ELEMENT','axios': 'axios'}}}
externals
处理之后就不会出现在打包之后的 js 包中了, 接下来还需要在vue
的入口html
中加入CDN链接
<!-- index.html --><script src="/xxx.min.js"></script><script src="/xxx.min.js"></script><script src="/xxx.min.js"></script><link href="https://xxx/index.css">
3、使用splitChunks
插件,配置分离规则
用SplitChunks
插件来控制Webpack
打包生成的js
文件的内容的精髓就在于,防止模块被重复打包,拆分过大的js
文件,合并零散的js
文件。最终的目的就是减少请求资源的大小和请求次数。
// splitChunks 的默认配置module.exports = {chainWebpack(config) {config.optimization.splitChunks({chunks: 'async',minSize: 30000,maxSize: 0,minChunks: 1,maxAsyncRequests: 6, // 同时也限制了同一 priority 最大分块次数maxInitialRequests: 4,automaticNameDelimiter: '~',cacheGroups: {vendors: {name: 'chunk-vendors',test: /[\\/]node_modules[\\/]/,priority: -10,chunks: 'all'},common: {name: 'chunk-common',minChunks: 2,priority: -20,chunks: 'initial',reuseExistingChunk: true}}}}}
在默认配置下(chunks === 'async')
:main.js中异步加载或间接异步加载的模块,都会被另外打包生成一个js文件
// 异步加载的模块:通过import('xxx') 或 require(['xxx'],() =>{}) 加载的模块。// 同步加载的模块:通过 import xxx 或 require('xxx') 加载的模块。
根据cacheGroups
中对vendors
的设定:
test
匹配项目从node_modules
中加载的模块并根据chunks
的设定提取打包当chunks: 'initial'
时表示:node_modules
在项目中被异步或同步加载多少次,那这个模块就被提取多少次,分别打包到不同文件中。当chunks: 'all'
时表示:node_modules
不管异步加载还是同步加载的模块都是提取打包到一个文件中。
可以看到所有的node_modules
模块都被提取到chunk-vendors
文件中去了
下一步继续提取chunk-vendors
减小它的体积:可以将诸如element-ui
、xlsx
等模块提取为一个个独立的文件
cacheGroups: {vendors: {name: 'chunk-vendors',test: /[\\/]node_modules[\\/]/,priority: -10,chunks: 'all'},common: {name: 'chunk-common',minChunks: 2,priority: -20,chunks: 'initial',reuseExistingChunk: true},// 继续添加其他提取规则 注意 priority 要比 vendors 大才能提取出来'element-ui': {name: 'element-ui',test: /[\\/]element-ui[\\/]/,chunks: 'all',priority: 0},'moment': {name: 'moment',test: /[\\/]moment[\\/]/,chunks: 'all',priority: 0},'xlsx': {name: 'xlsx',test: /[\\/]xlsx[\\/]/,chunks: 'all',priority: 0},'lodash': {name: 'lodash',test: /[\\/]lodash[\\/]/,chunks: 'all',priority: 0}}
还可以继续提取出现在多个chunk
中的文件
CoEffectReportComponents: {name: 'CoEffectReportComponents',test: /[\\/]CoEffectReportComponents[\\/]/,reuseExistingChunk: true,priority: 0},dataEntryInput: {name: 'dataEntryInput',test: /[\\/]dataEntryInput[\\/]/,reuseExistingChunk: true,priority: 0}
SplitChunks插件配置选项
chunks
选项,决定要提取那些模块。默认是
async
:只提取异步加载的模块出来打包到一个文件中。异步加载的模块:通过
import('xxx')
或require(['xxx'],() =>{})
加载的模块。
initial
:提取同步加载和异步加载模块,如果xxx在项目中异步加载了,也同步加载了,那么xxx这个模块会被提取两次,分别打包到不同的文件中。同步加载的模块:通过
import xxx
或require('xxx')
加载的模块。all
:不管异步加载还是同步加载的模块都提取出来,打包到一个文件中。
minSize
选项:规定被提取的模块在压缩前的大小最小值,单位为字节,默认为30000,只有超过了30000字节才会被提取。
maxSize
选项:把提取出来的模块打包生成的文件大小不能超过maxSize值,如果超过了,要对其进行分割并打包生成新的文件。单位为字节,默认为0,表示不限制大小。
minChunks
选项:表示要被提取的模块最小被引用次数,引用次数超过或等于minChunks值,才能被提取。
maxAsyncRequests
选项:最大的按需(异步)加载次数,默认为 6。
maxInitialRequests
选项:打包后的入口文件加载时,还能同时加载js文件的数量(包括入口文件),默认为4。先说一下优先级
maxInitialRequests
/maxAsyncRequests
<maxSize
<minSize
。
automaticNameDelimiter
选项:打包生成的js文件名的分割符,默认为~
。
name
选项:打包生成js文件的名称。
cacheGroups
选项,核心重点,配置提取模块的方案。里面每一项代表一个提取模块的方案。下面是cacheGroups
每项中特有的选项,其余选项和外面一致,若cacheGroups
每项中有,就按配置的,没有就使用外面配置的。
test
选项:用来匹配要提取的模块的资源路径或名称。值是正则或函数。priority
选项:方案的优先级,值越大表示提取模块时优先采用此方案。默认值为0。reuseExistingChunk
选项:true
/false
。为true
时,如果当前要提取的模块,在已经在打包生成的js文件中存在,则将重用该模块,而不是把当前要提取的模块打包生成新的js文件。enforce
选项:true
/false
。为true
时,忽略minSize
,minChunks
,maxAsyncRequests
和maxInitialRequests
外面选项
4、开启 gzip 压缩
gzip压缩能够缩小一倍多的体积,是非常有效的优化手段,兼容性也很好
响应头里显示有Content-Encoding: gzip
,表示浏览器支持并且启用了Gzip压缩的资源
代码层压缩
// CompressionPlugin 插件在 vue/cli 中的使用module.exports = {configureWebpack: (config) => {if (process.env.NODE_ENV === 'production') {return {plugins: [new CompressionPlugin({test: /\.js$|\.html$|\.css$|\.jpg$|\.jpeg$|\.png/, // 需要压缩的文件类型threshold: 10240, // 归档需要进行压缩的文件大小最小值 10kdeleteOriginalAssets: false // 是否删除原文件})]}}}}
服务端压缩 (nginx)
http {gzip on; # 开启gzip压缩gzip_min_length 4k; # 小于4k的文件不会被压缩,大于4k的文件才会去压缩gzip_buffers 16 8k; # 处理请求压缩的缓冲区数量和大小,比如8k为单位申请16倍内存空间;使用默认即可,不用修改gzip_http_version 1.1; # 早期版本http不支持,指定默认兼容,不用修改gzip_comp_level 2; # gzip 压缩级别,1-9,理论上数字越大压缩的越好,也越占用CPU时间。实际上超过2的再压缩,只能压缩一点点了,但是cpu确是有点浪费。因为2就够用了gzip_types text/plain application/x-javascript application/javascript text/javascript text/css application/xml application/x-httpd-php image/jpeg image/gif image/png application/vnd.ms-fontobject font/x-woff font/ttf; # 压缩的文件类型 MIME类型gzip_vary on; # 是否在http header中添加Vary: Accept-Encoding,一般情况下建议开启}
多层nginx
代理时gzip
不生效的坑:
# 第一层http {gzip_static on;gzip_http_version 1.1;gzip_proxied expired no-cache no-store private auth;gzip_vary on;}# 第二层http {gzip_static on;gzip_http_version 1.0;gzip_proxied expired no-cache no-store private auth;gzip_vary on;}