x-model 双向绑定 #
什么是 x-model? #
x-model 指令用于在表单元素和数据之间创建双向绑定。当用户修改表单时,数据会自动更新;当数据变化时,表单也会自动更新。
基本语法 #
html
<input x-model="属性名">
基本用法 #
文本输入框 #
html
<div x-data="{ name: '' }">
<input type="text" x-model="name" placeholder="输入你的名字">
<p>Hello, <span x-text="name"></span>!</p>
</div>
多行文本框 #
html
<div x-data="{ message: '' }">
<textarea x-model="message" placeholder="输入消息"></textarea>
<p>消息: <span x-text="message"></span></p>
</div>
表单元素类型 #
复选框(单个) #
html
<div x-data="{ agreed: false }">
<label>
<input type="checkbox" x-model="agreed">
同意条款
</label>
<p x-text="agreed ? '已同意' : '未同意'"></p>
</div>
复选框(多个) #
html
<div x-data="{
selectedFruits: [],
fruits: ['Apple', 'Banana', 'Orange']
}">
<template x-for="fruit in fruits">
<label>
<input
type="checkbox"
:value="fruit"
x-model="selectedFruits"
>
<span x-text="fruit"></span>
</label>
</template>
<p>已选择: <span x-text="selectedFruits.join(', ')"></span></p>
</div>
单选按钮 #
html
<div x-data="{ color: 'red' }">
<label>
<input type="radio" value="red" x-model="color"> 红色
</label>
<label>
<input type="radio" value="green" x-model="color"> 绿色
</label>
<label>
<input type="radio" value="blue" x-model="color"> 蓝色
</label>
<p>选择的颜色: <span x-text="color"></span></p>
</div>
下拉选择(单选) #
html
<div x-data="{
selectedCity: '',
cities: ['Beijing', 'Shanghai', 'Guangzhou']
}">
<select x-model="selectedCity">
<option value="">请选择城市</option>
<template x-for="city in cities">
<option :value="city" x-text="city"></option>
</template>
</select>
<p>选择的城市: <span x-text="selectedCity"></span></p>
</div>
下拉选择(多选) #
html
<div x-data="{
selectedCities: [],
cities: ['Beijing', 'Shanghai', 'Guangzhou', 'Shenzhen']
}">
<select x-model="selectedCities" multiple>
<template x-for="city in cities">
<option :value="city" x-text="city"></option>
</template>
</select>
<p>选择的城市: <span x-text="selectedCities.join(', ')"></span></p>
</div>
修饰符 #
.lazy #
默认情况下,x-model 在 input 事件时同步。使用 .lazy 修饰符改为在 change 事件时同步:
html
<div x-data="{ name: '' }">
<input type="text" x-model.lazy="name" placeholder="失去焦点后更新">
<p x-text="name"></p>
</div>
.number #
自动将输入值转换为数字:
html
<div x-data="{ age: '' }">
<input type="number" x-model.number="age" placeholder="输入年龄">
<p>类型: <span x-text="typeof age"></span></p>
<p>值: <span x-text="age"></span></p>
</div>
.debounce #
防抖处理,延迟更新:
html
<div x-data="{ search: '' }">
<input
type="text"
x-model.debounce.500ms="search"
placeholder="搜索..."
>
<p>搜索词: <span x-text="search"></span></p>
</div>
.throttle #
节流处理,限制更新频率:
html
<div x-data="{ scroll: 0 }">
<input
type="range"
x-model.throttle.100ms="scroll"
min="0"
max="100"
>
<p>值: <span x-text="scroll"></span></p>
</div>
组合修饰符 #
html
<input x-model.lazy.number="value">
<input x-model.debounce.300ms.number="value">
自定义值绑定 #
复选框自定义值 #
html
<div x-data="{ status: 'off' }">
<label>
<input
type="checkbox"
x-model="status"
true-value="on"
false-value="off"
>
开关状态
</label>
<p>状态: <span x-text="status"></span></p>
</div>
单选按钮自定义值 #
html
<div x-data="{ answer: null }">
<label>
<input
type="radio"
x-model="answer"
:value="{ text: 'Yes', code: 1 }"
>
是
</label>
<label>
<input
type="radio"
x-model="answer"
:value="{ text: 'No', code: 0 }"
>
否
</label>
<p x-show="answer">答案: <span x-text="answer?.text"></span></p>
</div>
实用示例 #
登录表单 #
html
<div x-data="{
form: {
email: '',
password: '',
remember: false
},
errors: {},
submit() {
this.errors = {}
if (!this.form.email) this.errors.email = '请输入邮箱'
if (!this.form.password) this.errors.password = '请输入密码'
if (Object.keys(this.errors).length === 0) {
console.log('提交:', this.form)
}
}
}" @submit.prevent="submit()">
<div>
<label>邮箱</label>
<input type="email" x-model="form.email">
<span x-show="errors.email" x-text="errors.email" style="color: red"></span>
</div>
<div>
<label>密码</label>
<input type="password" x-model="form.password">
<span x-show="errors.password" x-text="errors.password" style="color: red"></span>
</div>
<div>
<label>
<input type="checkbox" x-model="form.remember">
记住我
</label>
</div>
<button type="submit">登录</button>
</div>
搜索表单 #
html
<div x-data="{
search: '',
category: 'all',
results: [],
async searchItems() {
if (this.search.length < 2) {
this.results = []
return
}
const res = await fetch(`/api/search?q=${this.search}&cat=${this.category}`)
this.results = await res.json()
}
}">
<input
type="text"
x-model.debounce.300ms="search"
@input="searchItems()"
placeholder="搜索..."
>
<select x-model="category" @change="searchItems()">
<option value="all">全部分类</option>
<option value="products">产品</option>
<option value="articles">文章</option>
</select>
<ul>
<template x-for="result in results" :key="result.id">
<li x-text="result.title"></li>
</template>
</ul>
</div>
设置表单 #
html
<div x-data="{
settings: {
theme: 'light',
language: 'zh-CN',
notifications: {
email: true,
push: false,
sms: false
},
privacy: {
profile: 'public',
activity: 'friends'
}
},
save() {
console.log('保存设置:', this.settings)
}
}">
<h3>外观设置</h3>
<label>
主题:
<select x-model="settings.theme">
<option value="light">浅色</option>
<option value="dark">深色</option>
<option value="auto">自动</option>
</select>
</label>
<h3>通知设置</h3>
<label>
<input type="checkbox" x-model="settings.notifications.email">
邮件通知
</label>
<label>
<input type="checkbox" x-model="settings.notifications.push">
推送通知
</label>
<label>
<input type="checkbox" x-model="settings.notifications.sms">
短信通知
</label>
<h3>隐私设置</h3>
<label>
个人资料:
<select x-model="settings.privacy.profile">
<option value="public">公开</option>
<option value="friends">仅好友</option>
<option value="private">私密</option>
</select>
</label>
<button @click="save()">保存设置</button>
</div>
动态表单 #
html
<div x-data="{
fields: [
{ id: 1, label: '姓名', type: 'text', value: '' },
{ id: 2, label: '邮箱', type: 'email', value: '' },
{ id: 3, label: '电话', type: 'tel', value: '' }
],
addField() {
this.fields.push({
id: Date.now(),
label: '新字段',
type: 'text',
value: ''
})
},
removeField(id) {
this.fields = this.fields.filter(f => f.id !== id)
}
}">
<template x-for="field in fields" :key="field.id">
<div>
<label x-text="field.label"></label>
<input :type="field.type" x-model="field.value">
<button @click="removeField(field.id)">删除</button>
</div>
</template>
<button @click="addField()">添加字段</button>
<pre x-text="JSON.stringify(fields, null, 2)"></pre>
</div>
文件上传 #
html
<div x-data="{
files: [],
handleFiles(e) {
this.files = Array.from(e.target.files).map(file => ({
name: file.name,
size: (file.size / 1024).toFixed(2) + ' KB',
type: file.type
}))
}
}">
<input
type="file"
multiple
@change="handleFiles($event)"
>
<ul>
<template x-for="file in files" :key="file.name">
<li>
<span x-text="file.name"></span>
<small x-text="file.size"></small>
</li>
</template>
</ul>
</div>
范围滑块 #
html
<div x-data="{
price: {
min: 0,
max: 1000
},
value: [0, 500]
}">
<div>
<label>最低价格: <span x-text="value[0]"></span></label>
<input
type="range"
x-model.number="value[0]"
min="0"
max="1000"
step="10"
>
</div>
<div>
<label>最高价格: <span x-text="value[1]"></span></label>
<input
type="range"
x-model.number="value[1]"
min="0"
max="1000"
step="10"
>
</div>
<p>价格范围: <span x-text="`${value[0]} - ${value[1]}`"></span></p>
</div>
注意事项 #
1. IME 输入法 #
在使用中文等 IME 输入法时,x-model 可能在输入过程中频繁更新。使用 .lazy 修饰符解决:
html
<input x-model.lazy="text">
2. 表单重置 #
使用 x-model 时,表单的 reset() 方法不会重置数据。需要手动重置:
html
<div x-data="{
form: { name: '', email: '' },
reset() {
this.form = { name: '', email: '' }
}
}">
<form @reset.prevent="reset()">
<input x-model="form.name">
<input x-model="form.email">
<button type="reset">重置</button>
</form>
</div>
3. 初始值 #
x-model 会忽略表单元素的初始 value 属性:
html
<input x-model="name" value="Initial">
应该在 x-data 中设置初始值:
html
<div x-data="{ name: 'Initial' }">
<input x-model="name">
</div>
小结 #
x-model 指令要点:
- 实现表单元素与数据的双向绑定
- 支持所有表单元素类型
- 提供多种修饰符控制更新行为
- 支持自定义值绑定
- 注意 IME 输入法问题
下一章,我们将学习 x-text 和 x-html 指令进行内容绑定。
最后更新:2026-03-28