Vue3 组合式 API 实战指南Vue3 是 CSDN 前端板块流量最大的框架之一组合式 APIComposition API是 Vue3 的核心。本文从 setup 语法讲起覆盖 ref/reactive、computed、watch、生命周期、组件通信、Pinia 状态管理、Vite 构建、组合式函数封装全程带可运行代码。一、Vue3 与 Vue2 的区别1.1 三大核心变化1. 响应式系统Vue2 用 Object.definePropertyVue3 用 Proxy。维度Vue2Vue3拦截方式对象属性级对象整体代理新增属性不响应要 $set天然响应数组下标修改不响应响应性能大数据量卡快懒追踪2. 组合式 API逻辑从按选项分散变成按功能聚合。3. 多根节点模板可以有多个根元素。1.2 Options API 的问题Vue2 的逻辑组织方式exportdefault{data(){return{count:0}},computed:{...},methods:{...},watch:{...},created(){...}}问题一个功能比如搜索的代码分散在 data/computed/methods/watch 里——代码超过 500 行时改一个功能要上下翻几十次。组合式 API 把同一功能抽到一起。二、setup 与响应式基础2.1 setup 语法糖Vue3.2 推荐script setup——最简洁的写法template div p{{ count }}/p button clickincrement1/button /div /template script setup import { ref } from vue // 顶层声明的变量/函数自动暴露给模板 const count ref(0) const increment () count.value /script特点不用 export default、不用 return顶层 import 直接可用组件自动注册import 即用性能更好模板编译优化。2.2 ref 与 reactiveref包基础类型也可包对象取值要.valueimport{ref}fromvueconstcountref(0)// 数字constnameref(张三)// 字符串constobjref({a:1})// 对象内部用 reactive 处理console.log(count.value)// 0count.valuereactive包对象引用类型不能包基础类型import{reactive}fromvueconststatereactive({count:0,user:{name:张三,age:25}})state.count// 直接改不用 .valuestate.user.name李四// 深层响应选择经验基础类型 → ref复杂对象 → reactive 或 ref 都行统一用 ref 更不容易犯错Vue 官方文档也建议默认 ref。2.3 模板中的自动解包!-- 模板里 ref 自动解包不用 .value -- p{{ count }}/p坑模板里嵌套对象中的 ref 不会自动解包script里始终要.value。2.4 toRefs 解构保持响应import{reactive,toRefs}fromvueconststatereactive({count:0,name:张三})// 直接解构会丢失响应性// const { count } state ← count 变普通变量// 正确toRefs 保持响应const{count,name}toRefs(state)count.value// 响应还在三、computed 计算属性3.1 基本用法import{ref,computed}fromvueconstpriceref(100)constquantityref(2)// 只读计算属性consttotalcomputed(()price.value*quantity.value)模板里直接用{{ total }}——依赖变化自动更新。3.2 可写计算属性constfullNamecomputed({get:()firstName.value lastName.value,set:(val){[firstName.value,lastName.value]val.split( )}})3.3 计算属性 vs 方法consttotalcomputed(()price.value*quantity.value)functiontotalFn(){returnprice.value*quantity.value}computed有缓存依赖不变不重算方法每次渲染都执行。模板里调用多次时computed 性能更好。四、watch 侦听器4.1 基本用法import{ref,watch}fromvueconstkeywordref()constlistref([])// 侦听单个 refwatch(keyword,(newVal,oldVal){console.log(关键词变化,oldVal,→,newVal)search(newVal)})// 侦听多个watch([keyword,page],([k,p],[oldK,oldP]){fetchList({keyword:k,page:p})})4.2 立即执行 深度侦听watch(()state.user,(val)console.log(val),{immediate:true,// 初始化就执行一次deep:true// 深层对象变化也触发})4.3 watchEffect自动收集依赖依赖变就重新执行import{watchEffect}fromvuewatchEffect((){// 用到了什么就自动侦听什么console.log(当前搜索词,keyword.value)saveToStorage(keyword.value)})区别watch显式指定侦听目标需要时给出新旧值watchEffect自动追踪依赖适合副作用保存、请求。五、生命周期5.1 生命周期对比Vue2Vue3 组合式beforeCreatesetup 即替代createdsetup 即替代beforeMountonBeforeMountmountedonMountedbeforeUpdateonBeforeUpdateupdatedonUpdatedbeforeUnmountonBeforeUnmountunmountedonUnmountedimport{onMounted,onUnmounted}fromvueonMounted((){fetchData()window.addEventListener(resize,handleResize)})onUnmounted((){window.removeEventListener(resize,handleResize)})注意setup在beforeCreate之前执行——this不可用不依赖 this 是组合式 API 的优点。六、组件通信6.1 父子通信props / emit!-- 父组件 -- script setup import { ref } from vue import Child from ./Child.vue const msg ref(来自父组件) const handleReply (data) console.log(子组件说:, data) /script template Child :messagemsg replyhandleReply / /template!-- 子组件 -- script setup // defineProps / defineEmits 是编译器宏不用 import const props defineProps({ message: { type: String, required: true } }) const emit defineEmits([reply]) const send () emit(reply, 收到父组件) /script template p{{ message }}/p button clicksend回复/button /template6.2 事件校验constemitdefineEmits({reply:(data)typeofdatastring// 校验返回 false 警告})6.3 v-model 双向绑定组件上!-- 父组件 -- SearchBox v-modelkeyword / !-- 子组件modelValue update:modelValue -- script setup const props defineProps([modelValue]) const emit defineEmits([update:modelValue]) const onInput (e) emit(update:modelValue, e.target.value) /script template input :valuemodelValue inputonInput / /template6.4 跨层级通信provide / inject祖孙组件直接传不用层层 props// 祖先import{provide}fromvueprovide(theme,dark)// 后代import{inject}fromvueconstthemeinject(theme,light)// 第二个参数是默认值Pinia全局状态见第七章。七、Pinia 状态管理7.1 为什么是 PiniaVuex 4 已停止新功能官方推荐 Pinia更简单去掉 mutations直接改 state类型友好TS 完美支持轻量。7.2 安装与定义npminstallpinia// main.jsimport{createApp}fromvueimport{createPinia}frompiniaimportAppfrom./App.vueconstappcreateApp(App)app.use(createPinia())app.mount(#app)// stores/counter.jsimport{defineStore}frompiniaexportconstuseCounterStoredefineStore(counter,{state:()({count:0}),getters:{double:(state)state.count*2},actions:{increment(){this.count}}})7.3 组合式写法import{ref,computed}fromvueimport{defineStore}frompiniaexportconstuseUserStoredefineStore(user,(){consttokenref(localStorage.getItem(token)||)constisLogincomputed(()!!token.value)functionlogin(newToken){token.valuenewToken localStorage.setItem(token,newToken)}functionlogout(){token.valuelocalStorage.removeItem(token)}return{token,isLogin,login,logout}})7.4 组件中使用import{useCounterStore}from/stores/counterimport{storeToRefs}frompiniaconststoreuseCounterStore()// 解构保持响应必须用 storeToRefsconst{count,double}storeToRefs(store)// actions 直接解构不是响应式数据const{increment}store坑直接const { count } store会丢响应性——必须storeToRefs。八、组合式函数Composables8.1 什么是 Composables把带状态的逻辑抽成可复用函数——这是组合式 API 最强大的地方。8.2 实战封装 useFetch// composables/useFetch.jsimport{ref,watch}fromvueexportfunctionuseFetch(url){constdataref(null)constloadingref(false)consterrorref(null)asyncfunctionload(){loading.valuetrueerror.valuenulltry{constresawaitfetch(url.value||url)data.valueawaitres.json()}catch(e){error.valuee}finally{loading.valuefalse}}// url 变化自动重新请求if(typeofurlfunction){watch(url,load,{immediate:true})}else{load()}return{data,loading,error,retry:load}}8.3 组件中使用script setup import { useFetch } from /composables/useFetch const { data, loading, error, retry } useFetch(/api/users) /script template div v-ifloading加载中.../div div v-else-iferror出错了/div ul v-else li v-foru in data :keyu.id{{ u.name }}/li /ul button clickretry重试/button /template封装原则以use开头命名内部状态用 ref/reactive返回对象可解构不依赖组件实例可在任何地方用。8.4 常用组合式函数示例useLocalStorage读写 localStorage 并保持响应useDebounce防抖useEventListener事件监听 自动清理useInterval定时器 清理。// useDebounceimport{ref,watch}fromvueexportfunctionuseDebounce(value,delay300){constdebouncedref(value.value)watch(value,(val){clearTimeout(timer)timersetTimeout((){debounced.valueval},delay)})returndebounced}九、Vite 构建与工程化9.1 创建项目npmcreate vuelatest# 官方脚手架Vite# 选择TS / Router / Pinia / ESLint目录结构src/ ├── main.js ├── App.vue ├── router/ ├── stores/ ├── views/ ├── components/ └── composables/9.2 路由配置import{createRouter,createWebHistory}fromvue-routerconstroutercreateRouter({history:createWebHistory(),routes:[{path:/,component:()import(/views/Home.vue)},{path:/user/:id,component:()import(/views/User.vue)}]})// 路由守卫router.beforeEach((to,from){if(to.meta.requiresAuth!isLogin())return/login})9.3 环境变量# .env.developmentVITE_API_BASE/api# .env.productionVITE_API_BASEhttps://api.example.com// 使用必须以 VITE_ 开头才暴露constbaseimport.meta.env.VITE_API_BASE9.4 代码规范// .eslintrc 推荐 vue3 官方规则// prettier 统一格式十、常见坑速查现象原因解决模板不更新直接解构 reactivetoRefsref 忘 .value在 script 里用 ref记得 .value响应丢失reactive 整个替换用 Object.assign 或 refwatch 不触发深层对象没 deepdeep: true组件通信难层级太深provide/inject 或 Pinia生命周期不执行放在异步回调里顶层调用 onMounted打包体积大全量引入库按需引入unplugin-vue-components十一、Vue3 最佳实践总结默认 ref复杂嵌套用 reactive逻辑用 composables 抽组件只留模板和装配状态用 Pinia别在组件里传一堆 props模板表达式保持简单复杂逻辑放 computed异步请求统一封装useFetchloading/error 全覆盖组件库按需引入Element Plus 等控制包体积script setup TS是当前推荐组合。本章小结Vue3 组合式 API 的核心价值不是换了个写法而是逻辑可复用、代码可维护。掌握 ref/reactive、computed/watch、Pinia、composables 四块就掌握了 Vue3 的 80%。下一篇讲 PyTorch——AI 方向的 CSDN 流量之王。全文完