setup() 函数

什么是 setup() 函数?

setup() 函数是 Vue3 组合式 API 的入口点,它在组件实例创建之前执行,用于设置组件的状态、方法和生命周期钩子等。

1. 基本用法

setup() 函数接收两个参数:props 和 context。

示例:基本 setup() 函数

<template>
  <div>
    <h1>{{ title }}</h1>
    <p>计数: {{ count }}</p>
    <button @click="increment">增加</button>
  </div>
</template>

<script>
export default {
  name: 'SetupDemo',
  props: {
    title: {
      type: String,
      default: 'Hello Vue3'
    }
  },
  setup(props, context) {
    // 组件状态
    let count = 0;
    
    // 组件方法
    function increment() {
      count++;
    }
    
    // 返回值将暴露给模板
    return {
      count,
      increment
    };
  }
}
</script>

2. setup() 的参数

props 参数

props 是一个响应式对象,包含了父组件传递给子组件的所有属性。

示例:使用 props

<script>
export default {
  props: {
    message: String,
    count: Number
  },
  setup(props) {
    console.log(props.message); // 访问 props
    
    // 注意:props 是响应式的,但不能直接解构,否则会失去响应性
    // const { message } = props; // 错误:失去响应性
    
    return {};
  }
}
</script>

context 参数

context 是一个普通对象,包含了以下属性:

  • attrs:包含组件的非 props 属性
  • emit:用于触发事件的函数
  • slots:包含组件的插槽
  • expose:用于暴露组件公共方法的函数

示例:使用 context

<script>
export default {
  emits: ['update:count'],
  setup(props, { attrs, emit, slots, expose }) {
    // 使用 emit 触发事件
    function updateCount(newCount) {
      emit('update:count', newCount);
    }
    
    // 暴露公共方法
    expose({
      updateCount,
      reset: () => emit('update:count', 0)
    });
    
    return {
      updateCount
    };
  }
}
</script>

3. setup() 与响应式 API

在 setup() 中,需要使用 Vue3 提供的响应式 API 来创建响应式数据。

示例:使用响应式 API

<template>
  <div>
    <p>计数: {{ count }}</p>
    <p>双倍计数: {{ doubleCount }}</p>
    <button @click="increment">增加</button>
  </div>
</template>

<script>
import { ref, computed } from 'vue';

export default {
  setup() {
    // 使用 ref 创建响应式数据
    const count = ref(0);
    
    // 使用 computed 创建计算属性
    const doubleCount = computed(() => count.value * 2);
    
    function increment() {
      count.value++;
    }
    
    return {
      count,
      doubleCount,
      increment
    };
  }
}
</script>

4. setup() 与生命周期钩子

在 setup() 中,可以使用 onXXX 形式的生命周期钩子函数。

示例:使用生命周期钩子

<script>
import { onMounted, onUpdated, onUnmounted } from 'vue';

export default {
  setup() {
    onMounted(() => {
      console.log('组件挂载');
    });
    
    onUpdated(() => {
      console.log('组件更新');
    });
    
    onUnmounted(() => {
      console.log('组件卸载');
    });
    
    return {};
  }
}
</script>

5. script setup 语法糖

Vue3 提供了 <script setup> 语法糖,简化了 setup() 函数的写法。

示例:使用 script setup

<template>
  <div>
    <h1>{{ title }}</h1>
    <p>计数: {{ count }}</p>
    <button @click="increment">增加</button>
  </div>
</template>

<script setup>
// 导入响应式 API
import { ref } from 'vue';

// 定义 props
const props = defineProps({
  title: {
    type: String,
    default: 'Hello Vue3'
  }
});

// 定义事件
const emit = defineEmits(['update:count']);

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

// 定义方法
function increment() {
  count.value++;
  emit('update:count', count.value);
}

// 暴露给模板(自动暴露)
</script>

6. setup() 函数的注意事项

  • setup() 函数在 beforeCreate 和 created 生命周期钩子之前执行
  • setup() 函数中没有 this 上下文
  • setup() 函数的返回值会暴露给模板和组件实例
  • props 是响应式的,但不能直接解构
  • context 不是响应式的,可以安全解构

7. setup() 与选项式 API 的混合使用

在 Vue3 中,可以混合使用组合式 API 和选项式 API,但需要注意一些规则。

示例:混合使用 API

<script>
import { ref } from 'vue';

export default {
  data() {
    return {
      message: 'Hello from options API'
    };
  },
  setup() {
    const count = ref(0);
    
    function increment() {
      count.value++;
    }
    
    return {
      count,
      increment
    };
  },
  methods: {
    greet() {
      console.log(this.message);
      console.log(this.count); // 可以访问 setup() 中返回的值
    }
  }
}
</script>

使用场景

  • 构建复杂组件,需要更好的代码组织
  • 复用逻辑代码,提高代码的可维护性
  • 需要更细粒度的响应式控制
  • 与 TypeScript 结合使用,获得更好的类型推导

最佳实践

  • 优先使用 <script setup> 语法糖,简化代码
  • 将相关的逻辑组织在一起,提高代码的可读性
  • 使用响应式 API 创建组件状态
  • 避免在 setup() 中使用 this
  • 合理使用生命周期钩子