VUE 9個性能優化祕密?(vue-9-perf-secrets)

翻到一個star,VUE 性能優化給你們分享一下。
業精於勤,行成於思.html

Vue 9 Perf Secrets

栗子

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

源代碼中有個 Static 被註釋,沒有達到做者的優化效果。 vue


吃栗子

栗子中都分別寫在On,Off的對應組件文件中。對比兩份代碼。 git

Functional components

Functional components 官方文檔.github

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>
複製代碼

FunctionalOn

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

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>
複製代碼

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>

複製代碼

關於一個Issues

ustbhuangyi (附上原文地址),提出例子中性能問題,用了 heavy function,應該採用計算屬性 computed。這也是使用中,性能優化能夠考慮的點,何時該用計算屬性。性能優化

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.bash

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

<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
    }
  }
}
複製代碼

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.ide

Local variables

LocalOff

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

<script>
export default {
  props: ['start'],
  computed: {
    base () {
      return 42
    },
    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>
複製代碼

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>
複製代碼

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>
複製代碼

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>
複製代碼

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.page
      if (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>
複製代碼

Deferred features

DeferredOff

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

    <h2>I'm an heavy page</h2> <Heavy v-for="n in 8" :key="n"/> <Heavy class="super-heavy" :n="9999999"/> </div> </template> 複製代碼

DeferredOn

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

    <h2>I'm an heavy page</h2> <template v-if="defer(2)"> <Heavy v-for="n in 8" :key="n"/> </template> <Heavy v-if="defer(3)" class="super-heavy" :n="9999999"/> </div> </template> <script> import Defer from '@/mixins/Defer' export default { mixins: [ Defer(), ], } </script> 複製代碼

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
      },
    },
  }
}
複製代碼

Vuex demo

最後一個給你們好奇去看代碼。渲染10000條數據,一個是creash,優化後的沒壓力。性能

相關文章
相關標籤/搜索