webpack代码大全-我使用了项目中实际使用的22种Vue优化方法

代码不仅仅是运行,但只有一个废话:找到代码字并不容易。

1. 代码优化

在 v-for 中使用 key

使用v-for更新渲染元素列表时,默认使用就地复用策略; 当列表数据发生变化时,会根据key值判断某个值是否发生变化,如果发生变化,则重新渲染该item。 否则重用前一个元素;

使用按键注意事项:

在 v-if/v-else-if/v-else 中使用 key

很多人可能会忽视这一点

原因:默认情况下,Vue 会尽可能高效地更新 DOM。 这意味着当在相同类型的元素之间切换时,它会修复现有元素,而不是删除旧元素并在同一位置添加新元素。 如果不相同的元素被识别为相同,则可能会出现意想不到的副作用。

如果只有一个v-if而没有v-else或v-if-else,则不需要添加key

与v-for的key相比,v-if/v-else-if/v-else中的key比较简单,我们可以直接写一个固定的字符串或者字段

  
    
    
  
复制代码

.v-enter-active, .v-leave-active {
  transition: all 1s;
}
.v-enter, .v-leave-to {
  opacity: 0;
  transform: translateY(30px);
}
.v-leave-active {
  position: absolute;
}
复制代码

比如前面的代码,你会发现按钮上好像加了过渡效果,但是不加按键开关很难触发过渡

不要一起使用 v-for 和 v-if (Vue2)

这种优化方式仅限于Vue2,Vue3中对v-for和v-if的优先级进行了调整

你们都知道这一点

永远不要在同一个元素上同时使用 v-if 和 v-for。参考 Vue2.x 样式手册

原因是v-for的优先级低于v-if,所以当它们用在同一个标​​签上时,每次渲染都会先循环,然后进行条件判断

注意:Vue3中v-if的优先级低于v-for,所以v-for和v-if一起使用时,效果类似于Vue2中提升v-if的效果

例如下面这段代码在Vue2中是不推荐的,Vue也会给出相应的警告

  • {{ user.name }}
复制代码

我们应该尝试将 v-if 移至上层或使用计算属性来处理数据

  • {{ user.name }}
复制代码

如果你不希望循环的内容有不必要的上层容器,那么你可以选择使用template作为其父元素,这样template就不会被浏览器渲染为DOM节点

如果我想确定遍历对象上每一项的内容来选择渲染的数据,我可以使用compute来过滤遍历对象

// js
let usersActive = computed(()=>users.filter(user => user.active))
// template
  • {{ user.name }}
复制代码

v-if和v-show的合理选择

大家都熟悉v-if和v-show的区别; v-if通过直接操作DOM的删除和添加来控制元素的显示和隐藏; v-show是通过控制DOM显示和隐藏元素的显示CSS来控制的

由于对DOM进行添加/删除操作的性能远高于操作DOM的CSS属性

所以当元素需要频繁显示/隐藏时,我们使用v-show来提高性能。

当元素不需要频繁显示/隐藏时,我们使用 v-if 去掉 DOM,这样可以节省浏览器渲染这部分 DOM 所需的资源

使用简单的计算属性

gt5秘籍大全代码_红警秘籍秘籍大全代码_webpack代码大全

复杂的估计属性应拆分为尽可能多的简单属性。

易于测试当每个估计属性都包含一个非常简单且几乎没有依赖性的表达式时,编写测试以确保其正常工作就变得更加容易。 易于阅读 简化的估计属性要求您为每个值指定一个描述性名称,即使它不可重复使用。 这使得其他开发人员(以及未来的您)可以更轻松地专注于他们关心的代码并弄清楚发生了什么。 更好地“拥抱变化” 任何可以命名的值都可以在视图中使用。 作为一个反例,我们可能准备显示一条消息,告诉用户他们已经节省了多少钱; 我们也可能准备估算税收,但它们可能会被单独解释,而不是作为单价的一部分。 小而集中的估计属性减少了限制信息使用的假设,因此需求发生变化并需要更少的构建。

参考Vue2风格手册

Computed 大家都很熟悉,当它的表达式中依赖的响应式数据发送发生变化时,它会重新计算。 如果我们在估计属性中编写更复杂的表达式,那么它所依赖的反应性数据也会任意出现更多。当任何一个依赖项发生变化时,整个表达式都需要重新计算

let price = computed(()=>{
  let basePrice = manufactureCost / (1 - profitMargin)
  return (
      basePrice -
      basePrice * (discountPercent || 0)
  )
})
复制代码

当制造成本、利润率和折扣百分比中的任何一项发生变化时,整个价格将被重新估算。

但如果我们将其更改为以下内容

let basePrice = computed(() => manufactureCost / (1 - profitMargin))
let discount = computed(() => basePrice * (discountPercent || 0))
let finalPrice = computed(() => basePrice - discount)
复制代码

如果discountPercent发生变化,则仅discount和finalPrice会被重新估计,而basePrice由于compute的缓存特性不会被重新估计

函数式功能组件(Vue2)

请注意,这仅用作 Vue2 中的优化,在 3.x 中,有状态组件和功能组件之间的性能差异已大大减少,并且在大多数用例中微不足道。 因此,在 SFC 上使用函数式的开发人员的迁移路径是删除该属性,并将所有对 props 的引用重命名为 $props,将 attrs 重命名为 $attrs。

优化前

 
    
export default { props: ['value'], } 复制代码

优化

 
    
export default { props: ['value'], } 复制代码

拆分组件

webpack代码大全_红警秘籍秘籍大全代码_gt5秘籍大全代码

什么? 你写的一个vue文件有一千多行代码?

合理的组件拆分不仅可以优化性能,还可以让代码更加清晰易读。单一功能原理

来源于slides.com/akryum/vuec…

优化前


  
{{ heavy() }}
export default { props: ['number'], methods: { heavy () { /* HEAVY TASK */ } } } 复制代码

优化


  
export default { props: ['number'], components: { ChildComp: { methods: { heavy () { /* HEAVY TASK */ } }, render (h) { return h('div', this.heavy()) } } } } 复制代码

由于Vue的更新是组件特定的,虽然每一帧都会通过数据变化导致父组件重新渲染,但ChildComp不会重新渲染,因为它内部没有任何响应数据变化。所以优化后的组件不会执行时间-每次渲染时都会消耗任务

使用局部变量

优化前


  
{{ result }}
import { heavy } from '@/utils' export default { props: ['start'], computed: { base () { return 42 }, result () { let result = this.start for (let i = 0; i < 1000; i++) { result += heavy(this.base) } return result } } } 复制代码

优化


  
{{ result }}
import { heavy } from '@/utils' export default { props: ['start'], computed: { base () { return 42 }, result () { const base = this.base let result = this.start for (let i = 0; i < 1000; i++) { result += heavy(base) } return result } } } 复制代码

这主要是由于优化前后组件的属性估计结果的实现存在差异。 优化前的组件在估计过程中多次访问this.base,而优化后的组件在估计前使用局部变量base来缓存this.base。 稍后直接进入基地。

那么为什么这种差异会导致性能上的差异呢? 原因是每次访问this.base时,由于this.base是响应式对象,所以会触发它的getter,然后执行依赖收集相关的逻辑代码。 。 类似的逻辑执行多了webpack代码大全,像例子一样,几百个周期更新几百个组件,每个组件触发计算重评估,然后多次执行依赖收集相关逻辑,性能自然会提高。

从需求上来说,this.base执行一次依赖收集就足够了,并将其getter评估结果返回给局部变量base。 稍后再次访问base时,不会触发getter,不再遵循依赖收集的逻辑。 ,性能自然会得到提升。

webpack代码大全_gt5秘籍大全代码_红警秘籍秘籍大全代码

引领揭示Vue.js的九种性能优化方法

使用保活

当一些渲染成本比较高的组件需要频繁切换时,可以使用keep-alive来缓存该组件

使用keep-alive后,被keep-alive包裹的组件的vnode和DOM在第一次渲染后会被缓存起来webpack代码大全,然后下次再次渲染该组件时,会直接从缓存中获取对应的vnode和DOM ,然后渲染,不需要经过组件初始化、渲染、打补丁等一系列流程,减少了脚本执行时间,提高了性能。

注意:滥用keep-alive只会让你的应用程序显得更加卡顿,因为它会常年占用大量显存

事件破坏

当一个组件被销毁时,我们应该消除组件中添加的全局风暴和定时器,以避免内存泄漏

Vue3的HOOK可以让我们把storm的声明和销毁写在一起,可读性更强

function scrollFun(){ /* ... */}
document.addEventListener("scroll", scrollFun)
onBeforeUnmount(()=>{
  document.removeEventListener("scroll", scrollFun)
})
复制代码

Vue2仍然可以使用$once来达到这个效果。 当然你也可以在optionsAPI beforeDestroy中销毁storm,不过我推荐后者,因为前者会让相同功能的代码更加分散

function scrollFun(){ /* ... */}
document.addEventListener("scroll", scrollFun)
this.$once('hook:beforeDestroy', ()=>{
  document.addEventListener("scroll", scrollFun)
})
复制代码

function scrollFun(){ /* ... */}
export default {
  created() {
    document.addEventListener("scroll", scrollFun)
  },
  beforeDestroy(){
    document.addEventListener("scroll", scrollFun)
  }
}
复制代码

图片加载

图片延迟加载:适用于清理图片较多且一屏未显示所有图片的页面。 vue-lazyload插件为我们提供了一个非常方便的延迟加载命令 v-lazy

然而,并非所有图像都适合延迟加载。 例如横幅、相册等更建议使用图像预加载技术,先下载当前显示图像的前后图像。

使用合理的数据处理算法

webpack代码大全_红警秘籍秘籍大全代码_gt5秘籍大全代码

这个比较考验数据结构和算法的功底

例如将链表转换为多级结构的方法

/**
 * 数组转树形结构,时间复杂度O(n)
 * @param list 数组
 * @param idKey 元素id键
 * @param parIdKey 元素父id键
 * @param parId 第一级根节点的父id值
 * @return {[]}
 */
function listToTree (list,idKey,parIdKey,parId) {
    let map = {};
    let result = [];
    let len = list.length;
    // 构建map
    for (let i = 0; i < len; i++) {
        //将数组中数据转为键值对结构 (这里的数组和obj会相互引用,这是算法实现的重点)
        map[list[i][idKey]] = list[i];
    }
    // 构建树形数组
    for(let i=0; i < len; i++) {
        let itemParId = list[i][parIdKey];
        // 顶级节点
        if(itemParId === parId) {
            result.push(list[i]);
            continue;
        }
        // 孤儿节点,舍弃(不存在其父节点)
        if(!map[itemParId]){
            continue;
        }
        // 将当前节点插入到父节点的children中(由于是引用数据类型,obj中对于节点变化,result中对应节点会跟着变化)
        if(map[itemParId].children) {
            map[itemParId].children.push(list[i]);
        } else {
            map[itemParId].children = [list[i]];
        }
    }
    return result;
}
复制代码

其他

除了上面提到的方法之外,还有很多优化方法,不过我在项目中使用的并不太多

2.首屏/音量优化

我在项目中针对首屏优化主要有以下几个优化方向

体积优化

代码分割

代码分割的作用就是将打包后的产品一件一件的分割成小产品,这些小产品依赖于esModule。 因此,当您使用 import() 函数导出文件或依赖项时,该文件或依赖项将被单独打包为一个小产品。 路由延迟加载和异步组件都使用了这个原理。

对于UI库,我通常不会使用按需加载组件,而是更喜欢CDN引入的形式来优化。

3. 网络

CDN:首先是里面提到的CDN的介绍。 开发阶段使用本地库,打包时通过配置外部扩展(Externals)排除那些依赖。然后在html文件中以CDN的形式导入

服务器推送:HTTP2已经比较成熟; 前面介绍完CDN之后,我们就可以为网站使用HTTP2 Server Push功能,让浏览器提前加载这些CDN和其他文件。

启用gzip:这个已经说了,原理是当客户端和服务器都支持gzip传输时,服务器会先发送gzip压缩的文件,然后客户端收到后解压。

启用缓存:一般情况下,我使用协商缓存,但这并不适用于所有情况。 比如使用Server Push的文件,就不能随便改文件名。所以我一般都会固定生产的主文件名