主题设置配置:先决定什么该做成全局设置
主题设置最常见的问题不是不会写 schema,而是什么都往全局塞。设置项一多,商家在”主题设置”里翻半天找不到要改的东西,而你每加一个开关都要在模板里多一处 {% if %}——分支越多,越没人敢改主题。
判断标准只有一条:一个值如果在整站只有一份,它属于全局设置;如果每个模块各有一份,它属于 section 或 block 设置。 品牌色、字体、logo 是全局的;某个 Hero 的标题不是。
一、两个文件的关系
| 文件 | 内容 | 谁改 |
|---|---|---|
config/settings_schema.json | 设置的定义:分几类、每类有哪些项 | 你(开发者) |
config/settings_data.json | 设置的取值:商家在编辑器里选了什么 | 商家(通过编辑器) |
settings_schema.json 是一个数组,每个元素是一个设置分类对象,必须有 name 和 settings 两个字段:
[
{
"name": "theme_info",
"theme_name": "示例主题",
"theme_author": "你的团队",
"theme_version": "1.0.0",
"theme_documentation_url": "https://example.com/docs",
"theme_support_email": "support@example.com"
},
{
"name": "品牌",
"settings": [
{ "type": "image_picker", "id": "logo", "label": "Logo" },
{
"type": "range",
"id": "logo_width",
"label": "Logo 宽度",
"min": 60,
"max": 240,
"step": 10,
"unit": "px",
"default": 120
},
{
"type": "color_scheme_group",
"id": "color_schemes",
"definition": [
{
"type": "color",
"id": "background",
"label": "背景",
"default": "#ffffff"
},
{
"type": "color",
"id": "text",
"label": "文字",
"default": "#121212"
}
],
"role": { "background": "background", "text": "text" }
}
]
}
]第一个元素是特殊的:theme_info 用来在编辑器的主题操作菜单里展示主题元信息。它的 theme_name、theme_author、theme_version、theme_documentation_url 都是必填,另外 theme_support_email 与 theme_support_url 至少要提供一个。做主题分发(尤其要上 Theme Store)时这块必须齐全。
二、常用设置类型与它们的返回值
写 schema 之前先搞清楚取出来是什么,这比记住类型名更重要——很多 bug 来自把对象当字符串用。
| 类型 | 取值时得到什么 | 空值时 |
|---|---|---|
checkbox | 布尔 | 未设 default 时为 false |
text / textarea | 字符串 | 空字符串 |
richtext | 含 HTML 的字符串 | 空 |
inline_richtext | 字符串 | 空对象 |
number / range | 数字 | number 可能为 nil |
select / radio | 所选 value 字符串 | — |
color | 颜色对象 | blank |
image_picker | image 对象 | blank |
link_list | linklist 对象 | blank(含选中的菜单已删除的情况) |
product / collection / blog / page | 对应资源对象 | blank |
liquid | 可含 HTML 与有限 Liquid 的字符串 | — |
两个要点:
资源型设置返回的是对象,不是 handle。 所以直接 settings.featured_product.title 就能用,不需要 all_products[settings.featured_product] 那种老写法。
inline_richtext 空值是空对象而不是空字符串,所以判断要用 != blank 而不是 != ''——后者在空对象上不成立,会渲染出空标签。
number 类型还有个 placeholder 属性,但只在 settings_schema.json 里生效,section schema 里写了不会显示。
三、在模板里取值
全局设置统一通过 settings 对象访问:
{% if settings.logo != blank %}
<a class="header__logo" href="{{ routes.root_url }}">
{{
settings.logo
| image_url: width: 480
| image_tag:
widths: '120, 240, 360, 480',
sizes: '120px',
loading: 'eager',
alt: shop.name
}}
</a>
{% else %}
<a class="header__logo header__logo--text" href="{{ routes.root_url }}">
{{ shop.name }}
</a>
{% endif %}三个习惯值得固化:
每个可空设置都要有兜底分支。 上面 logo 没设时退回店名文字,而不是渲染一个空 <a>。商家新装主题时所有设置都是默认值,这条路径必须能看。
链接用 routes 而不是硬编码路径。 routes.root_url 在多语言与子路径场景下才正确。
数值型设置用 CSS 变量传给样式,不要在 {% stylesheet %} 里写 Liquid(那里的 Liquid 不会被渲染):
<div class="promo" style="--promo-radius: {{ settings.corner_radius }}px">
{{ settings.promo_text }}
</div>
{% stylesheet %}
.promo { border-radius: var(--promo-radius, 0); }
{% endstylesheet %}需要在编辑器里实时预览颜色变化时可以用 {% style %} 标签,它会随设置变更即时更新。
四、全局设置放什么、不放什么
| 该放全局 | 不该放全局 |
|---|---|
| 品牌色 / 配色方案 | 某个模块的标题文案 |
| 字体 | 某个 Hero 的图片 |
| Logo 与 favicon | 某个促销条的到期日 |
| 圆角、间距等设计语言基线 | 只影响一个页面的开关 |
| 社交账号链接 | 某个集合页的排序方式 |
| 商品卡片的统一样式选项 | 单个商品的自定义信息(用元字段) |
最后一行值得单独说:商品维度的自定义信息属于元字段(metafield),不属于主题设置。主题设置是全站一份,商品有几千个。把”这个商品的保养说明”做成主题设置是结构性错误。见 元字段与元对象。
配色建议用 color_scheme_group 定义方案,再让 section 通过 color_scheme 设置选用某个方案——而不是给每个 section 都放一组 color 设置。后者会让商家改一次品牌色要点开二十个模块。
五、三个真实坑位
改 id 等于让已保存的配置失联。 settings_data.json 用 id 作键。你把 color_primary 改成 brand_color,商家原来选的颜色就找不到了,页面静默回落到 default。要改就保留旧 id,或在发版说明里写清需要重设。
select 的 value 也是存储值。 改 value 和改 id 一样会失联,改 label 才是安全的。
默认值必须是可用的成品。 商家装上主题、什么都没配的那一刻,页面应该就是能看的。所有 default 合起来要构成一套完整可用的配置,而不是一堆占位符。
六、不要做的四件事
这几条在旧教程里很常见,但在 Shopify 主题里是错的:
用 JavaScript 在运行时”校验”主题设置。 设置是构建期由 Liquid 输出到 HTML 的,等页面跑起来再用 JS 检查颜色格式合不合法、必填项有没有填,既救不了任何东西,还要求你额外把设置塞到 window 上。该做的是在 schema 里用对类型、给对 default,并在 Liquid 里做 != blank 兜底。
做”设置迁移脚本”或”设置版本管理”。 主题体系里没有这套机制。设置的兼容性靠保持 id 稳定来实现,而不是靠运行时迁移代码。
用设置开关去动态加载第三方 CDN 脚本。 比如加一个”启用图片懒加载”的 checkbox,然后从 CDN 拉一个懒加载库——原生 loading="lazy" 早已通用,这个开关唯一的作用是让站点多一个外部依赖和一次额外的建连。
把每个视觉细节都做成设置。 每个设置都是一次商家的决策成本加一处你的分支。能用设计语言统一的(圆角、间距、字号阶梯)就定成基线,不要逐个模块开放。
七、上线自检
settings_schema.json是合法 JSON,且第一项是完整的theme_info;- 每个可空设置在模板里都有兜底分支;
- 空值判断用
!= blank(尤其inline_richtext); - 资源型设置按对象用,没有多余的 handle 查表;
- 数值走 CSS 变量,
{% stylesheet %}里没有 Liquid; - 全站一份的值才在全局,模块级的值在 section / block,商品级的用元字段;
- 配色通过
color_scheme_group定义、由 section 选用; - 所有
label与用户可见文案可翻译,必要时用t:键; - 全部
default合起来是一套开箱可用的配置; - 这次改动没有变更任何已发布过的
id或select的value。
八、延伸阅读
- 自定义分区开发 — section 与 block 级设置怎么写
- 布局模板开发 — 全局资源在 layout 里的位置
- Liquid 过滤器 —
image_url、image_tag、颜色类过滤器 - 本地化与多语言 — 设置文案的翻译
- Shopify 元字段与元对象 — 商品级数据的正确载体
- 主题开发实践 — 交付与维护约定
九、官方文档
小结:先用”整站是否只有一份”这条标准划分全局设置与模块设置,商品级数据交给元字段。写 schema 前先确认取值类型——资源型设置返回对象而非 handle,
inline_richtext的空值是空对象所以必须用!= blank。settings_data.json以id为键,因此改id或select的value等于丢配置,这是版本兼容的唯一抓手。最后,不要用 JS 运行时校验设置、不要写”设置迁移脚本”、不要用设置开关去拉第三方 CDN 脚本——这三件事在主题体系里都没有意义。