Handlebars基础 #
一、Handlebars概述 #
Handlebars是一种简洁的模板语言,Ember.js使用Handlebars作为其模板引擎。Handlebars模板看起来像普通的HTML,但包含额外的语法来插入动态内容。
1.1 基本结构 #
handlebars
{{! 这是一个Handlebars模板}}
<div class="user-profile">
<h1>{{user.name}}</h1>
<p>{{user.email}}</p>
</div>
1.2 Ember中的Handlebars #
Ember扩展了标准Handlebars,添加了:
- 组件支持
- 动作绑定
- 路由链接
- 响应式更新
二、基本语法 #
2.1 表达式 #
使用双花括号 {{}} 输出内容:
handlebars
{{! 输出属性}}
<h1>{{title}}</h1>
{{! 输出嵌套属性}}
<p>{{user.profile.name}}</p>
{{! 输出计算属性}}
<span>{{fullName}}</span>
2.2 HTML转义 #
Handlebars默认转义HTML:
handlebars
{{! 假设 content = "<strong>粗体</strong>"}}
<p>{{content}}</p>
{{! 输出: <strong>粗体</strong> (转义显示)}}
{{! 不转义HTML}}
<p>{{{content}}}</p>
{{! 输出: 粗体 (实际渲染)}}
2.3 注释 #
handlebars
{{! 单行注释,不会出现在输出中}}
{{!--
多行注释
可以跨越多行
也不会出现在输出中
--}}
三、块表达式 #
3.1 if条件 #
handlebars
{{#if this.isLoggedIn}}
<p>欢迎回来,{{this.user.name}}!</p>
{{else}}
<p>请先登录</p>
{{/if}}
3.2 unless条件 #
handlebars
{{#unless this.isPublished}}
<span class="draft">草稿</span>
{{/unless}}
3.3 each循环 #
handlebars
<ul>
{{#each @model as |post|}}
<li>{{post.title}}</li>
{{else}}
<li>暂无文章</li>
{{/each}}
</ul>
3.4 with上下文 #
handlebars
{{#with this.user.profile as |profile|}}
<p>姓名:{{profile.name}}</p>
<p>邮箱:{{profile.email}}</p>
{{/with}}
3.5 let变量 #
handlebars
{{#let this.user.firstName this.user.lastName as |first last|}}
<p>名字:{{first}}</p>
<p>姓氏:{{last}}</p>
<p>全名:{{first}} {{last}}</p>
{{/let}}
四、内置助手 #
4.1 条件助手 #
handlebars
{{! if助手}}
{{if this.isActive "激活" "未激活"}}
{{! unless助手}}
{{unless this.isComplete "未完成" "已完成"}}
4.2 数组助手 #
handlebars
{{! 数组创建}}
{{#each (array "苹果" "香蕉" "橙子") as |fruit|}}
<p>{{fruit}}</p>
{{/each}}
4.3 对象助手 #
handlebars
{{! 对象创建}}
{{#let (hash name="张三" age=25) as |user|}}
<p>{{user.name}},{{user.age}}岁</p>
{{/let}}
4.4 concat助手 #
handlebars
{{! 字符串拼接}}
<p class="{{concat "btn-" this.size " btn-" this.type}}">
按钮
</p>
{{! 等价于}}
<p class="btn-large btn-primary">按钮</p>
4.5 get助手 #
handlebars
{{! 动态获取属性}}
{{#each (array "name" "email" "phone") as |field|}}
<p>{{field}}: {{get @user field}}</p>
{{/each}}
五、路径表达式 #
5.1 属性路径 #
handlebars
{{! 简单属性}}
{{name}}
{{! 嵌套属性}}
{{user.profile.avatar}}
{{! 数组索引}}
{{items.0}}
{{items.1.name}}
5.2 this关键字 #
handlebars
{{! 当前上下文}}
{{this.name}}
{{this.user.email}}
{{! 在块表达式中}}
{{#each this.items as |item|}}
{{! item是局部变量}}
<p>{{item.name}}</p>
{{! this指向外部上下文}}
<p>{{this.title}}</p>
{{/each}}
5.3 @符号 #
handlebars
{{! @model - 路由模型}}
<h1>{{@model.title}}</h1>
{{! @args - 组件参数}}
<p>{{@user.name}}</p>
{{! @index - 循环索引}}
{{#each @items as |item|}}
<p>{{@index}}: {{item}}</p>
{{/each}}
{{! @key - 对象键}}
{{#each-in this.user as |key value|}}
<p>{{key}}: {{value}}</p>
{{/each-in}}
六、属性绑定 #
6.1 基本属性 #
handlebars
{{! 静态属性}}
<div class="container"></div>
{{! 动态属性}}
<div class="{{this.theme}}"></div>
{{! 布尔属性}}
<input type="checkbox" checked={{this.isChecked}} />
<button disabled={{this.isDisabled}}>提交</button>
6.2 属性拼接 #
handlebars
<div class="card {{if this.isActive "active"}} {{this.size}}">
内容
</div>
6.3 style属性 #
handlebars
<div style="color: {{this.color}}; font-size: {{this.fontSize}}px">
文本内容
</div>
七、事件绑定 #
7.1 on修饰符 #
handlebars
<button type="button" {{on "click" this.handleClick}}>
点击我
</button>
<form {{on "submit" this.handleSubmit}}>
<input type="text" />
<button type="submit">提交</button>
</form>
<input {{on "input" this.handleInput}} {{on "focus" this.handleFocus}} />
7.2 事件参数 #
handlebars
<button type="button" {{on "click" (fn this.handleClick item.id)}}>
删除
</button>
javascript
// app/components/my-component.js
import Component from '@glimmer/component';
import { action } from '@ember/object';
export default class MyComponent extends Component {
@action
handleClick(id, event) {
event.preventDefault();
console.log('删除项目:', id);
}
}
7.3 事件修饰符 #
handlebars
{{! 阻止默认行为}}
<form {{on "submit" (prevent-default this.handleSubmit)}}>
{{! 阻止冒泡}}
<div {{on "click" (stop-propagation this.handleClick)}}>
{{! 只触发一次}}
<button {{on "click" (once this.handleClick)}}>
八、内置组件 #
8.1 LinkTo组件 #
handlebars
{{! 基本链接}}
<LinkTo @route="about">关于我们</LinkTo>
{{! 带参数的链接}}
<LinkTo @route="posts.show" @model={{@post.id}}>
查看文章
</LinkTo>
{{! 带查询参数}}
<LinkTo @route="posts" @query={{hash page=1 sort="date"}}>
文章列表
</LinkTo>
{{! 激活状态类}}
<LinkTo @route="home" class="nav-link">
首页
</LinkTo>
{{! 激活时自动添加 "active" 类}}
8.2 Input组件 #
handlebars
{{! 文本输入}}
<Input @value={{this.name}} placeholder="请输入姓名" />
{{! 复选框}}
<Input @type="checkbox" @checked={{this.agree}} />
{{! 单选按钮}}
<Input @type="radio" @name="gender" @value="male" @checked={{eq this.gender "male"}} />
{{! 输入事件}}
<Input @value={{this.email}} {{on "input" this.validateEmail}} />
8.3 Textarea组件 #
handlebars
<Textarea @value={{this.content}} rows="5" cols="40" placeholder="请输入内容" />
九、组件调用 #
9.1 Angle Bracket语法 #
handlebars
{{! 推荐的Angle Bracket语法}}
<UserCard @user={{@currentUser}} />
{{! 带块内容}}
<UserCard @user={{@currentUser}}>
<p>额外内容</p>
</UserCard>
9.2 传递参数 #
handlebars
{{! 字符串参数}}
<Alert @message="操作成功" />
{{! 数字参数}}
<Progress @percent={{80}} />
{{! 布尔参数}}
<Modal @visible={{true}} />
{{! 对象参数}}
<UserProfile @user={{this.currentUser}} />
{{! 动作参数}}
<Button @onClick={{this.handleSubmit}}>提交</Button>
9.3 产出内容 #
handlebars
{{! 父组件}}
<Card @title="用户信息">
<p>姓名:{{@user.name}}</p>
<p>邮箱:{{@user.email}}</p>
</Card>
{{! Card组件模板}}
<div class="card">
<h2>{{@title}}</h2>
<div class="card-body">
{{yield}}
</div>
</div>
9.4 块参数 #
handlebars
{{! 父组件}}
<List @items={{@users} as |user|>
<li>{{user.name}}</li>
</List>
{{! List组件模板}}
<ul>
{{#each @items as |item|}}
{{yield item}}
{{/each}}
</ul>
十、模板调试 #
10.1 log助手 #
handlebars
{{! 控制台输出}}
{{log "当前用户:" @user}}
{{log "模型数据:" @model}}
10.2 debugger助手 #
handlebars
{{! 调试断点}}
{{debugger}}
{{! 带条件}}
{{#if this.hasError}}
{{debugger}}
{{/if}}
十一、模板最佳实践 #
11.1 保持模板简洁 #
handlebars
{{! 不推荐 - 复杂逻辑}}
<p>
{{#if user.isActive}}
{{#if user.hasPermission}}
{{#if user.isVerified}}
欢迎回来
{{else}}
请验证邮箱
{{/if}}
{{else}}
无权限
{{/if}}
{{else}}
账户未激活
{{/if}}
</p>
{{! 推荐 - 使用计算属性}}
<p>{{this.userStatus}}</p>
11.2 使用组件封装 #
handlebars
{{! 不推荐 - 重复代码}}
<div class="user-card">
<img src="{{user.avatar}}" alt="{{user.name}}" />
<h3>{{user.name}}</h3>
<p>{{user.email}}</p>
</div>
{{! 推荐 - 组件封装}}
<UserCard @user={{user}} />
11.3 合理使用块表达式 #
handlebars
{{! 推荐 - 使用块参数}}
{{#each @users as |user|}}
<UserRow @user={{user}} />
{{/each}}
{{! 推荐 - 使用let命名}}
{{#let (format-date @post.createdAt) as |formattedDate|}}
<p>发布于:{{formattedDate}}</p>
<p>最后修改:{{formattedDate}}</p>
{{/let}}
十二、总结 #
Handlebars是Ember模板的核心:
| 语法 | 用途 |
|---|---|
{{expression}} |
输出内容 |
{{#block}}{{/block}} |
块表达式 |
{{if condition}} |
条件渲染 |
{{#each items}} |
循环渲染 |
{{on "event" action}} |
事件绑定 |
<Component /> |
组件调用 |
{{yield}} |
产出内容 |
掌握Handlebars语法是Ember开发的基础,结合Ember的响应式系统,可以构建强大的用户界面。
最后更新:2026-03-28