news 2026/9/11 0:26:45

Budibase 字符串模板引擎全解析:基于 Handlebars 的跨端模板系统实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Budibase 字符串模板引擎全解析:基于 Handlebars 的跨端模板系统实战指南

Budibase 字符串模板引擎全解析:基于 Handlebars 的跨端模板系统实战指南

【免费下载链接】budibaseAI agents, automations and apps that run your operations. Model agnostic.项目地址: https://gitcode.com/GitHub_Trending/bu/budibase

@budibase/string-templates是 Budibase 平台中统一负责字符串模板渲染的核心包,Builder 设计器、客户端页面与服务端自动化共用同一套模板能力。本文以该包的 README 为主体,结合源码实现,系统讲解其模板语法、九大类 Helper、日期格式化、核心 API 与内部处理管线,帮助你直接在自己的模板字符串中运用{{...}}表达式、条件块与数组迭代,并理解每一次渲染背后发生的预处理、Helper 注册与后处理过程。

一、包定位:一套模板,三端共用

Budibase 是一个低代码应用构建平台,其页面、自动化、邮件通知等场景大量依赖"模板字符串 + 上下文数据"的渲染方式。string-templates包正是为此设计的公共基础设施,位于 packages/string-templates/:

  • 模板引擎内核为 Handlebars(Mustache 的逻辑增强扩展),具备{{variable}}{{#if}}{{#each}}等能力;
  • 在 Handlebars 之上通过@budibase/handlebars-helpers注册了一批扩展 Helper,覆盖数学、数组、字符串、比较、URL、正则等常见操作;
  • 包以 UMD 形式构建,既能被 Node 服务端(worker/server)引用,也能被浏览器端(builder/client)直接加载。

从 package.json 可以看到其依赖组合:handlebars4.7.9 作为引擎、@budibase/handlebars-helpers提供扩展 Helper、dayjs承担日期解析与格式化(取代了传统 moment 方案,显著降低打包体积)。

二、九大 Helper 集合:官方没有全部引入,只注册了精选子集

README 明确指出:并没有实现 handlebars-helpers 包提供的全部 Helper,只挑选了对 Budibase 场景有价值的 9 个集合。这一点在 helpers/constants.ts 中得到印证——源码中的EXTERNAL_FUNCTION_COLLECTIONS仅包含:

export const EXTERNAL_FUNCTION_COLLECTIONS = [ "math", "array", "number", "url", "string", "comparison", "object", "regex", "uuid", ]

注册逻辑在 helpers/external.ts:遍历这些集合调用helperscollection将 Helper 挂载到 Handlebars 实例,同时跳过与内置 Helper 重名的条目(如ifeachwith等,见HelperFunctionBuiltin)。

1. Math —— 数值运算

用于对数字执行逻辑运算:avgaddabsceilfloordividemultiplysubtractmoduloroundsumrandomremainder等。典型用法:

{{ add 1 2 }} -> 3 {{ avg 1 2 3 4 5 }} -> 3 {{ round 10.3 }} -> 10 {{ modulo 10 5 }} -> 0

2. Array —— 数组操作

对数组进行切片、拼接、迭代、过滤、排序。README 强调这类 Helper 在自动化中非常实用:

{{ after ['a','b','c','d'] 2 }} -> c,d {{ before ['a','b','c','d'] 3 }} -> a,b {{ first [1,2,3,4] 2 }} -> 1,2 {{ last [1,2,3] }} -> 3 {{ join array "-" }} -> 以 - 连接成字符串 {{#forEach array}} {{name}} {{/forEach}} -> 迭代数组

forEach是典型的块级迭代 Helper,块内会暴露indextotalisFirstisLast等变量,同时@index作为 Handlebars 私有变量可用。

3. Number —— 数字展示格式化

与 Math 不同,Number 集合侧重"把数字转成适合展示的格式":bytes(字节单位)、addCommas(千分位)、toPrecision(精度)、toFixedtoExponentialtoAbbr(缩写)、phoneNumber(电话格式):

{{ addCommas 1000000 }} -> 1,000,000 {{ bytes 1386 1 }} -> 1.4 kB {{ toFixed 1.1234 2 }} -> 1.12

4. URL —— URL 处理

用于构建自动化中要请求的 URL:encodeURIescapedecodeURIstripQueryStringstripProtocolurlResolveurlParse

{{ encodeURI 'https://myurl?Hello There' }} -> https%3A%2F%2Fmyurl%3FHello%20There {{ stripQuerystring 'https://myurl/api/test?foo=bar' }} -> 'https://myurl/api/test' {{ stripProtocol 'https://myurl/api/test' }} -> '//myurl/api/test'

5. String —— 字符串构建与展示

这是日常使用最频繁的集合:appendprependcamelcasecapitalizecapitalizeAlldowncaseupcaselowercaseuppercaseellipsistruncatetrimreplaceremovesplittitleizesentencepascalcasesnakecasedashcasestartsWithoccurrences等:

{{ append 'index' '.html' }} -> index.html {{ camelcase 'foo bar baz' }} -> fooBarBaz {{ ellipsis 'foo bar baz' 7 }} -> foo bar… {{ remove 'a b a b a b' 'a ' }} -> b b b {{ uppercase 'aBcDef' }} -> ABCDEF

6. Comparison —— 条件逻辑

主要用于按条件构造字符串,是条件块{{#gte score "50"}}的主力:andornoteqisisntgtgteltltecomparecontainsdefaulthasifEvenifOddifNthisTruthyisFalsey等,绝大多数支持块级与内联两种用法:

{{#and a b}}both{{else}}no{{/and}} {{#gte 4 3}}greater or equal{{else}}less{{/gte}} {{#contains ['a','b','c'] 'd'}}found{{else}}not found{{/contains}}

7. Object —— 对象解析与 JSON 输出

将对象转为 JSON 字符串,便于在模板中输出结构化数据。源码在 helpers/index.ts 中直接注册为:

new Helper(HelperFunctionNames.OBJECT, (value: any) => { return new Handlebars.SafeString(JSON.stringify(value)) })

测试用例 helpers.spec.ts 验证了其行为:

{{ object obj }} -> {"a":1}

8. Regex —— 正则测试

对字符串执行正则匹配,可用于条件语句:

{{#match 'foobar' 'foo'}}matched{{else}}not{{/match}}

9. Date —— 日期格式化

基于 moment 的helper-date改造而来,可把 ISO/时间戳日期格式化为人类可读文本:

{{ date dateProperty "DD-MM-YYYY" }}

需要注意:源码中的日期实现并未使用 moment,而是以 dayjs 重写了helper-date的逻辑(见 helpers/date.ts 头部注释),原因有二:原包同时依赖 moment 与 date.js 在简单语法上产生怪异 bug;换用 dayjs 后大幅削减 bundle 体积。该文件额外注册了durationdifferencedurationFromNow三个时间差值/时长 Helper,默认格式为MMMM DD, YYYY

三、日期格式化语法

本包采用标准的日期时间格式化记号,下表完整摘录自 README(YYYY、YY、Y、Q、M/MM、MMM/MMMM、D/DD、Do、DDD/DDDD、X、x):

输入示例描述
YYYY20144 位或 2 位年份。注意:严格模式下只有 4 位可解析
YY142 位年份
Y-25任意位数并带正负号的年份
Q1..4年份季度。会把月份设为该季度首月
M MM1..12月份数字
MMM MMMMJan..December由 moment.locale() 设定的语言环境下的月份名
D DD1..31月份中的第几天
Do1st..31st带序数后缀的日
DDD DDDD1..365一年中的第几天
X1410715640.579Unix 时间戳(秒)
x1410715640579Unix 毫秒时间戳

从源码看,dateHelper 支持更多细节:不传参数时返回当前时间按默认格式输出;传入字符串时区参数(如"utc")会切换到 UTC,否则使用dayjs.tz.guess()推测的本地时区;pattern传空字符串时返回toISOString()结果。

四、模板格式:内联表达式与条件/迭代块

模板系统支持两种主要写法。

4.1 单语句(类 Mustache)

Hello I'm building a {{uppercase adjective}} string with Handlebars!

给定上下文{adjective: "cool"},输出:

Hello I'm building a COOL string with Handlebars!

可以看到字符串 Helper 直接把结果嵌入了句子。这类语句还可以用圆括号堆叠多个 Helper

{{ uppercase (remove string "bad") }}

内层remove先移除子串,外层uppercase再对结果转大写。

4.2 条件块语句

Hello I'm building a {{ #gte score "50" }}Great{{ else }}Bad{{ /gte }} string with Handlebars!

根据上下文变量score"50"的比较结果,插入GreatBad。Comparison、String、Array 集合中的部分 Helper 都支持这种条件式块级用法。

4.3 迭代块

与条件块语法相近但产生循环操作,例如forEach数组 Helper:

{{#forEach orders}}{{name}} - {{total}}{{/forEach}}

这正是自动化中对一组记录逐条拼装文本的核心手段。

五、核心 API:七个主函数 + 若干进阶能力

包的对外使用方式是通过入口 src/index.ts 导出的函数。README 列出的七个主函数如下:

1.processString(string, object)(async)

异步处理单个模板字符串。给定模板字符串与上下文对象,经由预处理(pre-processors)、Handlebars 渲染、后处理(post-processors)三步产出结果字符串。从源码看,其内部直接委托给processStringSync

export async function processString(string, context, opts) { return processStringSync(string, context, opts) }

2.processObject(object, object)(async)

对对象内所有字符串属性递归执行processString的能力,常用于一次处理整份数据/配置。源码 index.ts 会先调用testObjectJSON.stringify探测循环引用(存在环会直接抛错,防止无限递归),然后逐键递归;大对象因深度递归可能较慢。

3.processStringSync(string, object)

同步版字符串处理,功能较异步版精简,风格类似 Node 的readdirSync。所有 JS 绑定、异步能力在同步模式下不可用。

4.processObjectSync(object, object)

processObject的同步版本,同样递归遍历对象并处理每个字符串属性。

5.makePropSafe(string)

处理 Handlebars 无法直接访问的属性名。例如Table 1含空格不是合法标识符,调用后变为[Table 1](字面量说明符包裹)。README 强调:对象访问的每一层都应调用此函数,即[Table 1].[property name]才是 Handlebars 需要的完整语法。源码实现非常简洁:

export function makePropSafe(property: any): string { return `[${property}]`.replace("[[", "[").replace("]]", "]") }

6.isValid(string)

检测给定字符串是否为合法模板,返回布尔值。源码 index.ts 的判定方式是直接编译并渲染一次模板,捕获 Handlebars 的语法错误信息,再通过错误消息特征(expecting '等无效特征与cannot read propertyundefined等有效特征)判断合法性。

7.getManifest()

返回描述全部 Helper 及其参数的清单 JSON。该文件由脚本从 Helper 元数据生成,位于 src/manifest.json,为 Builder 设计器提供"该 Helper 有哪些参数、示例是什么"的自动提示数据,例如add的参数为ab,示例{{ add 1 2 }} -> 3

进阶能力:源码中额外导出的实用函数

除 README 列出的七项外,源码还提供了一批进阶工具,适合在集成开发时使用:

  • processJsonStringSync(template, context, opts):专为 JSON 字符串模板设计,先把模板中{{...}}块替换为占位 token 以保证 JSON 语法有效,再解析、渲染、还原;多对象模板(如 Mongo 的{filter} {update})会按顶层对象拆分处理,注释明确说明"失败即关闭"策略——多对象模板必须是合法 JSON 对象,避免引号破坏导致的操作符注入(index.ts);
  • encodeJSBinding(javascript)/decodeJSBinding(handlebars)/isJSBinding(handlebars):将任意 JS 代码编码为{{ js "base64..." }}模板表达式(或反向解码),这是 Budibase 在前端执行自定义 JS 绑定的机制;
  • findHBSBlocks(string):提取字符串中所有{{...}}/{{{...}}}块;
  • doesContainString(template, string)/doesContainStrings(template, strings):判断模板中是否存在包含指定词的绑定,检测 JS 绑定时会先解码再匹配;
  • disableEscaping(string):把{{ name }}形式的双重花括号改写为{{{ name }}}三花括号,从而关闭 Handlebars 默认的 HTML 转义(由noEscaping处理选项触发);
  • convertToJS(hbs):把 Handlebars 模板编译为可执行的 JS 模板字符串,用于前端 JS 执行环境。

六、渲染管线:预处理 → Helper 执行 → 后处理

理解processString的完整行为需要看三层处理。渲染入口在 index.ts 的processStringSyncInternal:模板编译时注入当前时间戳now与内部选项__opts,执行后统一走postprocess/postprocessWithLogs

6.1 预处理(Preprocessors)

定义在 processors/preprocessor.ts,按顺序执行四个步骤:

  1. SWAP_TO_DOT:把[Table 1]这类字面量写法自动规范为.[Table 1]点号链访问语法;
  2. FIX_FUNCTIONS:修正{ #gte{ else{ /gte等带空格的块语法为{#gte标准写法,容忍用户在模板中手写的多余空格;
  3. NORMALIZE_SPACES:将{{(多个连续空格)规整为{{
  4. FINALISE:把绝大多数内联语句包装成{{ all ... }}形式——all是包内置的通用 Helper,负责统一处理空值输出、对象转 JSON、&amp;还原以及<>的 HTML 安全转义(helpers/index.ts)。函数类块语句(#else/开头)不会被包装。

此外在createTemplate中还有一个细节:如果上下文对象里的键与某个 Helper 重名(称为 overlapping helpers),会把这些键自动加上./前缀以避免与 Helper 冲突(index.ts)。

6.2 Helper 注册

helpers/index.ts 中registerAll分两层完成注册:

  • registerMinimum:注册包内置 Helper——object(对象转 JSON)、js(JS 绑定执行)、decodeId(解码 URL 编码的 ID)、all(全语句通用包装)、literal(为后处理留下字面量标记,如{{%LITERAL% number-42}});
  • registerAll:在最低集基础上追加外部 Helper 集合与datedurationdifferencedurationFromNow四个日期增强 Helper。

同时,包维护两个Handlebars 实例:完整实例hbsInstance(注册全部 Helper)与最小实例hbsInstanceNoHelpers(仅registerMinimum)。当处理选项noHelpers: true时走最小实例——测试 helpers.spec.ts 验证了{{ avg 1 1 1 }}noHelpers下输出为空字符串。

6.3 后处理(Postprocessors)

定义在 processors/postprocessor.ts:CONVERT_LITERALS识别%LITERAL%标记,根据标记中携带的类型(stringnumberbooleanobjectjs_result)把渲染结果还原为对应类型——这正是模板能输出数字、布尔和对象而非纯字符串的关键机制。

6.4 处理选项(ProcessOptions)

createTemplate中的默认选项(index.ts)反映了可调行为:

选项默认值作用
noHelpersfalsetrue时禁用全部外部 Helper,仅保留内置最小集
cacheTemplatesfalsetrue时按"模板字符串 + 选项"缓存编译结果,复用模板函数
noEscapingfalsetrue时把双花括号改为三花括号,关闭 HTML 转义
escapeNewlinesfalsetrue时把换行符替换为\n字面量
noFinalisefalsetrue时跳过all包装(isValid内部使用)
onlyFound-只处理字符串中已找到的 HBS 块,未命中时保留原输入
noThrowtrue渲染失败时不抛错,返回原始输入

七、开发与构建

该包与仓库中其他包一样由lerna管理,使用Rollup构建(Budibase 多数包的标准做法),产物为UMD格式以便 Node 与浏览器同时使用,同时生成TypeScript 声明文件,供 IDE 在引用包时提供代码补全。package.json 中的命令:

  1. yarn build—— 依次执行tsc --emitDeclarationOnly(生成类型声明)与rollup -c(打包出dist/bundle.cjsdist/bundle.mjs);
  2. yarn test—— 运行 Jest 测试套件,覆盖各 Helper 的核心行为与若干典型用例。测试文件位于 test/,例如 helpers.spec.ts 验证了 object Helper、noHelpers模式、math 的abs/add/avg、array 的after/before/filter/itemAt/join/sort/unique、number 的addCommas/phoneNumber等;basic.spec.ts 与 hbsToJs.spec.ts 覆盖基础渲染与 HBS 转 JS 的逻辑;
  3. yarn dev—— 内部命令,由 lerna 在仓库根目录执行yarn dev时调用rollup -cw监听文件变化并持续重建。

此外还提供yarn manifest(运行 scripts/gen-collection-info.ts 重新生成 Helper 清单 JSON)与yarn check:types(类型检查)。

八、典型应用场景与实战要点

结合 Budibase 的整体架构,这套模板系统主要出现在三类场景:

  1. 自动化节点中的文本/URL 组装:利用 Math、Array、URL、String 集合将上游数据拼装为 API 请求地址、通知内容或输出消息;
  2. 页面数据绑定的安全访问:通过makePropSafeTable 1这类带空格的表名/属性名包装为[Table 1]再嵌入模板,避免 Handlebars 解析失败;
  3. Builder 中的 Helper 提示getManifest()返回的清单驱动设计器向用户展示每个 Helper 的参数与示例。

实战中还需留意几个来自源码的行为边界:

  • 同步与异步的能力差异processStringSync是精简功能版,JS 绑定等完整能力以异步/后端路径为准(index.ts 的注释明确说明);
  • 循环引用防护processObject/processObjectSync对含环对象直接抛错;
  • 上下文与 Helper 同名:会自动加./前缀消除歧义;
  • JS 绑定限制processStringWithLogsSync在后端服务中会被禁用(Logging disabled for backend bindings),而isJSAllowed()在设置了环境变量NO_JS时会关闭 JS 执行能力(utilities.ts)。

综上,string-templates是一个"小而全"的模板基础设施:以 Handlebars 为引擎、以精选 Helper 集合为扩展、以三层处理管线保证输出安全与类型正确,并通过 UMD + 类型声明兼顾了三端复用与开发体验。无论是阅读 README 快速上手,还是深入 src/index.ts、src/helpers/ 与 src/processors/ 理解实现细节,都能为你在 Budibase 中编写、调试模板提供完整的知识闭环。

【免费下载链接】budibaseAI agents, automations and apps that run your operations. Model agnostic.项目地址: https://gitcode.com/GitHub_Trending/bu/budibase

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 0:21:43

OpenUI5空白符处理机制与Web开发实践

1. OpenUI5中的空白符处理机制解析在Web开发领域&#xff0c;空白符处理一直是个容易被忽视却至关重要的问题。OpenUI5作为企业级前端框架&#xff0c;其whitespaceReplacer.js模块正是为解决HTML模板渲染中的空白符问题而设计的核心组件。这个不到200行的工具类&#xff0c;实…

作者头像 李华
网站建设 2026/9/11 0:18:51

IoT DC3 本地部署实战:Spring Cloud 物联网平台从零搭建

IoT DC3 这个项目&#xff0c;最早是我做设备接入平台选型时挖到的。当时团队需要一个能快速跑起来的 Spring Cloud 物联网框架&#xff0c;既要能管设备&#xff0c;又要能收 MQTT 数据&#xff0c;还得有现成的存储链路。翻了好几个开源项目&#xff0c;DC3 给我的印象最直接…

作者头像 李华
网站建设 2026/9/11 0:18:01

2025 Gartner备份魔力象限解读:厂商格局与选型实践指南

Gartner的魔力象限报告&#xff0c;大概是备份和数据保护领域每年大家最关心的一份榜单了。这两天就有几个同行来问我&#xff0c;2025年版到底有哪些供应商在内&#xff0c;和上一版比变化大不大。说实话&#xff0c;这份报告不光是厂商排名那么简单&#xff0c;它背后的评审逻…

作者头像 李华
网站建设 2026/9/11 0:14:13

OSG AutoTransform类详解:3D场景智能变换技术

1. AutoTransform类核心功能解析OpenSceneGraph中的AutoTransform是一个智能化的场景节点类&#xff0c;它能够根据观察者的视角自动调整子节点的变换参数。这个类特别适合需要始终面向相机或保持特定显示特性的场景对象&#xff0c;比如游戏中的HUD元素、公告牌或者AR/VR场景中…

作者头像 李华
网站建设 2026/9/11 0:11:05

C# FileSystemWatcher文件监控的常见问题与解决方案

1. 项目概述&#xff1a;FileSystemWatcher的"深夜失联"现象做C#文件监控开发的朋友们&#xff0c;肯定都遇到过这个头疼的问题——FileSystemWatcher监听服务在无人值守时段&#xff08;特别是深夜&#xff09;莫名其妙停止工作。我十年前第一次用这个组件做日志监控…

作者头像 李华
网站建设 2026/9/11 0:06:44

Temu 怎么开店?2026 年 Temu 入驻全流程详解

摘要&#xff1a;Temu 开店的第一步是选对模式&#xff1a;全托管保证金 1000 元适合新手&#xff0c;半托管 10000 元适合有海外仓的卖家。本文拆解 Temu 入驻的模式选择、费用与五步流程。 想入局跨境电商&#xff0c;很多人把 Temu 当成第一站 —— 零入驻费、低保证金&…

作者头像 李华