4、TTML模板语法:数据绑定、条件渲染、列表渲染,多端兼容写法
说实话,TTML 这玩意儿,跟微信小程序的 WXML 长得是真像。我第一次从微信转抖音小程序时,差点以为拿错文档了。但别高兴太早,细节上还是有坑的。今天咱们就把数据绑定、条件渲染、列表渲染这几个核心语法掰开揉碎,顺便聊聊怎么写出多端兼容的代码。
4.1 数据绑定:从 Mustache 到动态属性
数据绑定用的是双花括号,也就是 Mustache 语法。这个大家应该不陌生。
<!-- TTML -->
<view>{{ message }}</view>
<view>{{ count + 1 }}</view>
<view>{{ isActive ? '激活' : '未激活' }}</view>
我个人习惯在 JS 里把数据处理干净,模板里只做简单的三元运算或字符串拼接。为什么?因为模板里写复杂逻辑,调试起来真的很痛苦。我曾经在一个项目里看到有人把三层嵌套的三元运算写在模板里……嗯,后来那个人被开了。
核心要点:TTML 支持在双花括号内做简单的运算,但不支持调用函数。如果你需要格式化数据,请在 JS 里提前处理好。
属性绑定也类似,但要注意一点:
<!-- 正确写法 -->
<view data-id="{{ itemId }}"></view>
<!-- 错误写法(别问我怎么知道的) -->
<view data-id="itemId"></view>
你想想看,如果不加花括号,它就是个纯字符串。我刚开始做小程序时,就因为这个 bug 查了半小时。
4.2 条件渲染:wx:if 还是 tt:if?
条件渲染的语法,微信用 wx:if,抖音用 tt:if。说白了就是前缀不同。
<!-- 抖音 TTML -->
<view tt:if="{{ condition }}">显示我</view>
<view tt:elif="{{ otherCondition }}">或者显示我</view>
<view tt:else>都不满足就显示我</view>
那多端兼容怎么写?我建议用条件编译,或者干脆封装一个工具函数。但最直接的方式,是写两套模板文件。别觉得麻烦,实际上维护起来反而清晰。
我的经验:如果条件渲染的逻辑很复杂,比如超过三层嵌套,我会考虑用 hidden 属性替代。因为 tt:if 是真正的条件渲染——条件为 false 时,节点会被移除;而 hidden 只是控制显示隐藏。频繁切换的场景用 hidden 性能更好。
<!-- hidden 方式 -->
<view hidden="{{ !condition }}">我还在 DOM 里</view>
4.3 列表渲染:tt:for 与 key 的重要性
列表渲染用 tt:for,默认的 item 变量名是 item,索引是 index。你可以用 tt:for-item 和 tt:for-index 重命名。
<view tt:for="{{ list }}" tt:key="id">
{{ index }}: {{ item.name }}
</view>
<!-- 重命名 -->
<view tt:for="{{ list }}" tt:for-item="user" tt:for-index="idx" tt:key="user.id">
{{ idx }}: {{ user.name }}
</view>
这里有个坑,我必须要说。tt:key 的值,在抖音小程序里必须是一个字符串,不能是 *this。微信小程序可以用 *this 表示 item 本身,但抖音不行。如果你要兼容两端,建议给每个列表项加一个唯一的 id 字段。
注意:抖音小程序不支持 tt:key="*this"。如果你从微信迁移代码,记得改掉。否则列表渲染会报 warning,而且性能会下降。
4.4 多端兼容写法:一套代码跑两端
说实话,完全一套代码跑两端是不现实的。但我们可以尽量减少差异。我常用的策略有这几种:
- 条件编译:用注释标记不同平台的代码块
- 抽象组件:把差异部分封装成组件,不同平台用不同实现
- 数据驱动:在 JS 里判断平台,动态生成模板数据
举个例子,条件渲染的兼容写法:
<!-- 微信小程序 -->
<view wx:if="{{ show }}">微信端</view>
<!-- 抖音小程序 -->
<view tt:if="{{ show }}">抖音端</view>
<!-- 我的做法:封装一个条件渲染组件 -->
<!-- components/If/index.ttml -->
<view tt:if="{{ condition }}">
<slot />
</view>
然后在 JS 里判断平台:
// utils/platform.js
const platform = {
isWechat: typeof wx !== 'undefined',
isDouyin: typeof tt !== 'undefined'
};
export default platform;
这样你在模板里只需要用自定义组件,底层实现由组件自己处理。我个人觉得这是最干净的方式。
4.5 避坑指南:我踩过的那些雷
最后分享几个我实际项目中遇到的坑:
- 数据绑定中的 undefined:如果绑定的数据是 undefined,抖音小程序会直接报错。微信则不会。所以一定要用
??或||给个默认值。 - 列表渲染中的嵌套:两层
tt:for嵌套时,内层的 item 会覆盖外层的 item。记得用tt:for-item重命名。 - 条件渲染的优先级:
tt:if的优先级高于tt:for。如果你在列表项里用条件渲染,要注意顺序。
总结一下:TTML 的模板语法跟微信小程序高度相似,但细节上有差异。多端兼容的核心思路是——把差异封装起来,不要让它们散落在模板的各个角落。这样后期维护起来,你会感谢自己的。
好了,这一章的内容就到这里。下一章我们聊聊事件系统和组件通信,那才是真正让人头疼的地方。