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