
Vue中的data()函数用于定义组件的响应式数据。
一、基本语法
1. data() 函数定义
javascript
export default {
data() {
return {
// 返回一个对象,包含响应式数据
message: 'Hello Vue',
count: 0,
user: {
name: '张三',
age: 25
},
list: [1, 2, 3],
isVisible: true
}
}
}
2. 为什么是函数而不是对象
javascript
// ❌ 错误:直接使用对象(仅Vue实例中可以使用)
export default {
data: {
message: 'Hello'
}
}
// ✅ 正确:组件中必须使用函数返回对象
export default {
data() {
return {
message: 'Hello'
}
}
}
原因: 组件可能被多次复用,使用函数可以确保每个组件实例都有独立的数据副本。
二、data() 工作原理
1. 数据响应式转换
data() 返回的数据 → Vue 代理 → 响应式数据
javascript
export default {
data() {
return {
count: 0
}
},
mounted() {
// ✅ Vue自动将count转换为响应式
this.count = 1 // 触发视图更新
// ❌ 添加的新属性不是响应式
this.newProp = 'test' // 不会触发视图更新
// ✅ 使用Vue.set添加响应式属性
this.$set(this, 'newProp', 'test')
}
}
2. 多实例隔离
javascript
// CounterComponent.vue
export default {
data() {
return {
count: 0
}
}
}
<template>
<div>
<!-- 每个组件实例有独立的count -->
<CounterComponent />
<CounterComponent />
<CounterComponent />
</div>
</template>
如果data是对象,所有实例会共享同一个count值。
三、data() 完整示例
-
基础用法
<template></template> <script> export default { name: 'MyComponent', data() { return { // 字符串 message: 'Hello Vue', // 数字 count: 0, // 对象 user: { name: '张三', age: 25, email: 'zhangsan@example.com' }, // 数组 list: ['苹果', '香蕉', '橙子'], // 布尔值 isVisible: true, // null detail: null, // undefined(通常不推荐) undefinedProp: undefined } }, methods: { increment() { this.count++ }, toggleVisible() { this.isVisible = !this.isVisible } } } </script>{{ message }}
计数:{{ count }}
用户:{{ user.name }} - {{ user.age }}岁
<ul> <li v-for="item in list" :key="item"> {{ item }} </li> </ul> <button @click="increment">增加</button> <button @click="toggleVisible">切换显示</button> <div v-if="isVisible"> 这是可见的内容 </div>
2. 表单数据
<template>
<a-form :form="form" @submit="handleSubmit">
<a-form-item label="用户名">
<a-input
v-model="form.username"
placeholder="请输入用户名"
/>
</a-form-item>
<a-form-item label="邮箱">
<a-input
v-model="form.email"
placeholder="请输入邮箱"
/>
</a-form-item>
<a-form-item label="年龄">
<a-input-number
v-model="form.age"
:min="1"
:max="100"
/>
</a-form-item>
<a-form-item label="性别">
<a-select v-model="form.gender">
<a-select-option value="male">男</a-select-option>
<a-select-option value="female">女</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="兴趣爱好">
<a-checkbox-group v-model="form.hobbies">
<a-checkbox value="reading">阅读</a-checkbox>
<a-checkbox value="sports">运动</a-checkbox>
<a-checkbox value="music">音乐</a-checkbox>
</a-checkbox-group>
</a-form-item>
<a-form-item>
<a-button type="primary" html-type="submit">
提交
</a-button>
</a-form-item>
</a-form>
</template>
<script>
export default {
data() {
return {
form: {
username: '',
email: '',
age: 25,
gender: 'male',
hobbies: ['reading']
}
}
},
methods: {
handleSubmit(e) {
e.preventDefault()
console.log('表单数据:', this.form)
this.$message.success('提交成功')
}
}
}
</script>
3. 列表数据
<template>
<div>
<a-button type="primary" @click="fetchData" :loading="loading">
获取数据
</a-button>
<a-table
:columns="columns"
:data-source="userList"
:loading="loading"
:pagination="pagination"
@change="handleTableChange"
/>
</div>
</template>
<script>
export default {
data() {
return {
loading: false,
userList: [],
// 表格列配置
columns: [
{
title: 'ID',
dataIndex: 'id',
width: 80
},
{
title: '用户名',
dataIndex: 'username'
},
{
title: '邮箱',
dataIndex: 'email'
},
{
title: '年龄',
dataIndex: 'age'
},
{
title: '性别',
dataIndex: 'gender',
customRender: (text) => text === 'male' ? '男' : '女'
},
{
title: '操作',
key: 'action',
scopedSlots: { customRender: 'action' }
}
],
// 分页配置
pagination: {
current: 1,
pageSize: 10,
total: 0,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total) => `共 ${total} 条`
}
}
},
mounted() {
this.fetchData()
},
methods: {
async fetchData() {
this.loading = true
try {
const { pageNo, pageSize } = this.pagination
// 调用API
const res = await this.$http.get('/api/users', {
params: { pageNo, pageSize }
})
// 更新数据
this.userList = res.data.list
this.pagination.total = res.data.total
} catch (error) {
this.$message.error('获取数据失败')
} finally {
this.loading = false
}
},
handleTableChange(pagination) {
this.pagination = {
...this.pagination,
current: pagination.current,
pageSize: pagination.pageSize
}
this.fetchData()
}
}
}
</script>
四、data() vs computed vs methods
1. 区别对比
| 特性 | data() | computed | methods |
|---|---|---|---|
| 定义 | 响应式数据源 | 计算属性 | 方法 |
| 缓存 | 无 | ✅ 有缓存 | ❌ 无缓存 |
| 依赖 | - | 自动追踪 | 手动调用 |
| 用途 | 存储状态 | 派生数据 | 事件处理 |
2. 实际示例
<template>
<div>
<p>计数:{{ count }}</p>
<p>双倍:{{ doubleCount }}</p>
<p>三倍:{{ tripleCount() }}</p>
<button @click="increment">增加</button>
<button @click="tripleCount">调用方法</button>
</div>
</template>
<script>
export default {
data() {
return {
// data():原始数据
count: 0
}
},
computed: {
// computed:自动计算,有缓存
doubleCount() {
console.log('计算doubleCount')
return this.count * 2
}
},
methods: {
// methods:手动调用,无缓存
tripleCount() {
console.log('计算tripleCount')
return this.count * 3
},
increment() {
this.count++
}
}
}
</script>
执行结果:
count变化时,doubleCount自动重新计算(有缓存,依赖未变时不计算)tripleCount()需要手动调用,每次都会执行
五、data() 常见错误
1. ❌ 返回null或undefined
javascript
export default {
data() {
return null // ❌ 错误:必须返回对象
}
}
// ✅ 正确
export default {
data() {
return {
message: 'Hello'
}
}
}
2. ❌ 使用箭头函数
javascript
export default {
data: () => { // ❌ 错误:箭头函数没有this
return {
count: 0
}
}
}
// ✅ 正确:使用普通函数
export default {
data() {
return {
count: 0
}
}
}
原因: 箭头函数不绑定this,无法访问Vue实例。
3. ❌ 动态添加属性
javascript
export default {
data() {
return {
user: {
name: '张三'
}
}
},
mounted() {
// ❌ 直接添加的属性不是响应式
this.user.age = 25
// ✅ 使用this.$set添加响应式属性
this.$set(this.user, 'age', 25)
// ✅ 或使用Object.assign
this.user = Object.assign({}, this.user, { age: 25 })
}
}
六、data() 最佳实践
1. 数据分类
javascript
export default {
data() {
return {
// ========== 表单数据 ==========
form: {
username: '',
email: '',
age: 25
},
// ========== 列表数据 ==========
userList: [],
// ========== 加载状态 ==========
loading: false,
submitting: false,
// ========== 弹窗状态 ==========
visible: false,
modalTitle: '',
// ========== 当前操作对象 ==========
currentRecord: null,
// ========== 分页配置 ==========
pagination: {
current: 1,
pageSize: 10,
total: 0
},
// ========== 查询参数 ==========
queryParams: {
keyword: '',
status: 1
}
}
}
}
2. 初始化数据
javascript
export default {
data() {
return {
// ✅ 设置合理的默认值
form: {
username: '',
email: '',
status: 1, // 默认激活状态
gender: 'male', // 默认性别
hobbies: [] // 空数组
},
pagination: {
current: 1,
pageSize: 10,
total: 0
}
}
}
}
3. 重置数据
javascript
export default {
data() {
return {
form: {
username: '',
email: ''
}
}
},
methods: {
// 重置表单到初始状态
resetForm() {
this.form = {
username: '',
email: ''
}
},
// 或使用Object.assign
resetForm() {
const defaultForm = {
username: '',
email: ''
}
this.form = Object.assign({}, defaultForm)
}
}
}
七、Vue 3中的data()
1. Options API(与Vue 2相同)
javascript
export default {
data() {
return {
count: 0
}
}
}
2. Composition API(推荐)
<script setup>
// 使用ref定义响应式数据
import { ref, reactive } from 'vue'
// 基本类型
const count = ref(0)
const message = ref('Hello Vue')
// 对象类型
const form = reactive({
username: '',
email: ''
})
// 数组类型
const userList = ref([])
// 修改值
function increment() {
count.value++ // ref需要.value
form.username = '张三' // reactive不需要.value
}
</script>
八、完整实战示例
<template>
<div>
<!-- 查询条件 -->
<a-form layout="inline" @submit="handleSearch">
<a-form-item label="关键词">
<a-input
v-model="queryParams.keyword"
placeholder="请输入关键词"
style="width: 200px"
/>
</a-form-item>
<a-form-item label="状态">
<a-select v-model="queryParams.status" style="width: 150px">
<a-select-option :value="null">全部</a-select-option>
<a-select-option :value="1">激活</a-select-option>
<a-select-option :value="0">禁用</a-select-option>
</a-select>
</a-form-item>
<a-form-item>
<a-button type="primary" html-type="submit">
查询
</a-button>
<a-button style="margin-left: 8px" @click="resetQuery">
重置
</a-button>
</a-form-item>
</a-form>
<!-- 操作按钮 -->
<div style="margin: 16px 0">
<a-button type="primary" @click="handleAdd">
新增
</a-button>
</div>
<!-- 数据表格 -->
<a-table
:columns="columns"
:data-source="userList"
:loading="loading"
:pagination="pagination"
@change="handleTableChange"
>
<template #action="{ text, record }">
<a @click="handleEdit(record)">编辑</a>
<a-divider type="vertical" />
<a @click="handleDelete(record.id)" style="color: red">删除</a>
</template>
</a-table>
<!-- 新增/编辑弹窗 -->
<a-modal
v-model="visible"
:title="modalTitle"
@ok="handleSubmit"
@cancel="handleCancel"
>
<a-form :form="form" :label-col="{ span: 6 }">
<a-form-item label="用户名">
<a-input
v-decorator="['username', {
rules: [{ required: true, message: '请输入用户名' }]
}]"
/>
</a-form-item>
<a-form-item label="邮箱">
<a-input
v-decorator="['email', {
rules: [
{ required: true, message: '请输入邮箱' },
{ type: 'email', message: '邮箱格式不正确' }
]
}]"
/>
</a-form-item>
<a-form-item label="年龄">
<a-input-number
v-decorator="['age', {
initialValue: 25,
rules: [{ required: true, message: '请输入年龄' }]
}]"
:min="1"
:max="100"
style="width: 100%"
/>
</a-form-item>
</a-form>
</a-modal>
</div>
</template>
<script>
export default {
name: 'UserList',
data() {
return {
// ========== 查询参数 ==========
queryParams: {
keyword: '',
status: null
},
// ========== 数据列表 ==========
userList: [],
// ========== 加载状态 ==========
loading: false,
// ========== 弹窗状态 ==========
visible: false,
modalTitle: '',
currentRecord: null,
// ========== 表单实例 ==========
form: this.$form.createForm(this),
// ========== 表格列配置 ==========
columns: [
{ title: 'ID', dataIndex: 'id', width: 80 },
{ title: '用户名', dataIndex: 'username' },
{ title: '邮箱', dataIndex: 'email' },
{ title: '年龄', dataIndex: 'age' },
{
title: '状态',
dataIndex: 'status',
customRender: (text) => text === 1 ? '激活' : '禁用'
},
{ title: '创建时间', dataIndex: 'createTime' },
{ title: '操作', key: 'action', scopedSlots: { customRender: 'action' } }
],
// ========== 分页配置 ==========
pagination: {
current: 1,
pageSize: 10,
total: 0,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total) => `共 ${total} 条`
}
}
},
mounted() {
this.fetchData()
},
methods: {
// 获取数据
async fetchData() {
this.loading = true
try {
const { current, pageSize } = this.pagination
const res = await this.$http.get('/api/users', {
params: {
...this.queryParams,
pageNo: current,
pageSize
}
})
this.userList = res.data.list
this.pagination.total = res.data.total
} catch (error) {
this.$message.error('获取数据失败')
} finally {
this.loading = false
}
},
// 查询
handleSearch() {
this.pagination.current = 1
this.fetchData()
},
// 重置查询
resetQuery() {
this.queryParams = {
keyword: '',
status: null
}
this.handleSearch()
},
// 表格变化
handleTableChange(pagination) {
this.pagination = {
...this.pagination,
current: pagination.current,
pageSize: pagination.pageSize
}
this.fetchData()
},
// 新增
handleAdd() {
this.currentRecord = null
this.modalTitle = '新增用户'
this.visible = true
this.$nextTick(() => {
this.form.resetFields()
this.form.setFieldsValue({
age: 25,
status: 1
})
})
},
// 编辑
handleEdit(record) {
this.currentRecord = record
this.modalTitle = '编辑用户'
this.visible = true
this.$nextTick(() => {
this.form.setFieldsValue({
username: record.username,
email: record.email,
age: record.age,
status: record.status
})
})
},
// 删除
handleDelete(id) {
this.$confirm('确定要删除吗?', '提示', {
type: 'warning'
}).then(async () => {
await this.$http.delete(`/api/users/${id}`)
this.$message.success('删除成功')
this.fetchData()
})
},
// 提交表单
handleSubmit() {
this.form.validateFields(async (err, values) => {
if (!err) {
try {
const isEdit = !!this.currentRecord
const api = isEdit ? this.$http.put : this.$http.post
const url = isEdit
? `/api/users/${this.currentRecord.id}`
: '/api/users'
await api(url, values)
this.$message.success(isEdit ? '更新成功' : '新增成功')
this.visible = false
this.fetchData()
} catch (error) {
this.$message.error('操作失败')
}
}
})
},
// 取消
handleCancel() {
this.form.resetFields()
this.visible = false
}
}
}
</script>
九、总结
| 要点 | 说明 |
|---|---|
| 定义 | data() 函数返回对象,包含响应式数据 |
| 必须是函数 | 组件复用时每个实例有独立数据副本 |
| 不能使用箭头函数 | 箭头函数不绑定this |
| 必须返回对象 | 不能返回null或undefined |
| 响应式限制 | 新增属性需用this.$set |
| 最佳实践 | 合理分类、设置默认值、提供重置方法 |
核心要点: data()是Vue组件的核心,定义响应式数据源,每个组件实例独立,使用普通函数返回对象。