Turn to a star, VUE performance optimization to share with you. Practice makes perfect and success depends on thought.

Vue 9 Perf Secrets

  • GitHub.
  • Demo

chestnuts

  • Functional components
  • Child component splitting
  • Local variables
  • Reuse DOM with v-show
  • Keep alive(DOM-Reusing Router View)
  • Deferred features
  • Vuex demo

There is a Static comment in the source code, which does not achieve the author’s optimization effect.


Eat chestnuts

Both are written in the corresponding component files of On and Off. Compare two pieces of code.

Functional components

Functional Components official documentation.

FunctionalOff

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

<script>
export default {
  props: ['value'],
}
</script>
Copy the code

FunctionalOn

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

Child component splitting

ChildOff

<template>
  <div :style="{ opacity: number / 300 }">
    <div>{{ heavy() }}</div>
  </div>
</template>

<script>
export default {
  props: ['number'],
  methods: {
    heavy () {
      const n = 100000
      let result = 0
      for (let i = 0; i < n; i++) {
        result += Math.sqrt(Math.cos(Math.sin(42)))
      }
      return result
    },
  },
}
</script>
Copy the code

ChildOn

<template>
  <div :style="{ opacity: number / 300 }">
    <ChildComp/>
  </div>
</template>

<script>
export default {
  components: {
    ChildComp: {
      methods: {
        heavy () {
          const n = 100000
          let result = 0
          for (let i = 0; i < n; i++) {
            result += Math.sqrt(Math.cos(Math.sin(42)))
          }
          return result
        },
      },
      render (h) {
        return h('div', this.heavy())
      },
    },
  },
  props: ['number'],
}
</script>

Copy the code

About an Issue

Ustbhuangyi, which presents the performance problem in the example, uses heavy Function and should use computed property computed. This is also the point where performance optimization can be considered in use, when to use computed properties.

First, thanks for the awesome Project!

In the Child Component Splitting Demo, the root cause of the performance problem is that we use heavy function during render, it will also be called when the component render.

Instead of using child component splitting, can we use computed property? For example:

<template>
  <div :style="{ opacity: number / 300 }">
    <div>{{ heavy }}</div>
  </div>
</template>

<script>
export default {
  props: ['number'],
  computed: {
    heavy () {
      const n = 100000
      let result = 0
      for (let i = 0; i < n; i++) {
        result += Math.sqrt(Math.cos(Math.sin(42)))
      }
      return result
    }
  }
}
Copy the code

Because create a child component will have some extra overhead, so computed property here is better ? Computed property will use cache if it’s dependencies not change, and I think we should use computed property as much as possible other than method, unless in some special cases.

Local variables

LocalOff

<template>
  <div :style="{ opacity: start / 300 }">{{ result }}</div>
</template>

<script>
export default {
  props: ['start'],
  computed: {
    base () {
      return42},result () {
      let result = this.start
      for (let i = 0; i < 1000; i++) {
        result += Math.sqrt(Math.cos(Math.sin(this.base))) + this.base * this.base + this.base + this.base * 2 + this.base * 3
      }
      return result
    },
  },
}
</script>
Copy the code

LocalOn

<template>
  <div :style="{ opacity: start / 300 }">{{ result }}</div>
</template>

<script>
export default {
  props: ['start'],
  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
    },
  },
}
</script>
Copy the code

Reuse DOM with v-show

HideOff

<template functional>
  <div class="cell">
    <div v-if="props.value" class="on">
      <Heavy :n="10000"/>
    </div>
    <section v-else class="off">
      <Heavy :n="10000"/>
    </section>
  </div>
</template>
Copy the code

HideOn

<template functional>
  <div class="cell">
    <div v-show="props.value" class="on">
      <Heavy :n="10000"/>
    </div>
    <section v-show=! "" props.value" class="off">
      <Heavy :n="10000"/>
    </section>
  </div>
</template>
Copy the code

Keep alive (DOM-Reusing Router View)

<template>
  <Benchmark title="DOM-Reusing Router View" class="keep-alive">
    <template #toolbar>
      <VueGroup v-model="page">
        <VueGroupButton :value="false">Simple page</VueGroupButton>
        <VueGroupButton :value="true">Heavy page</VueGroupButton>
      </VueGroup>

      <PlayToggle v-model="play"/>
    </template>

    <template #on>
      <keep-alive>
        <router-view/>
      </keep-alive>
    </template>

    <template #off>
      <router-view/>
    </template>
  </Benchmark>
</template>

<script>

export default {
  data () {
    return {
      play: false,
    }
  },

  computed: {
    page: {
      get () {
        return this.$route.name === 'bench-keep-alive-heavy'
      },
      set (value) {
        if (value) {
          this.$router.push({ name: 'bench-keep-alive-heavy'})}else {
          this.$router.push({ name: 'bench-keep-alive' })
        }
      },
    },
  },

  watch: {
    play (value) {
      if (value) {
        this.togglePage()
      } else {
        clearTimeout(this.$_timer)}}},created () {
    this.count = 300
  },

  methods: {
    togglePage() { this.page = ! this.pageif (this.play) {
        this.$_timer = setTimeout(this.togglePage, 2000)
      }
    },
  },
}
</script>

<style lang="stylus" scoped>
.keep-alive
  .router-multi-view
    height 100%

  >>>
    .simple-page,
    .deferred-off
      height 100%
      display flex
      flex-direction column
      align-items center
      padding 40px
      box-sizing border-box

      h2:not(:last-child)
        margin-bottom 24px
</style>
Copy the code

Deferred features

DeferredOff

<template>
  <div class="deferred-off">
    <VueIcon icon="fitness_center" class="gigantic"/>

    <h2>I'm an heavy page 
       
        Copy the code

DeferredOn

<template>
  <div class="deferred-on">
    <VueIcon icon="fitness_center" class="gigantic"/>

    <h2>I'm an heavy page 
       
         @/mixins/Defer' export default { mixins: [ Defer(), ], } Copy the code

mixin Defer

export default function (count = 10) {
  // @vue/component
  return {
    data () {
      return {
        displayPriority: 0,
      }
    },

    mounted () {
      this.runDisplayPriority()
    },

    methods: {
      runDisplayPriority () {
        const step = () => {
          requestAnimationFrame(() => {
            this.displayPriority++
            if (this.displayPriority < count) {
              step()
            }
          })
        }
        step()
      },

      defer (priority) {
        return this.displayPriority >= priority
      },
    },
  }
}
Copy the code

Vuex demo

The last one is for the curious to look at the code. Render 10000 data, one is Creash, optimized without pressure.