x-if 条件渲染 #

什么是 x-if? #

x-if 指令根据条件决定是否将元素渲染到 DOM 中。与 x-show 不同,x-if 会真正地添加或移除 DOM 元素,而不是简单地切换显示状态。

基本语法 #

x-if 必须在 <template> 标签上使用:

html
<template x-if="条件表达式">
    <div>条件内容</div>
</template>

基本用法 #

简单条件 #

html
<div x-data="{ show: false }">
    <button @click="show = !show">切换</button>
    <template x-if="show">
        <div>这段内容会被添加或移除</div>
    </template>
</div>

条件分支 #

html
<div x-data="{ score: 75 }">
    <input type="number" x-model="score">
    
    <template x-if="score >= 90">
        <p style="color: green">优秀</p>
    </template>
    
    <template x-if="score >= 60 && score < 90">
        <p style="color: blue">及格</p>
    </template>
    
    <template x-if="score < 60">
        <p style="color: red">不及格</p>
    </template>
</div>

使用函数 #

html
<div x-data="{
    user: null,
    isLoggedIn() {
        return this.user !== null
    }
}">
    <template x-if="isLoggedIn()">
        <div>欢迎, <span x-text="user.name"></span></div>
    </template>
    
    <template x-if="!isLoggedIn()">
        <div>请登录</div>
    </template>
</div>

为什么需要 template 标签? #

x-if 必须在 <template> 标签上使用,原因如下:

  1. DOM 规范<template> 标签的内容默认不会被渲染
  2. 单一根元素:确保条件内容有明确的边界
  3. 性能优化:Alpine.js 可以高效地管理模板内容的添加和移除
html
<template x-if="condition">
    <div class="card">
        <h2>标题</h2>
        <p>内容</p>
    </div>
</template>

x-if vs x-show #

关键区别 #

特性 x-if x-show
DOM 操作 添加/移除元素 切换 display
初始渲染 条件为真才渲染 始终渲染
切换成本 高(DOM 操作) 低(CSS 切换)
内存占用 条件为假时不占用 始终占用
事件监听器 移除时销毁 保留
生命周期 有创建/销毁过程

使用 x-if 的场景 #

html
<div x-data="{ showDetails: false }">
    <button @click="showDetails = true">显示详情</button>
    
    <template x-if="showDetails">
        <div>
            大量内容,很少需要显示
            <img src="large-image.jpg" alt="大图片">
            <video src="video.mp4"></video>
        </div>
    </template>
</div>

使用 x-show 的场景 #

html
<div x-data="{ active: false }">
    <button @click="active = !active">切换</button>
    
    <div x-show="active">
        频繁切换的内容
    </div>
</div>

高级用法 #

嵌套条件 #

html
<div x-data="{ 
    user: { role: 'admin', status: 'active' }
}">
    <template x-if="user">
        <div>
            <template x-if="user.role === 'admin'">
                <div>
                    <template x-if="user.status === 'active'">
                        <p>活跃管理员</p>
                    </template>
                    <template x-if="user.status === 'inactive'">
                        <p>非活跃管理员</p>
                    </template>
                </div>
            </template>
            <template x-if="user.role === 'user'">
                <p>普通用户</p>
            </template>
        </div>
    </template>
</div>

条件渲染列表 #

html
<div x-data="{
    items: [
        { id: 1, name: 'Apple', available: true },
        { id: 2, name: 'Banana', available: false },
        { id: 3, name: 'Orange', available: true }
    ]
}">
    <template x-for="item in items">
        <div>
            <template x-if="item.available">
                <span style="color: green" x-text="item.name + ' (可购买)'"></span>
            </template>
            <template x-if="!item.available">
                <span style="color: gray" x-text="item.name + ' (缺货)'"></span>
            </template>
        </div>
    </template>
</div>

异步内容 #

html
<div x-data="{
    loading: true,
    data: null,
    error: null,
    async fetchData() {
        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-init="fetchData()">
    <template x-if="loading">
        <div class="loading">加载中...</div>
    </template>
    
    <template x-if="error">
        <div class="error" x-text="'错误: ' + error"></div>
    </template>
    
    <template x-if="data && !loading">
        <div class="content">
            <template x-for="item in data">
                <p x-text="item.name"></p>
            </template>
        </div>
    </template>
</div>

实用示例 #

登录状态切换 #

html
<div x-data="{
    user: null,
    form: { email: '', password: '' },
    async login() {
        const res = await fetch('/api/login', {
            method: 'POST',
            body: JSON.stringify(this.form)
        })
        this.user = await res.json()
    },
    logout() {
        this.user = null
    }
}">
    <template x-if="!user">
        <form @submit.prevent="login()">
            <input type="email" x-model="form.email" placeholder="邮箱">
            <input type="password" x-model="form.password" placeholder="密码">
            <button type="submit">登录</button>
        </form>
    </template>
    
    <template x-if="user">
        <div>
            <span x-text="'欢迎, ' + user.name"></span>
            <button @click="logout()">退出</button>
        </div>
    </template>
</div>

权限控制 #

html
<div x-data="{
    user: {
        role: 'admin',
        permissions: ['read', 'write', 'delete']
    },
    can(permission) {
        return this.user.permissions.includes(permission)
    }
}">
    <template x-if="can('read')">
        <button>查看</button>
    </template>
    
    <template x-if="can('write')">
        <button>编辑</button>
    </template>
    
    <template x-if="can('delete')">
        <button>删除</button>
    </template>
</div>

表单步骤 #

html
<div x-data="{
    step: 1,
    form: {
        name: '',
        email: '',
        address: ''
    },
    nextStep() {
        if (this.step < 3) this.step++
    },
    prevStep() {
        if (this.step > 1) this.step--
    }
}">
    <template x-if="step === 1">
        <div>
            <h2>步骤 1: 基本信息</h2>
            <input x-model="form.name" placeholder="姓名">
            <input x-model="form.email" placeholder="邮箱">
            <button @click="nextStep()">下一步</button>
        </div>
    </template>
    
    <template x-if="step === 2">
        <div>
            <h2>步骤 2: 地址信息</h2>
            <input x-model="form.address" placeholder="地址">
            <button @click="prevStep()">上一步</button>
            <button @click="nextStep()">下一步</button>
        </div>
    </template>
    
    <template x-if="step === 3">
        <div>
            <h2>步骤 3: 确认</h2>
            <p>姓名: <span x-text="form.name"></span></p>
            <p>邮箱: <span x-text="form.email"></span></p>
            <p>地址: <span x-text="form.address"></span></p>
            <button @click="prevStep()">上一步</button>
            <button @click="submit()">提交</button>
        </div>
    </template>
</div>

空状态处理 #

html
<div x-data="{
    items: [],
    addItem() {
        this.items.push({ id: Date.now(), name: '新项目' })
    },
    removeItem(id) {
        this.items = this.items.filter(item => item.id !== id)
    }
}">
    <button @click="addItem()">添加项目</button>
    
    <template x-if="items.length === 0">
        <div class="empty-state">
            <p>暂无数据</p>
            <p>点击上方按钮添加项目</p>
        </div>
    </template>
    
    <template x-if="items.length > 0">
        <ul>
            <template x-for="item in items">
                <li>
                    <span x-text="item.name"></span>
                    <button @click="removeItem(item.id)">删除</button>
                </li>
            </template>
        </ul>
    </template>
</div>

错误处理 #

html
<div x-data="{
    errors: {},
    hasErrors() {
        return Object.keys(this.errors).length > 0
    },
    validate(field, value) {
        if (!value) {
            this.errors[field] = '此字段必填'
        } else {
            delete this.errors[field]
        }
    }
}">
    <input 
        @blur="validate('name', $el.value)"
        placeholder="姓名"
    >
    
    <template x-if="errors.name">
        <p class="error" x-text="errors.name"></p>
    </template>
    
    <template x-if="hasErrors()">
        <p class="error-summary">请修正表单中的错误</p>
    </template>
</div>

过渡动画 #

x-if 也支持过渡动画:

html
<div x-data="{ show: false }">
    <button @click="show = !show">切换</button>
    
    <template x-if="show">
        <div x-transition>
            带过渡动画的内容
        </div>
    </template>
</div>

自定义过渡 #

html
<template x-if="show">
    <div
        x-transition:enter="transition ease-out duration-300"
        x-transition:enter-start="opacity-0 transform scale-90"
        x-transition:enter-end="opacity-100 transform scale-100"
        x-transition:leave="transition ease-in duration-200"
        x-transition:leave-start="opacity-100 transform scale-100"
        x-transition:leave-end="opacity-0 transform scale-90"
    >
        内容
    </div>
</template>

注意事项 #

1. 必须使用 template #

html
<div x-if="condition"></div>

错误!必须使用 <template>

html
<template x-if="condition">
    <div></div>
</template>

2. 单一根元素 #

<template> 内部应该只有一个根元素:

html
<template x-if="condition">
    <div>元素1</div>
    <div>元素2</div>
</template>

推荐使用包装元素:

html
<template x-if="condition">
    <div>
        <div>元素1</div>
        <div>元素2</div>
    </div>
</template>

3. 键值优化 #

在循环中使用 x-if 时,确保有正确的 :key

html
<template x-for="item in items" :key="item.id">
    <template x-if="item.visible">
        <div x-text="item.name"></div>
    </template>
</template>

小结 #

x-if 指令要点:

  • 必须在 <template> 标签上使用
  • 真正地添加或移除 DOM 元素
  • 适合条件很少改变的场景
  • 适合包含大量内容的条件渲染
  • 支持过渡动画

下一章,我们将学习 x-for 指令进行列表渲染。

最后更新:2026-03-28