
reactive 返回的对象做 ES 解构后,解构出来的变量不再响应更新。toRefs/toRef 保持引用与代理,或避免直接解构。import { reactive, toRefs, toRef } from 'vue'
const state = reactive({ count: 0, user: { name: 'A' } })
// 错误
const { count } = state
// 正确
const { count: countRef } = toRefs(state)
const nameRef = toRef(state.user, 'name')reactive 对象整体重新赋值不生效。reactive 返回的是固定代理,替换变量仅更改引用。ref 持有对象并改 .value;需要局部更新用 Object.assign。import { reactive, ref } from 'vue'
const state = reactive({ list: [1] })
// 错误:不会触发
// state = { list: [2] }
// 正确:
Object.assign(state, { list: [2] })
// 或整体替换:
const holder = ref({ list: [1] })
holder.value = { list: [2] }ref({}) 后对 .value 直接替换有效,但对深层属性忘记加 .value。.value = 新对象;深层属性通过 .value.xxx = 修改。import { ref } from 'vue'
const profile = ref({ name: 'A', age: 18 })
profile.value.age = 19
profile.value = { ...profile.value, name: 'B' }watch(state, ...) 监听到大量无关变更或无法精准到字段更新。toRef,避免无谓触发;需要深度时设置 { deep: true }。import { reactive, toRef, watch } from 'vue'
const state = reactive({ query: '', items: [] })
watch(() => state.query, (nv, ov) => {/*...*/})
watch(toRef(state, 'items'), (nv) => {/*...*/}, { deep: true })watchEffect 中做网络请求或写入副作用,导致频繁触发。watch + 明确源;watchEffect 适合派生计算与轻量任务。import { ref, watch, watchEffect } from 'vue'
const id = ref('1')
watch(id, async (nv) => {/* 请求 */}, { flush: 'post' })
watchEffect(() => {/* 轻量派生 */})computed 中发请求或修改其他状态,导致不可预测。computed 必须纯函数且仅依赖响应源;副作用转移到 watch。import { computed, ref, watch } from 'vue'
const a = ref(1), b = ref(2)
const sum = computed(() => a.value + b.value)
watch(sum, (v) => {/* 根据值变化触发副作用 */})watch 中读取 DOM 未更新,或需要在 DOM 更新后执行逻辑。{ flush: 'post' } 或 await nextTick()。import { ref, watch, nextTick } from 'vue'
const show = ref(false)
watch(show, async () => { await nextTick(); /* 此时 DOM 已更新 */ }, { flush: 'post' })v-model 无效或无法双向绑定。modelValue 与 update:modelValue 事件约定。defineProps/defineEmits 对齐协议。<script setup lang="ts">
import { defineProps, defineEmits } from 'vue'
const props = defineProps<{ modelValue: string }>()
const emit = defineEmits<{ (e: 'update:modelValue', v: string): void }>()
function onInput(v: string) { emit('update:modelValue', v) }
</script>ref/reactive,注入后使用原引用或 toRef;避免解构原始值。// provider
import { provide, ref } from 'vue'
const theme = ref('light')
provide('theme', theme)
// consumer
import { inject } from 'vue'
const theme = inject('theme') as typeof themeonUnmounted 清理;watch 返回停止函数或在回调返回清理函数。import { onUnmounted, watch, ref } from 'vue'
const timer = setInterval(() => {}, 1000)
onUnmounted(() => clearInterval(timer))
const x = ref(0)
const stop = watch(x, (v, o, onCleanup) => { const ctrl = new AbortController(); onCleanup(() => ctrl.abort()) })toRef/toRefs:在组合函数中向外暴露字段时保持响应引用。effectScope:将一组副作用统一管理,避免泄漏。ref/computed,副作用内部管理并暴露停止函数。reactive/ref;浅响应配合 triggerRef 手动触发。import { shallowRef, triggerRef } from 'vue'
const data = shallowRef({ list: [] })
data.value.list.push(1)
triggerRef(data)<script setup> 中对 reactive 解构并直接在模板使用,视图不更新。toRefs 暴露到模板。import { reactive, toRefs } from 'vue'
const state = reactive({ count: 0 })
const { count } = toRefs(state)onMounted 前读取 DOM 或在 onUpdated 中引入副作用导致频繁触发。nextTick;副作用搬到 watch 并限制触发源。import { onMounted, nextTick, ref } from 'vue'
const ready = ref(false)
onMounted(async () => { ready.value = true; await nextTick() })ref/computed 与停止句柄。export function usePagination(total: number) {
const page = ref(1)
const size = ref(10)
const pages = computed(() => Math.ceil(total / size.value))
return { page, size, pages }
}watch 使用 onCleanup 中断,或引入请求层中断控制。import { ref, watch } from 'vue'
const id = ref('1')
watch(id, async (nv, _, onCleanup) => {
const ctrl = new AbortController()
onCleanup(() => ctrl.abort())
await fetch(`/api/${nv}`, { signal: ctrl.signal })
})onUnmounted 清理。import { onMounted, onUnmounted } from 'vue'
function useResize() {
const handler = () => {}
onMounted(() => window.addEventListener('resize', handler))
onUnmounted(() => window.removeEventListener('resize', handler))
}reactive/ref 的字段更新是否触发视图与计算值更新。watch/watchEffect 是否仅针对预期源触发,是否有清理。post 阶段或 nextTick 后执行。computed 做派生,减少 watch;对昂贵计算使用缓存与拆分。watch;精确监听源或使用局部 ref。setup 中做沉重同步工作。