Pl Vue

性能优化

以下优化在通常情况下是不需要的,如果您是一个追求极致性能的开发者或应用本身出现了严重的内存泄漏,请通过以下方式优化。

响应式对象声明

响应式对象本身会经过 proxy 处理,在数据庞大时可能会存在效率慢的问题。

  1. 在一些不需要响应式数据的情况下,可直接声明普通对象;
  2. 既数据庞大,又需要响应式,请考虑以下方式创建响应式数据。

shallowRef

与 ref 类似,不会进行深度监听,但会递归收集依赖。

const o = shallowRef({ a: 0 });

watchEffect(() => {
  console.log(o.value.a);
})

// 1.
o.value = { a: 1 };  // 整个对象发生变化,才会触发更新

// 2.
o.value.a++;
toggleRef(o);  // 强制触发更新

bestRef

性能最好的浅响应式对象(不会深度遍历收集依赖,也就无法强制更新)

const o = bestRef({ a: 0 });

watchEffect(() => {
  console.log(o.value.a);
})

o.value = { a: 1 };  // 只能通过改变整个对象来触发更新

响应式函数导致的内存泄漏

  • 自执行函数会在内存中一直存在,所以可能会存在内存泄漏;
  • 计算属性/监听器本身通过闭包实现,内部绑定函数并不会因为数据的回收而回收。所以需要在适当的时候将其清除。

清除自身自执行函数

const count = ref(0);

// 1.
const newCount = computed(() => count.value * 2);
newCount.effect.stop();  // 停止计算

// 2.
const stop = watchEffect(() => {
  console.log(count.value);
})
stop();

// 3.
const unwatch = watch(() => count.value, value => {
  console.log(value);
});
unwatch();

清除作用域内自执行函数

scope.stop() 会帮你自动执行内部的所有可以取消侦听的函数。

const scope = effectScope();
scope.run(() => {
  const newCount = computed(() => count.value * 2);

  watch(() => count.value, () => {
    console.log(doubled.value)
  })

  watchEffect(() => {
    console.log(count.value);
  })
})
scope.stop();

清除数据相关所有的自执行函数

  • 在特定的情况下使用(该数据绑定的函数已经没有任何更新的需要了);
  • 组件卸载时没有必要去执行,JS 自身的垃圾回收机制会自动回收。
const count = ref(0);
const newCount = computed(() => count.value * 2);

watchEffect(() => {
  console.log(count.value);
})
watchEffect(() => {
  console.log(newCount.value);
})

recycleDepend(count, newCount);  // 回收内存,清空响应式数据绑定的自执行函数
// 清除之后依然可以重新注册监听