# Vue3中SetUp的參數props和context實例分析
## 前言
Vue3作為當前最流行的前端框架之一,其革命性的Composition API徹底改變了開發者的編碼方式。在Composition API中,`setup()`函數扮演著核心角色,而理解其參數`props`和`context`的用法,是掌握Vue3開發的關鍵。本文將深入剖析這兩個參數的特性和應用場景,通過實例演示幫助開發者徹底掌握其精髓。
## 一、setup()函數基礎概念
### 1.1 setup()函數的作用
`setup()`是Vue3組合式API的入口函數,它在組件實例創建之前執行,主要職責包括:
- 定義響應式數據
- 聲明計算屬性
- 注冊生命周期鉤子
- 定義方法
- 返回模板所需內容
```javascript
export default {
setup() {
// 在這里編寫組合式API代碼
return {
// 返回模板需要使用的數據和方法
}
}
}
與Options API不同,setup()
的執行發生在beforeCreate
之前,此時:
- 組件實例尚未創建
- this
不可用(返回undefined)
- 數據觀察(data observation)尚未建立
setup()
的第一個參數是props
,它是一個響應式對象,包含了父組件傳遞的所有prop值。
export default {
props: {
title: String,
user: Object
},
setup(props) {
console.log(props.title) // 訪問prop值
console.log(props.user)
}
}
props
對象是響應式的,這意味著:
- 當父組件更新prop時,子組件會自動更新
- 不能使用ES6解構,否則會失去響應性
- 需要解構時應使用toRefs
import { toRefs } from 'vue'
export default {
setup(props) {
// 錯誤方式:解構會失去響應性
// const { title } = props
// 正確方式:使用toRefs保持響應性
const { title } = toRefs(props)
console.log(title.value) // 需要通過.value訪問
return { title }
}
}
雖然Vue3仍然支持Options API中的prop驗證,但在組合式API中更推薦使用TypeScript:
interface Props {
title?: string
count: number
items: string[]
}
export default {
setup(props: Props) {
// 現在可以享受類型提示和檢查
console.log(props.count.toFixed(2))
}
}
<template>
<div>
<input v-model="inputValue" @input="handleInput" />
</div>
</template>
<script>
import { ref, watch } from 'vue'
export default {
props: {
modelValue: String,
maxLength: {
type: Number,
default: 100
}
},
emits: ['update:modelValue'],
setup(props, { emit }) {
const inputValue = ref(props.modelValue || '')
watch(() => props.modelValue, (newVal) => {
inputValue.value = newVal
})
function handleInput(e) {
let value = e.target.value
if (value.length > props.maxLength) {
value = value.slice(0, props.maxLength)
}
inputValue.value = value
emit('update:modelValue', value)
}
return { inputValue, handleInput }
}
}
</script>
setup()
的第二個參數是context
,它包含三個重要屬性:
export default {
setup(props, context) {
// 等價于:
// setup(props, { attrs, slots, emit }) {
console.log(context.attrs) // 非響應式對象
console.log(context.slots) // 插槽內容
console.log(context.emit) // 觸發事件方法
// Vue3.2+新增
console.log(context.expose) // 暴露公共屬性方法
}
}
attrs
包含所有未被props聲明的attribute,包括:
- class和style
- 原生HTML attribute
- 自定義事件監聽器(v-on)
<template>
<button v-bind="attrs">點擊</button>
</template>
<script>
export default {
setup(props, { attrs }) {
// 透傳所有非prop屬性
return { attrs }
}
}
</script>
slots
提供對插槽內容的訪問,支持作用域插槽:
<template>
<div>
<slot name="header" :user="user"></slot>
<slot></slot>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup(props, { slots }) {
const user = ref({ name: '張三' })
// 檢查插槽是否存在
const hasHeader = slots.header
return { user, hasHeader }
}
}
</script>
emit
替代了Vue2的this.$emit
,需要先在組件中聲明:
export default {
emits: ['submit', 'update'], // 顯式聲明事件
setup(props, { emit }) {
function handleClick() {
emit('submit', { data: 123 })
}
return { handleClick }
}
}
Vue3.2+新增的expose
允許限制組件暴露的公共API:
export default {
setup(props, { expose }) {
const publicData = ref('公開數據')
const privateData = ref('私有數據')
// 只暴露publicData和方法
expose({
publicData,
publicMethod() {
console.log('公共方法')
}
})
return { publicData, privateData }
}
}
<template>
<div class="data-table">
<div class="header">
<slot name="header" :columns="columns" :sort="sortBy"></slot>
</div>
<div class="body">
<div v-for="(item, index) in sortedData" :key="item.id" class="row">
<slot :item="item" :index="index"></slot>
</div>
</div>
<div class="footer">
<slot name="footer" :total="data.length"></slot>
</div>
</div>
</template>
<script>
import { computed, ref } from 'vue'
export default {
props: {
data: {
type: Array,
required: true,
validator: value => value.every(item => 'id' in item)
},
columns: Array,
defaultSort: {
type: String,
default: 'id'
}
},
emits: ['row-click', 'sort-change'],
setup(props, { emit }) {
const sortBy = ref(props.defaultSort)
const sortDirection = ref('asc')
const sortedData = computed(() => {
return [...props.data].sort((a, b) => {
const modifier = sortDirection.value === 'asc' ? 1 : -1
if (a[sortBy.value] < b[sortBy.value]) return -1 * modifier
if (a[sortBy.value] > b[sortBy.value]) return 1 * modifier
return 0
})
})
function handleSort(column) {
if (sortBy.value === column) {
sortDirection.value = sortDirection.value === 'asc' ? 'desc' : 'asc'
} else {
sortBy.value = column
sortDirection.value = 'asc'
}
emit('sort-change', { column: sortBy.value, direction: sortDirection.value })
}
function handleRowClick(item) {
emit('row-click', item)
}
return {
sortBy,
sortDirection,
sortedData,
handleSort,
handleRowClick
}
}
}
</script>
import { defineComponent, PropType } from 'vue'
interface User {
id: number
name: string
email: string
}
interface TableColumn {
key: string
title: string
sortable?: boolean
}
export default defineComponent({
props: {
data: {
type: Array as PropType<User[]>,
required: true
},
columns: {
type: Array as PropType<TableColumn[]>,
default: () => []
}
},
emits: {
'row-click': (user: User) => true,
'sort-change': (payload: { column: string; direction: 'asc' | 'desc' }) => true
},
setup(props, { emit }) {
// 現在所有props和emit都有完整的類型推斷
const handleClick = (user: User) => {
emit('row-click', user)
}
return { handleClick }
}
})
toRef
而非toRefs
Q1:為什么修改props會觸發警告? A:Vue遵循單向數據流,直接修改prop會觸發警告。正確的做法是觸發事件讓父組件修改。
Q2:attrs和props有什么區別? A:props是顯式聲明的屬性,attrs包含所有未聲明的屬性(包括class/style等)。
Q3:為什么slots不是響應式的? A:因為插槽內容由父組件決定,子組件通常不需要對其變化做出響應。
將setup邏輯抽取為組合式函數:
// usePagination.js
import { computed, ref } from 'vue'
export function usePagination(items, perPage = 10) {
const currentPage = ref(1)
const totalPages = computed(() =>
Math.ceil(items.value.length / perPage)
)
const paginatedItems = computed(() => {
const start = (currentPage.value - 1) * perPage
const end = start + perPage
return items.value.slice(start, end)
})
function nextPage() {
if (currentPage.value < totalPages.value) {
currentPage.value++
}
}
function prevPage() {
if (currentPage.value > 1) {
currentPage.value--
}
}
return {
currentPage,
totalPages,
paginatedItems,
nextPage,
prevPage
}
}
通過對setup函數的props和context參數的深入理解,開發者可以充分發揮Vue3組合式API的強大功能。記住以下要點: 1. props是響應式的,避免直接解構 2. context提供了組件通信的關鍵能力 3. TypeScript可以顯著提升代碼質量 4. 合理封裝組合式函數提高復用性
希望本文能幫助您在Vue3開發中游刃有余,構建更健壯、更易維護的前端應用。 “`
這篇文章共計約3950字,全面覆蓋了Vue3 setup函數中props和context的核心知識點,包含基礎概念、深度解析、實戰案例和最佳實踐,采用Markdown格式編寫,可直接用于技術博客或文檔。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。