# JS / CSS 标签

> Shoplazza 主题的 JS / CSS 何时用 script_tag、stylesheet_tag 走 assets 文件，何时打包进 section，以及如何避免阻塞渲染。

主题有两种方式引入 JavaScript 和 CSS：用 `script_tag`、`stylesheet_tag` 过滤器引入 asset 文件，以及用 `{% javascript %}`、`{% stylesheet %}` 标签把代码打包在 section 里。用对了页面渲染快，用错了会悄悄阻塞渲染。本文讲两种机制的正确用法，以及怎么把体积压下来。

## 选对引入方式

- **全主题共用的资源**——全局样式表、公共脚本——放在 `assets` 目录，在布局里用 [`stylesheet_tag`](/docs/theme/references/liquid/filters/html#stylesheet_tag) 或 [`script_tag`](/docs/theme/references/liquid/filters/html#script_tag) 引入。布局里已经引入的资源，不要在模板或 section 里再引一遍。
- **section 专属的代码**放在 section 文件自身的 [`{% stylesheet %}` 和 `{% javascript %}` 标签](/docs/theme/references/liquid/tags/theme-tags#javascript)里。section 保持自包含、可跨主题移植，不渲染这个 section 的页面也不用为它的代码买单。

## 正确使用 `stylesheet_tag`

`stylesheet_tag` 生成标准的 `<link rel="stylesheet">` 标签：

```liquid
{{ 'theme.css' | shoplaza_asset_url | stylesheet_tag }}
```

```html title="输出"
<link href="//static.shoplazza.com/themes/theme-589f053bba.css" rel="stylesheet" type="text/css" media="all" />
```

注意输出里的 `media="all"`。[`media`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#media) 是 `<link>` 标签的标准 HTML 属性，表示这份样式表在什么场景下生效：`all` 是默认值（任何场景都生效），`screen` 表示只在屏幕上浏览时生效，`print` 表示只在访客打印页面或导出 PDF 时生效。

`stylesheet_tag` 唯一能配置的就是这个属性的值——在冒号后直接写，它会被原样填进 `media`：

```liquid
{{ 'theme.css' | shoplaza_asset_url | stylesheet_tag: 'print' }}
```

```html title="输出"
<link href="//static.shoplazza.com/themes/theme-589f053bba.css" rel="stylesheet" type="text/css" media="print" />
```

上面这份 CSS 只在打印时应用。这么标除了语义正确，还有性能收益：浏览器发现样式表的 `media` 和当前场景不匹配（在屏幕上浏览时遇到 `print` 样式），会降低它的下载优先级，而且不阻塞页面渲染——页面能更快显示出来。

:::warning
参数只能像上面这样直接写值。`stylesheet_tag` 没有 `preload` 或其他「参数名: 值」形式的选项——`stylesheet_tag: preload: true` 这样的调用不会报错，而是把整个参数塞进 `media` 属性，输出坏标签（`media="Array"`）。
:::

## 别让脚本阻塞渲染

`script_tag` 生成的始终是阻塞解析的标签——而且它会忽略任何参数，没有办法让它变成延迟加载：

```html
<script src="//static.shoplazza.com/theme.js" type="text/javascript"></script>
```

阻塞解析的脚本会让浏览器停止构建页面，直到脚本下载执行完毕，直接拖慢首屏。`script_tag` 只留给极少数确实必须在页面渲染前运行的脚本；其余的用 `shoplaza_asset_url` 拼 URL、自己写标签：

```liquid
<script src="{{ 'theme.js' | shoplaza_asset_url }}" defer></script>
```

执行顺序重要时用 `defer`（解析完成后按文档顺序执行），无所谓顺序时用 `async`。无法延迟的脚本放在 `body` 末尾，不放 `head`。

## 用 section 标签写自包含代码

在 section 里，`{% stylesheet %}` 的内容注入在 section 标记之前，`{% javascript %}` 的内容注入在 `</body>` 之前——加载行为见 [section assets](/docs/theme/architecture/sections/section-assets)。

两条实践保证安全：

- **JavaScript 包进自执行函数。** 变量留在闭包内，一个 section 的运行时异常不会波及其他 section：

  ```liquid
  {% javascript %}
    (function () {
      const el = document.querySelector('.product-detail');
      // ...
    })();
  {% endjavascript %}
  ```

- **`{% stylesheet %}` 内可以使用 Liquid**，可以直接引用主题设置：

  ```liquid
  {% stylesheet %}
    .promo-banner {
      background: {{ settings.color_btn_bg }};
    }
  {% endstylesheet %}
  ```

  即便如此，全局颜色仍建议通过[配色系统的 CSS 变量](/docs/theme/best-practices/color-system)流转（`background: var(--color-btn-bg)`），而不是在每个 section 里读 `settings`——单一来源，换色一致。

## 把体积压下来

- 压缩 `assets` 里发布的 JavaScript 和 CSS 文件。
- 视觉行为能用 CSS 就不用 JavaScript——过渡、开关、简单交互很少需要脚本。
- 别为小任务引入重型库；现代 DOM API 能覆盖主题的大多数需求，而每一 KB 都会发给每个访客。
- 页面专属的代码拆成独立 asset、只在用到它的模板里引入，不要打成一个到处加载的大文件。
