揭示 Vue 3 setup 函数的奥秘

news2024/11/27 2:43:21

setup 是 Vue 3 的新特性,也是组合式 API(Composition API)的核心。它提供了一种全新的方式设计组件的逻辑,便于复用同时也增加代码可读性。

1. 理解 setup

1、组合式 API 的入口

setup 是在组件实例创建之前执行的,因此无法访问 this,也没有生命周期钩子函数。可以使用 setup 定义组件的逻辑、状态和方法。

<script>
export default {
  setup(props, context) {
    console.log('this: ', this); // undefined
  }
}
</script>

2、函数式组件的支持

setup 让 Vue 组件变得更像一个函数式组件,在 setup 内部可以使用组合式 API 声明响应式数据、计算属性、监听器等。它替代了传统的选项式 API 中的 data、computed、methods 等,让逻辑关注点更加集中和模块化。

举个 🌰

<template>
  <div>
    <p>num1: {{ num1 }}</p>
    <p>num2: {{ num2 }}</p>
    <p>sum: {{ sum }}</p>
    <button @click="calculateSum" class="btn">计算</button>
  </div>
</template>

传统写法:

export default {
  name: 'Calculator',
  data() {
    return {
      num1: 10,
      num2: 20,
      sum: 0,
    };
  },
  computed: {
    // 也可以将 sum 声明为一个计算属性而不是方法
    // sum() {
    //   return this.num1 + this.num2;
    // }
  },
  // 使用 methods 选项来声明组件方法
  methods: {
    calculateSum() {
      this.sum = parseFloat(this.num1) + parseFloat(this.num2);
    }
  },
};

setup 写法:

import { computed, ref } from 'vue'
export default {
  name: 'Calculator',
  setup() {
    const num1 = ref(10)
    const num2 = ref(20)
    const sum = ref(0);
    // const sum = computed(() => num1 + num2);
    const calculateSum = () => {
      sum.value = num1.value + num2.value
    }
    return {
      num1,
      num2,
      sum,
      calculateSum
    }
  }
}

3、增强逻辑复用

使用组合函数(Composition Function)可以将 setup 中的逻辑,抽离到独立的函数或组件中,便于复用。

举个 🌰

<template>
  <div>
    <h3>count: {{ count }}</h3>
    <button @click="increment" style="margin-top: 13px; padding: 4px 6px">increment</button>
  </div>
</template>

<script>
import { ref } from 'vue'
function useCounter() {
  const count = ref(0)
  const increment = () => {
    count.value++
  }
  return {
    count,
    increment
  }
}

export default {
  setup() {
    const { count, increment } = useCounter()
    return { count, increment }
  }
}
</script>

使用 useCounter 的方式,不仅可以复用,还可以保持代码的简洁和模块化。  

2. 使用 setup

1、基本语法

setup 函数接收两个参数:props 和 context。props 是传入组件的属性,context 包含了 attrs、slots、emit 等对象。

export default {
  setup(props, context) {
    console.log('props: ', props);
    console.log('context: ', context);
  }
}

举个 🌰 

<template>
  <div>
    <h2>count: {{ count }}</h2>
    <h2>doubleCount: {{ doubleCount }}</h2>
    <button @click="increment" style="margin-top: 13px; padding: 4px 6px">Increment</button>
  </div>
</template>

<script>
import { ref, computed } from 'vue'
export default {
  setup(props, context) {
    const count = ref(0)
    const doubleCount = computed(() => count.value * 2)
    const increment = () => {
      count.value++;
      console.log('count: ', count.value);
    }
    return {
      count,
      doubleCount,
      increment
    }
  }
}
</script>

在这个代码中,count 是一个响应式数据,doubleCount 是一个计算属性,increment 是一个方法。这些都是通过 setup 函数声明,并通过返回值暴露给模板使用。

2、访问 props 和 context

- props 是响应式对象,可以在 setup 中直接使用。

- context 包含了 attrs(非 props 的属性)、slots(插槽)、emit(事件触发)等对象。

举个 🌰

<!-- 子组件 -->
<template>
  <div>
    <p>当前计数: {{ count }}</p>
    <button @click="increment" style="margin: 10px 0; padding: 4px 6px">增加计数</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue'

export default defineComponent({
  name: 'Counter',
  props: {
    initialCount: {
      type: Number,
      required: true, // 必填
    },
  },
  setup(props, { emit }) {
    const count = ref(props.initialCount);
    const increment = () => {
      count.value++;
      // 触发一个名为 'update:count' 的事件,传递当前的计数值
      emit('update:count', count.value);
    };
    // 将响应式数据和方法返回,以便在模板中使用
    return {
      count,
      increment,
    };
  },
})
</script>
<!-- 父组件 -->
<template>
  <div>
    <Counter :initialCount="5" @update:count="handleCountUpdate" />
    <p>父组件中的计数: {{ parentCount }}</p>
    <Calculator />
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';
import Counter from './components/Counter.vue';

export default defineComponent({
  components: {
    Counter,
  },
  setup() {
    const parentCount = ref(0);
    const handleCountUpdate = (newCount: number) => {
      parentCount.value = newCount;
    };
    return {
      parentCount,
      handleCountUpdate,
    };
  },
});
</script>

3. 对比 Vue 2 好处

1、更好的逻辑组织

在 Vue 2 中,组件的逻辑代码往往分散在多个选项(如 data、methods、computed、watch 等)中。对于复杂组件而言,不利于逻辑组织和后期维护。

在 Vue 3 中,setup 函数让所有的逻辑集中在一起,便于模块化和维护。

2、更强的类型支持和推断

在 Vue 2 中,类型推断主要依赖于外部工具(如 TypeScript),而在选项式 API (Option API)中,类型推断往往不够直观,特别是对于 data 返回的对象。

在 Vue 3 中,setup 函数提供了更好的 TypeScript 支持。因为 setup 是一个普通的JS 函数,所有的类型信息都可以直接使用函数签名定义,并且 Vue 3 内置了类型推断支持,使得类型检查更加精确和友好。

3、更简洁的代码

在 Vue 2 中,必须通过 this 访问组件的状态和方法,这增加了代码的复杂性,并且 this 的上下文在某些情况下可能不够清晰。

在 Vue 3 中,setup 函数中不再使用 this,直接通过变量引用即可访问状态和方法。这不仅使代码更简洁,还避免了 this 绑定的问题。

4、更容易的逻辑复用

在 Vue 2 中,逻辑复用主要通过混入(mixins)和高阶组件(HOC)实现。混入容易导致命名冲突和代码难以追踪,而高阶组件也会增加组件嵌套的复杂度。

在 Vue 3 的组合式 API 和 setup 函数让逻辑复用变得更加直观。开发者可以编写自定义的组合函数来复用逻辑。

4. 注意事项

1、不能使用 this

在 setup 中,组件实例还未创建,因此无法使用 this(undefined)。所有组件的状态和方法都需要通过返回值暴露。

2、生命周期钩子的使用

虽然 setup 中没有直接提供生命周期钩子,但可以通过 onMounted、onUpdated 等组合式 API 来注册生命周期钩子。

import { onMounted } from 'vue'

export default defineComponent({
  setup() {
    onMounted(() => {
      console.log('Component mounted')
    })
  }
})

3、setup 的返回值

setup 的返回值决定了组件模版中可以访问的内容。如果返回一个对象,那该对象的属性将直接暴露给模版使用。

4、setup 中的异步操作

虽然 setup 支持异步操作,但如果需要在组件渲染之前完成某些异步任务,最好不要直接使用 async setup,而是在 setup 中手动处理异步逻辑。

export default {
  setup() {
    const data = ref(null)
    const fetchData = async () => {
      data.value = await fetchSomeData()
    }
    fetchData()
    return { data }
  }
}

5、与 Vuex 和 Router 的结合

可以直接在 setup 中使用 useStore 和 useRouter 钩子来访问全局状态和路由。

import { useStore } from 'vuex';
import { useRouter } from 'vue-router';

export default defineComponent({
  setup() {
    const store = useStore();  // 获取 Vuex store 实例
    const router = useRouter(); // 获取 Vue Router 实例
  },
});

下文介绍,Vue 3 中 <script setup> 的使用和特点。 

详细讲述 Vue3 的 <script setup>-CSDN博客

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

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

相关文章

Windows单机安装配置mongodb+hadoop+spark+pyspark用于大数据分析

目录 版本选择安装配置Java环境配置Hadoop配置Spark配置 安装pyspark使用Jupyter Notebook进行Spark MongoDB测试参考 版本选择 根据Spark Connector&#xff1a;org.mongodb.spark:mongo-spark-connector_2.13:10.3.0 的前提要求 这里选择使用最新的MongoDB 7.0.12社区版 ht…

Qt-frameGeometry介绍使用(12)

描述 这个和上一篇讲的geometry很像&#xff0c;但是又有点不一样&#xff0c;而且还挺容易出问题的&#xff0c;所以这里要专门提一下&#xff0c;首先我们要明白Window frame窗口坐标是有点区别的&#xff0c;如图就像这样的&#xff0c;左上角是有两个需要注意的位置&#…

百元蓝牙耳机买哪一款的好?四款年度平价好用耳机推荐!

蓝牙耳机大家都再熟悉不过了&#xff0c;作为现代人最常用的智能配件之一&#xff0c;谁还没有用过蓝牙耳机呢&#xff0c;但是现在蓝牙耳机的牌子真的是遍地开花&#xff0c;让人眼花缭乱的&#xff0c;这让大众选择起来却更加麻烦了&#xff0c;每个牌子的蓝牙耳机都有各自的…

西门子触摸屏维修KTP400 6AV2123-2DB03-0AX0

西门子KTP400面板触摸屏维修&#xff1a;DP协议 PN协议两款。DP SIMATIC HMI&#xff0c;KTP400 Basic DP&#xff0c; 精简面板&#xff0c; 按键式/触摸式操作&#xff0c; 12" TFT 显示屏&#xff0c;65536 颜色&#xff0c; PROFIBUS 接口&#xff0c; 可用项目组态的…

TL431的稳压原理和用法,仿真验证

概述 TL431是三端可调节并联稳压器&#xff0c;可以通过两个外部电阻器将输出电压设置为介于VREF&#xff08;约为2.5V&#xff09;和36V之间的任意值。其输出阻抗典型值均为0.2欧姆&#xff0c;此类器件的有源输出电路具有非常明显的导通特性&#xff0c;因此非常适合用于替代…

基于vue框架的包装加工工业自动化管理系统05880(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;仓管员,维修员,产品信息,产品入库,产品出库,机器信息,机器维修 开题报告内容 基于Vue框架的包装加工工业自动化管理系统开题报告 一、引言 随着工业4.0时代的到来&#xff0c;包装加工行业正经历着前所未有的变革。传统的手工与半自动…

聊聊sessionStorag,以及什么情况下能够共享/复制sessionStroage

之前对sessionStorage的了解很表面&#xff0c;粗略的理解为在各标签页/窗口下独立&#xff0c;生命周期和标签页/窗口关联&#xff0c;标签页/窗口未关闭就存在&#xff08;除人为清除或者调用相关api删除&#xff09;。但是这种粗略的理解对于下面的场景似乎就有点不明确了。…

【SQL】直属部门

目录 题目 分析 代码 题目 表&#xff1a;Employee ------------------------ | Column Name | Type | ------------------------ | employee_id | int | | department_id | int | | primary_flag | varchar | ------------------------ 这张表的主键为 e…

【pyside6】添加应用图标

文章目录 什么是应用图标指定图标样式 什么是应用图标 这个图标就是应用图标&#xff1a; 默认图标是这样&#xff1a; 指定图标样式 app.setWindowIcon(QIcon("logo.png"))

深信服超融合平台Windows虚拟机磁盘在线扩容

深信服超融合版本&#xff1a; 该版本支持磁盘在线扩容&#xff0c;虚拟机无需重启&#xff0c;不影响业务。 本次扩容是为现有磁盘扩容&#xff0c;如果是新增磁盘也是一样的步骤&#xff0c;单独添加一块磁盘即可。 1、点击需要进行扩容的虚拟机名称 2、在控制台可以看到当…

Android经典实战之简化 Android 相机开发:CameraX 库的全面解析

本文首发于公众号“AntDream”&#xff0c;欢迎微信搜索“AntDream”或扫描文章底部二维码关注&#xff0c;和我一起每天进步一点点 CameraX 是 Android Jetpack 的一个组件库&#xff0c;旨在简化 Android 相机应用的开发。它提供了一系列易于使用的 API&#xff0c;支持从 An…

别怕!PMP考试难度全解析,助你一次过!

首先我得说&#xff0c;PMP考试&#xff0c;它难&#xff0c;也不是那么难。为啥这么说呢&#xff1f; 你看&#xff0c;PMP考试难&#xff0c;难在哪儿&#xff1f;就难在它那知识面太广了&#xff0c;就像个杂货铺&#xff0c;啥都得懂点儿。项目管理啊&#xff0c;得懂管理&…

【Hot100】LeetCode—543. 二叉树的直径

目录 1- 思路深搜——dfs 2- 实现⭐543. 二叉树的直径——题解思路 3- ACM 实现 原题连接&#xff1a;543. 二叉树的直径 1- 思路 深搜——dfs 递归三部曲 1- 递归参数和返回值 返回 public int depth(TreeNode root) 2- 终止条件 如果遇到 null&#xff0c;则返回 0 3- 递…

2024Go语言面试宝典Golang零基础实战项目面试八股力扣算法笔记等

2024最新Golang面试八股文&#xff0c;以及各种零基础全套实战项目&#xff0c;经典力扣算法题以及常见的面试题型&#xff0c;大厂面试题。go语言面试必备。包括GO基础类、GO并发编程、GO RUNTIME、微服务、容器技术、Redis、MySQL、Linux、缓存、网络和操作系统、消息队列、分…

USB3.2 摘录(六)

系列文章目录 USB3.2 摘录&#xff08;一&#xff09; USB3.2 摘录&#xff08;二&#xff09; USB3.2 摘录&#xff08;三&#xff09; USB3.2 摘录&#xff08;四&#xff09; USB3.2 摘录&#xff08;五&#xff09; USB3.2 摘录&#xff08;六&#xff09; 文章目录 系列文…

如何在VMwareWorkstation上安装的ESXi系统扩容存储

在做ESXi的相关实验的时候&#xff0c;需要扩容ESXi的存储&#xff0c;那么如何进行操作呢&#xff1f; 扩容VMwareWorkstation上虚拟机的存储 首先我们需要先扩容虚拟机上的存储&#xff08;可不关闭虚拟机&#xff09;按照图下所示&#xff0c;右键虚拟机&#xff0c;点击设…

PCL-基于RANSAC的平面拟合方法

基于RANSAC的平面拟合方法 一、简介二、实验代码三、实验结果 一、简介 随机采样一致性&#xff08;Random sample consensus&#xff0c;RANSAC&#xff09;&#xff1a;RANSAC是一种鲁棒的模型拟合方法&#xff0c;它可以处理存在大量噪声和异常值的数据。在进行平面拟合时&…

Qt QTableWidgetItem.setFlags()

查看 Qt 官方地址 Qt Namespace | Qt Core 5.15.14 得到如下说明 默认值是 63&#xff0c;即如下表达式的值&#xff08;位运算&#xff09;&#xff0c;表示可选择&#xff08;1&#xff09;、可编辑&#xff08;2&#xff09;、可拖拽&#xff08;4&#xff09;、可放入&…

Springboot查看版本和兼容的依賴

https://docs.spring.io/spring-boot/docs/2.7.18/reference/htmlsingle/#appendix-dependency-versions 如果需要查自己的版本&#xff0c;只需要修改2.7.18&#xff0c;因爲我的是2.7.18的版本。 然後如果你想查詢所兼容的其它依賴版本&#xff0c;可以打開頁面后直接search…

RK3588——网口实时传输视频

由于通过流媒体服务器传输画面延迟太高的问题&#xff0c;不知道是没有调试到合适的参数还是其他什么问题。诞生了这篇博客。 RK3588板端上接摄像头&#xff0c;采集画面&#xff0c;通过网口实时传输给上位机并显示。 第一代版本 RK3588代码 import cv2 import socket imp…