EN
Shopify 知识库 · 指南

Dawn 的加载策略:按分区引入资源、首屏图片与按需媒体

基于 Dawn v15.3.0 源码,整理分区级 CSS 与 JS 按条件引入、非关键样式异步加载、字体预连接与预加载、srcset 与 sizes、首屏图片立即加载与 fetchpriority、视频与 3D 延迟挂载、推荐区提前加载,以及 CartPerformance 埋点和几处失效的优化。
历史资料
请结合文中的适用版本和来源阅读。

本文基于 Dawn v15.3.0,源码固定到 ecb06c3(核验于 2026-10-01)。结论限于此提交;本篇为静态源码分析,未测量任何性能指标。Dawn 上游已发布 v16.0.0,本库 Dawn 正文统一以 v15.3.0 为基线,升级前需重新核对。

Dawn 没有打包工具,资源优化全部写在 Liquid 里,由服务端按页面内容决定输出哪些标签。平台层面的指标与优先级见店面性能,本文只记录 Dawn 的具体写法。

一、资源跟着分区走

全局只放公共部分。 布局固定加载 base.css 与 6 个基础脚本(theme.liquid#L31-L36、#L258),其余按全局设置决定:

条件引入的资源位置
animations_reveal_on_scrollanimations.js#L38-L40
cart_type == 'drawer'抽屉相关 5 个 CSS#L261-L267
多个国家或语言本地化表单 CSS 与 JS#L280-L283
predictive_search_enabled预测搜索 CSS(异步)#L285-L292

分区引入自己的样式与脚本。 sections/ 的 52 个文件中有 39 个直接输出 stylesheet_tag。商品主分区还按商品数据进一步筛选(main-product.liquid#L12-L58):只有一个默认变体时不加载变体选择器与色板 CSS;设置了阶梯价才加载阶梯价 CSS 与两个脚本;悬停放大才加载 magnify.js;有 3D 模型才加载模型样式。页面上没有的功能,不会产生请求。

循环中只输出一次。 集合页循环渲染商品卡时,第一张卡片输出卡片样式,之后传入 skip_styles: true(main-collection-product-grid.liquid#L155-L181、card-product.liquid#L23)。

非关键样式异步加载。 media="print" onload="this.media='all'" 让样式表不阻塞渲染,加载完成后再生效(如 theme.liquid#L259)。sections/、snippets/、layout/ 中共 12 处使用这种写法。

脚本一律 defer。 不阻塞解析,按文档顺序执行;组件的服务端 HTML 在脚本执行前已经可见(见组件组织)。

二、字体

写法位置作用
非系统字体时 preconnect 到 fonts.shopifycdn.comtheme.liquid#L14-L16提前建立连接
font_face: font_display: 'swap',含粗体、斜体派生#L44-L55字体加载前先显示后备字体
正文与标题字体 preload#L269-L278尽早下载首屏用到的两个字重

预加载用 theme-check-disable AssetPreload 包住,说明这是有意绕过 Theme Check 的建议。只预加载常规字重,粗体与斜体按需加载。

三、图片

srcset 只列不超过原图的尺寸。 商品卡对 165、360、533、720、940、1066 每个宽度都先判断 featured_media.width >= N,最后补上原图宽度(card-product.liquid#L64-L83),避免让 CDN 生成放大后的图片。同时写明 width、height,浏览器可以预留空间,减少布局偏移。

sizes 由页面宽度设置计算。 页面宽度达到 settings.page_width 后,每张卡片按 (page_width - 130) / 4 计算,窄屏按 4、3、2 列计算。

首屏立即加载,其余懒加载。

四、按需挂载与提前加载

  • 视频与 3D 模型。 DeferredMedia 先只显示海报,真正的 <video>、<iframe>、<model-viewer> 放在 <template> 里,点击后才克隆插入。自托管视频另设 preload: 'none'(product-thumbnail.liquid#L143)。
  • 商品推荐。 ProductRecommendations 用 IntersectionObserver 监听,rootMargin 底部 400px,即离可视区还有 400px 时就发请求,用户滚到时内容大概率已就绪。

五、CartPerformance 埋点

CartPerformance 用 performance.mark / measure 记录购物车交互,名称前缀为 cart-performance:。measureFromEvent 以事件的 timeStamp 作为起点,测量的是从用户点击到界面更新的完整耗时,而不只是请求时间。加购记录 add:user-action、add:wait-for-subscribers、add:paint-updated-sections(product-form.js#L69-L108)。这些测量可在浏览器 Performance 面板中查看,也能被 PerformanceObserver 采集;主题内未找到上报代码。

失效或打折的优化

均为静态阅读,未测量:

  • 缩放动画的节流没有生效。 animations.js#L64 调用 throttle(fn) 时没有传入间隔,throttle 中 now - lastCall < undefined 恒为 false,每次滚动事件都会执行。每个 animate--zoom-in 元素还各自注册一个 scroll 监听,并且从不移除。
  • sizes 固定按 4 列计算。 集合页桌面列数可设为 1 到 6(schema),商品卡的 sizes 却不读取这个设置:设为 2 列时浏览器会选择偏小的图片,设为 6 列时会下载偏大的图片。
  • 快速加购请求整页。 quick-add.js#L28 直接请求商品 URL,不带 section_id,取回包含页头、页脚在内的整页 HTML,只用其中的 product-info。
  • 一个埋点名称带了多余的引号。 cart.js#L165 的模板字符串是 `${eventTarget}:paint-updated-sections"`,生成的测量名称以 " 结尾,按名称筛选时会漏掉。

迁移到自己主题时

  1. 保留“分区自己引入资源,再按商品数据和设置裁剪”的做法;用了打包工具时,也应按分区拆分入口,而不是合并成一个大包。
  2. 首屏判断用 section.index 和循环序号,不要对所有图片统一加 loading="lazy"。
  3. sizes 应读取与布局相同的设置。
  4. 给关键交互加上与 CartPerformance 类似的埋点,并真正上报。

建议的验证范围

本篇均未执行:分别打开有、无变体的商品页,对比 CSS 请求列表;在集合页把列数设为 2 和 6,检查浏览器实际选择的 srcset 尺寸;首页首屏为横幅时确认 LCP 图片带 fetchpriority="high";在 Performance 面板中查看 cart-performance:* 测量。

源码基线