vue3-逻辑复用

news2024/11/12 21:25:34

什么是组合式函数

_无状态逻辑的函数_:它在接收一些输入后立刻返回所期望的输出。

比如 时间格式化的函数。

有状态逻辑的函数: 有状态逻辑负责管理会随时间而变化的状态。

比如 跟踪当前鼠标在页面中的位置。

在 Vue 应用的概念中,“组合式函数”(Composables) 是一个利用 Vue 的组合式 API 来封装和复用有状态逻辑的函数

一个简单的例子是跟踪当前鼠标在页面中的位置。在实际应用中,也可能是像触摸手势或与数据库的连接状态这样的更复杂的逻辑。

<script lang="ts" setup>
import { ref, onMounted, onUnmounted } from "vue";

const x = ref(0);
const y = ref(0);

function update(event: any) {
  x.value = event.pageX;
  y.value = event.pageY;
}

onMounted(() => window.addEventListener("mousemove", update));
onUnmounted(() => window.removeEventListener("mousemove", update));
</script>

<template>
  <div class="container">鼠标坐标是: {{ x }}, {{ y }}</div>
</template>

组合函数提取,进行逻辑复用封装

// 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 }
}

封装后的使用方式:

<script lang="ts" setup>
import { useMouse } from "@/components/common/mouse.js";
const { x, y } = useMouse();
</script>

<template>
  <div class="container">鼠标坐标是: {{ x }}, {{ y }}</div>
</template>

可以在组合式函数中使用所有的组合式 API。

组合式函数还可以嵌套多个组合式函数。

我们可以将添加和清除 DOM 事件监听器的逻辑也封装进一个组合式函数中

// 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))
}

有了它,之前的 useMouse() 组合式函数可以被简化为:

// mouse.js
import { ref } from 'vue'
import { useEventListener } from './event'

export function useMouse() {
  const x = ref(0)
  const y = ref(0)

  useEventListener(window, 'mousemove', (event) => {
    x.value = event.pageX
    y.value = event.pageY
  })

  return { x, y }
}

每一个调用 useMouse() 的组件实例会创建其独有的 x、y 状态拷贝,因此他们不会互相影响。

如果你想要在组件之间共享状态,后面回学到状态管理。

###异步状态示例

需要接收一个参数的组合式函数示例。在做异步数据请求时,我们常常需要处理不同的状态:加载中、加载成功和加载失败。

// fetch.js
import { ref, watchEffect, toValue } from 'vue'

export function useFetch(url) {
  const data = ref(null)
  const error = ref(null)

  const fetchData = () => {
    // reset state before fetching..
    data.value = null
    error.value = null

    fetch(toValue(url))
      .then((res) => res.json())
      .then((json) => (data.value = json))
      .catch((err) => (error.value = err))
  }

  watchEffect(() => {
    fetchData()
  })

  return { data, error }
}

toValue() 是一个在 3.3 版本中新增的 API。它的设计目的是将 ref 或 getter 规范化为值。如果参数是 ref,它会返回 ref 的值;如果参数是函数,它会调用函数并返回其返回值。否则,它会原样返回参数。它的工作方式类似于 unref(),但对函数有特殊处理。

注意 toValue(url) 是在 watchEffect 回调函数的内部调用的。这确保了在 toValue() 规范化期间访问的任何响应式依赖项都会被侦听器跟踪。

这个版本的 useFetch() 现在能接收静态 URL 字符串、ref 和 getter,使其更加灵活。watch effect 会立即运行,并且会跟踪 toValue(url) 期间访问的任何依赖项。如果没有跟踪到依赖项(例如 url 已经是字符串),则 effect 只会运行一次;否则,它将在跟踪到的任何依赖项更改时重新运行。

约定和最佳实践

命名

组合式函数约定用驼峰命名法命名,并以“use”作为开头。

输入参数

即便不依赖于 ref 或 getter 的响应性,组合式函数也可以接收它们作为参数。如果你正在编写一个可能被其他开发者使用的组合式函数,最好处理一下输入参数是 ref 或 getter 而非原始值的情况。可以利用 toValue() 工具函数来实现:

import { toValue } from 'vue'

function useFeature(maybeRefOrGetter) {
  // 如果 maybeRefOrGetter 是一个 ref 或 getter,
  // 将返回它的规范化值。
  // 否则原样返回。
  const value = toValue(maybeRefOrGetter)
}

如果你的组合式函数在输入参数是 ref 或 getter 的情况下创建了响应式 effect,为了让它能够被正确追踪,请确保要么使用 watch() 显式地监视 ref 或 getter,要么在 watchEffect() 中调用 toValue()。

返回值

你可能已经注意到了,我们一直在组合式函数中使用 ref() 而不是 reactive()。我们推荐的约定是组合式函数始终返回一个包含多个 ref 的普通的非响应式对象,这样该对象在组件中被解构为 ref 之后仍可以保持响应性:

// x 和 y 是两个 ref
const { x, y } = useMouse()

从组合式函数返回一个响应式对象会导致在对象解构过程中丢失与组合式函数内状态的响应性连接。与之相反,ref 则可以维持这一响应性连接。

如果你更希望以对象属性的形式来使用组合式函数中返回的状态,你可以将返回的对象用 reactive() 包装一次,这样其中的 ref 会被自动解包,例如:

const mouse = reactive(useMouse())
// mouse.x 链接到了原来的 x ref
console.log(mouse.x)
使用限制

组合式函数只能在 <script setup> 或 setup() 钩子中被调用。在这些上下文中,它们也只能被同步调用。在某些情况下,你也可以在像 onMounted() 这样的生命周期钩子中调用它们。

这些限制很重要,因为这些是 Vue 用于确定当前活跃的组件实例的上下文。访问活跃的组件实例很有必要,这样才能:

  • 将生命周期钩子注册到该组件实例上

  • 将计算属性和监听器注册到该组件实例上,以便在该组件被卸载时停止监听,避免内存泄漏。

<script setup> 是唯一在调用 await 之后仍可调用组合式函数的地方。编译器会在异步操作之后自动为你恢复当前的组件实例。

与其他模式的比较

和 Mixin 的对比

Vue 2 的用户可能会对 mixins 选项比较熟悉。它也让我们能够把组件逻辑提取到可复用的单元里。然而 mixins 有三个主要的短板:

不清晰的数据来源:当使用了多个 mixin 时,实例上的数据属性来自哪个 mixin 变得不清晰,这使追溯实现和理解组件行为变得困难。这也是我们推荐在组合式函数中使用 ref + 解构模式的理由:让属性的来源在消费组件时一目了然。

命名空间冲突:多个来自不同作者的 mixin 可能会注册相同的属性名,造成命名冲突。若使用组合式函数,你可以通过在解构变量时对变量进行重命名来避免相同的键名。

隐式的跨 mixin 交流:多个 mixin 需要依赖共享的属性名来进行相互作用,这使得它们隐性地耦合在一起。而一个组合式函数的返回值可以作为另一个组合式函数的参数被传入,像普通函数那样。

基于上述理由,我们不再推荐在 Vue 3 中继续使用 mixin。保留该功能只是为了项目迁移的需求和照顾熟悉它的用户。

和无渲染组件的对比

在组件插槽一章中,我们讨论过了基于作用域插槽的无渲染组件。我们甚至用它实现了一样的鼠标追踪器示例。

组合式函数相对于无渲染组件的主要优势是:组合式函数不会产生额外的组件实例开销。当在整个应用中使用时,由无渲染组件产生的额外组件实例会带来无法忽视的性能开销。

我们推荐在纯逻辑复用时使用组合式函数,在需要同时复用逻辑和视图布局时使用无渲染组件。

和 React Hooks 的对比

如果你有 React 的开发经验,你可能注意到组合式函数和自定义 React hooks 非常相似。组合式 API 的一部分灵感正来自于 React hooks,Vue 的组合式函数也的确在逻辑组合能力上与 React hooks 相近。然而,Vue 的组合式函数是基于 Vue 细粒度的响应性系统,这和 React hooks 的执行模型有本质上的不同。这一话题在组合式 API 的常见问题中有更细致的讨论。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1430059.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

智能汽车竞赛摄像头处理(3)——动态阈值二值化(大津法)

前言 &#xff08;1&#xff09;在上一节中&#xff0c;我们学习了对图像的固定二值化处理&#xff0c;可以将原始图像处理成二值化的黑白图像&#xff0c;这里面的本质就是将原来的二维数组进行了处理&#xff0c;处理后的二维数组里的元素都是0和255两个值。 &#xff08;2…

LeetCode 热题 100 | 链表(中上)

目录 1 141. 环形链表 1.1 哈希表 1.2 快慢指针 2 142. 环形链表 II 2.1 哈希表 2.2 快慢指针 3 21. 合并两个有序链表 4 2. 两数相加 菜鸟做题第三周&#xff0c;语言是 C 1 141. 环形链表 1.1 哈希表 解题思路&#xff1a;遍历链表&#xff0c;在哈希表中…

ROS方向第二次汇报(5)

文章目录 1.本方向内学习内容&#xff1a;1.1.自定义msg&#xff1a;1.1.1.定义msg文件&#xff1a;1.1.2.编辑配置文件&#xff1a; 1.2.自定义srv&#xff1a;1.2.1.定义srv文件&#xff1a;1.2.2.编辑配置文件&#xff1a; 1.3.服务通信案例实现&#xff1a;1.3.1.服务端实现…

HTML+CSS:导航栏组件

效果演示 实现了一个导航栏的动画效果&#xff0c;当用户点击导航栏中的某个选项时&#xff0c;对应的选项卡会向左平移&#xff0c;同时一个小圆圈会出现在选项卡的中心&#xff0c;表示当前选项卡的位置。这个效果可以让用户更加清晰地了解当前页面的位置和内容。 Code <…

正点原子--STM32定时器学习笔记(2)

书接上文&#xff0c;本篇是对基本定时器实验部分进行的总结~ 实验目标&#xff1a;通过TIM6基本定时器定时500ms&#xff0c;让LED0每隔500ms闪烁。 解决思路&#xff1a;使用定时器6&#xff0c;实现500ms产生一次定时器更新中断&#xff0c;在中断里执行“翻转LED0”。 定时…

【leetcode题解C++】98.验证二叉搜索树 and 701.二叉搜索树中的插入操作

98. 验证二叉搜索树 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。 示例…

kubernetes基本概念和操作

基本概念和操作 1.Namespace1.1概述1.2应用示例 2.Pod2.1概述2.2语法及应用示例 3.Label3.1概述3.2语法及应用示例 4.Deployment4.1概述4.2语法及应用示例 5.Service5.1概述5.2语法及应用示例5.2.1创建集群内部可访问的Service5.2.2创建集群外部可访问的Service5.2.3删除服务5.…

Zoho Mail企业邮箱商业扩展系列第1部分:入门指南与基础设置

今天让我们来认识一下王雪琳&#xff0c;她是一位独立经营的营销咨询机构的个体企业家。在开始自己的事业之前&#xff0c;她进行了广泛的市场调研&#xff0c;明确了自己的业务定位&#xff0c;并全力以赴地投入到了自己的企业中。 一、创业背景 王雪琳的营销业务主要集中在…

日志报错 git -c dif.mnemonicprefix=false -c core.guotepath=false 解决方法

前言: 在进行下面操作前,必须确保,你是否安装了Git。 查看Git 在命令行窗口中输入`git --version`: 如果这个命令成功显示了Git的版本信息,这表明Git已经被安装。 1. 使用Sourcetree SourceTree 是 Windows 和Mac OS X 下免费的 Git 和 Hg 客户端…

Kubernetes operator(五)api 和 apimachinery 篇

云原生学习路线导航页&#xff08;持续更新中&#xff09; 本文是 Kubernetes operator学习 系列第五篇&#xff0c;主要对 k8s.io/api 和 k8s.io/apimachinery 两个项目 进行学习基于 kubernetes v1.24.0 代码分析Kubernetes operator学习系列 快捷链接 Kubernetes operator&a…

数字巨轮航行大数据海洋:数据可视化引领时代潮流

在大数据时代的潮流中&#xff0c;数据可视化如同一艘畅行无阻的科技巨轮&#xff0c;引领我们穿越数字浩瀚的大海&#xff0c;使我们在信息的航程中游刃有余。下面我就从可视化从业者的角度&#xff0c;来简单说说数据可视化是如何帮助我们在大数据时代畅行无阻的。 数据可视化…

【Servlet】——Servlet API 详解

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【Servlet】 本专栏旨在分享学习Servlet的一点学习心得&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 一、HttpServlet二、Htt…

【unity小技巧】unity3d环境带雾的昼夜系统变化

最终效果 文章目录 最终效果眩光素材眩光配置全局灯光配置天空盒配置天空盒资产配置天空盒&#xff0c;开启雾 代码控制天空盒 环境 雾 灯光昼夜交替变化参考完结 眩光素材 链接&#xff1a;https://pan.baidu.com/s/1qlFSJSju6ZjwCylwkh14eA?pwdveww 提取码&#xff1a;veww…

Oracle和Mysql数据库

数据库 Oracle 体系结构与基本概念体系结构基本概念表空间(users)和数据文件段、区、块Oracle数据库的基本元素 Oracle数据库启动和关闭Oracle数据库启动Oracle数据库关闭 Sqlplussqlplus 登录数据库管理系统使用sqlplus登录Oracle数据库远程登录解锁用户修改用户密码查看当前语…

Android学习之路(29) Gradle初探

前言: 大家回想一下自己第一次接触Gradle是什么时候&#xff1f; 相信大家也都是和我一样&#xff0c;在我们打开第一个AS项目的时候&#xff0c; 发现有很多带gradle字样的文件&#xff1a;setting.gradle, build.gradle,gradle.warpper,以及在gradle文件中各种配置&#xff…

来看看Tomcat和Web应用的目录结构

在前面两篇大致了解了Tomcat的架构和运行流程&#xff0c;以及Tomcat应用中的web.xml。 聊一聊Tomcat的架构和运行流程&#xff0c;尽量通俗易懂一点-CSDN博客 来吧&#xff0c;好好理解一下Tomcat下的web.xml-CSDN博客 那接下来&#xff0c;再看看Tomcat的目录&#xff0c;…

BeanDefinitionRegistry学习

Spring版本5.1.x 简介 在Spring框架中&#xff0c;BeanDefinitionRegistry是一个接口&#xff0c;它主要用于向注册表中注册BeanDefinition实例&#xff0c;完成注册的过程。该接口的主要方法是registerBeanDefinition&#xff0c;用于将一个BeanDefinition实例注册到注册表中…

分享一个WPF项目

最近在学习WPF开发方式&#xff0c;找到一些项目进行拆解学习&#xff1b;本位主要分享一个WPF项目&#xff0c;叫做WPFDevelopers&#xff0c;在git上大约有1.3K星&#xff0c;话不多说&#xff0c;先看看效果&#xff1a; 这个项目开发可以编译启动后直接查看样例、Xaml、Cha…

如何搭建私有云盘SeaFile并实现远程访问本地文件资料

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-hsDnDEybLME85dTx {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

让AI作为人类的“副驾驶”

让AI作为副驾驶&#xff0c;辅助人类完成工作&#xff0c;是人工智能技术在实际应用中的一个重要方向。这个设想在多个领域都有广泛应用的可能性&#xff1a; 驾驶场景&#xff1a;自动驾驶技术中&#xff0c;AI副驾驶可以实时监测路况、规划最优行驶路线、预警潜在危险&#x…