前端Vue单页面应用性能优化
Posted 恒生LIGHT云社区
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端Vue单页面应用性能优化相关的知识,希望对你有一定的参考价值。
浏览器渲染步骤:
- 渲染引擎解析html,生成DOM Tree
- 解析CSS文件(包括内联和外联样式),生成CSSOM树,并根据DOM树和CSSOM树生成Render Tree
- 布局Render Tree,确认每个节点在页面中的位置
- 绘制Render Tree
Vue 框架通过数据双向绑定和虚拟DOM,帮我们处理了前端开发中最脏最累的 DOM 操作部分。但 Vue 项目是单页面应用,存在着首页加载缓慢的问题,所以我们仍需要去做Vue 项目性能方面的优化,使项目具有更高效的性能、更好的用户体验。本文通过以下三部分组成:
基础的 Web 技术层面的优化查找性能瓶颈Chrome Performance
使用方法:
- 打开 Chrome 开发者工具,切换到 Performance 面板
- 点击 Record 开始录制
- 刷新页面或展开某个节点
- 点击 Stop 停止录制
结果分析:
Loading:网络通信和HTML解析
Scripting:javascript执行
Rendering:样式计算和布局,即重排
Painting:重绘 对应的详细事件
针对截图中的JavScript执行占时较长,使用如下方式优化
JS文件加载会阻塞浏览器渲染线程,导致页面展示缓慢的情况
防止JS影响页面加载下面有三个解决方案
1、script 放在 body 里(一般是</body>
上面)
由于 DOM 是自上而下解析的,因此 JS 不会阻塞 DOM 的解析,而且这时候可以在 JS 中操作 DOM;
2、设置 defer 属性
通过给 script 标签设置 defer 属性,将脚本文件设置为延迟加载,当浏览器遇到带有 defer 属性的 script 标签时,会再开启一个线程去下载 JS 文件,同时继续解析 HTML,等
HTML 全部解析完、DOM 加载完成之后,再去执行加载好的
JS 文件。只适用于引用外部 JS 文件,并且可以确保所有加了
defer 属性的脚本会按顺序执行
3、设置 async 属性
async 属性和 defer 属性类似,也是会开启一个线程去下载js文件,但和 defer 不同的是,async
会在 JS 加载完成后立刻执行,而不是会等到 DOM 加载完成之后再执行,所以还是有可能会造成阻塞。同样的也是只适用于外部 JS 文件,如果有多个设置了 async 的 JS 文件,不能像 defer 那样保证按照顺序执行
动态创建脚本
let ma = document.createElement(\'script\');
ma.type = \'text/javascript\';
ma.src = \'http://xxxxxx/values/xtjtj?bmid=2&uuid=\' + uuid;
let s = document.getElementsByTagName(\'script\')[0];
s.parentNode.insertBefore(ma, s);
webpack 配置层面的优化
webpack构建结果输出分析
Webpack 输出的代码可读性非常差而且文件非常大。为了更简单、直观地分析输出结果,社区中出现了许多可视化分析工具。这些工具以图形的方式将结果更直观地展示出来,让我们快速了解问题所在。接下来讲解我们在 Vue 项目中用到的分析工具:webpack-bundle-analyzer 。
使用方法
我们在项目中 webpack.prod.conf.js 进行配置:
if (config.build.bundleAnalyzerReport) {
var BundleAnalyzerPlugin = require(\'webpack-bundle-analyzer\').BundleAnalyzerPlugin;
webpackConfig.plugins.push(new BundleAnalyzerPlugin());
}
执行 $ npm run build --report 后生成分析报告如下:
针对上图分析结果,外部插件体积较大,考虑到“更小的体积对于用户体验来说,就意味着更快的加载速度以及更好的用户体验。”,可以使用第三方插件的按需引入
我们在项目中经常会需要引入第三方插件,如果我们直接引入整个插件,会导致项目的体积太大,我们可以借助 babel-plugin-component ,然后可以只引入需要的组件,以达到减小项目体积的目的。以下为项目中引入 element-ui 组件库为例:
(1)首先,安装 babel-plugin-component :
npm install babel-plugin-component -D
(2)然后,将 .babelrc 修改为:
{
"presets": [["es2015", {"modules": false}]],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
(3)在 main.js 中引入部分组件:
import Vue from \'vue\';
import {Button, Select} from \'element-ui\';
Vue.use(Button)Vue.use(Select)
除了组件的按需加载还可以使用Webpack对静态资源进行压缩,这里不做详细说明。
Vue 代码层面的优化v-if 和 v-show 区分使用场景
v-if 是真正的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
v-show 就简单得多, 不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 的 display 属性进行切换。
所以,v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show 则适用于需要非常频繁切换条件的场景。
computed 和 watch 区分使用场景
computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed
的值时才会重新计算 computed 的值;
watch: 更多的是「观察」的作用,类似于某些数据的监听回调
,每当监听的数据变化时都会执行回调进行后续操作;
运用场景:
当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
(1)v-for 遍历必须为 item 添加 key
在列表数据进行遍历渲染时,需要为每一项 item 设置唯一 key 值,方便 Vue.js 内部机制精准找到该条列表数据。当 state 更新时,新的状态值和旧的状态值对比,较快地定位到 diff。
(2)v-for 遍历避免同`时使用 v-if
v-for 比 v-if 优先级高,如果每一次都需要遍历整个数组,将会影响速度,尤其是需要渲染很小一部分的时候,必要情况下应该替换成 computed 属性。
推荐使用:
computed: {
activeUsers: function () {
return this.users.filter(function (user) {
return user.isActive
})
}
}
不推荐:
事件的销毁
Vue 组件销毁时,会自动清理它与其它实例的连接,解绑它的全部指令及事件监听器,但是仅限于组件本身的事件。如果在 js 内使用 addEventListene 等方式是不会自动销毁的,我们需要在组件销毁时手动移除这些事件的监听,以免造成内存泄露,如:
created() {
addEventListener(\'click\', this.click, false)
},
beforeDestroy() {
removeEventListener(\'click\', this.click, false)
}
图片资源懒加载
门户项目的首页一般会有很多的图片组成,为了加速首页加载速度,很多时候我们需要将页面内未出现在可视区域内的图片先不做加载,
等到滚动到可视区域后再去加载。这样对于页面加载性能上会有很大的提升,提高了用户体验。项目中使用 Vue 的 vue-lazyload 插件:
(1)安装插件
npm install vue-lazyload --save-dev
(2)在入口文件 man.js 中引入并使用
import VueLazyload from \'vue-lazyload\'
然后再 vue 中直接使用
Vue.use(VueLazyload)
或者添加自定义选项
Vue.use(
VueLazyload,
{
preLoad: 1.3,
error: \'dist/error.png\',
loading: \'dist/loading.gif\',
attempt: 1
}
)
(3)在 vue 文件中将 img 标签的 src 属性直接改为
v-lazy ,从而将图片显示方式更改为懒加载显示
路由懒加载
Vue 是单页面应用,首页会有很多的路由引入 ,这样使用 webpack 打包后的文件很大,当进入首页时,加载的资源过多,页面会出现白屏的情况,不利于用户体验。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应的组件,这样就更加高效了。这样会大大提高首屏显示的速度,但是可能其他的页面的速度就会降下来。
路由懒加载例子:
const router = new VueRouter({routes: [{
path: \'/home\',
component: () => import(\'@/views/home/index.vue\'),
name: \'home\',
},
{
path: \'/home\',
component: () => import(\'@/views/home/index.vue\'),
name: \'home\',
},
]})
以上是关于前端Vue单页面应用性能优化的主要内容,如果未能解决你的问题,请参考以下文章