Vue性能优化方法

一、前言 

1.1 为什么需要性能优化

  • 用户体验:优化性能可以提升用户体验,降低加载时间和响应时间,让用户更快地看到页面内容。
  • SEO优化:搜索引擎更喜欢快速响应的网站,优化性能可以提高网站的排名。
  • 节约成本:优化性能可以减少服务器负载和带宽使用,节约成本。

1.2 性能优化的重要性- 性能优化可以提升用户体验,减少用户等待时间,提高用户满意度。

  • 性能优化可以降低服务器负载,减少服务器资源消耗,提高网站的可扩展性和稳定性。
  • 性能优化可以提高网站的SEO排名,因为搜索引擎更喜欢快速加载的网站。
  • 性能优化可以提高开发效率,因为优化后的代码更易于维护和扩展。

二、优化思路 

2.1 减少HTTP请求

  • 合并文件:将多个文件合并为一个文件,减少HTTP请求次数。
  • 压缩文件:将文件进行压缩,减小文件大小,减少HTTP请求时间。
  • 使用CDN:使用CDN加速服务,减少服务器的请求压力,提高页面加载速度。
  • 按需加载:将页面分为多个模块,按需加载,减少不必要的请求和页面加载时间。
  • 懒加载:对于图片等资源,采用懒加载的方式,当用户需要查看时再加载,减少页面加载时间。
  • 预加载:对于一些重要资源,采用预加载的方式,提前加载,提高用户体验。
  • 使用WebP格式:WebP格式的图片比JPEG和PNG格式的图片更小,加载速度更快,可以减少HTTP请求时间。
  • 使用字体图标:使用字体图标代替图片,减少HTTP请求次数和文件大小。
  • 使用缓存:合理使用浏览器缓存和服务器缓存,减少HTTP请求次数和请求时间。

2.2 代码优化

  • 减少不必要的计算和操作
            避免不必要的计算和操作,例如在模板中使用三元表达式,会多次计算同一个表达式,可以将其提取为计算属性
  • 避免在模板中使用大量的过滤器,过滤器会在每次渲染时都执行一次,可以考虑将过滤器的功能提取为计算属性或方法
  • 合理使用v-if和v-show
            v-if在条件不满足时会销毁元素及其事件监听器和子组件,可以减少不必要的性能开销,但是频繁切换v-if会造成DOM频繁销毁和重建,可以考虑使用v-show代替
            v-show只是简单的切换元素的display属性,不会销毁元素及其事件监听器和子组件,但是在初始渲染时会有一定的性能开销,当需要频繁切换元素的显示状态时,可以使用v-show代替v-if
  • 合理使用key
             在使用v-for渲染列表时,为每个元素添加一个唯一的key,可以帮助Vue跟踪元素的状态,减少不必要的DOM操作
  • 使用异步组件
             将页面按需加载可以减少初始渲染时的性能开销,可以使用Vue的异步组件功能,将页面按需加载
  • 使用keep-alive缓存组件
             使用keep-alive可以缓存已经渲染过的组件,当组件被切换时,可以直接从缓存中读取,减少不必要的性能开销
  • 使用懒加载
             当页面中存在大量图片时,可以使用懒加载的方式,将页面中的图片延迟加载,减少初始渲染时的性能开销
  • 使用CDN加速
             将静态资源(例如Vue、ElementUI等库文件)上传到CDN服务器,可以减少网络请求时的延迟,加快页面加载速度
  • 优化webpack配置
             在webpack配置中,可以使用Tree Shaking、Code Splitting等技术,减少打包后的文件体积,加快页面加载速度
             使用webpack-bundle-analyzer等工具,分析打包后的文件,找出体积较大的文件,进行优化
2.2.1 代码分割
  • 代码分割可以将代码按照不同的逻辑或业务分割成不同的模块,从而实现按需加载,减少页面加载时间。在Vue中,可以使用以下方式进行代码分割:

  • 使用Vue异步组件:将组件定义为函数返回一个Promise对象,在需要的时候再加载组件。

    Vue.component('async-component', () => import('./AsyncComponent.vue'));
  • 使用Webpack的代码分割功能:在Webpack配置文件中配置optimization.splitChunks进行代码分割。

    optimization: {splitChunks: {chunks: 'all'}
    }
    
  • 代码分割实例:

    1 在项目中,有一个比较大的组件需要加载,但是这个组件并不是每个页面都需要用到。在使用Vue异步组件进行代码分割之前,每次进入页面都需要加载这个组件,导致页面加载时间过长。使用Vue异步组件进行代码分割后,只有在需要用到这个组件的时候才会进行加载,从而减少了页面加载时间。

    2 在项目中,有多个页面都需要使用到一个公共的模块,但是这个模块比较大,每个页面都将这个模块打包进去会导致打包后的文件过大。使用Webpack的代码分割功能进行代码分割后,公共的模块只会被打包一次,从而减小了打包后文件的大小。

2.2.2 懒加载
  • 懒加载是将某些组件或资源推迟到实际需要的时候再加载,可以有效减少首屏加载时的资源压力。

  • 可以使用 Vue.lazy() 方法实现懒加载,也可以使用第三方库如 vue-lazyload

2.2.3 Tree Shaking (实用)

- Tree Shaking是什么?

  • Tree Shaking是指在打包过程中,去除没有使用过的代码,只保留用到的代码,从而减小打包后的文件体积。
  • 如何使用Tree Shaking?
        在webpack中,可以通过在配置文件中设置 optimization.minimize 为true来开启Tree Shaking。
        在代码中,可以使用ES6的import语法来引入需要的模块,避免使用require或者import * as的方式引入整个模块。
  • 示例:

        假设有一个工具库utils.js,其中包含了add、sub、mul、div四个函数,我们只需要使用add和sub函数,可以这样引入:

import { add, sub } from 'utils.js'

        在打包过程中,只有add和sub函数会被保留,mul和div函数会被去除。

2.2.4 代码压缩
  • 使用Webpack的UglifyJsPlugin插件进行代码压缩
  • 将不必要的代码进行删除和优化,如未使用的变量、函数等
  • 使用Tree shaking技术去除未引用的代码
  • 将常量提取出来,避免重复定义,例如使用webpack的DefinePlugin插件将常量定义为全局变量
  • 使用CDN引入第三方库,避免将其打包进项目中
  • 避免使用eval和with等语法,这些语法会导致代码难以压缩
  • 使用Gzip进行压缩,减小文件大小,提高加载速度
  • 使用Web Workers进行代码分离,提高运行效率
  • 使用异步加载组件,避免一次性加载所有组件,减小首屏加载时间
  • 使用路由懒加载,按需加载路由组件,提高性能

2.3 图片优化

  • 图片懒加载:使用vue-lazyload等插件,将图片的加载时机改为滚动到可视区域时再进行加载,减少页面初次加载时的请求量和时间。
  • 图片压缩:使用工具对图片进行压缩,减小图片文件大小,提升页面加载速度。
  • 雪碧图:将多张小图片合并成一张大图片,减少http请求次数,提升页面加载速度。
  • 响应式图片:根据不同设备的屏幕大小,加载不同尺寸的图片,减少不必要的资源浪费,提升页面加载速度。
  • CDN加速:使用CDN加速服务,将图片资源分发到全球各地的节点,提升图片加载速度,减少服务器负载。
2.3.1 图片压缩
  • 使用图片压缩工具,如TinyPNG或ImageOptim等,压缩图片大小,减少网络传输时间和页面加载时间。
  • 使用图片格式的最佳实践,如使用JPEG格式的照片,使用PNG格式的图标和透明图片等。
2.3.2 图片懒加载
  • 使用vue-lazyload插件实现图片懒加载
  • 将图片的src属性改为v-lazy指令
  • 可以设置loading属性来展示图片加载过程中的占位图
  • 可以设置error属性来展示图片加载失败时的占位图
<template><div><img v-for="img in imgList" :key="img.id" :src="img.src" v-lazy="img.lazySrc" /></div>
</template><script>
import VueLazyload from 'vue-lazyload'export default {data() {return {imgList: [{id: 1,src: 'https://example.com/image1.jpg',lazySrc: 'https://example.com/image1_lazy.jpg'},{id: 2,src: 'https://example.com/image2.jpg',lazySrc: 'https://example.com/image2_lazy.jpg'},// ...]}},// 注册插件created() {Vue.use(VueLazyload, {loading: 'https://example.com/loading.gif',error: 'https://example.com/error.jpg'})}
}
</script>

2.4 数据优化

  • 避免不必要的数据响应式化,可以使用Object.freeze()冻结数据对象,提高渲染性能。
// 在数据不需要响应式化时使用Object.freeze()
export default {data() {return {// 不需要响应式化的数据list: Object.freeze(['apple', 'banana', 'orange'])}}
}
  • 合理使用计算属性和缓存,避免重复计算。
<!-- 计算属性 -->
<template><div><p>商品数量:{{ count }}</p><p>商品总价:{{ totalPrice }}</p></div>
</template><script>
export default {data() {return {goodsList: [{ name: 'apple', price: 5, count: 2 },{ name: 'banana', price: 3, count: 3 },{ name: 'orange', price: 2, count: 4 }]}},computed: {count() {// 计算商品数量return this.goodsList.reduce((sum, item) => sum + item.count, 0)},totalPrice() {// 计算商品总价return this.goodsList.reduce((sum, item) => sum + item.price * item.count, 0)}}
}
</script><!-- 缓存 -->
<template><div><button @click="addCount">点击增加</button><p>计数器:{{ count }}</p></div>
</template><script>
export default {data() {return {count: 0}},methods: {addCount() {// 点击按钮增加计数器this.count++}},computed: {// 缓存计数器的值cachedCount() {return this.count}}
}
</script>
  • 使用v-if和v-show合理控制组件的显示和隐藏。
<!-- v-if -->
<template><div><button @click="toggle">点击切换</button><p v-if="show">显示内容</p></div>
</template><script>
export default {data() {return {show: false}},methods: {toggle() {// 点击按钮切换show的值this.show = !this.show}}
}
</script><!-- v-show -->
<template><div><button @click="toggle">点击切换</button><p v-show="show">显示内容</p></div>
</template><script>
export default {data() {return {show: false}},methods: {toggle() {// 点击按钮切换show的值this.show = !this.show}}
}
</script>
  • 使用key值避免不必要的组件销毁和重建。
<template><div><button @click="toggle">点击切换</button><child-component :key="show"></child-component></div>
</template><script>
import ChildComponent from './ChildComponent.vue'export default {components: {ChildComponent},data() {return {show: false}},methods: {toggle() {// 点击按钮切换show的值this.show = !this.show}}
}
</script>
2.4.1 使用缓存
  • 对于频繁使用的组件或数据,可以使用缓存来减少重复渲染和请求。
  • 可以使用Vue提供的 keep-alive 组件对组件进行缓存。
  • 可以使用浏览器提供的缓存机制对请求的数据进行缓存,减少重复请求。
  • 可以使用第三方库如lru-cache等进行数据缓存。
2.4.2 数据分页
  • 在使用分页组件时,应该避免将所有数据都一次性加载出来,而是应该采用分页的方式进行数据加载,减轻页面的渲染压力。
  • 可以使用第三方的分页插件,如Element UI的分页组件,也可以自己实现分页逻辑。
  • 在进行数据分页时,应该根据实际情况设置每页显示的数据量和总数据量,以达到最优的分页效果。
  • 可以使用懒加载技术,在用户滚动页面时再进行数据加载,减少页面的初始加载时间和渲染压力。
  • 可以使用虚拟滚动技术,将页面上不可见的部分进行虚拟化处理,减少页面的渲染压力,提高页面的渲染性能。
  • 可以使用表格的分页功能,将数据分页展示在表格中,同时提供搜索和筛选功能,方便用户查找和使用。
2.4.3 数据预加载
  • 在需要使用数据的组件中,提前预加载所需数据,避免在组件渲染时才发起请求,从而提高页面响应速度。
  • 可以使用Vue的beforeRouteEnter钩子函数,在路由进入组件前预加载数据。
  • 也可以在组件的created生命周期钩子函数中使用异步请求加载数据,并使用v-if指令控制组件的显示,避免页面渲染时出现空白的情况。
  • 可以使用Vue的keep-alive组件缓存组件的状态,避免每次进入组件都需要重新加载数据,提高页面加载速度。使用activated生命周期钩子函数在组件被激活时重新加载数据。

2.5 服务端优化

  • 使用CDN加速静态资源的访问,减轻服务器负担
  • 对于频繁请求的接口,使用缓存技术,减少服务器压力
  • 合理使用HTTP缓存,减少重复请求
  • 对于大量并发请求,考虑使用负载均衡技术,分流请求
  • 使用异步请求技术,减少同步请求对服务器的占用
  • 数据库优化:合理设计数据库表结构,使用索引等技术提高查询效率
  • 使用缓存技术,减少数据库查询次数
  • 合理使用数据库连接池,避免频繁创建和销毁连接
  • 使用NoSQL等技术,提高数据读写效率
  • 对于大量日志输出,使用日志收集和分析工具,避免对服务器性能的影响。
2.5.1 使用CDN
  • 使用CDN加速静态资源加载,例如:
  • 在vue.config.js中配置
// 环境区分主要为开发环境与其他环境(其他:生产,uat,测试等等)
const isNotDevelopMentEnv = process.env.NODE_ENV !== 'development'
const cdnData = {css: ['https://cdn.bootcdn.net/ajax/libs/element-ui/2.13.0/theme-chalk/index.css'],js: ['https://cdn.bootcdn.net/ajax/libs/vue/2.6.10/vue.min.js','https://cdn.bootcdn.net/ajax/libs/axios/0.19.2/axios.min.js','https://cdn.bootcdn.net/ajax/libs/vuex/3.1.0/vuex.min.js','https://cdn.bootcdn.net/ajax/libs/vue-router/3.0.6/vue-router.min.js','https://cdn.bootcdn.net/ajax/libs/element-ui/2.13.0/index.js','https://cdn.bootcdn.net/ajax/libs/jquery/1.12.1/jquery.min.js','https://cdn.bootcdn.net/ajax/libs/vee-validate/2.0.0-rc.21/vee-validate.min.js','https://cdn.bootcdn.net/ajax/libs/vee-validate/2.0.0-rc.21/locale/zh_CN.js'],/** * 属性名称 vue, 表示遇到 import xxx from 'vue' * 这类引入 'vue'的,不去 node_modules 中找,而是去找全局变量 Vue* 其他的为VueRouter、Vuex、axios、ELEMENT、echarts,注意全局变量是一个确定的值,不能修改为其他值,修改为其他大小写或者其他值会报错*/externals: {'vue': 'Vue','vuex': 'Vuex','vue-router': 'VueRouter','element-ui': 'ELEMENT','vuex': 'Vuex','axios': 'axios','vee-validate': 'VeeValidate','jQuery':"jquery",'jquery': 'window.$'}
}// 在configureWebpack中添加externals
configureWebpack: {externals: isNotDevelopMentEnv ? cdnData.externals : {}
}// 在chainWepack中添加如下
if (isNotDevelopMentEnv) {config.plugin('html').tap(args => {args[0].cdn = cdnDatareturn args})
}
  • 在index.html中引入CDN链接(根据环境,开发环境不使用CDN,生产环境才开放):
<html><head><!-- 样式文件优先加载 --><% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %><link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet"><% } %></head><body><div id="app"></div><!-- js加载 --><% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %><script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script><% } %></body>
</html>

采用CDN引入后,不需要删除原有依赖引入,因为在本地还是使用这些依赖进行调试的,打包后因为有CDN所以不会把这些依赖引入所以不用担心,import引入的不需要变更。例如main.js中使用import ElementUI from 'element-ui', 以上的代码已经实现在开发环境会设置不适用CDN,会使用依赖包文件;当发布到生产环境,因为我们已经在vue.config.js的externals中指代了element-ui,所以这个语句也是有效的可以直接使用CDN elementUI

易出错点: 

Router is not defined 

 

解决方案: 将Router 改为 'VueRouter' 

 

解决方案:修改externals 中‘'element-ui’的value为:ELEMENT 

2.5.2 服务器缓存
  • 服务器缓存可以减少服务器的请求次数,提高页面的响应速度。
  • 可以使用浏览器缓存和服务端缓存两种方式。
  • 浏览器缓存:在HTTP响应头中设置缓存策略,让浏览器缓存静态资源,减少请求次数。
  • 服务端缓存:可以使用Redis等缓存工具,将经常使用的数据缓存到内存中,减少数据库查询次数。
2.5.3 服务器压缩- 服务器压缩可以减少传输文件的大小,提高页面加载速度。
  • 使用Gzip压缩服务器响应的文件,可以减少文件大小,提高页面加载速度。
  • 在Nginx中开启Gzip压缩:
gzip on;
gzip_types text/plain application/xml text/css application/javascript;
  • 在Apache中开启Gzip压缩:
<IfModule mod_deflate.c>AddOutputFilterByType DEFLATE text/plainAddOutputFilterByType DEFLATE text/htmlAddOutputFilterByType DEFLATE text/xmlAddOutputFilterByType DEFLATE text/cssAddOutputFilterByType DEFLATE application/xmlAddOutputFilterByType DEFLATE application/xhtml+xmlAddOutputFilterByType DEFLATE application/rss+xmlAddOutputFilterByType DEFLATE application/javascriptAddOutputFilterByType DEFLATE application/x-javascript
</IfModule>
  • 使用CDN加速,CDN服务商会自动帮助压缩文件。

3. 优化实践

3.1 减少HTTP请求的实践
  • 通过使用WebPack的code-splitting特性,将Vue组件按需引入,减少首屏加载时间。
  • 使用Vue的异步组件,实现按需加载,减少页面首屏加载时间。
  • 使用Vue的keep-alive组件,缓存组件状态,避免重复渲染。
  • 使用CDN加速静态资源,减少HTTP请求时间。
  • 将小图片转为base64编码,减少HTTP请求。
  • 使用CSS Sprites技术,减少HTTP请求。
  • 使用HTTP缓存,减少HTTP请求。
  • 使用HTTP2协议,减少HTTP请求。
3.2 代码优化的实践

- 3.2 代码优化的实践

  • 合理使用v-if和v-show
    当需要频繁切换显示和隐藏时,使用v-show
    当需要条件渲染时,使用v-if
  • 避免使用复杂表达式
    将复杂表达式拆分为多个简单表达式
    使用computed属性或过滤器处理复杂逻辑
  • 避免频繁操作DOM
    尽可能减少DOM操作次数
    对于需要频繁操作的元素,使用v-for的key属性优化
  • 合理使用异步组件和按需加载
    将页面划分为多个组件,按需加载
    对于比较大的组件,使用异步组件
  • 减少数据监听
    对于不需要响应式的数据,使用Object.freeze()冻结对象
    对于需要响应式的数据,使用深度监听或手动触发更新
  • 使用CDN加速
    将静态资源部署到CDN上,加速访问速度
  • 优化图片
    压缩图片大小,减少加载时间
    使用webp格式,提高图片加载速度
3.3 图片优化的实践
  • 使用CSS Sprites技术将多个小图片合成一张大图,减少HTTP请求次数
  • 使用WebP格式代替JPEG或PNG格式,可以大幅度减小图片大小,提升加载速度
  • 使用lazyload技术,只有当图片进入可视区域才进行加载,减少不必要的网络请求
  • 对于大图,可以使用图片压缩工具进行压缩,减小图片大小
  • 针对移动端,可以使用srcset和picture标签进行响应式图片处理,提升移动端的加载速度
  • 对于需要动态生成的图片,可以使用canvas或SVG代替传统的图片格式
  • 使用CDN加速图片加载,提升用户体验
3.4 数据优化的实践
  • 避免频繁的数据更新,尽可能减少数据的双向绑定
  • 使用v-if和v-show合理控制组件的渲染
  • 合理使用computed属性和watcher监听数据变化
  • 对于列表数据,使用v-for的key属性进行优化
  • 使用懒加载和分页加载大量数据
  • 对于复杂的数据操作,使用Web Worker进行异步处理
  • 使用immutable.js等数据不可变库进行数据管理
  • 使用localStorage或IndexedDB等本地存储技术缓存数据
  • 使用CDN加速静态资源的加载
  • 对于大量图片资源,使用懒加载或预加载技术
  • 使用服务端渲染(SSR)技术减少客户端渲染压力
  • 使用缓存策略和CDN加速技术优化API请求的响应速度
3.5 服务端优化的实践- 服务端渲染(SSR):使用vue-server-renderer将vue组件在服务端渲染成HTML字符串,减少浏览器的渲染压力,提升首屏渲染速度。
  • 启用gzip压缩:对于静态资源如js、css、图片等启用gzip压缩,减少网络传输时间,提升加载速度。
  • 使用CDN加速:将静态资源托管到CDN上,利用CDN的分布式节点缓存,加速资源的加载速度。
  • 优化数据库查询:避免一次性查询大量数据,使用分页、缓存等技术来优化数据库查询,减少数据库的压力,提升响应速度。
  • 优化服务器配置:根据服务器的硬件配置和应用的特点,合理配置服务器参数,如最大连接数、内存大小等,提升服务器的性能。

4. 打包优化

4.1 移除 preload(预载) 与 prefetch (预取)

vue 脚手架默认开启了 preload 与 prefetch,对于小项目可以提升体验感,但当我们项目很大时,首屏加载就会很慢很慢。

先简单了解一下 preload 与 prefetch。

preload 与 prefetch 都是一种资源预加载机制;

preload 是预先加载资源,但并不执行,只有需要时才执行它;

prefetch 是意图预获取一些资源,以备下一个导航/页面使用;

preload 的优先级高于 prefetch。

配置文件:vue.config.js

chainWebpack: config => {// 移除 preload(预载) 插件config.plugins.delete('preload')// 移除 prefetch(预取) 插件config.plugins.delete('prefetch')
}
4.2 可视化资源分析工具

npm install webpack-bundle-analyzer --save-devg

const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')chainWebpack: config => {// 添加资源可视化工具config.plugins.push(new BundleAnalyzerPlugin())
}

它可以查看资源模块的体积分布,然后可以对应做优化处理。

 4.3 开启gzip压缩
// gzip压缩插件
const CompressionWebpackPlugin = require('compression-webpack-plugin')chainWebpack: config => {// 添加gzip压缩插件config.plugins.push(new CompressionWebpackPlugin({filename: info => {return `${info.path}.gz${info.query}`},algorithm: 'gzip',threshold: 5120, // 只有大小大于该值的资源会被处理 10240// test: new RegExp('\\.(' + ['js'].join('|') + ')$'),test: /\.js$|\.html$|\.json$|\.css/,minRatio: 0.8, // 只有压缩率小于这个值的资源才会被处理deleteOriginalAssets: false // 删除原文件}))
}

开启gzip压缩,需要配置nginx,打开nginx.config文件,写入以下(在http块内或者在单个server块里添加)

#开启gzip
gzip on;#低于1kb的资源不压缩 
gzip_min_length 1k;#压缩级别1-9,越大压缩率越高,同时消耗cpu资源也越多
gzip_comp_level 9;#需要压缩哪些响应类型的资源,多个空格隔开。不建议压缩图片.
gzip_types text/plain application/javascript application/x-javascript text/javascript text/xml text/css;#配置禁用gzip条件,支持正则。此处表示ie6及以下不启用gzip(因为ie低版本不支持)
gzip_disable "MSIE [1-6]\."; 
#是否添加“Vary: Accept-Encoding”响应头
gzip_vary on;

开启gzip压缩,服务器为tomcat,修改server.xml文件

<Connector port="8080" protocol="HTTP/1.1"connectionTimeout="20000"compression="on"   compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain,application/javascript"useSendfile="false"/>
// compression="on" 打开压缩功能 
// compressableMimeType="text/html,text/xml" 压缩类型
// useSendfile="false" 设置该属性将会压缩所有文件,不限阙值,不然可能按照阙值部分压缩

启用gzip压缩打包之后,会变成下面这样,自动生成gz包。目前大部分主流浏览器客户端都是支持gzip的,就算小部分非主流浏览器不支持也不用担心,不支持gzip格式文件的会默认访问源文件的,所以不要配置清除源文件。

配置好之后,打开浏览器访问线上,F12查看控制台,如果该文件资源的响应头里显示有Content-Encoding: gzip,表示浏览器支持并且启用了Gzip压缩的资源

1.尽量减少 data 中的数据

 data 中的数据初始化会增加getter 和 setter,会收集对应的watcher,值改变时整个应用会重新渲染,可以使用computed (当新的值需要大量计算才能得到,缓存的意义就非常大)

  • data后续不使用/变更的数据,使用Object.freeze()。这样可以避免vue初始化时候,做一些无用的操作,从而提高性能

 2.变量本地化

  <template><div :style="{ opacity: start / 300 }">{{ result }}</div></template><script>import { heavy } from '@/utils'export default {props: ['start'],computed: {base () { return 42 },result () {const base = this.base // 不要频繁引用this.baselet result = this.startfor (let i = 0; i < 1000; i++) {result += heavy(base)}return result}}}</script>

3.使用 v-show 复用DOM

 <template><div class="cell"><!--这种情况用v-show复用DOM,比v-if效果好--><div v-show="value" class="on"><Heavy :n="10000"/></div><section v-show="!value" class="off"><Heavy :n="10000"/></section></div></template>

4.v-for 遍历避免同时使用 v-if

v-for 遍历必须加 key,key 最好是 id 值,且避免同时使用 v-if

key存在意义:为了跟踪每个节点的特征,使其具有唯一性,高效更新虚拟dom
vue在更新已经渲染的元素序列时,会采用就地复用策略,都会在对顺序进行破坏时,不仅会产生真实dom更新,浪费资源,从而导致产生错误更新。
比如两个inputAB输入值,在头部添加一个InputC,结果按顺序,CA有值,B无值。

//哪怕我们只渲染一小部分元素,也得在每次重新渲染的时候遍历整个列表。如果 list 的数据有很多,就会造成性能低,页面可能卡顿的现象出现。
<div v-for="item in list" v-if="item .show" :key="item.id">
</div>
//每一次都这样运算
this.list.map( item=> {if (item.active) {return item.name}
});//解决办法 
//1.使用空标签 template.
<template v-for="item in list" ><div v-if="show" :key="item.id">{{item.name}}</div >
</template>
//2.使用compted过滤属性
computed:{items:function(){return this.list.filter(item=>{return item.show})}
}

  5.长列表性能优化

  - 如果列表是纯粹的数据展示,不会有任何改变,就不需要做响应化

    export default {data: () => ({users: []}),async created() {const users = await axios.get("/api/users");// 浅冻结this.users = Object.freeze(users);}};// 深冻结函数
function deepFreeze(obj) {var propNames = Object.getOwnPropertyNames(obj);propNames.forEach(function (name) {var prop = obj[name];// 如果prop是个对象,冻结它if (typeof prop == 'object' && prop !== null)deepFreeze(prop);});return Object.freeze(obj);
}

6. 如果是大数据长列表,可采用虚拟滚动,只渲染少部分区域的内容

可以处理不分页的10w条数据, 也可以处理分页的数据

    <recycle-scrollerclass="items":items="items":item-size="24"><template v-slot="{ item }"><FetchItemView:item="item"@vote="voteItem(item)"/></template></recycle-scroller>

    > 参考[vue-virtual-scroller](https://github.com/Akryum/vue-virtual-scroller)、[vue-virtual-scroll-list](https://github.com/tangbc/vue-virtual-scroll-list)

7.事件的销毁

  Vue 组件销毁时,会自动解绑它的全部指令及事件监听器,但是仅限于组件本身的事件。

  created() {this.timer = setInterval(this.refresh, 2000)},beforeDestroy() {clearInterval(this.timer)}

8.路由懒加载

只有在使用该路由时才加载路由。可缩减首屏加载时间。 

  const router = new VueRouter({routes: [{ path: '/foo', component: () => import('./Foo.vue') }]})

9.keep-alive缓存页面

keep-alive是vue中的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染dom;

包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们 

  <template><div id="app"><keep-alive><router-view/></keep-alive></div></template>

10.图片懒加载

  对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载, 等到滚动到可视区域后再去加载

  <img v-lazy="/static/img/1.png">

  > 参考项目:[vue-lazyload](https://github.com/hilongjw/vue-lazyload)

11.首屏显示优化

我们大家都知道我们vue页面中所有的dom都是通过js执行然后才渲染完成的,我们的html代码实际上仅仅只有几十行,那么我们如果加载js时很慢页面就会出现一段白屏阶段,那么这个白屏阶段给用户的体验就不是很好,我们是否可以给一些显示给用户看呢?

我们可以在项目中加入一个加载动效的动画图片在静态文件中,然后在html文件的app这个div中加入这个图片让它动就完事了,我们可以在app这个div中随便写东西,因为我们知道当一个元素被作为模板时,它原先里面的内容就会全部被覆盖,所以我们随便写:

当我们的js加载完成之后我们的img就会被覆盖了,所以这种效果是特别好的,因为静态资源是在我们的项目当中直接有的,所以加载效率会特别的快,所以尽量让图片的大小变小这样会更好的提高项目效率 

11.1 异步组件与路由懒加载

按需加载: 利用异步组件和路由懒加载,将不同路由或组件的加载推迟到实际需要时。这将显著降低初始加载体积,让用户能够更快地看到页面内容。 

const routes = [{path: '/home',component: () => import('./Home.vue')},// 其他路由...
];
11.2 预加载关键资源 

提前加载: Vue 3的路由支持预加载功能,可在用户浏览站点时预先加载下一个页面所需的资源。这将确保用户切换页面时的迅速加载和呈现。 

const routes = [{path: '/home',component: () => import('./Home.vue'),meta: { preload: true }},// 其他路由...
];
11.3 Webpack 代码分割与优化

模块拆分: 配置Webpack将代码拆分成多个小块,利用Tree Shaking、代码压缩等技术减少代码体积。这将减少初始加载所需的下载时间,提高页面加载速度。 

// vue.config.js
module.exports = {configureWebpack: {optimization: {splitChunks: {chunks: 'all'}}}
};

12.无状态的组件标记为函数式组件

  <template functional><div class="cell"><div v-if="props.value" class="on"></div><section v-else class="off"></section></div></template><script>export default {props: ['value']}</script>

 13.子组件分割

<template><div><ChildComp/></div></template><script>export default {components: {ChildComp: {methods: {heavy () { /* 耗时任务 */ }},render (h) {return h('div', this.heavy())}}}}</script>

14.防抖、节流运用

类型场景
函数防抖搜索框输入(只需要最后一次输入完成后再放松Ajax请求)
-滚动事件scroll(只需要执行触发后的最后一次滚动事件的处理程序)
-连续点击按钮事件
-窗口resiz改变事件
-文本输入的验证(连续输入文字后发送Ajax请求进行验证,停止输入后验证一次)
函数节流DOM元素的拖拽功能实现(mousemove)
-游戏中的刷新率,比如射击游戏,就算一直按着鼠标射击,也只会在规定射速内射出子弹
-Canvas画笔功能
-鼠标不断触发某事件时,如点击,只在单位事件内触发一次.

防抖 

在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时
防抖类似于王者英雄回城6秒,如果回城被打断,再次回城需要再等6秒
应用场景

//定义方法要做的事情
function fun(){console.log('我改变啦')
}
//定义事件触发要执行的方法,两个参数分别是传入的要做的事情和定时器的毫秒数
function debounce(fn,delay){//定义一个变量作为等会清除对象var flay=null;//使用闭包防止每次调用时将flay初始化为null;return function(){//在这里一定要清除前面的定时器,然后创建一个新的定时器clearTimeout(flay) //最后这个定时器只会执行一次//事件触发完成之后延迟500毫秒再触发//(这里的变量赋值是跟定时器建立连接,进行地址赋值,要重新赋值给flay//重新设置新的延时器flay=setTimeout(function(){fn.apply(this);   //修正fn函数的this指向},delay)}}
//给浏览器添加监听事件resize
window.addEventListener('resize', debounce(fun, 500));

节流

节流 规定在给定时间内,只能触发一次函数。如果在给定时间内触发多次函数,只有一次生效
节流类似于王者技能,放完一个英雄技能后,要6秒才能再次释放

//时间戳
//fn为需要执行的函数,wait为需要延迟的时间
function throttle(fn,wait){//记录上一次函数触发的时间var pre = Date.now();//使用闭包能防止让pre 的值在每次调用时都初始化return function(){var context = this;var args = arguments;//arguments 对象包含了传给函数的所有实参//arguments 是函数调用时传入的参数组成的类数组对象//func(1, 2),arguments 就约等于 [1, 2]var now = Date.now();if( now - pre >= wait){//修正this的指向问题fn.apply(context,args);//将时间同步pre = Date.now();}}
}
//定义要做的事情
function handle(){console.log(Math.random());
}
//触发函数
window.addEventListener("mousemove",throttle(handle,1000));//定时器
function throttle(fn,wait){var timer = null;return function(){var context = this;var args = arguments;if(!timer){timer = setTimeout(function(){fn.apply(context,args);timer = null;},wait)}}
}
function handle(){
console.log(Math.random());
}
window.addEventListener(“mousemove”,throttle(handle,1000));

 15.第三方插件按需引入

  像element-ui这样的第三方组件库可以按需引入避免体积太大。

  import Vue from 'vue';import { Button, Select } from 'element-ui';Vue.use(Button)Vue.use(Select)

16.生产环境清除console.log

npm i uglifyjs-webpack-plugin 

webpack.config.js文件下进行如下配置。

// 注:vue版本:“vue”:“^2.5.2”,webpack版本:“webpack”:" ^3.6.0"const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
module.exports = {// 省略...mode: "production",optimization: {minimizer: [new UglifyJsPlugin({uglifyOptions: {// 删除注释output:{comments: false},compress: {warnings: false,drop_console: true, // 删除所有调式带有console的drop_debugger: true, //去除打包后的debuggerpure_funcs: ['console.log'] // 删除console.log}}})]} 
}

 17.搜索引擎 SEO 优化

预渲染 

vue是一个单页面应用(spa),只有一个 html 文件(内容只有一个#app根节点),通过加载js脚本来填充页面要渲染的内容,然而这种方式无法被爬虫和百度搜索到。
构建阶段生成匹配预渲染路径的 html 文件(注意:每个需要预渲染的路由都有一个对应的 html)。构建出来的 html
文件已经有静态数据,需要ajax数据的部分未构建

解决问题

  • SEO:单页应用的网站内容是根据当前路径动态渲染的,html 文件中往往没有内容,网络爬虫不会等到页面脚本执行完再抓取;
  • 弱网环境:当用户在一个弱环境中访问你的站点时,你会想要尽可能快的将内容呈现给他们。甚至是在 js 脚本被加载和解析前;
  • 低版本浏览器:用户的浏览器可能不支持你使用的 js 特性,预渲染或服务端渲染能够让用户至少能够看到首屏的内容,而不是一个空白的网页。
//1.安装预渲染插件
npm install prerender-spa-plugin -D  #安装或者编译出错,npm换成cnpm
//一个 webpack 插件用于在单页应用中预渲染静态 html 内容。因此,该插件限定了你的单页应用必须使用 webpack 构建,且它是框架无关的,无论你是使用 React 或 Vue 甚至不使用框架,都能用来进行预渲染。
//原理:在 webpack 构建阶段的最后,在本地启动一个 phantomjs,访问配置了预渲染的路由,再将 phantomjs 中渲染的页面输出到 html 文件中,并建立路由对应的目录。//2.配置vue.config.jsconst path = require('path')
const PrerenderSPAPlugin = require('prerender-spa-plugin')
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer
module.exports = {configureWebpack: {plugins: [new PrerenderSPAPlugin({// 生成文件的路径,与webpack打包一致即可// 这个目录只能有一级,如果目录层次大于一级,在生成的时候不会有任何错误提示,在预渲染的时候只会卡着不动。staticDir: path.join(__dirname, 'dist'),// 需要预渲染的路由 // 对应自己的路由文件routes: ['/', '/about'],// 这个很重要,如果没有配置这段,也不会进行预编译renderer: new Renderer({inject: {foo: 'bar'},//renderer.headless为表示是否以无头模式运行,无头即不展示界面,如果设置为false,则会在浏览器加载页面时候展示出来,一般用于调试headless: false,//renderer.renderAfterTime可以让控制页面加载好后等一段时间再截图,保证数据已经都拿到,页面渲染完毕renderAfterTime: 5000,// 在 main.js 中 document.dispatchEvent(new Event('render-event')),//两者的事件名称要对应上。在程序入口执行renderAfterDocumentEvent: 'render-event',})})]}
}
//4.修改main.js
new Vue({el: '#app',router,components: { App },template: '<App/>',// 添加mounted,不然不会执行预编译mounted () {document.dispatchEvent(new Event('render-event'))}})
//5.相关路由文件
export default new Router({
<!-- 要用history模式 -->mode: 'history',routes
})
//npm run build
看一下生成的 dist 的目录里是不是有每个路由名称对应的文件,有就对了

小知识:seo为啥对vue单页面不友好?

  • 爬虫在爬取的过程中,不会去执行js,所以隐藏在js中的跳转也不会获取到。
  • vue通过js控制路由然后渲染出对应的页面,而搜索引擎蜘蛛是不会去执行页面的js的,导致搜索引擎蜘蛛只能收录index.html一个页面,在百度中就搜索不到相关的子页面的内容。
  • 我们加载页面的时候,浏览器的渲染包含:html的解析、dom树的构建、cssom构建、javascript解析、布局、绘制,当解析到javascript的时候才回去触发vue的渲染,然后元素挂载到id为app的div上,这个时候我们才能看到我们页面的内容,所以即使vue渲染机制很快我们仍然能够看到一段时间的白屏情况,用户体验不好。

服务端渲染 SSR,nuxt.js 

服务端渲染:网页上面呈现的内容在服务器端就已经生成好了,当用户浏览网页时,服务器把这个在服务端生成好的完整的html结构内容响应给浏览器,而浏览器拿到这个完整的html结构内容后直接显示(渲染)在页面上的过程
SSR=> 后端把.vue文件编译成.html文件返回给前端渲染,它的好处就是有利于SEO

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/183005.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ubuntu16.04部署gitlab-runner触发gitlab流水线

环境&#xff1a;ubuntu16.04 gitlab服务器&#xff1a;192.168.1.12 runner服务器&#xff1a;192.168.1.11 1.下载 环境&#xff1a;192.168.1.11 cd /usr/local/srcwget https://gitlab-runner-downloads.s3.amazonaws.com/latest/deb/gitlab-runner_amd64.debsudo dpkg …

Pandas进阶:20个实用的Pandas函数的基本使用

1. ExcelWriter 很多时候dataframe里面有中文&#xff0c;如果直接输出到csv里&#xff0c;中文将显示乱码。而Excel就不一样了&#xff0c;ExcelWriter是pandas的一个类&#xff0c;可以使dataframe数据框直接输出到excel文件&#xff0c;并可以指定sheets名称。 df1 pd.Da…

人工智能在内容相关性Content Relevance方面的应用

许多公司在向客户和潜在客户提供内容服务时犯了一个错误&#xff0c;即定制性不足&#xff0c;内容过于通用&#xff0c;可能与每位目标客户都不相关。谈及内容相关性时&#xff0c;人们希望获得有用的信息和问题解决方法&#xff0c;或具有娱乐性和参与性的内容。 为客户提供…

对话式数据需求激增,景联文科技提供高质量多轮对话数据定制采集标注服务

大模型的快速发展使得数据服务需求激增&#xff0c;产品整体处于供不应求状态。对话式数据集成为当下需求热点&#xff0c;人们对于更复杂、更真实的多轮对话数据需求不断增加&#xff0c;定制化服务占据市场需求主流。 通过对多轮对话数据的训练&#xff0c;模型可以更好地理解…

Python Xorbits库:实现无限可能的编程旅程

概要 Python Xorbits是一个强大而多功能的开源Python库&#xff0c;为开发者提供了实现创新和复杂计算的能力。它提供了各种功能和工具&#xff0c;帮助开发者在编程旅程中探索无限可能。本文将详细介绍Python Xorbits的用途和使用教程&#xff0c;帮助读者了解和掌握这个令人…

GEE 23:基于GEE实现物种分布模型之随机森林法

基于GEE实现物种分布模型之随机森林法 1.物种分布数据2.研究区绘制3.预测因子选择 1.物种分布数据 根据研究目的和需要导入物种数据&#xff1a; // Load presence data var Data ee.FeatureCollection("users/************736/Distribution"); print(Original da…

web和微信小程序设置placeholder样式

文章目录 一、场景二、web2.1、概念2.2、用法2.3、样式 三、小程序四、最后 一、场景 在页面布局时经常会用到input输入框&#xff0c;有时为了提示用户输入正确的信息&#xff0c;需要用placeholder属性加以说明。 二、web 2.1、概念 placeholder 是HTML5 中新增的一个属性…

cocos creator-碰撞检测

碰撞检测文档 刚体自行选择&#xff0c;刚体正常设置分组、tag&#xff0c;tag用于区分是哪个物体被碰撞了 正常在一个node下挂载脚本就行 注意&#xff1a;Builtin 2D 物理模块只会发送 BEGIN_CONTACT 和 END_CONTACT 回调消息。ccclass(TestContactCallBack) export class …

NoSql非关系型数据库

前言&#xff1a;Nosql not only sql&#xff0c;意即“不仅仅是sql”&#xff0c;泛指非关系型数据库。这些类型的数据存储不需要固定的模式&#xff08;当然也有固定的模式&#xff09;&#xff0c;无需多余的操作就可以横向扩展。NoSql数据库中的数据是使用聚合模型来进行处…

A*算法学习

系列文章目录 前言 在总结 2023华为软件精英挑战赛——全赛段思路分享与总结 - 知乎 (zhihu.com)时&#xff0c;发现自己还有很多技术细节没搞懂&#xff0c;这里看静态全局路径规划最常见的A*算法&#xff0c;这个博主讲得很好&#xff1a; A-Star&#xff08;A*&#xff0…

基于谷歌Flutter的媒体资讯APP的设计与实现

基于谷歌Flutter框架媒体资讯App的设计与实现 摘要&#xff1a; 当今社会&#xff0c;随着经济和科技的发展&#xff0c;人们的生活节奏也愈来愈快&#xff0c;人们生活的阅读时间也越来越少&#xff0c;越发的流行碎片化阅读&#xff0c;而同样的对于互联网的客户端开发者&am…

目标检测常用评价指标

1 基本概念 1.1 IOU(Intersection over Union) 1.2 TP TN FP FN 2. 各种率 3. PR曲线 4. mAP的计算 4.1 AP的计算 4.2 mAP 4.3 mAP0.5和mAP0.5:0.95 1.1 IOU(Intersection over Union) 1.2 TP TN FP FN TP(Truth Positive)&#xff1a; 预测正类&#xff0c;实际正类&#x…

文件重命名:如何删除文件名中的下划线,特殊符号批量删除

在日常的工作中&#xff0c;经常会遇到文件名中包含特殊符号的情况&#xff0c;例如&#xff0c;一些文件名可能包含下划线、空格或其他特殊符号&#xff0c;这些符号可能会干扰我们的文件搜索和识别。此外&#xff0c;一些文件名可能包含无法识别的非标准字符&#xff0c;这可…

Neural Architecture Search for Deep Image Prior

深度图像先验的神经结构搜索 论文链接&#xff1a;https://arxiv.org/abs/2001.04776 项目链接&#xff1a;https://github.com/Pol22/NAS_DIP Abstract 在最近提出的深度图像先验算法(DIP)下&#xff0c;我们提出了一种神经结构搜索(NAS)技术来提高无监督图像去噪、修复和超…

MySQL之undo日志

聊聊undo log 什么是undo log undo log&#xff08;回滚事务&#xff09;&#xff0c;在事务没有提交前&#xff0c;MySQL将记录更新操作的反向操作到undo log日志中&#xff0c;以便进行回退保证事务的原子性 undo log的作用 1.提供回滚操作 我们在进行数据更新操作的时候…

uniapp和vue3+ts实现自定义头部导航栏左侧胶囊内容

由于某些原因&#xff0c;可能需要我们自己定义头部导航栏的内容&#xff0c;实现各种设计师画的设计稿&#xff0c;所以就需要这个自定义的组件&#xff0c;实现的内容&#xff1a;自定义标题和左侧胶囊图标内容&#xff0c;也可以自定义搜索内容到里面&#xff0c;实现的效果…

html-video:计算视频是否完整播放 / 计算视频完播率

一、video 播放视频 <video width"100%"id"myVideo"object-fit"fill":autoplay"true":loop"false":enable-auto-rotation"true":enable-play-gesture"true":src"videoInfo.videoUrl":pos…

人工智能(pytorch)搭建模型21-基于pytorch搭建卷积神经网络VoVNetV2模型,并利用简单数据进行快速训练

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能(pytorch)搭建模型21-基于pytorch搭建卷积神经网络VoVNetV2模型&#xff0c;并利用简单数据进行快速训练。VoVNetV2模型是计算机视觉领域的一个重要研究成果&#xff0c;它采用了Voice of Visual Residual&…

安装vmware_esxi 超详细

安装vmware_esxi 超详细 </h2><div id"cnblogs_post_body" class"blogpost-body blogpost-body-html">esxi安装手册 1、esxi介绍 ESXI原生架构模式的虚拟化技术&#xff0c;是不需要宿主操作系统的&#xff0c;它自己本身就是操作系统。因此…

vue3跟vue2的区别?

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;Vue篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来vue篇专栏内容:vue3和vue2的区别 目录 一、Vue3介绍 哪些变化 速度更快 体积更小 更易维护 compositon Api …