Skip to Content
🎉 探索 Shopify 的无限可能 结构化知识 + 实战案例,持续更新中...
进阶教程Shopify主题开发

Shopify 主题开发完整指南

Shopify 主题开发自 2021 年 Online Store 2.0 推出后发生了根本性变化——从基于固定模板的”主题”,演进为以 Sections + Blocks + JSON Templates 为核心的”可视化拖拽系统”。本文聚焦在生产级主题开发的关键模式与决策,不重复 Liquid 基础语法(基础语法见 Liquid 文档)。

阅读前置:

  • 熟悉 Liquid 基础(assign、for、if、render)
  • 熟悉 Shopify 主题目录结构
  • 推荐先看 Liquid 进阶技巧

一、Online Store 2.0 与旧主题的差异

主题文件结构(2.0)

my-theme/ ├── assets/ # 静态资源(CSS、JS、字体、图片) ├── config/ │ ├── settings_schema.json # 主题设置 │ └── settings_data.json # 主题配置实际值 ├── layout/ │ └── theme.liquid # 主布局 ├── sections/ # 可复用 section 与 block │ ├── header.liquid │ ├── footer.liquid │ └── *.liquid ├── snippets/ # 可复用代码片段 ├── templates/ # 页面模板(JSON 或 Liquid) │ ├── product.json # 2.0 推荐:JSON 模板 │ ├── product.liquid # 旧:Liquid 模板 │ ├── collection.json │ └── index.json ├── locales/ # 多语言文件 └── blocks/ # 2024 新增:theme blocks(更细粒度)

JSON Templates 取代 Liquid Templates

旧主题:

{# templates/product.liquid #} <div class="product"> {% section 'product-info' %} {% section 'product-recommendations' %} </div>

2.0 主题:

{ "sections": { "main": { "type": "main-product", "settings": {} }, "recommendations": { "type": "product-recommendations", "settings": {} } }, "order": ["main", "recommendations"] }

JSON 模板让运营人员可以在主题编辑器中拖拽改变 section 顺序,无需开发介入。

Sections Everywhere

旧主题:sections 仅在首页可用,其他页面是静态 Liquid。

2.0:每个页面都可由多个 sections 组成,每个 section 可包含多个 blocks。运营自由组合。

推荐做法

  • 所有新主题项目用 2.0 架构
  • 老主题升级时,把 Liquid templates 改为 JSON
  • 主 section(如 main-product)建为完整的 section,子组件用 blocks

二、Section 与 Block 设计

Section Schema 完整结构

{% schema %} { "name": "Featured Products", "tag": "section", "class": "section-featured-products", "settings": [ { "type": "text", "id": "title", "label": "标题", "default": "热门产品" }, { "type": "collection", "id": "collection", "label": "集合" }, { "type": "range", "id": "products_per_row", "label": "每行产品数", "min": 2, "max": 6, "step": 1, "default": 4 } ], "blocks": [ { "type": "product_card", "name": "产品卡片", "settings": [ { "type": "product", "id": "product", "label": "产品" } ] } ], "presets": [ { "name": "Featured Products", "category": "Products" } ], "max_blocks": 12 } {% endschema %}

Block 设计原则

  • 每种 block type 独立 schema 定义
  • 多个 block 可在同一 section 中重复(运营可加多个产品 / 多张图)
  • 单 section 的 blocks 总数有上限(默认 16,可在 schema 设上限)
  • 不同 block type 的 settings 完全独立

渲染 blocks

<div class="featured-products"> {%- if section.settings.title != blank -%} <h2>{{ section.settings.title }}</h2> {%- endif -%} <div class="product-grid product-grid--{{ section.settings.products_per_row }}-cols"> {%- for block in section.blocks -%} {%- case block.type -%} {%- when 'product_card' -%} <div class="product-grid__item" {{ block.shopify_attributes }}> {%- if block.settings.product != blank -%} {% render 'product-card', product: block.settings.product %} {%- endif -%} </div> {%- endcase -%} {%- endfor -%} </div> </div>

{{ block.shopify_attributes }} 必填——让主题编辑器能识别并高亮可点击的 block。

三、theme.liquid 布局规范

头部规范

<!doctype html> <html lang="{{ request.locale.iso_code }}"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="theme-color" content="{{ settings.color_brand }}"> {%- liquid assign page_title_with_brand = page_title unless page_title contains shop.name assign page_title_with_brand = page_title | append: ' - ' | append: shop.name endunless -%} <title>{{ page_title_with_brand }}</title> <meta name="description" content="{{ page_description | default: shop.description | escape }}"> <link rel="canonical" href="{{ canonical_url }}"> {# Hreflang 多语言 #} {%- for locale in shop.published_locales -%} <link rel="alternate" hreflang="{{ locale.iso_code }}" href="{{ canonical_url }}"> {%- endfor -%} <link rel="alternate" hreflang="x-default" href="{{ canonical_url }}"> {# 预连接 + DNS Prefetch #} <link rel="preconnect" href="https://cdn.shopify.com" crossorigin> <link rel="preconnect" href="https://fonts.shopifycdn.com" crossorigin> {# 关键 CSS(首屏) #} {{ 'critical.css' | asset_url | stylesheet_tag: preload: true }} {# 非关键 CSS(异步) #} <link rel="preload" href="{{ 'theme.css' | asset_url }}" as="style" onload="this.onload=null;this.rel='stylesheet'"> {{ content_for_header }} </head> <body class="template-{{ template | replace: '.', '-' }}"> <a class="skip-link" href="#MainContent">跳转到主要内容</a> {% section 'announcement-bar' %} {% section 'header' %} <main id="MainContent" role="main"> {{ content_for_layout }} </main> {% section 'footer' %} {# 非关键 JS 延迟加载 #} <script src="{{ 'theme.js' | asset_url }}" defer></script> </body> </html>

关键点

  • lang 属性自动取当前 locale
  • <title>shop.name 智能拼接避免重复
  • canonical 与 hreflang 默认输出
  • preconnect Shopify CDN 减少 DNS 时间
  • critical.css 内联 + theme.css 异步加载

四、性能优化模式

性能问题的常见来源

按真实项目影响排序:

  1. 循环外缺少 assign 缓存集合对象
  2. image_url 未指定 width 或 width 过大
  3. 首屏图片错加 loading="lazy"
  4. <img> 缺 width/height(CLS 飙升)
  5. 第三方脚本同步加载(INP 飙升)

详细诊断见 Shopify 商店性能优化

主题代码层面的性能模式

模式 1:响应式图片 snippet

{# snippets/responsive-image.liquid #} {%- assign sizes = sizes | default: "100vw" -%} {%- assign widths = widths | default: "300, 600, 900, 1200" -%} {%- assign widths_array = widths | split: ", " -%} {%- capture srcset -%} {%- for w in widths_array -%} {{ image | image_url: width: w }} {{ w }}w{%- unless forloop.last -%},{%- endunless -%} {%- endfor -%} {%- endcapture -%} <img src="{{ image | image_url: width: 600 }}" srcset="{{ srcset }}" sizes="{{ sizes }}" alt="{{ image.alt | escape }}" loading="{{ loading | default: 'lazy' }}" fetchpriority="{{ priority | default: 'auto' }}" width="{{ image.width }}" height="{{ image.height }}" >

调用:

{# 首屏 hero 图 #} {% render 'responsive-image', image: section.settings.hero_image, sizes: '100vw', loading: 'eager', priority: 'high' %} {# 产品卡片图 #} {% render 'responsive-image', image: product.featured_image, sizes: '(min-width: 768px) 33vw, 100vw', widths: '300, 600' %}

模式 2:循环外预加载

{%- liquid assign featured_products = collections['featured'].products assign product_count = featured_products.size -%} {%- if product_count > 0 -%} {%- for product in featured_products limit: 8 -%} {%- render 'product-card', product: product -%} {%- endfor -%} {%- endif -%}

模式 3:第三方脚本延迟

<script> function loadDeferred() { if (window.deferredLoaded) return; window.deferredLoaded = true; var scripts = ['{{ "tracking.js" | asset_url }}', 'https://example.com/widget.js']; scripts.forEach(function(src) { var s = document.createElement('script'); s.src = src; s.async = true; document.head.appendChild(s); }); } ['scroll', 'click', 'keydown'].forEach(function(e) { window.addEventListener(e, loadDeferred, { once: true, passive: true }); }); setTimeout(loadDeferred, 5000); </script>

五、JavaScript 架构

现代 Shopify 主题 JS 趋势

  • Web Components:原生 Custom Elements,零依赖
  • Alpine.js:轻量框架,适合简单交互
  • Stimulus:HTML-first 框架
  • vanilla TypeScript:性能最优但开发复杂

不推荐

  • jQuery(过时、体积大)
  • 完整 React 框架(除非 Headless)
  • 多个框架混用

Web Components 示例:Cart Drawer

class CartDrawer extends HTMLElement { constructor() { super(); this.openButton = this.querySelector('[data-open]'); this.closeButton = this.querySelector('[data-close]'); } connectedCallback() { this.openButton?.addEventListener('click', () => this.open()); this.closeButton?.addEventListener('click', () => this.close()); document.addEventListener('cart:updated', () => this.refresh()); } open() { this.setAttribute('aria-hidden', 'false'); document.body.style.overflow = 'hidden'; } close() { this.setAttribute('aria-hidden', 'true'); document.body.style.overflow = ''; } async refresh() { const response = await fetch('/cart.js'); const cart = await response.json(); this.updateUI(cart); } updateUI(cart) { this.querySelector('[data-cart-count]').textContent = cart.item_count; this.querySelector('[data-cart-subtotal]').textContent = this.formatMoney(cart.total_price); } formatMoney(cents) { return (cents / 100).toFixed(2); } } customElements.define('cart-drawer', CartDrawer);
<cart-drawer aria-hidden="true"> <button data-open>购物车 ({{ cart.item_count }})</button> <div class="drawer"> <button data-close>关闭</button> <ul> {%- for item in cart.items -%} {# 渲染 item #} {%- endfor -%} </ul> <p>小计:<span data-cart-subtotal>{{ cart.total_price | money }}</span></p> </div> </cart-drawer>

Cart API 交互

// 加入购物车 async function addToCart(variantId, quantity) { const response = await fetch('/cart/add.js', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: variantId, quantity: quantity }) }); if (!response.ok) { throw new Error('Add to cart failed'); } const item = await response.json(); document.dispatchEvent(new CustomEvent('cart:added', { detail: item })); document.dispatchEvent(new CustomEvent('cart:updated')); return item; } // 更新数量 async function updateCartItem(line, quantity) { const response = await fetch('/cart/change.js', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ line, quantity }) }); return response.json(); }

六、SEO 集成

自动生成的结构化数据

针对产品页:

{# snippets/product-schema.liquid #} <script type="application/ld+json"> { "@context": "https://schema.org/", "@type": "Product", "name": {{ product.title | json }}, "image": [ {%- for image in product.images limit: 5 -%} {{ image | image_url: width: 1200 | json }} {%- unless forloop.last -%},{%- endunless -%} {%- endfor -%} ], "description": {{ product.description | strip_html | json }}, "sku": {{ product.selected_or_first_available_variant.sku | json }}, "brand": { "@type": "Brand", "name": {{ product.vendor | json }} }, "offers": { "@type": "Offer", "url": {{ shop.url | append: product.url | json }}, "priceCurrency": {{ cart.currency.iso_code | json }}, "price": "{{ product.price | divided_by: 100.00 }}", "availability": "{%- if product.available -%}https://schema.org/InStock{%- else -%}https://schema.org/OutOfStock{%- endif -%}", {% if product.metafields.reviews.rating_count > 0 %} "aggregateRating": { "@type": "AggregateRating", "ratingValue": "{{ product.metafields.reviews.rating.value }}", "reviewCount": "{{ product.metafields.reviews.rating_count }}" } {% endif %} } } </script>

Meta 标签

{# Open Graph #} <meta property="og:type" content="{% if template contains 'product' %}product{% else %}website{% endif %}"> <meta property="og:title" content="{{ page_title | escape }}"> <meta property="og:description" content="{{ page_description | default: shop.description | escape }}"> <meta property="og:url" content="{{ canonical_url }}"> {%- if template contains 'product' -%} <meta property="og:image" content="https:{{ product.featured_image | image_url: width: 1200 }}"> <meta property="product:price:amount" content="{{ product.price | divided_by: 100.0 }}"> <meta property="product:price:currency" content="{{ cart.currency.iso_code }}"> {%- endif -%} {# Twitter Card #} <meta name="twitter:card" content="summary_large_image">

七、可访问性(A11y)

语义化 HTML

{# 不推荐 #} <div class="header"> <div class="nav"> <div class="menu-item"><a href="...">Home</a></div> </div> </div> {# 推荐 #} <header> <nav aria-label="主导航"> <ul> <li><a href="..." {% if request.path == '/' %}aria-current="page"{% endif %}>Home</a></li> </ul> </nav> </header>

表单标签

<form action="/contact" method="post"> <label for="contact-email">电子邮箱</label> <input id="contact-email" name="contact[email]" type="email" required aria-describedby="email-help"> <small id="email-help">仅用于回复您的咨询</small> <button type="submit">提交</button> </form>

跳转链接

<a class="skip-link" href="#MainContent">跳转到主要内容</a>

配合 CSS:

.skip-link { position: absolute; left: -9999px; } .skip-link:focus { position: fixed; top: 0; left: 0; background: white; padding: 1rem; z-index: 9999; }

颜色对比

按 WCAG AA 标准:

  • 正文文字 ≥ 4.5:1
  • 大字号 ≥ 3:1
  • UI 组件 ≥ 3:1

用浏览器 DevTools Lighthouse Accessibility 检查。

八、Shopify CLI 开发流程

安装

npm install -g @shopify/cli @shopify/theme shopify auth login

本地开发

# 拉取主题代码 shopify theme pull --store=my-store.myshopify.com --theme=THEME_ID # 启动本地预览 cd my-theme shopify theme dev --store=my-store.myshopify.com # 修改代码 → 自动热重载

推送修改

# 推送到 unpublished theme(推荐先测试) shopify theme push --unpublished # 直接更新现有主题 shopify theme push --theme=THEME_ID

检查代码

# 静态检查 shopify theme check # 输出错误、警告、建议

九、生产部署流程

推荐工作流

本地开发 ↓ shopify theme push --unpublished Unpublished Theme(测试) ↓ Theme editor 测试 ↓ Lighthouse 性能验证 ↓ 关键流程手动测试(购买、加购、登录) Live Theme(生产)

版本管理

主题代码用 Git 管理:

main # 当前生产版本 develop # 开发分支 feature/xxx # 功能开发

每次发布 tag:v1.2.3,便于回滚。

灰度发布

大改动时:

  1. 推送到 unpublished theme
  2. 通过 Shopify Theme Preview 共享 preview URL
  3. 内部测试 1-3 天
  4. 灰度部分流量(用第三方 A/B 工具如 Intelligems)
  5. 全量切换

十、避免的常见错误

错误 1:未升级到 Online Store 2.0

旧 Liquid templates 已不再获得 Shopify 新功能支持。新主题项目必须 2.0。

错误 2:硬编码英文文案

{# 反例 #} <button>Add to Cart</button> {# 推荐 #} <button>{{ 'products.product.add_to_cart' | t }}</button>

错误 3:忽略 block.shopify_attributes

未输出该属性的 block 在主题编辑器中不能被选中编辑。

错误 4:在主题中存储大量逻辑

复杂业务逻辑应该在应用 / 服务器端实现,主题只负责展示。

错误 5:不验证主题在 mobile 上的表现

主题改完只在桌面测试。多数 Shopify 流量来自移动端。

十一、相关教程

最后更新时间: