Liquid 最佳实践
本指南汇总了 Shopify Liquid 开发中的最佳实践,帮助您编写高质量、高性能、可维护的代码。
代码组织和结构
1. 清晰的文件组织
<!-- 按功能组织 snippets -->
snippets/
├── product/
│ ├── product-card.liquid
│ ├── product-gallery.liquid
│ ├── product-form.liquid
│ └── product-reviews.liquid
├── cart/
│ ├── cart-drawer.liquid
│ ├── cart-item.liquid
│ └── cart-summary.liquid
├── navigation/
│ ├── main-menu.liquid
│ ├── breadcrumbs.liquid
│ └── pagination.liquid
└── utilities/
├── image-responsive.liquid
├── price-display.liquid
└── social-sharing.liquid
2. 一致的命名约定
<!-- 好的命名约定 -->
{% assign product_is_available = product.available %}
{% assign customer_is_vip = customer.tags contains 'vip' %}
{% assign cart_has_items = cart.item_count > 0 %}
{% assign collection_is_empty = collection.products.size == 0 %}
<!-- 避免的命名方式 -->
{% assign pa = product.available %}
{% assign c_vip = customer.tags contains 'vip' %}
{% assign cart_items = cart.item_count > 0 %}
{% assign empty = collection.products.size == 0 %}
3. 逻辑分层
<!-- 数据准备层 -->
{% comment %} === 数据收集和预处理 === {% endcomment %}
{% assign featured_products = collection.products | where: 'tags', 'featured' %}
{% assign sale_products = collection.products | where: 'compare_at_price_max', '>', 0 %}
{% assign product_count = collection.products.size %}
{% comment %} === 用户状态检查 === {% endcomment %}
{% assign customer_is_logged_in = customer != null %}
{% assign customer_is_vip = customer.tags contains 'vip' %}
{% assign is_mobile_device = request.user_agent contains 'Mobile' %}
{% comment %} === 业务逻辑层 === {% endcomment %}
{% if customer_is_vip and featured_products.size > 0 %}
{% assign display_products = featured_products %}
{% assign section_title = "VIP 专享精选" %}
{% elsif sale_products.size > 0 %}
{% assign display_products = sale_products %}
{% assign section_title = "限时特价" %}
{% else %}
{% assign display_products = collection.products %}
{% assign section_title = collection.title %}
{% endif %}
{% comment %} === 显示层 === {% endcomment %}
<section class="products-section">
<h2>{{ section_title }}</h2>
<div class="products-grid">
{% for product in display_products limit: 12 %}
{% render 'product-card', product: product, context: 'collection' %}
{% endfor %}
</div>
</section>
性能最佳实践
1. 避免重复计算
<!-- 不好的做法 -->
{% for product in collection.products %}
{% if collection.products.size > 10 %}
<!-- size 在每次循环中都被计算 -->
{% endif %}
{% if collection.products | where: 'available', true | size > 5 %}
<!-- 复杂过滤在每次循环中都被执行 -->
{% endif %}
{% endfor %}
<!-- 好的做法 -->
{% assign total_products = collection.products.size %}
{% assign available_products = collection.products | where: 'available', true %}
{% assign available_count = available_products.size %}
{% for product in collection.products %}
{% if total_products > 10 %}
<!-- 使用预计算的值 -->
{% endif %}
{% if available_count > 5 %}
<!-- 使用预计算的值 -->
{% endif %}
{% endfor %}
2. 高效的对象访问
<!-- 缓存深层对象访问 -->
{% assign selected_variant = product.selected_or_first_available_variant %}
{% assign featured_image = product.featured_image %}
{% assign customer_address = customer.default_address %}
<!-- 使用缓存的值 -->
{% if selected_variant.available %}
<p>价格: {{ selected_variant.price | money }}</p>
<p>库存: {{ selected_variant.inventory_quantity }}</p>
{% endif %}
{% if featured_image %}
<img src="{{ featured_image | img_url: '400x400' }}"
alt="{{ featured_image.alt | default: product.title }}">
{% endif %}
3. 智能过滤和排序
<!-- 链式过滤优化 -->
{% assign featured_available_products = collection.products
| where: 'available', true
| where: 'tags', 'featured'
| sort: 'created_at'
| reverse %}
<!-- 条件性过滤 -->
{% if settings.show_only_available %}
{% assign display_products = collection.products | where: 'available', true %}
{% else %}
{% assign display_products = collection.products %}
{% endif %}
{% if settings.sort_by_price %}
{% assign display_products = display_products | sort: 'price' %}
{% elsif settings.sort_by_date %}
{% assign display_products = display_products | sort: 'created_at' | reverse %}
{% endif %}
4. 循环优化
<!-- 使用 limit 避免处理过多数据 -->
{% for product in collection.products limit: 20 %}
{% render 'product-card', product: product %}
{% endfor %}
<!-- 早期跳出和继续 -->
{% for product in collection.products %}
{% unless product.available %}
{% continue %}
{% endunless %}
{% if forloop.index > 12 %}
{% break %}
{% endif %}
{% render 'product-card', product: product %}
{% endfor %}
<!-- 条件性循环 -->
{% if collection.products.size > 0 %}
{% for product in collection.products %}
{% render 'product-card', product: product %}
{% endfor %}
{% else %}
{% render 'empty-collection' %}
{% endif %}
安全性最佳实践
1. 输出转义
<!-- 总是转义用户输入和不信任的内容 -->
<h1>{{ product.title | escape }}</h1>
<p>{{ product.description | escape }}</p>
<meta name="description" content="{{ product.description | strip_html | escape }}">
<!-- URL 安全 -->
<a href="{{ product.url | escape }}">{{ product.title | escape }}</a>
<!-- 属性值转义 -->
<img src="{{ product.featured_image | img_url: '400x400' }}"
alt="{{ product.featured_image.alt | escape | default: product.title | escape }}">
<!-- JavaScript 上下文中的转义 -->
<script>
var productTitle = {{ product.title | json }};
var productData = {{ product | json }};
</script>
2. 安全的动态内容
<!-- 验证和清理用户输入 -->
{% assign search_query = request.params.q | strip | escape %}
{% if search_query.size > 0 and search_query.size <= 100 %}
<p>搜索: "{{ search_query }}"</p>
{% endif %}
<!-- 安全的 URL 构建 -->
{% assign safe_handle = product.handle | escape %}
<a href="/products/{{ safe_handle }}">{{ product.title | escape }}</a>
<!-- 验证对象存在性 -->
{% if customer and customer.email %}
<p>欢迎, {{ customer.first_name | escape }}!</p>
{% endif %}
3. 防止 XSS 攻击
<!-- 在 HTML 属性中使用内容 -->
<div data-product-id="{{ product.id }}"
data-product-title="{{ product.title | escape }}"
data-product-price="{{ product.price }}">
</div>
<!-- 在 CSS 中使用内容 -->
<style>
.product-{{ product.id }} {
/* 只使用已知安全的值 */
background-color: {{ settings.product_bg_color }};
}
</style>
<!-- 避免在 JavaScript 中直接输出 -->
<!-- 不好的做法 -->
<script>
var title = "{{ product.title }}"; // 可能被注入
</script>
<!-- 好的做法 -->
<script>
var title = {{ product.title | json }}; // 安全的 JSON 序列化
</script>
错误处理和容错性
1. 防御性编程
<!-- 检查对象存在性 -->
{% if product and product.featured_image %}
<img src="{{ product.featured_image | img_url: '400x400' }}"
alt="{{ product.featured_image.alt | default: product.title }}">
{% else %}
<div class="no-image-placeholder">
<span>暂无图片</span>
</div>
{% endif %}
<!-- 提供默认值 -->
{% assign product_title = product.title | default: "未命名商品" %}
{% assign product_price = product.price | default: 0 %}
{% assign product_description = product.description | default: "暂无描述" %}
<!-- 安全的数组访问 -->
{% if product.images.size > 0 %}
{% assign first_image = product.images[0] %}
{% assign gallery_images = product.images | offset: 1 %}
{% endif %}
2. 回退机制
<!-- 集合回退 -->
{% assign featured_collection = collections[settings.featured_collection_handle] %}
{% assign display_collection = featured_collection | default: collections.all %}
<!-- 图片回退 -->
{% assign product_image = product.featured_image | default: 'placeholder.svg' %}
<img src="{{ product_image | img_url: '400x400' }}"
alt="{{ product.title }}">
<!-- 模板回退 -->
{% if template == 'product.special' %}
{% render 'product-special' %}
{% elsif template == 'product.simple' %}
{% render 'product-simple' %}
{% else %}
{% render 'product-default' %}
{% endif %}
3. 错误状态处理
<!-- 空状态处理 -->
{% if collection.products.size > 0 %}
<div class="products-grid">
{% for product in collection.products %}
{% render 'product-card', product: product %}
{% endfor %}
</div>
{% else %}
<div class="empty-collection">
<h3>暂无商品</h3>
<p>该分类下还没有商品,请查看其他分类或稍后再来。</p>
<a href="/collections" class="btn">浏览所有分类</a>
</div>
{% endif %}
<!-- 加载状态 -->
{% if search.performed %}
{% if search.results.size > 0 %}
<!-- 显示搜索结果 -->
{% else %}
<div class="no-results">
<h3>未找到相关结果</h3>
<p>请尝试使用不同的关键词进行搜索。</p>
</div>
{% endif %}
{% else %}
<div class="search-prompt">
<h3>搜索我们的产品</h3>
<p>输入关键词找到您需要的商品。</p>
</div>
{% endif %}
可维护性最佳实践
1. 模块化设计
<!-- 将复杂组件分解为小的可重用片段 -->
<!-- snippets/product/product-card.liquid -->
<div class="product-card" data-product-id="{{ product.id }}">
{% render 'product/product-image', product: product, size: size %}
{% render 'product/product-info', product: product, show_vendor: show_vendor %}
{% render 'product/product-actions', product: product, quick_shop: quick_shop %}
</div>
<!-- snippets/product/product-image.liquid -->
{% assign image_size = size | default: '300x300' %}
{% if product.featured_image %}
<div class="product-image">
<img src="{{ product.featured_image | img_url: image_size }}"
alt="{{ product.featured_image.alt | default: product.title }}"
loading="lazy">
{% if product.images.size > 1 %}
<div class="image-count">+{{ product.images.size | minus: 1 }}</div>
{% endif %}
</div>
{% else %}
<div class="product-image-placeholder">
<span>暂无图片</span>
</div>
{% endif %}
2. 配置驱动的开发
<!-- 使用设置来控制行为 -->
{% assign show_vendor = show_vendor | default: settings.show_product_vendor %}
{% assign show_reviews = show_reviews | default: settings.show_product_reviews %}
{% assign card_style = card_style | default: settings.product_card_style %}
<div class="product-card product-card--{{ card_style }}">
{% if show_vendor and product.vendor %}
<p class="product-vendor">{{ product.vendor }}</p>
{% endif %}
<h3 class="product-title">
<a href="{{ product.url }}">{{ product.title }}</a>
</h3>
{% render 'price-display', product: product %}
{% if show_reviews %}
{% render 'product-reviews-summary', product: product %}
{% endif %}
</div>
3. 文档化代码
{% comment %}
产品卡片组件
参数:
- product: 产品对象 (必需)
- size: 图片尺寸,例如 '300x300' (可选,默认 '300x300')
- show_vendor: 是否显示品牌 (可选,默认 false)
- show_reviews: 是否显示评论 (可选,默认 false)
- context: 使用上下文,例如 'collection', 'search' (可选)
使用示例:
{% render 'product-card',
product: product,
size: '400x400',
show_vendor: true,
context: 'collection' %}
{% endcomment %}
{% assign image_size = size | default: '300x300' %}
{% assign show_vendor = show_vendor | default: false %}
{% assign show_reviews = show_reviews | default: false %}
<div class="product-card" data-context="{{ context }}">
<!-- 组件内容 -->
</div>
国际化和本地化
1. 多语言支持
<!-- 使用翻译键 -->
<h2>{{ 'products.general.featured_title' | t }}</h2>
<p>{{ 'products.general.showing_count' | t: count: collection.products.size }}</p>
<!-- 带参数的翻译 -->
{% assign remaining = 50000 | minus: cart.total_price %}
<p>{{ 'cart.general.free_shipping_remaining' | t: amount: remaining | money }}</p>
<!-- 复数形式处理 -->
<span class="cart-count">
{{ 'cart.general.item_count' | t: count: cart.item_count }}
</span>
<!-- 日期本地化 -->
<time datetime="{{ article.published_at | date: '%Y-%m-%d' }}">
{{ article.published_at | date: format: 'month_day_year' }}
</time>
2. 货币和地区适配
<!-- 货币显示 -->
<span class="price">{{ product.price | money }}</span>
<span class="price-no-currency">{{ product.price | money_without_currency }}</span>
<span class="price-with-currency">{{ product.price | money_with_currency }}</span>
<!-- 地区特定格式 -->
{% case shop.locale %}
{% when 'zh-CN' %}
<p>免费送货到中国大陆</p>
{% when 'en-US' %}
<p>Free shipping to United States</p>
{% else %}
<p>{{ 'shipping.free_shipping' | t }}</p>
{% endcase %}
可访问性最佳实践
1. 语义化 HTML
<!-- 使用正确的 HTML 结构 -->
<nav aria-label="主导航">
<ul>
{% for link in linklists.main-menu.links %}
<li>
<a href="{{ link.url }}"
{% if link.current %}aria-current="page"{% endif %}>
{{ link.title }}
</a>
</li>
{% endfor %}
</ul>
</nav>
<!-- 表单标签 -->
<form action="/search" method="get">
<label for="search-input">搜索产品</label>
<input type="search"
id="search-input"
name="q"
placeholder="输入关键词"
value="{{ search.terms }}"
aria-describedby="search-help">
<div id="search-help" class="sr-only">
输入产品名称或关键词进行搜索
</div>
<button type="submit">搜索</button>
</form>
2. 图片无障碍
<!-- 提供有意义的替代文本 -->
<img src="{{ product.featured_image | img_url: '400x400' }}"
alt="{% if product.featured_image.alt %}{{ product.featured_image.alt }}{% else %}{{ product.title }}的产品图片{% endif %}"
loading="lazy">
<!-- 装饰性图片 -->
<img src="{{ 'decoration.svg' | asset_url }}"
alt=""
role="presentation">
<!-- 图片说明 -->
<figure>
<img src="{{ article.image | img_url: '800x400' }}"
alt="{{ article.image.alt }}">
{% if article.image.alt %}
<figcaption>{{ article.image.alt }}</figcaption>
{% endif %}
</figure>
3. 键盘导航和屏幕阅读器
<!-- 跳转链接 -->
<a href="#main-content" class="skip-link">跳转到主要内容</a>
<!-- 可访问的按钮 -->
<button type="button"
class="cart-toggle"
aria-expanded="false"
aria-controls="cart-drawer"
aria-label="打开购物车,当前有 {{ cart.item_count }} 件商品">
购物车 ({{ cart.item_count }})
</button>
<!-- 状态通知 -->
<div id="cart-status"
class="sr-only"
aria-live="polite"
aria-atomic="true">
<!-- 动态更新购物车状态 -->
</div>
测试和调试
1. 开发环境调试
<!-- 开发模式检查 -->
{% assign is_development = request.host contains '.myshopify.com' %}
{% if is_development or settings.debug_mode %}
<div class="debug-info">
<h4>调试信息</h4>
<ul>
<li>模板: {{ template }}</li>
<li>页面类型: {{ request.page_type }}</li>
<li>用户代理: {{ request.user_agent | truncate: 50 }}</li>
{% if product %}
<li>产品ID: {{ product.id }}</li>
<li>产品句柄: {{ product.handle }}</li>
{% endif %}
</ul>
</div>
{% endif %}
2. 性能监控
<!-- 简单的性能指标 -->
{% if settings.show_performance_info %}
<div class="performance-info">
<p>对象数量: {{ collection.products.size }}</p>
<p>渲染时间: {{ 'now' | date: '%s' }}</p>
</div>
{% endif %}
部署和维护
1. 环境管理
<!-- 环境特定配置 -->
{% case request.host %}
{% when 'localhost' %}
{% assign analytics_id = 'dev-123' %}
{% assign debug_mode = true %}
{% when 'staging.myshop.com' %}
{% assign analytics_id = 'staging-456' %}
{% assign debug_mode = true %}
{% else %}
{% assign analytics_id = settings.analytics_id %}
{% assign debug_mode = false %}
{% endcase %}
2. 版本控制友好的代码
<!-- 避免硬编码值 -->
{% assign featured_limit = settings.featured_products_limit | default: 8 %}
{% assign grid_columns = settings.products_per_row | default: 4 %}
<!-- 使用版本化的资源 -->
{{ 'theme.css' | asset_url | stylesheet_tag }}
{{ 'app.js' | asset_url | script_tag }}
总结
遵循这些最佳实践将帮助您:
- 提高代码质量 - 编写更清晰、更可维护的代码
- 提升性能 - 减少不必要的计算和优化渲染
- 增强安全性 - 防止安全漏洞和数据泄露
- 改善用户体验 - 提供更好的可访问性和多语言支持
- 简化维护 - 通过模块化和文档化降低维护成本
下一步学习
掌握最佳实践后,建议继续学习:
持续改进和遵循最佳实践是成为优秀 Liquid 开发者的关键!
最后更新时间: