关于 b2c-isml
面向 Salesforce B2C Commerce 开发者的 ISML 模板技能,用于创建生成动态 HTML 的服务端模板。它汇总标签、表达式、模板复用、本地化和缓存等实践,并强调将复杂逻辑移至控制器或辅助脚本。
主要功能
- 01使用 ISML 标签编写条件与循环
- 02通过表达式输出动态内容
- 03复用模板并采用装饰器布局
- 04调用本地化与 URL 工具
- 05应用缓存和输出编码实践
适用场景
- 01创建商品详情和首页模板
- 02构建可复用的页面组件
- 03为页面添加本地化文本和链接
- 04整理模板逻辑并改善页面布局一致性
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ salesforcecommercecloud/ b2c-developer-tooling --skill b2c-isml安装后在 Claude Code 中输入/b2c-isml确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
evals/trigger-evals.jsonreferences/EXPRESSIONS.mdTAGS.md详细介绍
ISML 技能
本技能将指导你在 Salesforce B2C Commerce 中创建和使用 ISML(Isomorphic Markup Language)模板。ISML 模板将 HTML 与动态服务器端标签结合起来。
概述
ISML 模板是用于生成 HTML 的服务器端模板。它们使用以 is 为前缀的特殊标签,以及 ${...} 语法的表达式来嵌入动态内容。
文件位置
模板位于 cartridge 的 templates 目录中:
/my-cartridge
/cartridge
/templates
/default # Default locale
/product
detail.isml
tile.isml
/home
homepage.isml
/util
modules.isml # Custom tag definitions
/fr_FR # French-specific templates
/product
detail.isml
基本标签
条件逻辑
<isif condition="${product.available}">
<span class="in-stock">In Stock</span>
<iselseif condition="${product.preorderable}">
<span class="preorder">Pre-order</span>
<iselse>
<span class="out-of-stock">Out of Stock</span>
</isif>
循环
<isloop items="${products}" var="product" status="loopstate">
<div class="product ${loopstate.odd ? 'odd' : 'even'}">
<span>${loopstate.count}. ${product.name}</span>
<isif condition="${loopstate.first}">
<span class="badge">Featured</span>
</isif>
</div>
</isloop>
循环状态属性:
count- 迭代次数(从 1 开始)index- 当前索引(从 0 开始)first- 布尔值,在第一次迭代时为 truelast- 布尔值,在最后一次迭代时为 trueodd- 布尔值,在奇数次迭代时为 trueeven- 布尔值,在偶数次迭代时为 true
变量
<isset name="productName" value="${product.name}" scope="page"/>
<span>${productName}</span>
<isremove name="productName" scope="page"/>
作用域(必需): page、request、session、pdict
输出
<isprint value="${product.name}"/>
<isprint value="${htmlContent}" encoding="off"/>
<isprint value="${price}" style="CURRENCY"/>
<isprint value="${order.creationDate}" style="DATE_SHORT"/>
引入模板
<isinclude template="product/components/price"/>
<isinclude url="${URLUtils.url('Product-GetPrice', 'pid', product.ID)}"/>
装饰器模式
基础装饰器(layouts/pagelayout.isml):
<!DOCTYPE html>
<html>
<head>
<title>${pdict.pageTitle}</title>
</head>
<body>
<header>
<isinclude template="components/header"/>
</header>
<main>
<isreplace/>
</main>
<footer>
<isinclude template="components/footer"/>
</footer>
</body>
</html>
使用装饰器的页面:
<isdecorate template="layouts/pagelayout">
<isslot id="home-banner" context="global"/>
<div class="homepage-content">
<h1>${pdict.welcomeMessage}</h1>
</div>
</isdecorate>
表达式
表达式使用 ${...} 语法来嵌入动态值:
${product.name}
${product.price.sales.value}
${product.getAvailabilityModel().isInStock()}
${pdict.myVariable}
${session.customer.firstName}
${request.httpParameterMap.pid.stringValue}
${price > 100 ? 'expensive' : 'affordable'}
${firstName + ' ' + lastName}
${quantity * unitPrice}
内置实用工具
URLUtils
<a href="${URLUtils.url('Product-Show', 'pid', product.ID)}">View</a>
<a href="${URLUtils.https('Account-Show')}">My Account</a>
<img src="${URLUtils.staticURL('/images/logo.png')}" alt="Logo"/>
<a href="${URLUtils.abs('Home-Show')}">Home</a>
Resource(本地化)
${Resource.msg('button.addtocart', 'product', null)}
${Resource.msgf('cart.items', 'cart', null, cartCount)}
StringUtils
${StringUtils.truncate(description, 100, '...')}
${StringUtils.formatNumber(quantity, '###,###')}
自定义模块
在 util/modules.isml 中定义可重复使用的自定义标签:
<ismodule template="components/productcard"
name="productcard"
attribute="product"
attribute="showPrice"
attribute="showRating"/>
<isinclude template="util/modules"/>
<isproductcard product="${product}" showPrice="${true}" showRating="${true}"/>
组件模板(components/productcard.isml):
<div class="product-card">
<img src="${product.image.url}" alt="${product.name}"/>
<h3>${product.name}</h3>
<isif condition="${pdict.showPrice}">
<span class="price">${product.price.sales.formatted}</span>
</isif>
<isif condition="${pdict.showRating && product.rating}">
<span class="rating">${product.rating} stars</span>
</isif>
</div>
缓存
<iscache type="relative" hour="24"/>
<iscache type="daily" hour="0" minute="0"/>
<iscache type="relative" hour="1" varyby="price_promotion"/>
将 <iscache> 放在模板开头。
内容类型
<iscontent type="text/html" charset="UTF-8"/>
<iscontent type="application/json" charset="UTF-8"/>
<iscontent type="application/xml" charset="UTF-8"/>
嵌入式脚本
<isscript>
var ProductMgr = require('dw/catalog/ProductMgr');
var product = ProductMgr.getProduct(pdict.pid);
var price = product.priceModel.price;
</isscript>
<span>${price.toFormattedString()}</span>
最佳实践: 尽量精简 <isscript> 代码块。将复杂逻辑移至控制器或辅助脚本中。
注释
<iscomment>
ISML comment - stripped from output.
Use for documentation and hiding sensitive info.
</iscomment>
标签位置限制
并非所有 ISML 标签都可以在任意位置使用。重要限制如下:
| 标签 | 允许的位置 |
|---|---|
<iscontent> | 必须位于 DOCTYPE 声明之前 |
<isredirect> | 必须位于 DOCTYPE 声明之前 |
<isprint> | 仅限 <body> 中 |
<isbreak> | 仅限 <body> 中的 <isloop> 内 |
<iscontinue> | 仅限 <body> 中的 <isloop> 内 |
<isnext> | 位于 <isloop> 内 |
<isreplace> | 必须位于 <isdecorate> 标签内 |
<isactivedatahead> | 仅限 <head> 中 |
可在任意位置使用的标签:<isif>、<isloop>、<isinclude>、<isset>、<isremove>、<iscache>、<iscomment>、<ismodule>、<iscookie>、<isstatus>。
最佳实践
- 对敏感信息使用
<iscomment>,而非 HTML 注释 - 在需要使用
<iscontent>的模板中将其放在最前面 - 在
util/modules.isml中定义模块,以保持一致性 - 保持模板简洁——将逻辑移至控制器/辅助程序中
- 使用装饰器来统一页面布局
- 使用
<iscache>为可缓存页面启用缓存 - 对输出进行编码——默认编码可防止 XSS
详细参考
如需完整的标签文档: