参考:https://blog.csdn.net/qq_37939251/article/details/100031285
Vue 框架通过数据双向绑定和虚拟 DOM 技术,帮我们处理了前端开发中最脏最累的 DOM 操作部分, 我们不再需要去考虑如何操作 DOM 以及如何最高效地操作 DOM;但 Vue 项目中仍然存在项目首屏优化、Webpack 编译配置优化等问题。
目录
1.3、v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
3.4、使用 Chrome Performance 查找性能瓶颈
本文内容分为以下三部分组成:
-
Vue 代码层面的优化;
-
webpack 配置层面的优化;
-
基础的 Web 技术层面的优化。
1、代码层面的优化
1.1、v-if 和 v-show 区分使用场景
- v-if是动态的向DOM树内添加或者删除DOM元素,有更高的切换消耗;
- v-show 不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 的 display 属性进行切换。
运用场景:
- v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景;
- v-show 则适用于需要频繁切换显隐的场景。
1.2、computed 和 watch 区分使用场景
- computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;
- watch: 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;
运用场景:
- 当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
- 当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
1.3、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 优先级高,如果每一次循环都需要通过if判断遍历整个数组,寻找需要渲染的部分,将会影响速度,尤其是只需要渲染很小一部分的时候。必要情况下应该替换成 computed 属性,提前对数组过滤;
(3)推荐:
<ul>
<li
v-for="user in activeUsers" //使用计算属性过滤后的数组
:key="user.id">
{{ user.name }}
</li>
</ul>
computed: {
activeUsers: function () { //对数组过滤
return this.users.filter(function (user) {
return user.isActive
})
}
}
(4)不推荐:
<ul>
<li
v-for="user in users"
v-if="user.isActive" //每次循环都进行if判断
:key="user.id">
{{ user.name }}
</li>
</ul>
1.4 图片资源懒加载
对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载, 等到滚动到可视区域后再去加载。这样对于页面加载性能上会有很大的提升,也提高了用户体验。我们在项目中使用 Vue 的 vue-lazyload 插件:
(1)安装插件
npm install vue-lazyload --save-dev
(2)在入口文件 man.js 中引入
import VueLazyload from 'vue-lazyload'
(3)在入口文件 man.js 中使用
Vue.use(VueLazyload)
// 或者添加自定义选项
Vue.use(VueLazyload, {
preLoad: 1.3,
error: 'dist/error.png',
loading: 'dist/loading.gif',
attempt: 1
})
(4)在 vue 文件中将 img 标签的 src 属性直接改为 v-lazy ,从而将图片显示方式更改为懒加载显示:
<img v-lazy="/static/img/1.png">
1.5 路由懒加载
Vue 是单页面应用,可能会有很多的路由引入 ,这样使用 webpcak 打包后的文件很大,当进入首页时,加载的资源过多,页面会出现白屏的情况,不利于用户体验。而运用懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力。这样就更加高效了。这样会大大提高首屏显示的速度,但是可能其他的页面的速度就会降下来。
步骤:
(1)安装开发依赖@babel/plugin-syntax-dynamic-import
(2)在配置文件babel.config.js中声明该插件
(3)将路由改为按需加载的形式
其中 import(/*分组名:划分大同一分组的多个组件*/ 路由组件)
1.6 第三方插件的按需引入
我们在项目中经常会需要引入第三方插件,如果我们直接引入整个插件,会导致项目的体积太大,我们可以借助 babel-plugin-component ,然后可以只引入需要的组件,以达到减小项目体积的目的。以下为项目中引入 element-ui 组件库为例,只引入部分组件Button、Select:
(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)
1.7 长列表性能优化
不是一次性的渲染长列表,有两种方式:
- 无限滚动加载,就是懒加载,就是在滚动到页面底部的时候,再去加载剩余的数据,说白了也是对数据进行分页,一种常见的首屏优化方案
- 按需加载,假设总数据为1000条,可视区域高度为500px,每个列表item高度为50px,那么在可视区域内只需要渲染10个就可以了,而不是1000条,根据用户滚动在对可视区域的节点位置和下标进行计算,在总数据中进行截取替换,每次只是渲染可视区域的数据节点,懒加载则是一直追加;
具体过程:
- 监听页面的的滚动事件;
- 当页面滚动时,触发相应的方法;
- 该方法用来判断当前滚动区域的列表id,如果满足要求,则更新当前页的数据。
比如一页展示10条数据,最后一条数据就是10、20、30...,判断当前是否已渲染最后一条(根据盒子高/列表高获取渲染数量)
实现请参考:前端开发中的长列表优化_喵大叔、的博客-CSDN博客
2、webpack 配置层面的优化
2.1 Webpack 对图片进行压缩
在 vue 项目中除了可以在 webpack.base.conf.js 的 url-loader 中设置 limit 大小来对图片处理,对小于 limit 的图片转化为 base64 格式,其余的不做操作。所以对有些较大的图片资源,在请求资源的时候,加载会很慢,我们可以用 image-webpack-loader来压缩图片:
(1)首先,安装 image-webpack-loader :
npm install image-webpack-loader --save-dev
(2)然后,在 webpack.base.conf.js 中进行配置:
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use:[
{
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
loader: 'image-webpack-loader',
options: {
bypassOnDebug: true,
}
}
]
}
2.2 提取公共代码
如果项目中没有去将每个页面的第三方库和公共模块提取出来,则项目会存在以下问题:
- 相同的资源被重复加载,浪费用户的流量和服务器的成本。
- 每个页面需要加载的资源太大,导致网页首屏加载缓慢,影响用户体验。
所以我们需要将多个页面的公共代码抽离成单独的文件,来优化以上问题 。Webpack 内置了专门用于提取多个Chunk 中的公共部分的插件 CommonsChunkPlugin,我们在项目中 CommonsChunkPlugin 的配置如下:
// 所有在 package.json 里面依赖的包,都会被打包进 vendor.js 这个文件中。
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function(module, count) {
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
);
}
}),
// 抽取出代码模块的映射关系
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
chunks: ['vendor']
})
3、基础的 Web 技术优化
3.1、开启 gzip 压缩
gzip(GNU- ZIP)是一种压缩技术。经过gzip压缩后页面大小可以变为原来的30%甚至更小,这样,用户浏览页面的时候速度会快得多。gzip的压缩页面需要浏览器和服务器双方都支持,实际上就是服务器端压缩,传到浏览器后浏览器解压并解析。目前的大多数浏览器都支持解析gzip压缩过的资源文件(Chrome,firefox,IE)、常见的服务器如 Apache,Nginx,IIS 同样支持。
下面我们使用 express服务器 开启 gzip压缩:
(1)安装依赖
npm install compression --save
(2)然后在已有代码里(一般是app.js)找到以下代码:
var app = express();
(3)添加代码逻辑,修改为:
var compression = require('compression')
var app = express();
// 启用gzip
app.use(compression());
(4)重启服务,观察网络面板里面的 response header,如果看到如下红圈里的字段则表明 gzip 开启成功
3.2 浏览器缓存
为了提高用户加载页面的速度,对静态资源进行缓存是非常必要的,根据是否需要重新向服务器发起请求来分类,将 HTTP 缓存规则分为两大类(强缓存,协商缓存)
3.3 CDN 的使用
CDN全称叫做“Content Delivery Network”,中文叫内容分发网络。
CDN详解参考:https://blog.csdn.net/zhenghongcs/article/details/104285577
(1)问题:
问题1:浏览器从服务器上下载 CSS、js 和图片等文件时都要和服务器连接,而大部分服务器的带宽有限,如果超过限制,网页就半天反应不过来(浏览器内存最多执行四十个进程,需要等到加载完前面的才能执行后面的代码)。
问题2:此外,默认情况下,通过import语法导入的第三方依赖包,最终会被打包合并到同一个文件中,从而导致打包成功后,单文件体积过大的问题。
(2)解决:
如果我们采用cdn的方式来引入一些第三方资源,就可以缓解我们服务器的压力,原理是CDN 可以通过不同的域名来加载文件,将我们的压力分给其他服务器点, 从而使下载文件的并发连接数大大增加。且CDN 具有更好的可用性,更低的网络延迟和丢包率 。
对于问题2,可通过webpack中的externals节点,来配置并加载外部的CDN资源。凡是声明在externals中的第三方依赖包,都不会被打包。
(3)具体的代码实现:
1)先删除通过import引入资源的语句:
2)再添加相应的CDN资源引用:
3) 在vue.config.js文件中配置。 在externals中声明依赖包,避免打包,减少体积
// 在externals中声明依赖包,避免打包,减少体积
config.set('externals', {
vue: 'Vue',
'vue-router': 'VueRouter',
axios: 'axios',
lodash: '_',
echarts: 'echarts',
nprogress: 'NProgress',
'vue-quill-editor': 'VueQuillEditor'
})
3.4、使用 Chrome Performance 查找性能瓶颈
Chrome 的 Performance 面板可以录制一段时间内的 js 执行细节及时间。使用 Chrome 开发者工具分析页面性能的步骤如下。
1)打开 Chrome 开发者工具,切换到 Performance 面板
2)点击 Record 开始录制
3)刷新页面或展开某个节点
4)点击 Stop 停止录制