您好,登录后才能下订单哦!
# 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进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。