Appearance
setup 语法糖
setup 语法糖是Vue3.2+中引入的一个重要特性,它允许你在 <script setup> 标签中直接使用组合式API,而不需要返回变量和函数。本章节将详细介绍Vue3中setup语法糖的使用方法和最佳实践。
什么是 setup 语法糖?
setup 语法糖是Vue3.2+中对组合式API的一种简化写法,它允许你在 <script setup> 标签中直接定义变量和函数,这些变量和函数会自动暴露给模板使用,不需要在 setup() 函数中返回。
setup 语法糖的基本使用
1. 基本语法
vue
<template>
<div>
<h1>{{ title }}</h1>
<p>{{ message }}</p>
<button @click="increment">Increment</button>
<p>Count: {{ count }}</p>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const title = ref('Hello Vue3!')
const message = ref('Welcome to Vue3')
const count = ref(0)
const increment = () => {
count.value++
}
onMounted(() => {
console.log('Component mounted')
})
</script>2. 与传统 setup 函数的对比
传统 setup 函数
vue
<template>
<div>
<h1>{{ title }}</h1>
<p>{{ message }}</p>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const title = ref('Hello Vue3!')
const message = ref('Welcome to Vue3')
return {
title,
message
}
}
}
</script>setup 语法糖
vue
<template>
<div>
<h1>{{ title }}</h1>
<p>{{ message }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
const title = ref('Hello Vue3!')
const message = ref('Welcome to Vue3')
</script>setup 语法糖的优势
1. 代码更简洁
setup 语法糖避免了在 setup() 函数中返回变量和函数的繁琐写法,使代码更加简洁。
2. 自动暴露变量和函数
在 <script setup> 标签中定义的变量和函数会自动暴露给模板使用,不需要手动返回。
3. 更好的类型推导
setup 语法糖提供了更好的TypeScript支持,类型推导更加准确。
4. 更好的代码组织
setup 语法糖允许你按功能组织代码,使代码更加清晰。
setup 语法糖的核心特性
1. 导入组件
在 <script setup> 标签中,你可以直接导入并使用组件,不需要注册:
vue
<template>
<div>
<HelloWorld />
</div>
</template>
<script setup>
import HelloWorld from './components/HelloWorld.vue'
</script>2. 定义 props
在 <script setup> 标签中,你可以使用 defineProps 函数来定义组件的props:
vue
<template>
<div>
<h1>{{ title }}</h1>
<p>{{ message }}</p>
</div>
</template>
<script setup>
defineProps({
title: {
type: String,
required: true
},
message: {
type: String,
default: 'Welcome to Vue3'
}
})
</script>3. 定义 emit
在 <script setup> 标签中,你可以使用 defineEmits 函数来定义组件的emit事件:
vue
<template>
<div>
<button @click="handleClick">Click me</button>
</div>
</template>
<script setup>
const emit = defineEmits(['click'])
const handleClick = () => {
emit('click')
}
</script>4. 定义 expose
在 <script setup> 标签中,你可以使用 defineExpose 函数来暴露组件的属性和方法:
vue
<template>
<div>
<p>Count: {{ count }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
const increment = () => {
count.value++
}
defineExpose({
count,
increment
})
</script>5. 使用 ref
在 <script setup> 标签中,你可以使用 ref 来创建响应式数据:
vue
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
const increment = () => {
count.value++
}
</script>6. 使用 reactive
在 <script setup> 标签中,你可以使用 reactive 来创建响应式对象:
vue
<template>
<div>
<p>Name: {{ user.name }}</p>
<p>Age: {{ user.age }}</p>
<button @click="user.age++">Increment Age</button>
</div>
</template>
<script setup>
import { reactive } from 'vue'
const user = reactive({
name: 'John',
age: 25
})
</script>7. 使用 computed
在 <script setup> 标签中,你可以使用 computed 来创建计算属性:
vue
<template>
<div>
<p>Count: {{ count }}</p>
<p>Double Count: {{ doubleCount }}</p>
<button @click="count++">Increment</button>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
</script>8. 使用 watch
在 <script setup> 标签中,你可以使用 watch 来监听数据变化:
vue
<template>
<div>
<input v-model="message" type="text">
<p>{{ message }}</p>
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
const message = ref('Hello')
watch(message, (newValue, oldValue) => {
console.log(`Message changed from ${oldValue} to ${newValue}`)
})
</script>9. 使用生命周期钩子
在 <script setup> 标签中,你可以使用生命周期钩子:
vue
<template>
<div>
<h1>Component</h1>
</div>
</template>
<script setup>
import { onMounted, onUnmounted } from 'vue'
onMounted(() => {
console.log('Component mounted')
})
onUnmounted(() => {
console.log('Component unmounted')
})
</script>setup 语法糖的使用场景
1. 简单组件
对于简单的组件,setup语法糖可以使代码更加简洁:
vue
<template>
<div class="button">
<button :class="{ 'btn-primary': type === 'primary' }" @click="handleClick">
{{ text }}
</button>
</div>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue'
const props = defineProps({
text: {
type: String,
default: 'Button'
},
type: {
type: String,
default: 'primary'
}
})
const emit = defineEmits(['click'])
const handleClick = () => {
emit('click')
}
</script>
<style scoped>
.button {
display: inline-block;
}
button {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.btn-primary {
background-color: #42b983;
color: white;
}
</style>2. 复杂组件
对于复杂的组件,setup语法糖可以使代码更加清晰:
vue
<template>
<div class="user-profile">
<h1>{{ user.name }}</h1>
<p>{{ user.email }}</p>
<button @click="fetchUser">Refresh User</button>
<div v-if="loading">Loading...</div>
<div v-else-if="error">{{ error }}</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const user = ref({})
const loading = ref(false)
const error = ref('')
const fetchUser = async () => {
loading.value = true
error.value = ''
try {
const response = await fetch('https://api.example.com/user')
if (!response.ok) {
throw new Error('Failed to fetch user')
}
const data = await response.json()
user.value = data
} catch (err) {
error.value = err.message
} finally {
loading.value = false
}
}
onMounted(() => {
fetchUser()
})
</script>3. 可复用逻辑
对于可复用的逻辑,setup语法糖可以使代码更加简洁:
javascript
// composables/useCounter.js
import { ref, computed } from 'vue'
export function useCounter(initialValue = 0) {
const count = ref(initialValue)
const doubleCount = computed(() => count.value * 2)
const increment = () => {
count.value++
}
const decrement = () => {
count.value--
}
return {
count,
doubleCount,
increment,
decrement
}
}然后在组件中使用:
vue
<template>
<div>
<p>Count: {{ count }}</p>
<p>Double Count: {{ doubleCount }}</p>
<button @click="increment">Increment</button>
<button @click="decrement">Decrement</button>
</div>
</template>
<script setup>
import { useCounter } from '../composables/useCounter'
const { count, doubleCount, increment, decrement } = useCounter(0)
</script>setup 语法糖的最佳实践
1. 按功能组织代码
使用setup语法糖时,应该按功能组织代码,将相关的逻辑放在一起:
javascript
// 好的做法:按功能组织代码
const user = ref({})
const loading = ref(false)
const error = ref('')
const fetchUser = async () => {
// 逻辑代码
}
onMounted(() => {
fetchUser()
})
// 不好的做法:按API类型组织代码
const user = ref({})
const loading = ref(false)
const error = ref('')
onMounted(() => {
fetchUser()
})
const fetchUser = async () => {
// 逻辑代码
}2. 合理使用响应式 API
根据数据类型选择合适的响应式API:
- ref:用于基本类型数据
- reactive:用于对象或数组
- computed:用于计算属性
- watch:用于监听数据变化
3. 避免在模板中使用复杂表达式
模板中的表达式应该简单明了,复杂逻辑应该放在setup语法糖中:
vue
<!-- 好的做法 -->
<template>
<div>
<p>{{ formattedPrice }}</p>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const price = ref(199.99)
const formattedPrice = computed(() => {
return `$${price.value.toFixed(2)}`
})
</script>
<!-- 不好的做法 -->
<template>
<div>
<p>{{ `$${price.toFixed(2)}` }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
const price = ref(199.99)
</script>4. 合理使用 props 和 emit
使用 defineProps 和 defineEmits 来定义组件的props和emit事件:
vue
<template>
<div>
<h1>{{ title }}</h1>
<button @click="handleClick">Click me</button>
</div>
</template>
<script setup>
const props = defineProps({
title: {
type: String,
required: true
}
})
const emit = defineEmits(['click'])
const handleClick = () => {
emit('click')
}
</script>5. 合理使用 expose
使用 defineExpose 来暴露组件的属性和方法:
vue
<template>
<div>
<p>Count: {{ count }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
const increment = () => {
count.value++
}
defineExpose({
count,
increment
})
</script>总结
setup语法糖是Vue3.2+中一个重要的特性,它允许你在 <script setup> 标签中直接使用组合式API,而不需要返回变量和函数。通过setup语法糖,你可以创建更加简洁、清晰的组件。
在使用setup语法糖时,你应该按功能组织代码,合理使用响应式API,避免在模板中使用复杂表达式,以及合理使用props、emit和expose。
在后续的章节中,我们将学习Vue3的响应式API,包括ref、reactive、computed、watch等内容。
