网站建设得多钱客流分析系统公司
2026/1/12 21:27:48 网站建设 项目流程
网站建设得多钱,客流分析系统公司,免费wordpress 主题,网站做实名验证码Vue3的Composition API彻底改变了Vue的开发方式#xff0c;本文将深入剖析组合式API的核心概念和最佳实践#xff0c;帮助你快速掌握Vue3开发。 一、为什么需要Composition API#xff1f; 1.1 Options API的痛点 痛点表现#xff1a; 逻辑分散#xff1a;相关代码被da…Vue3的Composition API彻底改变了Vue的开发方式本文将深入剖析组合式API的核心概念和最佳实践帮助你快速掌握Vue3开发。一、为什么需要Composition API1.1 Options API的痛点痛点表现逻辑分散相关代码被data、methods、computed等选项分割代码复用困难mixins容易命名冲突来源不清晰类型推导弱TypeScript支持不够友好大组件难维护代码量大时难以理解和维护// ❌ Options API逻辑分散exportdefault{data(){return{count:0,user:null,loading:false}},computed:{doubleCount(){returnthis.count*2}},methods:{increment(){this.count},asyncfetchUser(){this.loadingtruethis.userawaitapi.getUser()this.loadingfalse}},mounted(){this.fetchUser()}}1.2 Composition API的优势✅ 逻辑聚合相关代码组织在一起✅ 代码复用通过组合函数轻松复用✅ 类型推导完美支持TypeScript✅ Tree-shaking未使用的代码可以被移除二、核心API详解2.1 ref 和 reactive关键点ref用于基本类型reactive用于对象类型。template div !-- ref需要.value访问模板中自动解包 -- p计数: {{ count }}/p p双倍: {{ doubleCount }}/p !-- reactive对象直接访问属性 -- p用户: {{ user.name }}/p p年龄: {{ user.age }}/p button clickincrement增加/button button clickupdateUser更新用户/button /div /template script setup import { ref, reactive, computed } from vue // ✅ ref基本类型响应式 const count ref(0) const message ref(Hello) // 在JS中需要.value访问 console.log(count.value) // 0 count.value console.log(count.value) // 1 // ✅ reactive对象类型响应式 const user reactive({ name: 张三, age: 25, address: { city: 北京 } }) // 直接访问属性 console.log(user.name) // 张三 user.age 26 // computed计算属性 const doubleCount computed(() count.value * 2) // 方法 const increment () { count.value } const updateUser () { user.name 李四 user.age 30 } // ❌ 常见错误解构reactive会失去响应式 const { name, age } user // 失去响应式 // ✅ 正确使用toRefs保持响应式 import { toRefs } from vue const { name, age } toRefs(user) // 保持响应式 /script痛点解决清晰的响应式数据管理避免this指向问题。2.2 生命周期钩子关键点组合式API中的生命周期钩子以on开头。script setup import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, onErrorCaptured } from vue // 组件挂载前 onBeforeMount(() { console.log(组件即将挂载) }) // 组件挂载后最常用 onMounted(() { console.log(组件已挂载) // 适合DOM操作、发起请求、初始化第三方库 fetchData() initChart() }) // 组件更新前 onBeforeUpdate(() { console.log(组件即将更新) }) // 组件更新后 onUpdated(() { console.log(组件已更新) // 注意避免在这里修改状态可能导致无限循环 }) // 组件卸载前 onBeforeUnmount(() { console.log(组件即将卸载) // 适合清理定时器、取消请求、移除事件监听 }) // 组件卸载后 onUnmounted(() { console.log(组件已卸载) }) // 错误捕获 onErrorCaptured((err, instance, info) { console.error(捕获到错误:, err) return false // 阻止错误继续传播 }) // ✅ 可以多次调用同一个钩子 onMounted(() { console.log(第一个mounted) }) onMounted(() { console.log(第二个mounted) }) /script对比Options APIOptions APIComposition APIbeforeCreatesetup()createdsetup()beforeMountonBeforeMountmountedonMountedbeforeUpdateonBeforeUpdateupdatedonUpdatedbeforeUnmountonBeforeUnmountunmountedonUnmounted2.3 watch 和 watchEffect关键点watch需要明确指定监听源watchEffect自动追踪依赖。script setup import { ref, reactive, watch, watchEffect } from vue const count ref(0) const user reactive({ name: 张三, age: 25 }) // ✅ watch监听单个ref watch(count, (newVal, oldVal) { console.log(count从${oldVal}变为${newVal}) }) // ✅ watch监听多个源 watch([count, () user.age], ([newCount, newAge], [oldCount, oldAge]) { console.log(count或age变化了) }) // ✅ watch监听reactive对象深度监听 watch(user, (newVal, oldVal) { console.log(user对象变化了) // 注意newVal和oldVal是同一个对象引用 }, { deep: true }) // ✅ watch监听reactive对象的某个属性 watch(() user.name, (newName, oldName) { console.log(name从${oldName}变为${newName}) }) // ✅ watchEffect自动追踪依赖 watchEffect(() { // 自动追踪count和user.age的变化 console.log(count: ${count.value}, age: ${user.age}) }) // watch选项 watch(count, (newVal) { console.log(newVal) }, { immediate: true, // 立即执行一次 deep: true, // 深度监听 flush: post // 在DOM更新后执行 }) // 停止监听 const stop watch(count, (newVal) { console.log(newVal) if (newVal 10) { stop() // 停止监听 } }) // ❌ 常见错误监听reactive对象的属性 watch(user.name, (newVal) { // 错误 console.log(newVal) }) // ✅ 正确写法 watch(() user.name, (newVal) { console.log(newVal) }) /script痛点解决灵活的数据监听避免不必要的性能开销。2.4 computed 计算属性关键点computed具有缓存特性只有依赖变化时才重新计算。script setup import { ref, computed } from vue const firstName ref(张) const lastName ref(三) // ✅ 只读计算属性 const fullName computed(() { console.log(计算fullName) // 只在依赖变化时执行 return ${firstName.value} ${lastName.value} }) // ✅ 可写计算属性 const fullNameWritable computed({ get() { return ${firstName.value} ${lastName.value} }, set(value) { const names value.split( ) firstName.value names[0] lastName.value names[1] } }) // 使用 console.log(fullName.value) // 张 三 fullNameWritable.value 李 四 // 触发set console.log(firstName.value) // 李 console.log(lastName.value) // 四 // ❌ 常见错误在computed中修改依赖 const badComputed computed(() { firstName.value 王 // 错误不要在computed中修改依赖 return firstName.value }) // ✅ computed vs method const list ref([1, 2, 3, 4, 5]) // computed有缓存依赖不变不重新计算 const filteredList computed(() { console.log(计算filteredList) return list.value.filter(n n 2) }) // method无缓存每次调用都执行 const getFilteredList () { console.log(执行getFilteredList) return list.value.filter(n n 2) } /script template div !-- computed多次访问只计算一次 -- p{{ filteredList }}/p p{{ filteredList }}/p !-- method每次都执行 -- p{{ getFilteredList() }}/p p{{ getFilteredList() }}/p /div /template痛点解决自动缓存计算结果避免重复计算提升性能。三、组合函数Composables3.1 基础组合函数关键点将可复用的逻辑提取为组合函数实现代码复用。// composables/useCounter.jsimport{ref,computed}fromvueexportfunctionuseCounter(initialValue0){constcountref(initialValue)constdoubleCountcomputed(()count.value*2)constincrement(){count.value}constdecrement(){count.value--}constreset(){count.valueinitialValue}return{count,doubleCount,increment,decrement,reset}}!-- 使用组合函数 -- template div p计数: {{ count }}/p p双倍: {{ doubleCount }}/p button clickincrement1/button button clickdecrement-1/button button clickreset重置/button /div /template script setup import { useCounter } from /composables/useCounter // 可以创建多个独立的计数器 const { count, doubleCount, increment, decrement, reset } useCounter(10) const counter2 useCounter(0) /script3.2 实战鼠标位置追踪// composables/useMouse.jsimport{ref,onMounted,onUnmounted}fromvueexportfunctionuseMouse(){constxref(0)constyref(0)constupdate(event){x.valueevent.pageX y.valueevent.pageY}onMounted((){window.addEventListener(mousemove,update)})onUnmounted((){window.removeEventListener(mousemove,update)})return{x,y}}template div p鼠标位置: {{ x }}, {{ y }}/p /div /template script setup import { useMouse } from /composables/useMouse const { x, y } useMouse() /script3.3 实战异步数据获取// composables/useFetch.jsimport{ref,watchEffect,toValue}fromvueexportfunctionuseFetch(url){constdataref(null)consterrorref(null)constloadingref(false)constfetchDataasync(){loading.valuetrueerror.valuenulldata.valuenulltry{// toValue可以处理ref和普通值consturlValuetoValue(url)constresponseawaitfetch(urlValue)if(!response.ok){thrownewError(HTTP error! status:${response.status})}data.valueawaitresponse.json()}catch(e){error.valuee.message}finally{loading.valuefalse}}// 当url变化时重新获取watchEffect((){fetchData()})constrefetch(){fetchData()}return{data,error,loading,refetch}}template div div v-ifloading加载中.../div div v-else-iferror错误: {{ error }}/div div v-else-ifdata h3{{ data.title }}/h3 p{{ data.body }}/p /div button clickrefetch刷新/button /div /template script setup import { ref } from vue import { useFetch } from /composables/useFetch const userId ref(1) const url computed(() https://jsonplaceholder.typicode.com/posts/${userId.value}) const { data, error, loading, refetch } useFetch(url) /script3.4 实战本地存储// composables/useLocalStorage.jsimport{ref,watch}fromvueexportfunctionuseLocalStorage(key,defaultValue){// 从localStorage读取初始值conststoredValuelocalStorage.getItem(key)constdataref(storedValue?JSON.parse(storedValue):defaultValue)// 监听变化并同步到localStoragewatch(data,(newValue){localStorage.setItem(key,JSON.stringify(newValue))},{deep:true})// 清除constremove(){localStorage.removeItem(key)data.valuedefaultValue}return{data,remove}}template div input v-modelusername.data placeholder输入用户名 p保存的用户名: {{ username.data }}/p button clickusername.remove清除/button /div /template script setup import { useLocalStorage } from /composables/useLocalStorage const username useLocalStorage(username, ) /script痛点解决逻辑复用变得简单代码更加模块化和可维护。四、高级技巧4.1 provide / inject 依赖注入关键点跨层级组件通信避免props层层传递。!-- 父组件 -- template div ChildComponent / /div /template script setup import { provide, ref, readonly } from vue import ChildComponent from ./ChildComponent.vue const theme ref(dark) const user ref({ name: 张三, role: admin }) // 提供数据 provide(theme, theme) provide(user, readonly(user)) // 只读防止子组件修改 // 提供方法 const updateTheme (newTheme) { theme.value newTheme } provide(updateTheme, updateTheme) /script!-- 子组件任意层级 -- template div :classtheme p当前主题: {{ theme }}/p p用户: {{ user.name }}/p button clickupdateTheme(light)切换主题/button /div /template script setup import { inject } from vue // 注入数据 const theme inject(theme) const user inject(user) const updateTheme inject(updateTheme) // 提供默认值 const config inject(config, { timeout: 3000 }) /script4.2 defineExpose 暴露组件方法关键点script setup默认是封闭的需要显式暴露给父组件。!-- 子组件 -- template div p{{ message }}/p /div /template script setup import { ref } from vue const message ref(Hello) const count ref(0) const updateMessage (newMessage) { message.value newMessage } const increment () { count.value } // 暴露给父组件 defineExpose({ message, count, updateMessage, increment }) /script!-- 父组件 -- template div ChildComponent refchildRef / button clickcallChildMethod调用子组件方法/button /div /template script setup import { ref } from vue import ChildComponent from ./ChildComponent.vue const childRef ref(null) const callChildMethod () { // 访问子组件暴露的属性和方法 console.log(childRef.value.message) childRef.value.updateMessage(New Message) childRef.value.increment() } /script4.3 自定义指令// directives/vFocus.jsexportconstvFocus{mounted(el){el.focus()}}// directives/vClickOutside.jsexportconstvClickOutside{mounted(el,binding){el.clickOutsideEvent(event){if(!(elevent.target||el.contains(event.target))){binding.value(event)}}document.addEventListener(click,el.clickOutsideEvent)},unmounted(el){document.removeEventListener(click,el.clickOutsideEvent)}}template div !-- 自动聚焦 -- input v-focus placeholder自动聚焦 !-- 点击外部关闭 -- div v-click-outsidecloseDropdown classdropdown button clickshowDropdown !showDropdown下拉菜单/button ul v-ifshowDropdown li选项1/li li选项2/li /ul /div /div /template script setup import { ref } from vue import { vFocus, vClickOutside } from /directives const showDropdown ref(false) const closeDropdown () { showDropdown.value false } /script五、实战案例Todo应用template div classtodo-app h1待办事项/h1 !-- 输入框 -- div classinput-box input v-modelnewTodo keyup.enteraddTodo placeholder添加新任务... button clickaddTodo添加/button /div !-- 过滤器 -- div classfilters button v-forfilter in filters :keyfilter :class{ active: currentFilter filter } clickcurrentFilter filter {{ filter }} /button /div !-- 任务列表 -- ul classtodo-list li v-fortodo in filteredTodos :keytodo.id :class{ completed: todo.completed } input typecheckbox v-modeltodo.completed span{{ todo.text }}/span button clickremoveTodo(todo.id)删除/button /li /ul !-- 统计 -- div classstats span总计: {{ todos.length }}/span span已完成: {{ completedCount }}/span span未完成: {{ activeCount }}/span /div /div /template script setup import { ref, computed } from vue import { useLocalStorage } from /composables/useLocalStorage // 使用本地存储 const { data: todos } useLocalStorage(todos, []) const newTodo ref() const currentFilter ref(全部) const filters [全部, 未完成, 已完成] // 添加任务 const addTodo () { if (!newTodo.value.trim()) return todos.value.push({ id: Date.now(), text: newTodo.value, completed: false }) newTodo.value } // 删除任务 const removeTodo (id) { const index todos.value.findIndex(todo todo.id id) if (index -1) { todos.value.splice(index, 1) } } // 过滤任务 const filteredTodos computed(() { switch (currentFilter.value) { case 未完成: return todos.value.filter(todo !todo.completed) case 已完成: return todos.value.filter(todo todo.completed) default: return todos.value } }) // 统计 const completedCount computed(() todos.value.filter(todo todo.completed).length ) const activeCount computed(() todos.value.filter(todo !todo.completed).length ) /script style scoped .todo-app { max-width: 600px; margin: 50px auto; padding: 20px; } .input-box { display: flex; gap: 10px; margin-bottom: 20px; } .input-box input { flex: 1; padding: 10px; border: 1px solid #ddd; border-radius: 4px; } .filters { display: flex; gap: 10px; margin-bottom: 20px; } .filters button { padding: 8px 16px; border: 1px solid #ddd; background: white; cursor: pointer; border-radius: 4px; } .filters button.active { background: #42b983; color: white; border-color: #42b983; } .todo-list { list-style: none; padding: 0; } .todo-list li { display: flex; align-items: center; gap: 10px; padding: 10px; border-bottom: 1px solid #eee; } .todo-list li.completed span { text-decoration: line-through; color: #999; } .stats { display: flex; justify-content: space-around; margin-top: 20px; padding: 10px; background: #f5f5f5; border-radius: 4px; } /style六、最佳实践✅ 命名规范组合函数以use开头useCounter、useMouse事件处理函数以handle或on开头handleClick、onSubmit布尔值以is、has、should开头isLoading、hasError✅ 代码组织// 推荐的代码组织顺序script setup// 1. 导入import{ref,computed,watch}fromvueimport{useRouter}fromvue-router// 2. 组合函数constrouteruseRouter()const{data,loading}useFetch(/api/data)// 3. 响应式数据constcountref(0)constuserreactive({name:})// 4. 计算属性constdoubleCountcomputed(()count.value*2)// 5. 监听器watch(count,(newVal){console.log(newVal)})// 6. 生命周期onMounted((){fetchData()})// 7. 方法constincrement(){count.value}// 8. 暴露defineExpose({increment})/script✅ 性能优化使用shallowRef和shallowReactive处理大对象使用markRaw标记不需要响应式的对象合理使用computed缓存计算结果避免在模板中使用复杂表达式七、总结Vue3 Composition API的核心优势逻辑复用- 通过组合函数轻松复用逻辑代码组织- 相关代码聚合在一起易于维护类型推导- 完美支持TypeScript性能优化- 更好的Tree-shaking支持记住Composition API不是替代Options API而是提供了更灵活的选择。相关资源Vue3官方文档VueUse - 强大的组合函数库Pinia - Vue3状态管理小贴士从小项目开始尝试Composition API逐步掌握其精髓关注我获取更多Vue干货

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询