vue3的setup使用

412 阅读6分钟

前言

由于在前端代码的构建当中,我们对代码的简洁易用的需求与日俱增Vue3推出的setup语法糖

setup语法糖的优势

Vue 3 的 Composition API 和 setup 语法糖带来了一些优势

相较于 Vue 2 的选项 API,它提供了更灵活和可组合的方式来组织组件逻辑。

以下是一些使用 Vue 3 setup 语法糖的好处,并与 Vue 2 进行比较的例子:

更清晰的逻辑组织

Vue 2(使用选项 API):

<script lang="js">
export default {
  // 数据
  data() {
    return {
      count: 0,
    };
  },
  // 计算属性
  computed: {
    doubleCount() {
      return this.count * 2;
    },
  },
  // 方法
  methods: {
    increment() {
      this.count++;
    },
  },
};
</script>

Vue 3(使用 setup 的传统写法,不建议):

<script lang="js">
import {computed, ref} from 'vue';

export default {
  setup() {
    // 响应式数据
    const count = ref(0);

    // 计算属性
    const doubleCount = computed(():number => count.value * 2);

    // 方法
    const increment = (): void => {
      count.value++;
    };

    // 返回组件需要的数据和方法
    return {
      count,
      doubleCount,
      increment,
    };
  },
};
</script>
// 相对于Vue2的写法,简化的并不明显

Vue 3(setup 语法糖写法,推荐!):

<script setup lang="js">
//自动注入
import {computed, ref} from 'vue';

// 响应式数据
const count = ref(0);

// 计算属性
const doubleCount = computed((): number => count.value * 2);

// 方法
const increment = (): void => {
  count.value++;
};
</script>
//和Vue2以及setup传统写法相比,简化了50%的代码
<script setup> 是 Vue 3.0.0+ 中引入的新语法糖,它的目标是简化组件的写法,特别是对于那些相对简单的组件。

更好的类型推断和IDE支持

由于 setup 使用了函数的返回值来定义组件的 API,IDE 能够更好地推断类型和提供代码补全,使开发过程更加流畅。

无需导入 不需要手动导入和声明 ref、reactive 等,它会自动处理。

自动注入组件的上下文 不需要手动使用 this 访问组件上下文,变量和方法都会被自动注入到当前作用域中。

更好的类型推断 TypeScript 用户会受益于更好的类型推断,因为

更灵活的组合逻辑

Vue 3 的 Composition API 允许将相关的逻辑组合在一起,而不是按照选项 API 的生命周期来组织。

这使得代码更容易重用和维护。

下面是一个例子,展示了如何组合逻辑:

<template>
  <div>
    <p>Count: {{ count }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

<script setup lang="js">
// 导入 ref 函数
import { ref } from 'vue';

// 定义组合逻辑 - 计数器
const useCounter = () => {
  // 响应式数据
  const count = ref(0);

  // 方法
  const increment = (): void => {
    count.value++;
  };

  // 返回组合逻辑的数据和方法
  return {
    count,
    increment,
  };
};

// 使用组合逻辑
const { count, increment } = useCounter();
</script>
    

更好的响应式能力

Vue 3 的响应式系统经过改进,更加高效。setup 内部的响应式数据可以更细粒度地进行管理,提高性能。

下面是一个使用

<template>
  <div>
    <p>Count: {{ count }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

<script setup lang="ts">
// 导入 ref 函数
import { ref } from 'vue';

// 定义响应式数据
const count = ref(0);

// 定义方法
const increment = (): void => {
  count.value++;
};
</script>
    

Vue2和Vue3 setup语法糖的生命周期比较

生命周期

image.png

基本上,Vue 的每个主要生命周期事件都被分成两个钩子,分别在该事件之前和之后调用。在你的 Vue 应用中,有四个主要事件(8个主要钩子)可以利用。

创建(Creation) 在组件创建时运行

挂载(Mounting) 当 DOM 被挂载时运行

更新(Updates) 当响应式数据被修改时运行。

销毁(Destruction) 在元素被销毁之前运行

这些生命周期事件和钩子在 Vue 组件的整个生命周期中发挥着重要作用,允许你在组件的不同阶段执行代码,以适应应用的需求。

Vue2和Vue3中生命周期的对应关系

如下图所示:

image.png

setup的执行时机

在beforeCreate钩子之前执行

如下图所示:

image.png

setup函数是处于生命周期函数 beforeCreate 和 Created 两个钩子函数之前的函数

执行 setup 时,组件实例尚未被创建(在 setup() 内部,this 不会是该活跃实例的引用,即不指向vue实例,Vue 为了避免我们错误的使用,直接将 setup函数中的this修改成了 undefined)

三、setup语法糖注意事项 尽量不要与Vue2.配置混用 Vue2x配置 (data、methos、computed…) 中可以访问到setup中的属性、方法 但在setup中不能访问到Vue2.x配置(data、methos、computed…) 如果有重名,setup优先 setup函数中不能使用this Vue为了避免我们错误的使用,直接将setup函数中的this修改为undefined

setup 接受两个参数 props context(包含attrs(非props属性)、slots(插槽)、emit(事件派发)) 代码示例如下:

<template>
  <div>
    <p>Message: {{ message }}</p>
    <button @click="handleClick">Click me</button>
  </div>
</template>

<script setup lang="js">
// 导入 ref 函数
import { ref, defineProps, defineEmits, withDefaults } from 'vue';

// 定义 props 类型
interface MyProps {
  message: string;
}

// 定义组件逻辑
const setup = (props: MyProps, context: any) => {
  // 使用 defineProps 获取 props
  const { message } = defineProps(props);

  // 使用 ref 定义响应式数据
  const count = ref(0);

  // 使用 defineEmits 获取 emit
  const emit = defineEmits();

  // 定义方法
  const handleClick = () => {
    count.value++;
    // 使用 emit 触发自定义事件
    emit('click', count.value);
  };

  // 返回组件需要的数据和方法
  return {
    message,
    count,
    handleClick,
  };
};
</script>

在这个例子中,MyProps 接口定义了 message 属性的类型。通过 defineProps 函数,我们可以获取到正确类型的 props 对象。而通过 defineEmits 函数,我们可以获取到 emit 函数,用于在 setup 函数中派发自定义事件。

setup语法糖中和模板(Template)的双向绑定 从 setup()中返回的对象上的 property 返回并可以在模板中被访问时,它将自动展开为内部值

不需要在模板中追加 .value

代码如下图所示:

<template>
  <div>
    <p>Count: {{ count }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

<script setup lang="js">
// 导入 ref 函数
import { ref } from 'vue';

// 在 setup 函数中定义响应式数据
const count = ref(0);

// 在 setup 函数中定义方法
const increment = () => {
  count.value++;
};
</script>    

在这个例子中,count 是一个由 ref 创建的响应式数据,而 increment 是一个在 setup 函数中定义的方法。

它们会被自动展开为内部值,因此在模板中直接访问 count 和调用 increment 即可,无需再添加 .value。

这种简化的写法使得在模板中使用响应式数据更加直观和清晰。

setup函数只能是同步的不能是异步的 setup() 函数被设计为在组件实例被创建时同步执行的,它负责设置组件的初始状态和逻辑。

Vue 3 的组合式 API 使得在 setup() 函数中使用响应式数据、计算属性、生命周期钩子等变得更加方便和灵活。

如果您需要在组件加载后执行异步操作,您可以将异步逻辑放在 setup() 函数外部的普通函数中,并在 setup() 函数中调用该函数。

代码示例如下:

<template>
  <div>
    <p>{{ message }}</p>
  </div>
</template>

<script setup lang="js">
// 导入 ref 函数
import { ref, onMounted } from 'vue';

// 在 setup 函数中定义响应式数据
const message = ref('');

// 错误示例:在 setup 函数中使用异步操作
// 以下代码将导致编译错误
const fetchData = async () => {
  const response = await fetch('https://example.com/data');
  message.value = await response.text();
};

// 正确示例:使用 onMounted 钩子来进行异步操作
// onMounted 钩子在组件挂载后执行,适合异步操作
onMounted(async () => {
  const response = await fetch('https://example.com/data');
  message.value = await response.text();
});
</script>

在错误的示例中,我们尝试在 setup 函数中使用异步函数 fetchData,这将导致编译错误。

相反,在正确的示例中,我们使用了 onMounted 钩子,该钩子在组件挂载后异步执行,因此适合包含异步操作