跨层级传值:provide / inject,与逻辑复用:组合式函数

左边是三层嵌套:Page → Panel → Leaf。Page 只 provide 一次,Panel 一行相关代码都不写,Leaf 直接 inject 就能用。 右边是逻辑复用:把「ref + 方法」打包成 useCounter、useLocalStorage 这样的组合式函数,谁需要谁调用,各拿一份独立状态。

一句话记住:provide 传 ref,值变了下游才变;provide 传普通数值 = 交出去一份拷贝,provide 普通对象(没套 reactive)也不会触发任何重新渲染 —— 它只是等别的响应式数据逼出一次重渲染时被顺带刷出来。 inject 一定写默认值:inject('missing', '没有这个 key,走默认值'),不写就只剩 undefined。 组合式函数就是 ref + 方法的打包,不是 Vue 的某种新对象:能在里面对外 provide,但也必须同步调用。

组合式函数完整源码(照抄就能用)

function useCounter(init) {              // 一份状态 + 一组方法
  const count = Vue.ref(init)            // 每次调用都新建一份
  const step = (n) => { count.value += n }
  const reset = () => { count.value = init }
  return { count, step, reset }
}
function useLocalStorage(key, fallback) {   // 一份状态 + 自动落盘
  const data = Vue.ref(fallback)
  try { const s = localStorage.getItem(key)
        if (s !== null) data.value = JSON.parse(s) } catch (e) {}   // 读回来
  Vue.watch(data, (v) => {                  // 变化就写回去
    try { localStorage.setItem(key, JSON.stringify(v)) } catch (e) {}
  })
  const clear = () => { localStorage.removeItem(key); data.value = fallback }
  return { data, clear, key }
}

组合式函数的约定与坑

约定 / 坑怎么做为什么
放哪、叫什么 文件放 src/composables/,一个文件一个函数,命名 useXxx(useCounter.js) 它不是组件、不是插件,use 前缀就是「这是个带响应式的可复用逻辑」的记号
返回值用 ref,别用 reactive 整体 return { count, step },调用方 const { count } = useCounter(0) 照常解构 返回一个 reactive 对象再解构,解构出来的是普通值,响应性当场丢
可以在里面对外 provide function useTheme() { const t = ref('light'); provide('theme', t); return t } provide / inject 必须在组件实例上登记,所以只能在 setup 的同步过程里调
必须在 setup(或另一个 setup)同步期间调用 不要在 setTimeout、then、普通工具函数里调 inject / provide / watch 那时已经没有「当前组件实例」了,inject 拿到 undefined,provide 无处可挂(点上面的按钮看现场)

速查:这四种手段什么时候用哪个

手段方向选择依据典型场景
props / emit(示例 09)父子,一层一层只有两层、且数据是「这个组件的输入」列表项、表单项、弹窗
provide / inject祖先 → 任意深后代中间层只是路过、不该替它搬箱子;或者一个组件要喂一整棵子树主题、语言、表单实例、弹层容器
组合式函数 useXxx不是传值,是传逻辑同样的「状态 + 行为」在两个以上组件里重复出现计数器、本地存储、防抖输入、分页
Pinia(示例 15)全局,任意组件平级读写需要跨页面、跨无关分支共享,还要 devtools 时间旅行和可测试登录用户、购物车、全局配置
[迷你控制台] Vue 开发版的警告会同时显示在这里和浏览器控制台