Alpine.reactive 响应式对象 #
什么是 Alpine.reactive? #
Alpine.reactive 用于创建独立的响应式对象。与 x-data 和 Alpine.store 不同,它可以在任何 JavaScript 代码中使用,提供更灵活的状态管理方式。
基本语法 #
javascript
const state = Alpine.reactive({
property: 'value'
})
基本用法 #
创建响应式对象 #
javascript
const counter = Alpine.reactive({
count: 0,
increment() {
this.count++
},
decrement() {
this.count--
}
})
在组件中使用 #
html
<div x-data>
<span x-text="counter.count"></span>
<button @click="counter.increment()">增加</button>
</div>
<script>
const counter = Alpine.reactive({
count: 0,
increment() { this.count++ }
})
</script>
与 Alpine.store 的区别 #
| 特性 | Alpine.reactive | Alpine.store |
|---|---|---|
| 命名 | 无需命名 | 需要名称 |
| 访问方式 | 直接引用 | $store.name |
| 注册时机 | 任意时刻 | alpine:init 事件 |
| 使用场景 | 局部状态、模块状态 | 全局状态 |
响应式特性 #
自动追踪依赖 #
javascript
const state = Alpine.reactive({
firstName: 'John',
lastName: 'Doe'
})
Alpine.effect(() => {
console.log('Full name:', state.firstName, state.lastName)
})
state.firstName = 'Jane'
嵌套对象响应式 #
javascript
const state = Alpine.reactive({
user: {
profile: {
name: 'John'
}
}
})
state.user.profile.name = 'Jane'
数组响应式 #
javascript
const state = Alpine.reactive({
items: ['a', 'b', 'c']
})
state.items.push('d')
state.items.pop()
实用示例 #
表单状态管理 #
javascript
const formState = Alpine.reactive({
data: {
name: '',
email: '',
message: ''
},
errors: {},
loading: false,
validate() {
this.errors = {}
if (!this.data.name) this.errors.name = '请输入姓名'
if (!this.data.email) this.errors.email = '请输入邮箱'
return Object.keys(this.errors).length === 0
},
async submit() {
if (!this.validate()) return
this.loading = true
try {
await fetch('/api/contact', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(this.data)
})
this.reset()
} finally {
this.loading = false
}
},
reset() {
this.data = { name: '', email: '', message: '' }
this.errors = {}
}
})
html
<form x-data @submit.prevent="formState.submit()">
<div>
<input x-model="formState.data.name" placeholder="姓名">
<span x-show="formState.errors.name" x-text="formState.errors.name"></span>
</div>
<div>
<input x-model="formState.data.email" placeholder="邮箱">
<span x-show="formState.errors.email" x-text="formState.errors.email"></span>
</div>
<div>
<textarea x-model="formState.data.message" placeholder="消息"></textarea>
</div>
<button type="submit" :disabled="formState.loading">
<span x-show="!formState.loading">提交</span>
<span x-show="formState.loading">提交中...</span>
</button>
</form>
数据表格状态 #
javascript
const tableState = Alpine.reactive({
data: [],
loading: false,
page: 1,
perPage: 10,
sortField: 'id',
sortOrder: 'asc',
filters: {},
get total() {
return this.data.length
},
get totalPages() {
return Math.ceil(this.total / this.perPage)
},
get paginatedData() {
let result = [...this.data]
for (const [field, value] of Object.entries(this.filters)) {
if (value) {
result = result.filter(item =>
String(item[field]).toLowerCase().includes(value.toLowerCase())
)
}
}
result.sort((a, b) => {
const aVal = a[this.sortField]
const bVal = b[this.sortField]
const order = this.sortOrder === 'asc' ? 1 : -1
return aVal > bVal ? order : -order
})
const start = (this.page - 1) * this.perPage
return result.slice(start, start + this.perPage)
},
async fetchData() {
this.loading = true
try {
const res = await fetch('/api/data')
this.data = await res.json()
} finally {
this.loading = false
}
},
sort(field) {
if (this.sortField === field) {
this.sortOrder = this.sortOrder === 'asc' ? 'desc' : 'asc'
} else {
this.sortField = field
this.sortOrder = 'asc'
}
},
goToPage(page) {
this.page = Math.max(1, Math.min(page, this.totalPages))
}
})
搜索状态 #
javascript
const searchState = Alpine.reactive({
query: '',
results: [],
loading: false,
selectedIndex: -1,
async search() {
if (this.query.length < 2) {
this.results = []
return
}
this.loading = true
try {
const res = await fetch(`/api/search?q=${encodeURIComponent(this.query)}`)
this.results = await res.json()
this.selectedIndex = -1
} finally {
this.loading = false
}
},
selectNext() {
if (this.selectedIndex < this.results.length - 1) {
this.selectedIndex++
}
},
selectPrevious() {
if (this.selectedIndex > 0) {
this.selectedIndex--
}
},
selectCurrent() {
if (this.selectedIndex >= 0) {
const result = this.results[this.selectedIndex]
console.log('Selected:', result)
this.results = []
}
}
})
状态机 #
javascript
const createStateMachine = (states, initial) => {
return Alpine.reactive({
current: initial,
states,
can(transition) {
return this.states[this.current]?.transitions?.includes(transition)
},
transition(name) {
if (this.can(name)) {
this.current = name
return true
}
return false
},
is(state) {
return this.current === state
}
})
}
const orderState = createStateMachine({
pending: { transitions: ['confirmed', 'cancelled'] },
confirmed: { transitions: ['shipped', 'cancelled'] },
shipped: { transitions: ['delivered'] },
delivered: { transitions: [] },
cancelled: { transitions: [] }
}, 'pending')
配合 Alpine.effect #
自动响应变化 #
javascript
const state = Alpine.reactive({
count: 0
})
Alpine.effect(() => {
console.log('Count changed:', state.count)
document.title = `Count: ${state.count}`
})
state.count++
清理副作用 #
javascript
const state = Alpine.reactive({ count: 0 })
const dispose = Alpine.effect(() => {
console.log(state.count)
})
dispose()
嵌套 effect #
javascript
const state = Alpine.reactive({
user: { name: 'John' },
settings: { theme: 'light' }
})
Alpine.effect(() => {
console.log('User:', state.user.name)
Alpine.effect(() => {
console.log('Theme:', state.settings.theme)
})
})
与组件集成 #
通过 x-data 引用 #
html
<div x-data="{
get state() { return externalState }
}">
<span x-text="state.count"></span>
</div>
<script>
const externalState = Alpine.reactive({
count: 0
})
</script>
通过 Alpine.data #
javascript
const sharedState = Alpine.reactive({
items: []
})
Alpine.data('listComponent', () => ({
get items() {
return sharedState.items
},
addItem(item) {
sharedState.items.push(item)
}
}))
最佳实践 #
1. 模块化状态 #
javascript
const createUserState = () => Alpine.reactive({
user: null,
loading: false,
async fetch(id) {
this.loading = true
this.user = await fetch(`/api/users/${id}`).then(r => r.json())
this.loading = false
}
})
const userState = createUserState()
2. 封装操作 #
javascript
const state = Alpine.reactive({
_items: [],
get items() {
return this._items
},
addItem(item) {
this._items = [...this._items, item]
},
removeItem(id) {
this._items = this._items.filter(item => item.id !== id)
}
})
3. 类型安全 #
javascript
const createCounter = (initial = 0) => Alpine.reactive({
count: initial,
min: 0,
max: 100,
increment() {
if (this.count < this.max) this.count++
},
decrement() {
if (this.count > this.min) this.count--
},
reset() {
this.count = initial
}
})
注意事项 #
1. 解构失去响应性 #
javascript
const state = Alpine.reactive({ count: 0 })
const { count } = state
count++
解决:
javascript
const state = Alpine.reactive({ count: 0 })
state.count++
2. 替换整个对象 #
javascript
const state = Alpine.reactive({ count: 0 })
state = { count: 1 }
解决:
javascript
const state = Alpine.reactive({ count: 0 })
Object.assign(state, { count: 1 })
3. this 绑定 #
javascript
const state = Alpine.reactive({
count: 0,
increment() {
this.count++
}
})
const fn = state.increment
fn()
解决:
javascript
const fn = state.increment.bind(state)
小结 #
Alpine.reactive 要点:
- 创建独立的响应式对象
- 可在任何 JavaScript 代码中使用
- 配合
Alpine.effect实现响应式副作用 - 适合模块化状态管理
- 注意解构和替换对象的问题
下一章,我们将学习组件化开发。
最后更新:2026-03-28