alioth/before/cha/14=vue3/docs/学习vue3/vue3逻辑复用.md
2025-05-30 09:18:01 +08:00

245 lines
5.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 逻辑复用
> 高深莫测
## 什么是“组合式函数”?
- 利用 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
<script setup>
import { useMouse } from './mouse.js'
const { x, y } = useMouse()
// x,y是可以监听变化的
</script>
<template>Mouse position is at: {{ x }}, {{ y }}</template>
```
### 异步状态示例
- 封装请求
```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
<script setup>
import { useFetch } from './fetch.js'
const { data, error } = useFetch('...')
</script>
```
### 接收响应式状态
```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
<script setup>
// 在模板中启用 v-focus
const vFocus = {
mounted: (el) => el.focus()
}
// 选项式
export default {
setup() {
/*...*/
},
directives: {
// 在模板中启用 v-focus
focus: {
/* ... */
}
}
}
</script>
<template>
<!-- 使用后会在挂在这个元素时聚焦-->
<input v-focus />
</template>
```
### 指令钩子
```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) => {
// 在这里编写插件代码
}
}
```