前言:vue-cli项目开发打包部署后,存在问题有首次首页加载过慢,包括加载缓慢问题,需要进行vue项目优化。下面是对vue性能优化方法进行归纳,后面会对方法进行亲测。
主要包括:代码包打包优化、编码优化、用户体验优化
可以在谷歌浏览器的调试工具(F12)中看到打包后生成的app.js文件过大;
1、屏蔽sourceMap
进行打包源码上线环节,需要对项目开发环节的开发提示信息以及错误信息进行屏蔽,一方面可以减少上线代码包的大小;另一方面提高系统的安全性。在vuejs项目的config目录下有三个文件dev.env.js(开发环境配置文件)、prod.env.js(上线配置文件)、index.js(通用配置文件)。vue-cli脚手架在上线配置文件会自动设置允许sourceMap打包,所以在上线前可以屏蔽sourceMap。如下所示,index.js的配置如下,通用配置文件分别对开发环境和上线环境做了打包配置分类,在build对象中的配置信息中,productionSourceMap修改成false:
module.exports = {
dev: {
...
},
build: {
// Template for index.html
index: path.resolve(__dirname, '../dist/ndindex.html'),
// Paths
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: './',
/**
* Source Maps
*/
productionSourceMap: false, //这里关闭Source Maps
// https://webpack.js.org/configuration/devtool/#production
devtool: '#source-map',
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: true,
productionGzipExtensions: ['js', 'css','svg'],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
}
}
2、对项目代码中的JS/CSS/SVG(*.ico)文件进行gzip压缩
在vue-cli脚手架的配置信息中,有对代码进行压缩的配置项,例如index.js的通用配置,productionGzip设置为true,但是首先需要对compress-webpack-plugin支持,所以需要通过 npm install --save-dev compression-webpack-plugin,gzip会对js、css文件进行压缩处理;对于图片进行压缩问题,对于png,jpg,jpeg没有压缩效果,对于svg,ico文件以及bmp文件压缩效果达到50%,在productionGzipExtensions: ['js', 'css','svg']设置需要进行压缩的什么格式的文件。对项目文件进行压缩之后,需要浏览器客户端支持gzip以及后端支持gzip。下面可以查看成功支持gzip状态:
module.exports = {
dev: {
...
},
build: {
...
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: true,
productionGzipExtensions: ['js', 'css','svg'],
...
}
}
1、对路由组件进行懒加载
懒加载也叫延迟加载,即在需要的时候进行加载,随用随载。
在路由配置文件里,这里是router.js里面引用组件。如果使用同步的方式加载组件,在首屏加载时会对网络资源加载加载比较多,资源比较大,加载速度比较慢。所以设置路由懒加载,按需加载会加速首屏渲染。在没有对路由进行懒加载时,在Chrome里devtool查阅可以看到首屏网络资源加载情况(6requests 3.8MB transfferred Finish:4.67s DOMContentLoaded 2.61s Load 2.70s)。在对路由进行懒加载之后(7requests 800kb transffered Finish2.67s DOMContentLoaded 1.72s Load 800ms),可以看见加载速度明显加快。但是进行懒加载之后,实现按需加载,那么项目打包不会把所有js打包进app.[hash].js里面,优点是可以减少app.[hash].js体积,缺点就是会把其它js分开打包,造成多个js文件,会有多次https请求。如果项目比较大,需要注意懒加载的效果
使用如下:
routes: [
{ path: "/", redirect: "index" },
{
path: "/",
name: "home",
component: resolve=>require(["@/views/home"],resolve),
children: [
{
// 员工查询
path: "/employees",
component: resolve=>require(["@/components/employees"],resolve)
},
{
// 首页
path: "/index",
component: resolve=>require(["@/views/index"],rolve)
},
]
}
]
2、组件异步加载
vue官网指南,提到异步组件的使用,在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。Vue 只有在这个组件需要被渲染的时候才会触发工厂函数,且会把结果缓存起来供未来重渲染。
2.1、v-if惰性结合setTimeout 使组件异步加载
加载首页的时候,可以先给首页的子组件设置v-if = “false”,在页面初始化的时候再给子组件设置为true,此方法利用了v-if的惰性,setTimeout会使子组件在所有的组件初始化完成并显示后再对其子组件进行初始化。
注:在实际开发中还遇到了另一种情况也可以用此方法解决,在入口js中获取了app的token,但是在具体页面中发现不管是在created还是mounted中都是有时候能获取到token,有时候又不可以,是因为执行顺序的原因,可以通过 setTimeout 时间设置为0 这种方法把用到token的请求方法给排到最后,这样就能保证请求方法中有token了。
2.2、异步组件,按需加载
webpack 2 结合 ES2015 语法如下。
Vue.component(
'async-webpack-example',
// 这个 `import` 函数会返回一个 `Promise` 对象。
() => import('./my-async-component')
)
上面全局组件,当使用局部注册的时候如下,因为import函数返回的是一个promise对象,因此可以用promise本身的then()和catch()方法去监听到组件的加载。
new Vue({
// ...
components: {
'my-component': () => import('./my-async-component')
}
})
3、vue-lazyload插件,图片懒加载
项目中过多的图片会严重影响网页的加载速度,并且移动网络下的流量消耗巨大,所以说延迟加载几乎是标配了。 图片懒加载,显示当前用户界面再加载显示图片,实现的原理很简单,就是我们先设置图片的data-set属性(当然也可以是其他任意的,只要不会发送http请求就行了,作用就是为了存取值)值为其图片路径,由于不是src,所以不会发送http请求。 然后我们计算出页面scrollTop的高度和浏览器的高度之和, 如果图片举例页面顶端的坐标Y(相对于整个页面,而不是浏览器窗口)小于前两者之和,就说明图片就要显示出来了(合适的时机,当然也可以是其他情况),这时候我们再将 data-set 属性替换为 src 属性即可。
3.1、JavaScript实现:
Lazyload 2
3.2、vue-cli项目中vue-lazyload 插件实现
3.2.1. 安装插件:
npm install vue-lazyload --save-dev
3.2.2. main.js引入插件:
import VueLazyLoad from 'vue-lazyload'
Vue.use(VueLazyLoad,{
error:'./static/error.png',
loading:'./static/loading.png'
})
3.2.3. vue文件中将需要懒加载的图片绑定 v-bind:src 修改为 v-lazy
功能扩展:
图片懒加载的简单效果已经实现了,然后就可以按这开发文档的api进行扩展了:
key | description | default | options |
---|---|---|---|
preLoad |
proportion of pre-loading height(预加载高度比例) | 1.3 |
Number |
error |
src of the image upon load fail(图片路径错误时加载图片) | 'data-src' |
String |
loading |
src of the image while loading(预加载图片) | 'data-src' |
String |
attempt |
attempts count(尝试加载图片数量) | 3 |
Number |
listenEvents |
events that you want vue listen for (想要监听的vue事件) 默认['scroll']可以省略, 当插件跟页面中的动画或过渡等事件有冲突是, 可以尝试其他选项 |
|
Desired Listen Events |
adapter |
dynamically modify the attribute of element (动态修改元素属性) |
{ } |
Element Adapter |
filter |
the image's listener filter(动态修改图片地址路径) | { } |
Image listener filter |
lazyComponent |
lazyload component | false |
Lazy Component |
dispatchEvent |
trigger the dom event | false |
Boolean |
throttleWait |
throttle wait | 200 |
Number |
observer |
use IntersectionObserver | false |
Boolean |
observerOptions |
IntersectionObserver options | { rootMargin: '0px', threshold: 0.1 } | IntersectionObserver |
4、引入外部插件或CDN引用,不要在vue中引入
我们可以打包 时不打包 vue、vuex、vue-router、axios 等,换用国内的 bootcdn 直接引入到根目录的 index.html 中,这样可以减少app.js大小。采用CDN外部加载,去掉其他页面的组件import,修改webpack.base.config.js,在externals中加入该组件,这是为了避免编译时找不到组件报错。
externals: {
'vue': 'Vue',
'vue-router': 'VueRouter',
'vuex': 'Vuex',
'axios': 'axios'
}
5、使用到第三方库的时按需引用
在项目开发中,我们会用到很多第三方库,如果可以按需引入,我们可以只引入自己需要的组件,来减少组件库所占空间,如element-ui组件库按需只加载部分组件Button、Select,官网 按需引入element-ui
5.1. 安装babel-plugin-component插件:
npm install babel-plugin-component -D
5.2. 配置插件,将 .babelrc修改为:
{
"presets": [["es2015", { "modules": false }]],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
5.3. 引入部分组件,比如 Button 和 Select,那么需要在 main.js 中写入以下内容:
import Vue from 'vue';
import { Button, Select } from 'element-ui';
import App from './App.vue';
Vue.component(Button.name, Button);
Vue.component(Select.name, Select);
/* 或写为
* Vue.use(Button)
* Vue.use(Select)
*/
new Vue({
el: '#app',
render: h => h(App)
});
1、loading加载效果
用于加载数据时显示动效。当请求服务端接口需要一定时间时,在请求时加上一个loading 加载动画效果将极大提升用户体验和减轻服务端压力。
实现方案:
a、使用elementUI的loading组件,可以通过指令或服务的形式调用。
指令形式调用:可以自定义加载动画的文字、遮罩层颜色、spinner加载图标的类名,如下:
上述loading布尔值可以结合vuex状态进行全局控制是否展示loading效果;
引入 Loading 服务:
import { Loading } from 'element-ui';
在需要调用时:
Loading.service(options);
其中 options
参数为 Loading 的配置项,具体见下表。LoadingService
会返回一个 Loading 实例,可通过调用该实例的 close
方法来关闭它:
let loadingInstance = Loading.service(options);
this.$nextTick(() => { // 以服务的方式调用的 Loading 需要异步关闭
loadingInstance.close();
});
需要注意的是,以服务的方式调用的全屏 Loading 是单例的:若在前一个全屏 Loading 关闭前再次调用全屏 Loading,并不会创建一个新的 Loading 实例,而是返回现有全屏 Loading 的实例:
let loadingInstance1 = Loading.service({ fullscreen: true });
let loadingInstance2 = Loading.service({ fullscreen: true });
console.log(loadingInstance1 === loadingInstance2); // true
此时调用它们中任意一个的 close
方法都能关闭这个全屏 Loading。
如果完整引入了 Element,那么 Vue.prototype 上会有一个全局方法 $loading
,它的调用方式为:this.$loading(options)
,同样会返回一个 Loading 实例
b、可以使用命令【npm install --save vue-element-loading】安装该插件后直接使用
使用:
import Vue from 'vue'
import VueElementLoading from 'vue-element-loading'
Vue.component('VueElementLoading', ElementLoading)
Or
import VueElementLoading from 'vue-element-loading'
export default {
components: {
VueElementLoading
}
}
//全屏
//组件内容器
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Fusce id fermentum quam. Proin sagittis, nibh id hendrerit imperdiet, elit sapien laoreet elit.
Options
Props |
Type |
Default |
Description |
---|---|---|---|
active | Boolean | - | Status for show/hide loading |
spinner | String | spinner | Spinner icon name: spinner, mini-spinner, ring, line-wave, line-scale, line-down, bar-fade, bar-fade-scale |
color | String | #ccc | Color of spinner icon |
is-full-screen | Boolean | false | Loader will overlay the full page |
background-color | String | rgba(255, 255, 255, .9) | Background color of spinner icon (for overlay) |
size | String | 40 | The size to display the spinner in pixels (NOTE: this will not affect custom spinner images) |
duration | String | 0.6 | The duration of one 'loop' of the spinner animation, in seconds (NOTE: this will not affect custom spinner images) |
text | String | - | Text will appear below loader |
text-style | Object | {} | Change style of the text below loader |
使用参考网址:https://biigpongsatorn.github.io/#/vue-element-loading可以看到有不同的loading动画效果,如下图
2、骨架屏加载
背景:使用Vue和Webpack进行**MPA(单、多页面应用)**的开发,一般会在页面进行数据接口等待时增加Loading动画,为用户提供较好的交互体验。但是会发现,Loading展示的时机是在Vue框架解析后,也就是说需要如下几个条件才能显示:
因此:等待的时间=HTML加载时间+JS加载时间+JS全局环境创建的执行时间。若是JS资源文件较大,或者存在过多的图片资源,导致资源速度下载较慢时,用户所能看到的白屏时间便较长。
因此添加骨架屏,其优势在于:
骨架屏的作用主要是在网络请求较慢时,提供基础占位,当数据加载完成,恢复数据展示。这样给用户一种很自然的过渡,不会造成页面长时间白屏或者闪烁等情况。 常见的骨架屏实现方案有ssr
服务端渲染和prerender
两种解决方案。
详细使用见本人文章:(亲测)vue-cli项目添加骨架屏多种方式,自动生成骨架屏
一些开发经验或习惯可以参考文章:https://blog.csdn.net/crazywoniu/article/details/73480344
可以学习的有:
v-if
,因为减少了 dom 数量,加快首屏渲染,v-if是懒加载,当状态为true时才会加载,并且为false时不会占用布局空间;v-show是无论状态是true或者是false,都会进行渲染,并对布局占据空间对于在项目中,需要频繁调用,不需要权限的显示隐藏,可以选择使用v-show,可以减少系统的切换开销。。v-if="isShow && isAdmin && (a || b)"
,这种表达式虽说可以识别,但是不是长久之计,当看着不舒服时,适当的写到 methods 和 computed 里面封装成一个方法,这样的好处是方便我们在多处判断相同的表达式,其他权限相同的元素再判断展示的时候调用同一个方法即可。(item, index) in arr
,然后 :key="index"
来确保 key 的唯一性。在列表数据进行遍历渲染时,需要为每一项item设置唯一key值,方便vuejs内部机制精准找到该条列表数据。当state更新时,新的状态值和旧的状态值对比,较快地定位到diff。.header-title__text
之类的 class,直接 .title
搞定。.fl -- float: left
到全局文件里去,然后又要 .clear
,现在的浏览器还不至于弱到非要用 float
去兼容,完全可以 flex,grid 兼容性一般,功能其实 flex 布局都可以实现,float 会带来布局上的麻烦,用过的都知道不相信解释坑了。export default {}
内的方法顺序一致,方便查找对应的方法。我个人习惯 data、props、钩子、watch、computed、components。:width="" :heigth=""
不要 :option={}
,细化的好处是只传需要修改的参数,在子组件 props 里加数据类型,是否必传,以及默认值,便于排查错误,让传值更严谨this.$store.dispatch('update', { ... })
参考文章:
浅谈 Vue 项目优化:https://blog.csdn.net/crazywoniu/article/details/73480344
vuejs项目性能优化总结:https://www.jianshu.com/p/41075f1f5297
关于vue在app首次加载缓慢的解决办法:https://www.jianshu.com/p/6262772bdc9c
图片懒加载和预加载:https://www.cnblogs.com/rlann/p/7296660.html
vue-lazyload 使用:https://www.cnblogs.com/xyyt/p/7650539.html
vue骨架屏官网:https://github.com/lavas-project/vue-skeleton-webpack-plugin
vue-element-loading: https://biigpongsatorn.github.io/#/vue-element-loading