x-data 数据声明 #

什么是 x-data? #

x-data 是 Alpine.js 最核心的指令,用于声明一个组件的响应式数据作用域。所有 Alpine.js 组件都必须以 x-data 开始。

基本语法 #

html
<div x-data="{ 属性名: 值, 方法名() { ... } }">
    <!-- 组件内容 -->
</div>

基本用法 #

声明简单数据 #

html
<div x-data="{ message: 'Hello Alpine!' }">
    <p x-text="message"></p>
</div>

声明多个属性 #

html
<div x-data="{ 
    name: 'John',
    age: 25,
    city: 'Beijing'
}">
    <p>姓名: <span x-text="name"></span></p>
    <p>年龄: <span x-text="age"></span></p>
    <p>城市: <span x-text="city"></span></p>
</div>

声明方法 #

html
<div x-data="{ 
    count: 0,
    increment() { this.count++ },
    decrement() { this.count-- },
    reset() { this.count = 0 }
}">
    <p>计数: <span x-text="count"></span></p>
    <button @click="increment()">增加</button>
    <button @click="decrement()">减少</button>
    <button @click="reset()">重置</button>
</div>

声明计算属性 #

使用 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="{
    string: 'Hello',
    number: 42,
    boolean: true,
    null: null,
    undefined: undefined
}">
</div>

对象类型 #

html
<div x-data="{
    user: {
        name: 'John',
        email: 'john@example.com',
        address: {
            city: 'Beijing',
            country: 'China'
        }
    }
}">
    <p x-text="user.name"></p>
    <p x-text="user.address.city"></p>
</div>

数组类型 #

html
<div x-data="{
    items: ['Apple', 'Banana', 'Orange'],
    users: [
        { id: 1, name: 'John' },
        { id: 2, name: 'Jane' }
    ]
}">
    <template x-for="item in items">
        <p x-text="item"></p>
    </template>
</div>

使用函数初始化 #

内联函数 #

html
<div x-data="{ 
    count: 0,
    items: [],
    init() {
        this.items = ['a', 'b', 'c']
    }
}">
</div>

外部函数 #

html
<div x-data="userComponent()">
    <p x-text="name"></p>
</div>

<script>
function userComponent() {
    return {
        name: 'John',
        email: 'john@example.com',
        greet() {
            alert(`Hello, ${this.name}!`)
        }
    }
}
</script>

带参数的函数 #

html
<div x-data="counter(10)">
    <span x-text="count"></span>
    <button @click="increment()">+</button>
</div>

<script>
function counter(initialValue) {
    return {
        count: initialValue,
        increment() {
            this.count++
        }
    }
}
</script>

Alpine.data() 注册组件 #

注册全局组件 #

javascript
document.addEventListener('alpine:init', () => {
    Alpine.data('dropdown', () => ({
        open: false,
        toggle() {
            this.open = !this.open
        },
        close() {
            this.open = false
        }
    }))
})

使用注册的组件 #

html
<div x-data="dropdown">
    <button @click="toggle()">切换</button>
    <div x-show="open" @click.away="close()">
        下拉内容
    </div>
</div>

带参数的组件 #

javascript
Alpine.data('counter', (initial = 0) => ({
    count: initial,
    increment() { this.count++ },
    decrement() { this.count-- }
}))
html
<div x-data="counter(100)">
    <span x-text="count"></span>
</div>

作用域与继承 #

作用域范围 #

x-data 创建一个作用域,子元素可以访问父作用域的数据:

html
<div x-data="{ message: 'Hello' }">
    <p x-text="message"></p>
    
    <div>
        <p x-text="message"></p>
    </div>
</div>

作用域覆盖 #

x-data 可以覆盖父作用域的同名属性:

html
<div x-data="{ name: 'Parent' }">
    <p x-text="name"></p>
    
    <div x-data="{ name: 'Child' }">
        <p x-text="name"></p>
    </div>
</div>

作用域隔离 #

独立的 x-data 组件之间互不影响:

html
<div x-data="{ count: 0 }">
    <button @click="count++">计数: <span x-text="count"></span></button>
</div>

<div x-data="{ count: 100 }">
    <button @click="count++">计数: <span x-text="count"></span></button>
</div>

访问 this #

在方法中,this 指向当前组件的数据对象:

html
<div x-data="{
    name: 'John',
    greet() {
        console.log(this.name)
    },
    updateName(newName) {
        this.name = newName
    }
}">
    <button @click="greet()">问候</button>
    <button @click="updateName('Jane')">改名</button>
</div>

箭头函数注意事项 #

箭头函数没有自己的 this,在箭头函数中访问数据需要使用普通函数:

javascript
Alpine.data('example', () => ({
    count: 0,
    
    increment: () => {
        this.count++
    },
    
    decrement() {
        this.count--
    }
}))

推荐使用普通函数:

javascript
Alpine.data('example', function() {
    return {
        count: 0,
        increment() {
            this.count++
        }
    }
})

实用示例 #

表单组件 #

html
<form x-data="{
    form: {
        name: '',
        email: '',
        message: ''
    },
    errors: {},
    submit() {
        this.errors = {}
        if (!this.form.name) this.errors.name = '请输入姓名'
        if (!this.form.email) this.errors.email = '请输入邮箱'
        if (Object.keys(this.errors).length === 0) {
            console.log('提交:', this.form)
        }
    }
}" @submit.prevent="submit()">
    <div>
        <input x-model="form.name" placeholder="姓名">
        <span x-show="errors.name" x-text="errors.name" style="color: red"></span>
    </div>
    <div>
        <input x-model="form.email" placeholder="邮箱">
        <span x-show="errors.email" x-text="errors.email" style="color: red"></span>
    </div>
    <button type="submit">提交</button>
</form>

标签页组件 #

html
<div x-data="{
    tabs: ['首页', '关于', '联系'],
    activeTab: '首页',
    setActive(tab) {
        this.activeTab = tab
    }
}">
    <div class="tabs">
        <template x-for="tab in tabs">
            <button 
                @click="setActive(tab)"
                :class="{ active: activeTab === tab }"
                x-text="tab"
            ></button>
        </template>
    </div>
    <div class="content">
        <div x-show="activeTab === '首页'">首页内容</div>
        <div x-show="activeTab === '关于'">关于内容</div>
        <div x-show="activeTab === '联系'">联系内容</div>
    </div>
</div>

手风琴组件 #

html
<div x-data="{
    items: [
        { title: '标题1', content: '内容1', open: false },
        { title: '标题2', content: '内容2', open: false },
        { title: '标题3', content: '内容3', open: false }
    ],
    toggle(index) {
        this.items[index].open = !this.items[index].open
    }
}">
    <template x-for="(item, index) in items">
        <div class="accordion-item">
            <button @click="toggle(index)" x-text="item.title"></button>
            <div x-show="item.open" x-text="item.content"></div>
        </div>
    </template>
</div>

最佳实践 #

1. 保持数据扁平化 #

javascript
Alpine.data('user', () => ({
    user: {
        profile: {
            name: '',
            email: ''
        }
    }
}))

2. 分离复杂逻辑 #

javascript
Alpine.data('form', () => ({
    data: {},
    validators: {},
    errors: {},
    
    validate(field) {
        const validator = this.validators[field]
        if (validator) {
            this.errors[field] = validator(this.data[field])
        }
    },
    
    validateAll() {
        Object.keys(this.validators).forEach(field => this.validate(field))
        return Object.values(this.errors).every(e => !e)
    }
}))

3. 使用初始化钩子 #

javascript
Alpine.data('asyncData', () => ({
    loading: false,
    data: null,
    error: null,
    
    async init() {
        await this.fetch()
    },
    
    async fetch() {
        this.loading = true
        try {
            const res = await fetch('/api/data')
            this.data = await res.json()
        } catch (e) {
            this.error = e.message
        } finally {
            this.loading = false
        }
    }
}))

小结 #

x-data 是 Alpine.js 的核心指令:

  • 定义组件的响应式数据作用域
  • 支持属性、方法、计算属性
  • 可以使用函数或 Alpine.data() 初始化
  • 子元素继承父作用域
  • 注意箭头函数的 this 问题

下一章,我们将学习 x-show 指令控制元素的显示和隐藏。

最后更新:2026-03-28