乐趣区

关于javascript:Vue-优化速查

首发传送门:https://ssshooter.com/2021-03…

拆分组件

我也曾认为,拆分子组件是用于形象,但实际通知我,拆分子组件是晋升性能的一种形式(特定状况)。

在我的理论工作中遇到这么个问题,有一个很大的表格,外面有多个新增条目标对话框,当数据很多的时候,填写新增数据都会变卡。

起因就是,在一个组件里,批改值会造成整个组件的数据检查和 diff。然而明晓得大表单什么都没改,我还要浪费时间查看个啥呢?

为了解决这个问题,把对话框独自抽出来就成了非常无效的优化办法。

在子组件更新时,默认是不会触发父组件更新的,除非子组件扭转了父组件的数据。

我就以 element UI 的 dialog 举例吧:

关上此链接间接关上可运行事例

写一个页面,外面蕴含两个 dialog,一个是间接写到页面中,另一个形象为组件。

<template>
  <div>
    <el-button type="text" @click="dialogVisible = true"
      > 点击关上 Dialog</el-button
    >
    <el-button type="text" @click="dialogData.visible = true"
      > 点击关上 Dialog2</el-button
    >
    <div>{{renderAfter() }}</div>
    <el-dialog
      :append-to-body="true"
      title="提醒"
      :visible.sync="dialogVisible"
      width="30%"
    >
      <el-input v-model="xxx" />
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false"> 取 消 </el-button>
        <el-button type="primary" @click="dialogVisible = false"
          > 确 定 </el-button
        >
      </span>
    </el-dialog>
    <my-dialog :dialogData="dialogData" />
  </div>
</template>

<script>
  import MyDialog from './Dialog'
  export default {components: { MyDialog},
    name: 'HelloWorld',
    props: {msg: String,},
    data() {
      return {
        xxx: '',
        dialogVisible: false,
        dialogData: {visible: false,},
      }
    },
    methods: {renderAfter() {if (!window.renderCountTech) window.renderCountTech = 1
        else window.renderCountTech++
        console.log(
          '%c 渲染函数检测',
          'color:#fff;background:red',
          window.renderCountTech
        )
      },
    },
  }
</script>

以下是 dialog 组件的内容:

<template>
  <el-dialog
    :append-to-body="true"
    title="提醒"
    :visible.sync="dialogData.visible"
    width="30%"
  >
    <el-input v-model="xxx" />
    <span slot="footer" class="dialog-footer">
      <el-button @click="dialogData.visible = false"> 取 消 </el-button>
      <el-button type="primary" @click="dialogData.visible = false"
        > 确 定 </el-button
      >
    </span>
  </el-dialog>
</template>

<script>
  export default {props: ['dialogData'],
    data() {
      return {xxx: '',}
    },
  }
</script>

实际可知,在 dialog 关上敞开、以及输入框批改数据时,会触发整个组件的渲染函数,而在 dialog2 无论关上敞开或输出时都不会触发父组件更新。在对话框所在组件的数据量少的话的确差异不大,然而量大的时候在对话框输出的时候会有可感知的卡顿。(一句话:对话框自成一个组件,外部更新不影响父组件)

不止如此,反过来说,父组件更新的时候会渲染 dialog1 而不会渲染 dialog2,切实是两全其美。(一句话:父组件更新时不扭转没有数据变动的子组件)

即便这个组件不复用,也能够把对话框用到的办法拆散到独自文件,不必和主页面的办法混到一起。如果一个 dialog 有一大堆逻辑的话,拆散到独自文件相对是一个不错的办法。

不过毛病当然也有:

首先,数据交互有点不不便,不过总能活用 $parent 和 Vuex 等形式解决。

第二个问题是批改 dialogData.visible 时会报错 Unexpected mutation of "dialogData" prop. (vue/no-mutating-props)

作为 Vue 的最佳实际,父给子的 prop 不得由子间接批改。我的观点是如果你晓得本人在做什么,而且副作用不强的话……这样做大略也不妨,不晓得大家的意见如何呢?

如果保持最佳实际,有两个计划:

emit

诚实地用 on 和 emit,多些几句代码:

<text-document v-bind:title.sync="doc.title"></text-document>

这么写等于:

<text-document
  v-bind:title="doc.title"
  v-on:update:title="doc.title = $event"
></text-document>

而后再在子组件通过 this.$emit('update:title', newTitle) 更新父组件。

$refs

可见性由 dialog 本人管制,在父组件通过 refs 设置 dialog 的可见性:

this.$refs.child.visible = true

反过来呢?你也能够在子里批改父(这就不算是批改 prop 了,奇技淫巧)

this.$parent.visible = true

对于更新粒度更具体的解释能够看这里:Vue 和 React 对于组件的更新粒度有什么区别

PS:另外有一个暗藏论断,一个组件用了 slot 的话,子组件是会随着父组件从新渲染的

computed

通过官网文档的强调,能够说是家喻户晓,computed 能够缓存计算结果,在依赖的值不变时缩小计算耗费工夫。

同样家喻户晓的还有 KeepAlive 组件。

缩小应用重型组件和插件

很多 UI 框架非常欠缺,但就是因为太欠缺,各种性能相互作用可能会让你运行过慢或者不好 debug。

间接改原有框架当然能够,然而了解老本也不低,而且改完了,更新之后要合并代码更是好受,所以我更偏向于本人做一个。

我不违心做一个全世界都实用的轮子。因为每个人的需要都是不同的,如果要做一个满足所有人的要求的轮子,这个轮子就会变得很重,我集体不太违心看到……

所以我心愿做的“轮子”是一个轻量化的,满足最小性能要求的“框架”,这样大家批改也不便,同时也不用放心随着不断更新越来越重。例如 v-vld 和 [tbl]。(https://github.com/v-cpn/cpn-tbl)

单向绑定甚至齐全不绑定

MDN Object.defineProperty()

单向绑定是指数据应用 definePropertyconfigurable 设置成 false,这样使数据相应化的 defineReactive 会跳过响应式设置:

Object.defineProperty(data, key, {configurable: false,})

然而你依然能够通过 v-model 向绑定的指标赋值,只是赋值后界面不会更新。

这种做法在数据嵌套很深时有起效 ,阻断 defineReactive 后不会递归解决数据里的子对象。( 数据扁平化 也能够罢黜递归)

齐全不绑定就是官网写的 Object.freeze 一个对象再赋值,这么做对象外部的值(第一层)就间接不能改了,能够利用于纯展现的数据。

缓存 ajax 数据

能够封装得像跟一般 axios 的 get 一样,间接替换原来的 axios 对象:

import axios from 'axios'
import router from './router'
import {Message} from 'element-ui'
let baseURL = process.env.VUE_APP_BASEURL
let ajax = axios.create({
  baseURL,
  withCredentials: true,
})
let ajaxCache = {}

ajaxCache.get = (...params) => {let url = params[0]
  let option = params[1]
  let id = baseURL + url + (option ? JSON.stringify(option.params) : '')
  if (sessionStorage[id]) {return Promise.resolve(JSON.parse(sessionStorage[id]))
  }
  return ajax.get(...params)
}

ajax.interceptors.response.use(function (response) {
    // 其余解决
    // ……
    if (response.data.code === '20000') {
      let params = response.config.params
      let id = response.config.url + (params ? JSON.stringify(params) : '')
      sessionStorage[id] = JSON.stringify(response.data.data)
      return response.data.data
    }
  },
  function (error) {Message.error('连贯超时')
    return Promise.reject(error)
  }
)

export default ajaxCache

函数式组件

<template functional>
  <div class="cell">
    <div v-if="props.value" class="on"></div>
    <section v-else class="off"></section>
  </div>
</template>

https://codesandbox.io/s/func…

PS:函数式组件因为没有实例化,所以每次应用都会从新渲染,想要齐全动态要用 v-once

PS2:在 Vue3 中,functional 和一般组件速度差异简直能够疏忽

缩小应用 this

简略来说就是要留神 computed、watch 和 render 外面每一次 this 取值的代价都蕴含依赖收集的代码,实际上这些代码只有运行一次就足够了。

{
  computed: {base () {return 42},
    result ({base, start}) {
      let result = start
      for (let i = 0; i < 1000; i++) {result += Math.sqrt(Math.cos(Math.sin(base))) + base * base + base + base * 2 + base * 3
      }
      return result
    },
  },
}

想要更具体理解这个问题,能够看这里:https://mp.weixin.qq.com/s/wu…

v-show 重用 DOM

v-show 诚然能够放慢组件显示速度,然而 v-showv-if 的均衡也要把握好。v-if 能够用于首屏加载速度优化。

分片

  • 分批赋值响应式数据,从而缩小每次渲染的工夫,进步用户感知到的晦涩度。
  • 重型组件应用 v-if 延后展现
  • 能够借助 requestAnimationFrame

相干传送门:requestAnimationFrame

PS:集体体验,如果多个 ajax 牵扯到雷同的一堆数据,分片渲染的速度恐怕并不会快,我会抉择用 Promise.all 合并渲染

总结

组件角度优化:

  • 拆分组件,利用组件级别的更新粒度优化更新速度
  • 慎用重型组件,有必要时本人造,插件同理
  • 应用函数式组件(低优先)

解决响应式的副作用:

  • 利用响应式的反模式
  • 缩小在依赖收集时应用 this

升高渲染压力:

  • v-showv-if 的均衡
  • 分片渲染

Vue 自带的缓存:

  • keepalive
  • computed

其余优化:

  • 数据缓存
  • 虚构滚动
  • 去除 console.log
  • 启用 performance 配置

举荐文章

  • 揭秘 Vue.js 九个性能优化技巧
退出移动版