data 和 methods 是 Vue 实例最核心的选项。理解它们的工作方式对于构建 Vue 应用至关重要。
data 是 Vue 实例的数据对象,Vue 会将其转换为响应式。
new Vue({
data: {
message: 'Hello',
count: 0,
user: {
name: 'John',
age: 30
}
}
})
组件中 data 必须是函数:
Vue.component('my-component', {
data() {
return {
message: 'Hello',
count: 0
}
}
})
为什么必须是函数?因为组件可能被复用多次,函数确保每个实例有独立的数据副本。
data 中的属性是响应式的:
const vm = new Vue({
data: {
message: 'Hello'
}
})
vm.message = 'World'
视图会自动更新。
对象:无法检测新属性
const vm = new Vue({
data: {
user: {
name: 'John'
}
}
})
vm.user.age = 30
age 不是响应式的。需要使用 $set:
vm.$set(vm.user, 'age', 30)
或者替换整个对象:
vm.user = { ...vm.user, age: 30 }
数组:无法检测某些操作
const vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[0] = 'x'
vm.items.length = 0
这些操作不会触发更新。应该使用:
vm.items.splice(0, 1, 'x')
vm.items.splice(0)
data 在实例创建时初始化,之后添加的属性不是响应式的:
const vm = new Vue({
data: {
message: 'Hello'
}
})
vm.newProp = 'value'
newProp 不是响应式的。
如果需要动态添加响应式属性,使用 $set:
vm.$set(vm, 'newProp', 'value')
methods 包含实例的方法。
new Vue({
data: {
count: 0
},
methods: {
increment() {
this.count++
},
decrement() {
this.count--
},
reset() {
this.count = 0
}
}
})
methods 中的 this 自动绑定到实例:
new Vue({
data: {
message: 'Hello'
},
methods: {
greet() {
console.log(this.message)
},
delayedGreet() {
setTimeout(function() {
console.log(this.message)
}, 1000)
},
arrowGreet() {
setTimeout(() => {
console.log(this.message)
}, 1000)
}
}
})
delayedGreet 会报错,因为普通函数的 this 不是 Vue 实例。使用箭头函数解决。
new Vue({
methods: {
greet(name) {
console.log(`Hello, ${name}!`)
},
handleClick(event) {
console.log(event.target)
}
}
})
模板中使用:
<button @click="greet('John')">Greet</button>
<button @click="handleClick">Click</button>
不要用箭头函数定义 methods:
new Vue({
data: {
message: 'Hello'
},
methods: {
greet: () => {
console.log(this.message)
}
}
})
箭头函数没有自己的 this,会指向外层作用域。
computed 是计算属性,基于依赖缓存。
new Vue({
data: {
firstName: 'John',
lastName: 'Doe'
},
computed: {
fullName() {
return `${this.firstName} ${this.lastName}`
}
}
})
new Vue({
data: {
message: 'Hello'
},
computed: {
reversedMessage() {
return this.message.split('').reverse().join('')
}
},
methods: {
getReversedMessage() {
return this.message.split('').reverse().join('')
}
}
})
计算属性基于依赖缓存,只有依赖变化时才重新计算。方法每次调用都执行。
new Vue({
data: {
firstName: 'John',
lastName: 'Doe'
},
computed: {
fullName: {
get() {
return `${this.firstName} ${this.lastName}`
},
set(value) {
const [first, last] = value.split(' ')
this.firstName = first
this.lastName = last
}
}
}
})
watch 用于监听数据变化。
new Vue({
data: {
searchQuery: ''
},
watch: {
searchQuery(newVal, oldVal) {
this.search(newVal)
}
},
methods: {
search(query) {
console.log('Searching:', query)
}
}
})
new Vue({
data: {
user: {
name: 'John',
age: 30
}
},
watch: {
user: {
handler(newVal, oldVal) {
console.log('User changed')
},
deep: true
}
}
})
new Vue({
data: {
userId: 1
},
watch: {
userId: {
handler(newVal) {
this.fetchUser(newVal)
},
immediate: true
}
},
methods: {
fetchUser(id) {
console.log('Fetching user:', id)
}
}
})
new Vue({
data: {
user: {
name: 'John'
}
},
watch: {
'user.name'(newVal, oldVal) {
console.log('Name changed:', newVal)
}
}
})
当需要根据现有数据计算新值时:
computed: {
fullName() {
return `${this.firstName} ${this.lastName}`
},
discountedPrice() {
return this.price * (1 - this.discount)
}
}
当需要在数据变化时执行异步操作时:
watch: {
searchQuery(newVal) {
this.debounceSearch(newVal)
}
}
new Vue({
data: {
items: [],
filter: ''
},
computed: {
filteredItems() {
return this.items.filter(item =>
item.name.includes(this.filter)
)
}
},
watch: {
filter(newVal) {
localStorage.setItem('filter', newVal)
}
}
})
new Vue({
data() {
return {
form: {
username: '',
email: '',
password: ''
},
errors: {}
}
},
computed: {
isValid() {
return (
this.form.username.length >= 3 &&
this.form.email.includes('@') &&
this.form.password.length >= 6
)
}
},
methods: {
async submit() {
if (!this.isValid) return
try {
await api.register(this.form)
this.$emit('success')
} catch (error) {
this.errors = error.response.data.errors
}
},
reset() {
this.form = {
username: '',
email: '',
password: ''
}
this.errors = {}
}
}
})
new Vue({
data() {
return {
query: '',
results: [],
loading: false
}
},
computed: {
hasResults() {
return this.results.length > 0
}
},
watch: {
query(newVal) {
if (newVal.length < 2) {
this.results = []
return
}
this.search(newVal)
}
},
methods: {
async search(query) {
this.loading = true
try {
const response = await api.search(query)
this.results = response.data
} finally {
this.loading = false
}
}
}
})
始终在 data 中声明所有需要的属性:
data() {
return {
user: null,
loading: false,
error: null
}
}
使用动词开头:
methods: {
fetchData() {},
handleSubmit() {},
resetForm() {},
validateInput() {}
}
使用名词或形容词:
computed: {
fullName() {},
isValid() {},
filteredItems() {},
totalPrice() {}
}
computed: {
fullName() {
this.counter++
return `${this.firstName} ${this.lastName}`
}
}