Vue入门到精通八之data 函数

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() 完整示例

  1. 基础用法

    <template>

    {{ 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>
    </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>

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
必须返回对象 不能返回nullundefined
响应式限制 新增属性需用this.$set
最佳实践 合理分类、设置默认值、提供重置方法

核心要点: data()是Vue组件的核心,定义响应式数据源,每个组件实例独立,使用普通函数返回对象。

相关推荐
XW01059992 小时前
5-8能被3,5和7整除的数的个数(用集合实现)
前端·javascript·数据结构·数据库·python·for循环
历程里程碑2 小时前
37 线程安全单例模式深度解析
java·服务器·开发语言·前端·javascript·c++·排序算法
wuhen_n2 小时前
v-once和v-memo完全指南:告别不必要的渲染,让应用飞起来
前端·javascript·vue.js
干前端2 小时前
Vue3 组件库实战(六):从本地到 NPM,Vue 组件库工程化构建与打包全指南(上)
前端·vue.js·npm
喵叔哟2 小时前
10. 【Blazor全栈开发实战指南】--JavaScript调用Blazor
开发语言·javascript·windows·udp
云浪2 小时前
5 分钟入门 fetch
前端·javascript
晓得迷路了2 小时前
栗子前端技术周刊第 120 期 - Vite 8.0、Solid v2.0.0 Beta、TypeScript 6.0 RC...
前端·javascript·vite
optimistic_chen2 小时前
【Vue入门】组件及组件化
前端·javascript·vue.js·html·组件
下雨打伞干嘛2 小时前
手写Promise
开发语言·前端·javascript