响应式基础 #
什么是响应式? #
响应式是一种编程范式,当数据发生变化时,依赖这些数据的视图会自动更新。Alpine.js 的响应式系统让开发者无需手动操作 DOM,只需关注数据的变化。
html
<div x-data="{ count: 0 }">
<button @click="count++">增加</button>
<span x-text="count"></span>
</div>
当 count 变化时,x-text 绑定的内容会自动更新。
响应式原理 #
Proxy 代理 #
Alpine.js 使用 JavaScript 的 Proxy 对象实现响应式:
javascript
const data = { count: 0 }
const reactive = new Proxy(data, {
get(target, key) {
track(target, key)
return target[key]
},
set(target, key, value) {
target[key] = value
trigger(target, key)
return true
}
})
依赖收集 #
当组件渲染时,Alpine.js 会记录哪些数据被使用:
html
<div x-data="{ count: 0, name: 'John' }">
<span x-text="count"></span>
</div>
只有 count 被使用,name 变化不会触发更新。
触发更新 #
当数据变化时,Alpine.js 会通知所有依赖该数据的组件重新渲染:
html
<div x-data="{ count: 0 }">
<span x-text="count"></span>
<span x-text="count * 2"></span>
<button @click="count++">增加</button>
</div>
点击按钮后,两个 span 都会更新。
响应式数据 #
在 x-data 中定义 #
html
<div x-data="{
message: 'Hello',
count: 0,
user: { name: 'John' },
items: ['a', 'b', 'c']
}">
</div>
使用 Alpine.reactive #
创建独立的响应式对象:
javascript
const state = Alpine.reactive({
count: 0,
user: { name: 'John' }
})
数据变更检测 #
能检测的变更 #
javascript
this.count = 10
this.user.name = 'Jane'
this.items.push('d')
this.items.pop()
this.items.splice(0, 1)
不能检测的变更 #
javascript
this.items[0] = 'new'
this.items.length = 0
Object.assign(this.user, { email: 'test@example.com' })
解决方案 #
javascript
this.items = [...this.items]
this.items[0] = 'new'
this.items = this.items.filter(item => item.active)
this.user = { ...this.user, email: 'test@example.com' }
$watch 监听器 #
基本用法 #
监听数据变化:
html
<div x-data="{
count: 0
}" x-init="
$watch('count', (value, oldValue) => {
console.log(`count 从 ${oldValue} 变为 ${value}`)
})
">
<button @click="count++">增加</button>
</div>
监听嵌套属性 #
html
<div x-data="{
user: { name: 'John' }
}" x-init="
$watch('user.name', (value) => {
console.log('name changed:', value)
})
">
<input x-model="user.name">
</div>
监听数组 #
html
<div x-data="{
items: ['a', 'b', 'c']
}" x-init="
$watch('items', (value) => {
console.log('items changed:', value)
})
">
<button @click="items.push('d')">添加</button>
</div>
深度监听 #
html
<div x-data="{
user: {
profile: {
name: 'John'
}
}
}" x-init="
$watch('user', (value) => {
console.log('user changed:', value)
}, { deep: true })
">
<input x-model="user.profile.name">
</div>
x-effect #
基本用法 #
x-effect 在依赖数据变化时重新执行:
html
<div x-data="{ count: 0 }" x-effect="console.log('count:', count)">
<button @click="count++">增加</button>
</div>
与 $watch 的区别 #
| 特性 | x-effect | $watch |
|---|---|---|
| 自动追踪依赖 | 是 | 需要指定属性 |
| 返回旧值 | 否 | 是 |
| 使用场景 | 副作用 | 监听特定变化 |
实用示例 #
html
<div x-data="{
firstName: 'John',
lastName: 'Doe',
fullName: ''
}" x-effect="fullName = firstName + ' ' + lastName">
<input x-model="firstName">
<input x-model="lastName">
<p x-text="fullName"></p>
</div>
Alpine.effect #
全局副作用 #
javascript
Alpine.effect(() => {
console.log('count:', Alpine.store('app').count)
})
清理副作用 #
javascript
const cleanup = Alpine.effect(() => {
console.log('effect')
})
cleanup()
计算属性 #
使用 getter #
html
<div x-data="{
firstName: 'John',
lastName: 'Doe',
get fullName() {
return this.firstName + ' ' + this.lastName
}
}">
<p x-text="fullName"></p>
</div>
计算属性缓存 #
计算属性会缓存结果,只有依赖变化时才重新计算:
html
<div x-data="{
items: [1, 2, 3, 4, 5],
get sum() {
console.log('计算 sum')
return this.items.reduce((a, b) => a + b, 0)
}
}">
<p x-text="sum"></p>
<p x-text="sum"></p>
<button @click="items.push(6)">添加</button>
</div>
响应式最佳实践 #
1. 避免深层嵌套 #
javascript
{
user: {
profile: {
address: {
city: 'Beijing'
}
}
}
}
推荐扁平化:
javascript
{
userCity: 'Beijing'
}
2. 使用不可变操作 #
javascript
this.items = [...this.items, newItem]
this.items = this.items.filter(item => item.id !== id)
3. 合理使用计算属性 #
html
<div x-data="{
items: [],
get activeItems() {
return this.items.filter(item => item.active)
},
get itemCount() {
return this.items.length
}
}">
</div>
4. 避免在模板中复杂计算 #
不推荐:
html
<span x-text="items.filter(i => i.active).map(i => i.name).join(', ')"></span>
推荐:
html
<span x-text="activeItemNames"></span>
javascript
get activeItemNames() {
return this.items
.filter(i => i.active)
.map(i => i.name)
.join(', ')
}
常见问题 #
1. 数据更新但视图不更新 #
javascript
this.items[0] = 'new'
解决:
javascript
this.items = [...this.items]
2. 新属性不响应 #
javascript
this.newProp = 'value'
解决:
javascript
this.data = { ...this.data, newProp: 'value' }
3. 监听不触发 #
确保使用正确的方式修改数据:
javascript
this.items.push('new')
this.items = this.items
小结 #
响应式系统要点:
- 使用 Proxy 实现响应式
- 自动收集依赖和触发更新
$watch监听数据变化x-effect执行响应式副作用- 使用 getter 定义计算属性
- 注意数据变更检测的限制
下一章,我们将学习 Alpine.store 全局状态管理。
最后更新:2026-03-28