Skip to Content
🎉 探索 Shopify 的无限可能 结构化知识 + 实战案例,持续更新中...
Liquid 开发布局和模板

Liquid 布局模板开发:layout 越薄,主题越好维护

layout/theme.liquid 是每个页面的最外层包装。它最容易被写坏的方式不是语法错,而是装了太多东西:页头页脚直接硬编码在里面、第三方脚本一层层往 <head> 里加、甚至按设备分出 theme.mobile.liquid。结果是商家在编辑器里什么都改不了,任何调整都得找开发。

现在的分工很清晰:layout 只负责 HTML 骨架与全局资源,一切可编辑内容交给 sections 与 section groups。这一篇讲清这条边界怎么落地。


一、theme.liquid 的必需元素

一个合法的 layout 只有两个硬要求:

标签位置作用
{{ content_for_header }}<head>Shopify 注入脚本、埋点、应用所需的头部内容
{{ content_for_layout }}<body>渲染当前模板的内容

这两个都不能省、不能挪到别处、不能包在条件里content_for_header 缺失会让后台功能、应用和分析大面积失效——而且症状往往滞后出现,很难查。

一个干净的骨架:

<!doctype html> <html lang="{{ request.locale.iso_code }}"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>{{ page_title }}</title> {% if page_description %} <meta name="description" content="{{ page_description | escape }}"> {% endif %} <link rel="canonical" href="{{ canonical_url }}"> {{ content_for_header }} </head> <body class="template-{{ template.name }}"> <a class="skip-link" href="#main">{{ 'general.skip_to_content' | t }}</a> {% sections 'header-group' %} <main id="main" role="main"> {{ content_for_layout }} </main> {% sections 'footer-group' %} </body> </html>

几个细节值得说明:

langrequest.locale.iso_code,不要写死 zh-CN。多语言店铺里写死等于告诉搜索引擎所有语言版本都是同一种语言。

跳转链接(skip-link)是无障碍基本项,配合 <main id="main"> 让键盘用户能跳过页头。文案走 {{ 'key' | t }},别硬编码中文——同时必须去 locales/en.default.json 加上对应键,否则 theme-check 会直接报 does not have a matching entry,商家侧则渲染成一段可见的键名:

{ "general": { "skip_to_content": "Skip to content" } }

这条是新手最常漏的一步:| t 只负责查表,不负责建表。

template.name 挂在 body,给 CSS 一个稳定的页面类型钩子,比在 layout 里写一堆 {% if template == ... %} 分支干净得多。


二、用 section groups 取代布局里的静态 section

这是老教程和现代主题最大的差距。过去页头页脚是这样写的:

{% section 'announcement-bar' %} {% section 'header' %}

这种写法商家只能改该 section 内部的设置,不能调整顺序、不能增删。section groups 解决了这个问题:把它们收进一个 JSON 文件,layout 里只留一个标签。

文件sections/header-group.json

{ "type": "header", "name": "Header Group", "sections": { "announcement-bar": { "type": "announcement-bar", "settings": {} }, "header": { "type": "header", "settings": {} } }, "order": ["announcement-bar", "header"] }

然后在 layout 里把两行静态 section 换成一行:

{% sections 'header-group' %}

注意标签是 sections(复数),参数是 group 文件名(不含 .json)。

group 文件的四个字段:type(组类型,如 header / footer / aside)、name(编辑器里显示的名字)、sections(以 ID 为键的 section 数据)、order(渲染顺序的 ID 数组)。和 JSON 模板一样,一个 group 最多渲染 25 个 section,每个 section 最多 50 个 block

迁移时的一个好消息:当商家从”用静态 section”的旧版本更新到”把该 section 收进 group”的新版本时,Shopify 会尝试把原静态 section 的设置自动复制到 group 里对应 section 上,映射依据是 section 的类型与 ID。所以迁移不一定会丢配置——但仍应在预览主题里验证一遍,尤其是你同时改了 schema 字段名的时候。


三、备用布局:什么时候真的需要第二个 layout

多个 layout 是合法的,但理由必须是页面骨架结构不同,而不是内容不同。

场景该不该新建 layout
密码页应该。layout/password.liquid 是平台约定
落地页要去掉页头页脚应该。骨架确实不同
结账后的感谢页需要极简结构应该
手机端想换个样子不应该。用 CSS 响应式,见下一节
某个页面文案不同不应该。那是 section 设置的活

指定 layout 有两种方式:

Liquid 模板里{% layout %}

{% layout 'full-width' %}

JSON 模板里用根级 layout 属性。它接受布局文件名,也接受 false

{ "layout": "full-width", "sections": { "main": { "type": "main-page" } }, "order": ["main"] }

"layout": false 表示不套任何布局渲染。要注意它的代价:没有布局的模板无法在主题编辑器里定制。除了极特殊的场景(比如需要输出纯净结构的页面),一般不要用。

JSON 模板还有一个 wrapper 属性,用来指定包裹所有 section 的 HTML 元素——需要给整个模板套一层语义容器时用它,而不是在每个 section 里各写一层 div


四、三个常见的坏做法

按设备拆布局。 老教程里常见 theme.mobile.liquid 加 UA 判断。这在 Shopify 上是错的:页面有 CDN 缓存,服务端按 UA 分流会导致缓存串味;而且你要维护两套骨架。响应式 CSS 加上按需渲染就够——真的需要区分时,用 CSS 媒体查询和容器查询,而不是两个 layout。

把第三方脚本堆进 <head> 每加一个同步脚本,所有页面的首屏都变慢一点。能延后的用 defer,能不加的走应用的 app embed(商家可自行开关),需要提前建连的用 preconnect 而不是直接引脚本。

在 layout 里写业务内容。 判断标准很简单:这段内容商家会想改吗? 会 → 它属于某个 section。layout 里出现具体文案、具体商品、具体促销信息,都是设计错位。


五、性能上真正值得做的两件事

不要在 layout 里做花活,值得做的只有两类:

给关键资源提前建连或预载。 首屏字体和 LCP 图片可以用 preload;第三方域名用 preconnect。注意 preload 是稀缺资源,超过两三个就会互相抢带宽,反而变慢。

让非关键 JS 让路。defer 而不是把脚本塞在 </body> 前就算完事;组件级的脚本用 {% javascript %} 写在自己的 section / block 里,由平台合并,而不是全塞进一个全局文件。

组件级 CSS/JS 只在 snippets/blocks/sections/ 里可用——layout 里不能用 {% stylesheet %} / {% javascript %},这是平台限制,不是风格偏好。


六、上线自检

  • {{ content_for_header }}<head> 内,{{ content_for_layout }}<body> 内,都没被条件包裹;
  • <html lang> 取自 request.locale.iso_code
  • 页头页脚走 {% sections '...-group' %},layout 里没有硬编码的 {% section %}
  • 有 skip-link 与 <main id="main">
  • layout 里没有具体业务文案;
  • 没有按设备拆分的第二套 layout;
  • <head> 里没有可以 defer 却同步加载的脚本;
  • 所有用户可见文案走 {{ 'key' | t }}
  • 新增 layout 后,用到它的模板确实声明了 {% layout %} 或 JSON 的 layout

七、延伸阅读


八、官方文档


小结:layout 的职责只有 HTML 骨架与全局资源两件事。必需元素是 <head> 里的 {{ content_for_header }}<body> 里的 {{ content_for_layout }},缺了会造成滞后且难查的故障。页头页脚不要写静态 {% section %},改用 sections/*-group.json{% sections %},商家才能增删排序(且从旧版本更新时平台会尝试自动迁移设置)。新建 layout 的唯一正当理由是骨架结构不同——按设备拆布局在有 CDN 缓存的平台上是错的,用响应式 CSS 解决。

最后更新时间: