在使用vue开发时,都是使用vue脚手架工具,其webpack配置文件已经将大部分性能优化了,比如代码压缩,图片base64转换,代码分离等,这些我们统统不需要去管。但使用vue时,其实还是有些细节点可以优化的,本文总结了一些可优化点以及优化方案。
01
—
图片处理
1、图片压缩
推荐使用图片压缩插件compressor.js,实现了在上传前对图片进行压缩,同时该插件默认会自动调整方向:
npm install compressorjs//安装
import Compressor from "compressorjs";//引入
//new Compressor(file[, options])//使用
new Compressor(file, {
quality: 0.6,//压缩品质,0-1,压缩后的质量与比率随着这个数值变化而变化,默认0.8,推荐0.8和0.6
maxWidth: 800,//最大宽度;
maxHeight: 500,//最大高度,宽高还是原图的比例,也可设置最低宽高或者固定宽高;
success(result) {//执行成功的方法,还有一些其他方法,比如执行前等;
const formData = new FormData();
formData.append('file', result, result.name);
},
error(err) {
this.message.error(err.message);
},
});
复制
2、图片的懒加载
即延迟加载,图片加载完成前使用备用图片或者纯色背景来代替从而避免一些无谓的性能开销,这里使用了lazysizes.min.js插件:
npm i vue-lazyload -S 安装
Vue.use(VueLazyload, {
preLoad: 1.3,//预加载
error: 'dist/error.png',//加载失败使用的图片
loading: 'dist/loading.gif',//加载中使用的图片
attempt: 1 尝试次数
})
<script src="https://oss-emcsprod-public.modb.pro/wechatSpider/modb_20210624_18b91f24-d500-11eb-8d30-00163e068ecd.png" async></script> 或者直接引入
<img data-src="img.jpg" class="lazyload" width="500" height="500"/> 使用
复制
3、图片的渐进式
要求每张图片都需要他的一张模糊略缩图,图片实现由模糊至清晰展示,插件使用了progressive-image:
4、图片的WebP 格式
WebP 的优势体现在它具有更优的图像数据压缩算法,能带来更小的图片体积,而且拥有肉眼识别无差异的图像质量;同时具备了无损和有损的压缩模式、Alpha 透明以及动画的特性,在 JPEG 和 PNG 上的转化效果都相当优秀、稳定和统一。但是某些浏览器无法兼容,所以经常会保存图片的两种格式,通过判断该浏览器是否支持WebP 格式而选择性展示。
5、雪碧图
雪碧图就是将许多个小图标合并成一个大图标,根据位置展示对应的图标,使用的时候需要(1)设置好一个固定的宽高;(2)需要设置background-position的值(默认为(0,0),也就是图片的左上角),即移动图片到自己想要的图标位置。
6、使用字体图标代替图片,减少http请求数量
02
—
移除阻塞渲染的资源
准确的来说可以算是一种规范,要求:
1、引入<script>标签需要加上defer或者async属性
defer:标识脚本可以延迟到文档完全被解析和显示之后再执行。只对外部脚本文件有效。
async:表示应该立即下载脚本,但不应妨碍页面中的其他操作,比如下载其他资源或等待加载其他脚本,只对外部脚本文件有效。
2、<link>标签需要加上disabled标签或者media标签
media:media属性规定被链接文档将显示在什么设备上,为不同的媒体类型规定不同的样式。
disabled:disabled表示不加载,通常动态修改是否加载。
3、移除未使用的Css和Js文件
03
—
预加载关键请求
建议使用 `<link rel=preload>` 来优先提取当前在网页加载后期请求的资源。
可以指明哪些资源是在页面加载完成后即刻需要的。对于这种即刻需要的资源,你可能希望在页面加载的生命周期的早期阶段就开始获取,在浏览器的主渲染机制介入前就进行预加载。这一机制使得资源可以更早的得到加载并可用,且更不易阻塞页面的初步渲染,进而提升性能。
preload和prefetch的出现为我们提供了可以更加细粒度地控制浏览器加载资源的方法。
preload
1.preload加载的资源是在浏览器渲染机制之前进行处理的,并且不会阻塞onload事件;
2.preload可以支持加载多种类型的资源,并且可以加载跨域资源;
3.preload加载的js脚本其加载和执行的过程是分离的。即preload会预加载相应的脚本代码,待到需要时自行调用;
prefetch
prefetch是一种利用浏览器的空闲时间加载页面将来可能用到的资源的一种机制;通常可以用于加载非首页的其他页面所需要的资源,以便加快后续页面的首屏速度;
04
—
router的懒加载
未使用路由懒加载,打包后生成一个app.js文件,首次加载时载入;使用路由懒加载,打包后生成多个js文件,每个路由对应的页面首次加载只会加载当前页面需要的js文件并缓存,这也是按需加载;
按需加载,即只有等到在页面里显示该组件的时候才会从服务器加载并缓存,不显示的话就不会加载。vue有三种方式,
1. vue异步组件技术;
2. es提案的import();(需要webpack > 2.4);
3. webpack提供的require.ensure();
这里使用了webpack提供的require.ensure(),每个页面下的组件封装到一个js文件中:
1、我这里首先需要安装一个dynamic-import-webpack,用以解析识别import()动态导入语法---并非转换,而是解析识别:
npm install babel-plugin-syntax-dynamic-import//安装
.babelrc文件添加:
{
"plugins": ["syntax-dynamic-import"]
}
复制
2、然后使用webpack提供的require.ensure()去配置路由,第一个参数为组件的路径,第二个参数为打包为js的名称:
const HomePage = resolve => require.ensure([], () => resolve(require('@/views/HomePage')), 'HomePage')
const BookManagePage = resolve => require.ensure([], () => resolve(require('@/views/BookManagePage')), 'BookManagePage')
const PreceptsPage = resolve => require.ensure([], () => resolve(require('@/views/PreceptsPage')), 'PreceptsPage')
......
复制
//vue.config.js文件里边配置
module.exports = {
chainWebpack: config => {
config.plugins.delete("prefetch") //取消首页加载其他页面的js
}
}
复制
3、build后你会发现,每个路由对应有自己的js,只有当你访问当前页面时,当前页面所需的js才会加载:
05
—
移除.map文件
map文件是js文件压缩后,文件的变量名替换对应、变量所在位置等元信息数据文件,一般这种文件和js主文件放在同一个目录下。比如压缩后原变量是map,压缩后通过变量替换规则可能会被替换成a,这时map文件会记录下这个mapping的信息,这样的好处就是说,在调试的时候,如果有一些JS报错,那么浏览器会通过解析这个map文件来重新merge压缩后的js,使开发者可以用未压缩前的代码来调试,这样会给我们带来很大的方便!而这种还原性调试功能,目前只有chorme才具有。
这种文件是帮助调试用的,正式站其实作用不大,而且处于安全考虑,可以直接移除。方法就是在配置文件里边修改一参数:
module.exports = {
productionSourceMap: false
}
复制
06
—
compression-webpack-plugin打包文件
webpack的CompressionWebpackPlugin插件可以在打包的时候帮我们生成gzip压缩文件,可以很大程度减少包的大小,非常适合于上线部署,更小的体积对于用户体验来说就意味着更快的加载速度以及更好的用户体验。
1、安装
npm install compression-webpack-plugin --save-dev
复制
2、修改配置
在bulid/webpack.base.conf.js文件中(vue.config.js)配置:
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const productionGzipExtensions = ['js', 'css']
module.exports ={
configureWebpack: {
plugins: [
new CompressionWebpackPlugin({
algorithm: 'gzip',
test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
threshold: 10240,
minRatio: 0.8,
deleteOriginalAssets: false
})
]
}
}
复制
3、打包
最后服务器端需要启用gzip,就ok了
07
—
引入CDN
从上图中可以看到,造成加载时间过慢的元凶之一是vendor文件,该文件存放的是项目中所有的第三方依赖。
然后使用webpack-bundle-analyzer插件打包后看到的文件组织构图如下,可以看出每个文件的大小(这个是打包后的,打包前足足有8、9M):
引入一个库又不想让webpack打包,通过CDN引入,引入的话其实很简单:
1、引入
index.html页面的body里边引入需要引入的cdn,我这边把vue、vuex、element、moment、echarts给使用CDN引入,打包不需要打包这些:
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script src="https://cdn.bootcss.com/vuex/3.0.1/vuex.min.js"></script>
<script src="https://cdn.bootcss.com/element-ui/2.12.0/index.js"></script>
<script src="https://cdn.bootcss.com/moment.js/2.24.0/moment.min.js"></script>
<script src="https://cdn.bootcss.com/echarts/4.2.1/echarts.min.js"></script>
</body
复制
2、配置
在bulid/webpack.base.conf.js文件中(vue.config.js)配置,即说明这些在打包的时候不需要被打包进去:
module.exports ={
configureWebpack: {
plugins: [
externals: {
vue: 'Vue',
vuex: 'Vuex',
element-ui: 'ELEMENT',
moment: 'moment',
echarts: 'echarts'
}
]
}
}
复制
3、注释
可以把在main.js里边import引入的这些给注释掉了。
最后这里可以简单看看webpack-bundle-analyzer的使用:
npm intall webpack-bundle-analyzer –save-dev//安装
//配置(在build/webpack.prod.config.js或者vue.config.js中)
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
new BundleAnalyzerPlugin(),
//启动
npm run build --report
//查看
浏览器打开127.0.0.1:8888地址可以看到一下效果图
复制
08
—
预渲染
那么进一步提高首屏加载的方案还有两个,一个是预渲染,一个是SSR,即服务端渲染,后者的方案较为复杂,采用预渲染的方式进一步加快首屏加载:
1、安装
prerender-spa-plugin 需要翻墙安装会失败,这里可以使用淘宝镜像:
cnpm install --save prerender-spa-plugin
//因为在执行安装的过程中需要执行install.js,这里会下载Chromium,官网建议是进行跳过,我们可以执行 —ignore-scripts 忽略这个js执行
npm i --save puppeteer --ignore-scripts
复制
2、配置
在bulid/webpack.base.conf.js文件中(vue.config.js)配置:
const PrerenderSPAPlugin = require('prerender-spa-plugin')
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer
const path = require('path');
module.exports = {
configureWebpack: {
plugins: [
new PrerenderSPAPlugin({
staticDir: path.join(__dirname, 'dist'),
// 对应自己的路由文件,比如a有参数,就需要写成 /a/param1。
routes: ['/home'],
// 这个很重要,如果没有配置这段,也不会进行预编译
renderer: new Renderer({
inject: {
foo: 'bar'
},
headless: false,
// 在 main.js 中 document.dispatchEvent(new Event('render-event')),两者的事件名称要对应上。
renderAfterDocumentEvent: 'render-event'
})
})
]
}
}
//然后在main.js里边添加:
var vue = new Vue({
router,
store,
render: h => h(App),
mounted() {
document.dispatchEvent(new Event('render-event'))
}
}).$mount('#app')
复制
3、运行
添加预渲染的页面会生成该页面的文件夹,里边放在的index.html文件有内容即代表添加成功:
09
—
引入CDN
通过设置http头中的cache-control和expires的属性,可设定浏览器缓存,缓存时间可以是数天,甚至是几个月。可改变文件名来进行更新,进行避免一次性过多的更新,那样会导致服务器负载骤增,网络阻塞等问题。
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
//Cache-Control(缓存控制) HTTP头信息
//Expires期限(过期时间
//实际上在微信环境中并没有用
<meta http-equiv="Cache-Control" content="max-age=31536000">
给个时间期限
复制