# 逻辑复用 > 高深莫测 ## 什么是“组合式函数”? - 利用 Vue 的组合式 API 来封装和复用有状态逻辑的函数 ```js // mouse.js import { ref, onMounted, onUnmounted } from 'vue' // 按照惯例,组合式函数名以“use”开头 export function useMouse() { // 被组合式函数封装和管理的状态 const x = ref(0) const y = ref(0) // 组合式函数可以随时更改其状态。 function update(event) { x.value = event.pageX y.value = event.pageY } // 一个组合式函数也可以挂靠在所属组件的生命周期上 // 来启动和卸载副作用 onMounted(() => window.addEventListener('mousemove', update)) onUnmounted(() => window.removeEventListener('mousemove', update)) // 通过返回值暴露所管理的状态 return { x, y } } ``` ```vue ``` ### 异步状态示例 - 封装请求 ```js // fetch.js import { ref } from 'vue' export function useFetch(url) { const data = ref(null) const error = ref(null) fetch(url) .then((res) => res.json()) .then((json) => (data.value = json)) .catch((err) => (error.value = err)) return { data, error } } ``` ```vue ``` ### 接收响应式状态 ```js // fetch.js import { ref, toValue, watchEffect } from 'vue' // url必须是响应式参数才能被watchEffect监听到 export function useFetch(url) { const data = ref(null) const error = ref(null) setTimeout(() => { data.value = "数据" + url.value }, 3000) watchEffect(() => { data.value = "数据SSS" + toValue(url) }) return { data, error } } ``` ### 约定和最佳实践 ### 命名: 用驼峰命名法命名,以“use”作为开头。 ### 输入参数 - 最好处理一下输入参数是 ref 或 getter 而非原始值的情况。可以利用 toValue() 工具函数来实现 ```js import { toValue } from 'vue' function useFeature(maybeRefOrGetter) { // 如果 maybeRefOrGetter 是一个 ref 或 getter, // 将返回它的规范化值。 // 否则原样返回。 const value = toValue(maybeRefOrGetter) } ``` ### 返回值 - 使用 `ref()` 而不是 `reactive()`, 使用一个不是响应式的对象包含响应式参数,可以被很好的解构赋值 ## 自定义指令 - 由一个包含类似组件生命周期钩子的对象来定义 ```vue ``` ### 指令钩子 ```js const myDirective = { // 在绑定元素的 attribute 前 // 或事件监听器应用前调用 created(el, binding, vnode, prevVnode) { // 下面会介绍各个参数的细节 }, // 在元素被插入到 DOM 前调用 beforeMount(el, binding, vnode, prevVnode) {}, // 在绑定元素的父组件 // 及他自己的所有子节点都挂载完成后调用 mounted(el, binding, vnode, prevVnode) {}, // 绑定元素的父组件更新前调用 beforeUpdate(el, binding, vnode, prevVnode) {}, // 在绑定元素的父组件 // 及他自己的所有子节点都更新后调用 updated(el, binding, vnode, prevVnode) {}, // 绑定元素的父组件卸载前调用 beforeUnmount(el, binding, vnode, prevVnode) {}, // 绑定元素的父组件卸载后调用 unmounted(el, binding, vnode, prevVnode) {} } ``` ### 全局 ```js const app = createApp({}) // 使 v-focus 在所有组件中都可用 app.directive('focus', { /* ... */ }) ``` ### 钩子参数 - el:指令绑定到的元素。这可以用于直接操作 DOM。 - binding:一个对象,包含以下属性。 - value:传递给指令的值。例如在 v-my-directive="1 + 1" 中,值是 2。 - oldValue:之前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否更改,它都可用。 - arg:传递给指令的参数 (如果有的话)。例如在 v-my-directive:foo 中,参数是 "foo"。 - modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }。 - instance:使用该指令的组件实例。 - dir:指令的定义对象。 - - vnode:代表绑定元素的底层 VNode。 - prevVnode:代表之前的渲染中指令所绑定元素的 VNode。仅在 beforeUpdate 和 updated 钩子中可用。 ### 在组件上使用 - 会透传,但是不建议在组件上使用 ## 插件 ### 使用 ```js import { createApp } from 'vue' const app = createApp({}) app.use(myPlugin, { /* 可选的选项 */ }) ``` ### 插件常见场景 1. 通过 app.component() 和 app.directive() 注册一到多个全局组件或自定义指令。 2. 通过 app.provide() 使一个资源可被注入进整个应用。 3. 向 app.config.globalProperties 中添加一些全局实例属性或方法 4. 一个可能上述三种都包含了的功能库 (例如 vue-router)。 ### 编写插件 ```js // plugins/i18n.js export default { install: (app, options) => { // 在这里编写插件代码 } } ```