Open8

Vue3のcomputedパフォーマンス考察

こういう状況のときに、

const map = ref<{ [key:string]: number }>({ a: 1, b: 2 })
const keys = computed(() => Object.keys(map.value))

こうすると、keysは再評価される。

map.value = { a: 1, b: 2 }

こうすると、keysは再評価されない。

map.value.b = 3

hooksでもこう書いたら結果は同様。

const keys = useMemo(() => Object.keys(map), [map])

こう書いたら再評価は回避できる?できる気がする
(実際は算出関数がもっと複雑かつkeysにだけ依存しているとして)

const keys = useMemo(() => Object.keys(map), [Object.keys(map)])

compositionで同様な再評価タイミングを実現させるにはキャッシュ用の状態をもってwatchを使うしかないか?
中身に興味はないけどキー一覧を使って重い算出をしたいような状況。

const keys = ref<string[]>([])
watch(() => Object.keys(map.value), () => {
  return Object.keys(map.value)
})

vue3って書いたけど多分このあたりの挙動はvue2から変わってないと思われる。

computedに無意味なデフォルト値を返させたい場合、primitiveでない場合は常にundefinedを返すようにしたほうがよい。

if (~~) return {}という風にその場で空objektを返してしまうと、そのcomputedはdirtyと見なされて後続がすべて再評価されていく。
undefined判定が増えてしまうがそれを補ってパフォーマンスが向上する。

https://zenn.dev/miyanokomiya/scraps/270dcfe3ef2a9c#comment-19cd10eba314a8

このwatchの書き方ではObject.keys(map.value)に変化がなくてもコールバックは実行されてしまうっぽかった。Object.keys(map.value).sort().join(',')とかにしてもだめ。
評価の関数はcomputedと同じく参照の更新を見ているのかもしれない(戻り値はcallbackへ渡すために必要なだけか?)。

onBeforeUpdateを使って自力で値を評価しないとだめそう。

let keys = ''
onBeforeUpdate(() => {
  const nextKeys = Object.keys(map.value).sort().join(',')
  if (keys !== nextKeys) {
    ~~ callback ~~
    keys = nextKeys
  }
})

onBeforeUpdateの中でref等を変更しても再度onBeforeUpdateが実行されることはないっぽい。

provideinjectを使うと、それを参照しない中間コンポーネントの再評価をスキップできる。
propsで受け渡していくと中間コンポーネントも再評価される。

v-memoという新たなdirectiveが3.2から導入された。
<template>内でしか使えないがProxy式ではなく値で評価してくれる。

ログインするとコメントできます