Vue3+Vite+VueRouter+Pinia+Axios+ElementPlus教程
一、初识
官方给出的解释:“组合式函数”(Composables) 是一个利用 Vue 的组合式 API 来封装和复用有状态逻辑的函数。
有状态の逻辑
有状态の逻辑负责管理随着时间而变化的状态。在项目开发过程中,如:触摸手势、数据库的连接状态这些功能的实现。
无状态の逻辑
在接收一些参数后,立即返回所期望的值,这就是无状态の逻辑。如构建前端应用时,常常会把一些日期格式化的函数,抽取到一个独立的 time.js 文件中,以便于在不同的组件或脚本中复用。当然无状态的逻辑库也有很多,如:lodash、moment 等。
这里需要提一下 “无渲染组件” 的概念:它只包括了可重用的逻辑 (数据获取、分页等) 而不需要自己渲染内容,视图输出通过作用域插槽全权交给了消费者组件。
大部分能用无渲染组件实现的功能都可以通过组合式 API 以另一种更高效的方式实现,并且还不会带来额外组件嵌套的开销。所以如果只是纯逻辑的封装(不需要视图输出),建议使用组合式API实现。
二、与 Mixin 对比
mixin 是 Vue2 中的 api,它也让我们能够把组件逻辑提取到可复用的单元里。然而 mixins 有三个主要的短板:
- 1. 不清晰的数据来源
当使用了多个 mixin 时,实例上的数据属性来自哪个 mixin 变得不清晰,这使追溯实现和理解组件行为变得困难。这也是我们推荐在组合式函数中使用 ref + 解构模式的理由:让属性的来源在消费组件时一目了然。
- 2. 命名空间冲突
多个来自不同作者的 mixin 可能会注册相同的属性名,造成命名冲突。若使用组合式函数,你可以通过在解构变量时对变量进行重命名来避免相同的键名。
- 3. 隐式的跨 mixin 交流
多个 mixin 需要依赖共享的属性名来进行相互作用,这使得它们隐性地耦合在一起。而一个组合式函数的返回值可以作为另一个组合式函数的参数被传入,像普通函数那样。
所以,官方在 Vue 3 中不再推荐继续使用 mixin。
三、灵活性
随着组件复杂度的增高,你可能会最终发现组件多得难以查询和理解。组合式 API 会给予你足够的灵活性,让你可以基于逻辑问题将组件代码拆分成更小的函数。这更利于代码的组织和复用,从而改善代码结构。
<script setup> import { useFeatureA } from './featureA.js' import { useFeatureB } from './featureB.js' import { useFeatureC } from './featureC.js' const { foo, bar } = useFeatureA() const { baz } = useFeatureB(foo) const { qux } = useFeatureC(baz) </script>
四、案例
组件中定义鼠标跟随案例
<script setup> import { onMounted, onUnmounted, reactive } from 'vue'; // 实现鼠标追踪器效果 const movePosi = reactive({ x: 0, y: 0 }) function MyMouseMove(e) { movePosi.x = e.clientX movePosi.y = e.clientY } onMounted(() => window.addEventListener('mousemove', MyMouseMove)) onUnmounted(() => window.removeEventListener('mousemove', MyMouseMove)) </script> <template> <div class="mouseRender" :style="{ top: movePosi.y + 'px', left: movePosi.x + 'px' }"> <div>x:{{ movePosi.x }}</div> <div>y:{{ movePosi.y }}</div> </div> </template> <style scoped> .mouseRender { position: fixed; top: 0; left: 0; z-index: 1000; } </style>
如果想在多个组件中复用这个逻辑,需要把这个逻辑以一个组合式函数的形式提取到外部文件中,如下:
// event.js import { onMounted, onUnmounted } from 'vue' export function useEventListener(target, event, callback) { // 如果你想的话, // 也可以用字符串形式的 CSS 选择器来寻找目标 DOM 元素 onMounted(() => target.addEventListener(event, callback)) onUnmounted(() => target.removeEventListener(event, callback)) }
import { ref,reactive } from 'vue' import { useEventListener } from './event' // 按照惯例,组合式函数以“use”开头 export function useMouse() { const movePosi = reactive({ x: 0, y: 0 }) // 组合式函数可以随时更改其状态。 useEventListener(window, 'mousemove', (event) => { movePosi.x = event.clientX movePosi.y = event.clientY }) // 通过返回值暴露所管理的状态 return movePosi }
注:组合式函数约定用驼峰命名法命名,并以“use”作为开头。
组件复用
<script setup> import { useMouse } from './mouse.js' const { x, y } = useMouse() </script> <div class="mouseRender" :style="{ top: y + 'px', left: x + 'px' }"> <div>x:{{ x }}</div> <div>y:{{ y }}</div> </div>