Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Vue3の一歩踏み込んだパフォーマンスチューニング2024

Hal
October 19, 2024

 Vue3の一歩踏み込んだパフォーマンスチューニング2024

VueFes2024 セッションにてお話しさせていただきました。

## URL
https://vuefes.jp/2024/en/sessions/hal

## 概要
弊社ではユーザー向け機能、及び業務支援システムをVue/Nuxtで構築しており、昨年12月にはVue/Nuxt3へのマイグレーションを行いました。
日々の開発でVue2時代とは大量データの扱いやパフォーマンスチューニングの勘所が変化してきていると感じており、Vue3.4のリリースでもパフォーマンスの大幅な改善について触れられています。
そこで現在のVue、及びVue周辺ライブラリでのパフォーマンス改善がどれだけ行えるのか(3.4バージョンアップによる改善も含む)についてお話いたします。
単なるチューニング方法だけでなく、これまで行われていたが今は不要となったチューニング方法など、これまでの弊社での取り組みを踏まえてお話できればと思います。

Hal

October 19, 2024
Tweet

Other Decks in Programming

Transcript

  1. R.H

  2. [ { "id": "11112222", "list_data_A": [ "ABC", "DEF", "GHI" ],

    "object_data_A": { "key1": "value1", "key2": "value2" }, "value": 10000, // …以降40件のプロパティ }, // …最大1000件のデータ ]
  3. <template> <!-- Sample1 --> <ul> <li v-for="(item, idx) in listItems"

    :key="`list-item-${idx}`"> {{ item }} </li> </ul> <!-- Sample2 --> <q-table> <template #body="props"> <tr :key="`q-table-row- ${props.row.rowIndex}`"> <td>{{ props.row.key }}</td> <td>{{ props.row }}</td> </tr> </template> </q-table> </template>
  4. <!-- Child Component --> <template> <input type="text" v-model="temporaryValue" @blur="emitValue()" />

    </template> <script setup lang="ts"> const emit = defineEmits<{ (e: 'change-value', value: string): void }>() const temporaryValue = ref('') const emitValue = () => { emit('change-value', temporaryValue.value)} </script> <!-- 親コンポーネント --> <template> <ul> <li v-for="(_, idx) in reactiveItems“ :key=“idx”> <ChildComponent @change-value="(val:string)=>{ reactiveItems[idx].editValue = val}" /> </li> </ul> </template> <script setup lang="ts"> import ChildComponent from '****/ChildComponent.vue' const reactiveItems = ref([ { editValue: 'a' }, { editValue: 'b' }]) </script>
  5. <template> <!-- valueが変更するたびに heavyMethodが再計算される --> <div> <p>{{ value }}</p> <div>{{

    heavyMethod() }}</div> </div> <!-- 子コンポーネントに閉じると、valueが変更しても heavyMethodは再実行されない --> <div> <p>{{ value }}</p> <largeComponent /> </div> </template>
  6. <template> <div class="q-pa-md"> <q-table style="height: 400px" flat bordered title="Treats" :rows="rows"

    :columns="columns" row-key="index" virtual-scroll v-model:pagination="pagination" :rows-per-page-options="[0]" /> </div> </template> https://quasar.dev/vue-components/table/#example--basic-virtual- scroll
  7. // Vue2 Vue.component('async-example', function (resolve, reject) { setTimeout(function () {

    resolve({ template: '<div>I am async!</div>' }) }, 1000) }) //Vue3 import { defineAsyncComponent } from 'vue' const AsyncComp = defineAsyncComponent(() => { return new Promise((resolve, reject) => { // ...サーバーからコンポーネントを読み込む resolve(/* 読み込まれたコンポーネント */) }) }) // ... `AsyncComp` を普通のコンポーネントと同じように 使用する
  8. https://ja.vuejs.org/api/reactivity-advanced#reactivity-api-advanced const state = shallowRef({ count: 1 }) // 初回実行時に一度だけ

    1 をログ出力する watchEffect(() => { console.log(state.value.count) }) // 変更をトリガーしない state.value.count = 2 // 変更をトリガーする state.value = { count: 5 } // この時点では”10”はログ出力されない state.value.count = 10 // "10" が出力される triggerRef(state) const scope = effectScope() scope.run(() => { const doubled = computed(() => state.value.count * 2) watch(doubled, () => console.log(doubled.value)) watchEffect(() => console.log('Count: ', doubled.value)) }) // スコープ内のすべてのエフェクトを破棄 scope.stop()
  9. https://vueuse.org/shared/computedWithControl/ https://vueuse.org/shared/watchDebounced/ import { computedWithControl } from '@vueuse/core' const source

    = ref('foo') const counter = ref(0) const computedRef = computedWithControl( () => source.value, // watch source, same as `watch` () => counter.value // computed getter, same as `computed` ) import { watchDebounced } from '@vueuse/core' watchDebounced(source, () => { console.log('changed!') }, { debounce: 500, maxWait: 1000 })
  10. https://ja.vuejs.org/api/built-in-directives#v-memo <template> <ul> <!-- v-memoはv-for内部では機能しないので要注意 --> <li v-for="(item, idx) in

    items" :key="idx" v-memo="[idx, item.key]"> <p>{{ item.label }}</p> <p>{{ item.key }}</p> </li> </ul> </template> <script setup lang="ts"> const items = ref<{ label: string; key: string }[]>([]) </script>
  11. https://ja.vuejs.org/guide/essentials/watchers https://v2.ja.vuejs.org/v2/guide/computed#%E3%82%A6%E3%82%A9 %E3%83%83%E3%83%81%E3%83%A3 watch(postSource, callback, { flush: 'post', //主コンポーネントのDOM更新後に実行 })

    watch(syncSource, (newValue, oldValue) => { // Vue管理の更新と同期的に実行される }, { flush: 'sync' }) watch(eagerSource, (newValue, oldValue) => { // すぐに実行され、`source` が変更されると再び実 行される }, { immediate: true })
  12. https://enterprisevue.dev/blog/deep-dive-vue-watchers/ https://vueuse.org/shared/watchPausable/#watchpausable https://blog.vuejs.org/posts/vue-3-5#onwatchercleanup const stopWatcher = watch(valueToWatch, (newValue, oldValue) =>

    { // Watcher function logic }) // Stop the watcher stopWatcher() import { watch, onWatcherCleanup } from 'vue' watch(id, (newId) => { const controller = new AbortController() fetch(`/api/${newId}`, { signal: controller.signal }).then(() => { // callback logic }) onWatcherCleanup(() => { // abort stale request controller.abort() }) })