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.liquid2. 一致的命名约定
<!-- 好的命名约定 -->
{% 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 开发者的关键!
最后更新时间: