Handlebars 简介

Handlebars 预编译与运行时:生产环境的高效用法

了解 Handlebars 的预编译机制,学会使用 handlebars-runtime 减少浏览器端开销,提升模板渲染性能。

🎯 引言

学会这篇文章,你将了解 Handlebars 的 预编译 机制。在前端项目中,每次都把模板字符串传给浏览器再编译会很慢。预编译可以提前把模板编译好,浏览器只需要加载轻量的运行时,渲染速度更快,也更适合生产环境。


🧱 运行时与完整版的区别

Handlebars 有两种使用方式:

方式特点适用场景
完整版(handlebars)包含编译和渲染功能Node.js 后端、开发环境
运行时版(handlebars/runtime)只包含渲染功能,体积小浏览器前端、生产环境

完整版可以在运行时把模板字符串编译成函数;运行时版只能执行已经编译好的模板函数。


✨ 为什么需要预编译

在浏览器里使用完整版 Handlebars,流程是:

  1. 加载完整版 JS(体积大)。
  2. 把模板字符串传给 Handlebars.compile()
  3. 得到渲染函数,再传入数据得到 HTML。

每一步都在浏览器里执行,会增加页面加载和运行时间。

预编译的流程是:

  1. 在构建阶段用 Node.js 把模板编译成 JS 函数。
  2. 浏览器只加载运行时版(体积小很多)。
  3. 直接调用编译好的函数渲染。
预编译就像是“提前把菜切好”,浏览器上桌时只需要简单加热,速度快、体验好。

💡 使用命令行预编译

Handlebars 提供了命令行工具,可以把 .hbs 文件预编译成 JS。

安装

npm install handlebars@4.7.9 --save-dev

预编译单个文件

npx handlebars src/templates/user.hbs -f dist/templates/user.js

-f 指定输出文件路径。编译后的文件是一个 AMD/CommonJS/全局变量兼容的模块。

预编译整个目录

npx handlebars src/templates -f dist/templates/all.js

这样会把 src/templates 下所有 .hbs 文件打包到一个 JS 文件里。


🛠 浏览器里怎么用预编译模板

假设你已经用命令行把 user.hbs 预编译成了 dist/templates/user.js。现在要在浏览器里使用它,只需要做三件事:

  1. 引入运行时:加载体积很小的 handlebars.runtime.min.js
  2. 引入预编译模板:加载 dist/templates/user.js
  3. 传入数据渲染:调用 Handlebars.templates.user(数据) 得到 HTML。
index.html
<!-- 1. 引入 Handlebars 运行时(只负责渲染,体积小) -->
<script src="node_modules/handlebars/dist/handlebars.runtime.min.js"></script>

<!-- 2. 引入预编译好的模板文件 -->
<script src="dist/templates/user.js"></script>

<!-- 3. 传入数据,得到 HTML -->
<script>
    var html = Handlebars.templates.user({ name: '小明', age: 18 });
    document.getElementById('app').innerHTML = html;
</script>
Handlebars.templates.user 这个名字对应你预编译时的文件名 user.hbs。如果文件叫 card.hbs,那么调用方式就是 Handlebars.templates.card(数据)
浏览器里一定要引入 handlebars.runtime.min.js,而不是完整版 handlebars.min.js。完整版会把编译功能也带进来,体积更大,预编译就没意义了。

⚡ 不想用命令行,怎么在 Node.js 里预编译

如果你希望用 Node.js 脚本代替命令行,可以这样做:

build.js
const fs = require('fs');
const Handlebars = require('handlebars');

// 1. 读取模板文件内容
const source = fs.readFileSync('./src/templates/user.hbs', 'utf-8');

// 2. 预编译,得到中间描述对象
const spec = Handlebars.precompile(source);

// 3. 保存到 JS 文件
fs.writeFileSync('./dist/templates/user.js', `module.exports = ${spec};`);

预编译得到的 spec 还不能直接调用,需要先用 Handlebars.template() 包装一下:

index.js
const Handlebars = require('handlebars/runtime');
const spec = require('./dist/templates/user.js');

// 把预编译结果包装成可执行的模板函数
const template = Handlebars.template(spec);

// 传入数据渲染
const html = template({ name: '小明', age: 18 });
console.log(html);
对初学者来说,优先使用命令行预编译更简单。上面的 Node.js 脚本方式适合需要把预编译集成到自动化流程中的场景。

🧰 与构建工具结合

实际项目中,Handlebars 预编译通常会交给构建工具处理:

  • Webpack:使用 handlebars-loader
  • Vite:使用 vite-plugin-handlebars
  • Rollup:使用 rollup-plugin-handlebars

这些插件会自动把 .hbs 文件预编译好,你只需正常 import 使用。

本课程只讲解预编译的核心概念和手动用法。具体项目里,优先使用构建工具插件来自动化这个过程。

🧾 小节总结

  • Handlebars 完整版包含编译和渲染,运行时版只包含渲染。
  • 预编译可以在构建阶段完成,浏览器只需要加载运行时版,性能更好。
  • 使用命令行 handlebars 可以把 .hbs 文件预编译成 JS。
  • 浏览器端通过 Handlebars.templates.模板名 调用预编译模板。

❓ 知识问答

Q1:预编译后的模板还能修改吗?

A:不能。预编译结果是编译好的函数,模板内容需要在源 .hbs 文件里修改后重新预编译。

Q2:开发环境也需要预编译吗?

A:可以预编译,也可以直接用完整版实时编译。开发环境更注重方便调试,生产环境建议预编译。

Q3:浏览器端加载了完整版 Handlebars,还能用预编译模板吗?

A:可以,但没必要。完整版体积更大,会浪费流量和加载时间。

Q4:Handlebars 预编译和 Vue/React 的编译有什么区别?

A:Handlebars 预编译是把模板字符串变成渲染函数;Vue/React 的编译会更进一步,生成针对各自框架运行时的渲染代码。思想上类似,但产物不同。


🧪 小练习

请完成以下步骤:

  1. 创建一个 src/templates/card.hbs 文件,内容如下:
src/templates/card.hbs
<div class="card">
    <h3>{{title}}</h3>
    <p>{{desc}}</p>
</div>
  1. 执行预编译命令:
# 请在这里输入预编译命令
  1. 编写 index.html,在浏览器中使用运行时版加载并渲染该模板。

🎉 恭喜你完成了 Handlebars 课程的学习!你已经掌握了从基础语法、内置函数、模板复用、自定义 Helper 到预编译的完整知识体系,可以在项目中灵活使用 Handlebars 了。