Javascript 性能和网站速度是每个人都关心的事情。你的老板、你的客户,每个人都会要求你让他们的在线商店更快。
作为一名工程师,您将承担这项任务,但经常会发现自己陷入代码混乱之中。事实上,网站速度没有灵丹妙药。你尝试的每件事都只会给你带来一瞬间的进步。找到那个大的改进就像寻宝一样。
有许多资源可以帮助您找到解决方案,但每个电子商务平台都有其挑战。在本文中,我想提出一种固执己见的方法来处理站点清理或重构,从不合时宜的代码组织开始。这六个步骤将帮助您重构 Shopify 站点以提高 Shopify 商家的性能。
通过 Shopify 合作伙伴计划发展您的业务
无论您是提供营销、定制还是网页设计和开发服务,Shopify 合作伙伴计划都会助您取得成功。免费加入并获得收益分享机会、发展业务的工具以及充满激情的商业社区。
1.找出最重要的资产
设身处地为客户的客户着想。为了产生购买意向,您需要看到的最重要的资产是什么?是否能够在页面加载后立即点击购买按钮?或者是显示产品外观的图像?在电子商务网站的大多数情况下,展示商家产品的图片是最重要的——客户希望看到他们购买的是什么。
如果网站的布局是用脚本构建的,例如单页应用程序,这确实会改变,但就本文而言,我将图像视为网站上最重要的资产。
您可能还喜欢: 通过缩小改进商店加载时间的 5 种方法。
2. 将 Javascript 保留在预先确定的位置
在我们删除任何未使用的代码或开始四处移动之前,我们需要首先使用最佳性能实践定义 Javascript 在任何给定文档中的位置。如果您以前没有,我建议您阅读以下资源:
如果您已阅读这些建议,但仍然不知道如何将这些建议应用于 Shopify 网站,请继续阅读。
定义 Javascript 应该在哪里很重要,这样我们才能发现脚本不应该在哪里。Javascript 应该只存在三个文档区域:
</head>
标签前<body>
标签后</body>
标签前
由于我们已将图像确定为最重要的资产,因此大多数 Javascript 应该存在于标签之前</body>
,除非您有充分的理由不这样做。我将对每个脚本部分进行推理。
“要警惕剧本的放置位置,即使你不是把它放在那里的人。”
<head>
标签中允许的脚本
每个第三方供应商都会告诉您他们的脚本需要位于标签的最顶部<head>
,但您可以给他们Terminator stop hand。标记中的脚本<head>
呈现阻塞。这意味着在浏览器完成解析您的标签中的内容之前,访问者将无法看到您漂亮的网站<head>
。
那么标签中允许有哪些<head>
脚本呢?
没有任何。
Shopify 平台是一个使用Liquid模板的服务器端框架。下载源文档的那一刻,应该足以渲染最关键的第一个渲染视图,不需要任何 Javascript 库来填补空白(外部 CSS 样式除外)。
这并不意味着任何网站都不应在标记中包含脚本head
。以下是一些脚本有正当理由留在头脑中的例子:
-
- 单页应用程序。如果单页应用程序与服务器端渲染相结合,那就更好了。
- 不触发下载外部脚本并执行时间敏感操作的内联脚本,例如:
<script type=”text/javascript”>
window.performance && window.performance.mark('head_start');
</script>
如果确实有一些脚本需要放在头部,那么其中的大部分应该就在结束标记结束之前</head>
。这确保站点针对浏览器资源优先级优化进行了优化。
还有一件事。关于使用async
or的脚本存在误解defer
。一个包含的脚本async
告诉浏览器这个脚本可以乱序执行,这使得这个脚本不会渲染阻塞。这意味着async
一旦脚本资源完成下载,脚本就会执行代码。脚本将defer
Javascript 的执行推到最后。然而,这两种类型的脚本声明仍然会影响下载资源的网络带宽,我们可以利用它来提高性能,以下载最重要的资源进行首次渲染。

<body>
标签后允许的脚本
文档这方面的脚本不是最差的,但也不是最好的。我们仍然以第一个渲染视图为目标。我们越往后推送外部脚本的下载越好。如果您有希望访问者看到的英雄图片或最新产品的集合,这些应该优先于访问者永远不会看到的脚本。
浏览器在资源优先级方面是系统的。使用脚本,浏览器以自上而下的顺序阅读源文档。如果您在英雄图片之前有外部脚本,则外部脚本的下载优先级高于英雄图片。
</body>
标签前允许的脚本
所有脚本都应该在这里。
这包括分析脚本。如果访问者的浏览器甚至无法到达页面的这一点,您的分析有什么用?
您可能还喜欢: 延迟加载如何优化您的 Shopify 主题图片。
3.创建站点基准
这个很重要。没有基准测试,几乎不可能判断您所做的任何事情是否提高了性能。我们可以争辩说,有些改进会产生巨大的变化,但如果没有适当的测量来了解正在发生的事情,您可能会进行令人惊讶的升级,但没人会注意到,因为它们没有改变您老板正在查看的指标。
为了正确测量,我们将使用性能标记。这是世界上大多数浏览器都支持的原生 Javascript API。性能标记需要在它们执行的地方就位。具体来说,我们将测量浏览器解析持续时间:浏览器解析您的 Javascript 所花费的时间。
我们还将使用 Chrome 专门为我们收集的性能指标:paint。
performance.getEntriesByType('paint');
让我们将性能标记放在适当的位置。首先,我们需要了解如何测量解析持续时间。为此,将性能标记放置在 中,如下例所示theme.liquid
。
<!doctype html> | |
<head> | |
<script> | |
window.performance.mark(‘parse_head_start’); | |
function marks (name) { | |
return { | |
name, | |
get start () { | |
return `${this.name}_start`; | |
}, | |
get end () { | |
return `${this.name}_end`; | |
} | |
}; | |
} | |
window.markNames = { | |
head: marks(‘parse_head’), | |
body: marks(‘parse_body’) | |
}; | |
</script> | |
<!– REST OF HEAD CODE –> | |
<script>window.performance.mark(window.markNames.head.end);</script> | |
</head> | |
<body> | |
<script>window.performance.mark(window.markNames.body.start);</script> | |
<!– REST OF BODY CODE –> | |
<script> | |
window.addEventListener(“load”, function(){ | |
Object.keys(window.markNames).forEach((name) => { | |
mark = window.markNames[name] | |
performance.measure(mark.name, mark.start, mark.end); | |
}); | |
window.requestIdleCallback(() => { | |
var getShopifyResourceByType = (type) => { | |
return performance.getEntriesByType(‘resource’).filter((resource) => { | |
return resource.name.indexOf(‘cdn.shopify.com’) != –1 | |
&& resource.initiatorType === type; | |
}) | |
}; | |
// You can send this information to whatever analytics vendor | |
// For now, we are just outputting in console | |
console.log(‘############ PERFORMANCE METRICS ############’); | |
console.log(performance.getEntriesByType(‘measure’)); | |
console.log(performance.getEntriesByType(‘paint’)); | |
}); | |
}); | |
window.performance.mark(window.markNames.body.end); | |
</script> | |
</body> | |
</html> |
您还应该在 ChromeDevTools 中将网络节流设置为Fast 3G和Disable cache。
这应该会在 ChromeDevTools 中输出大量指标。我们将专注于我们刚刚实现的解析指标。

此处的时间测量均来自以毫秒为单位的高分辨率时间戳。我们可以看到浏览器花费了 2563 毫秒(在 Fast 3G 上为 2.6 秒)来解析 head 标签中的任何内容。我们还可以看到,直到浏览器在 3522 毫秒(开始时间 959 毫秒 + 持续时间 2563 毫秒)完成对头部的解析后,第一次绘制才会发生。
这是什么意思?这意味着您的访问者在看到您的网站之前等待了两秒半(减去网络时间)的白屏(也就是什么都没有)。
目标 1:减少 head 标签中的解析时间
假设您无法删除网站上的任何脚本,您的目标应该是改变指标以优化性能。第一个绘制指标是一个重要的数字,可以降低性能。然而,阻碍它的不仅仅是 head 标签中的内容——body 标签的部分内容也对此负责,因为浏览器也需要在呈现之前对其进行解析。因此,让我们也将性能标记放在 body 标签中,如下所示:
<body> | |
<script>window.performance.mark(window.markNames.body.start);</script> | |
<script>window.performance.mark(window.markNames.bodyLayout.start);</script> | |
<!– BODY LAYOUT CODE –> | |
<script>window.performance.mark(window.markNames.bodyLayout.end);</script> | |
<script>window.performance.mark(window.markNames.bodyEndScripts.start);</script> | |
<!– END OF BODY SCRIPT CODE –> | |
<script> | |
window.addEventListener(“load”, function(){ | |
Object.keys(window.markNames).forEach((name) => { | |
mark = window.markNames[name] | |
performance.measure(mark.name, mark.start, mark.end); | |
}); | |
window.requestIdleCallback(() => { | |
var getShopifyResourceByType = (type) => { | |
return performance.getEntriesByType(‘resource’).filter((resource) => { | |
return resource.name.indexOf(‘cdn.shopify.com’) != –1 | |
&& resource.initiatorType === type; | |
}) | |
}; | |
// You can send this information to whatever analytics vendor | |
// For now, we are just outputting in console | |
console.log(‘############ PERFORMANCE METRICS ############’); | |
console.log(performance.getEntriesByType(‘measure’)); | |
console.log(performance.getEntriesByType(‘paint’)); | |
console.log(performance.getEntriesByType(‘navigation’)); | |
console.log(getShopifyResourceByType(‘script’)); | |
console.log(getShopifyResourceByType(‘link’)); | |
console.log(getShopifyResourceByType(‘css’)); | |
console.log(getShopifyResourceByType(‘img’)); | |
}); | |
}); | |
window.performance.mark(window.markNames.bodyEndScripts.end); | |
window.performance.mark(window.markNames.body.end); | |
</script> | |
</body> |
添加后,我们应该看到以下内容:

从这里我们可以看出,第一次绘制是在解析头部之后,但在完成解析身体之前的某个时间。如果我们可以减少主体布局部分的解析持续时间,它应该有助于降低第一个绘制值。
目标 2:减少正文布局中的解析时间
考虑到这两个目标,让我们在这里了解一些数学知识。如果我们无法删除项目中的任何代码行,我们可以做些什么来减少 head 和 body 布局中的解析时间?答案是将解析持续时间推到主体末尾附近的主体脚本。让浏览器首先解析重要的内容,以便它可以尽快呈现它。
持续时间(基准) | 期望的结果 | |
头 | 2560 毫秒 | 在这里花的时间更少 |
车身布局 | 2107 毫秒 | 在这里花的时间更少 |
结束正文脚本 | 17 毫秒 | 更多时间在这里度过 |
第一次油漆 | 身体布局中的时机 | 快点 |
我们正在尝试更改每个部分中的解析持续时间以最终减少第一次绘制度量时间。
4.清理不属于任何地方的脚本
想象一下你要搬进一所新房子。您通常不会从收拾每天需要使用的衣服开始——您会从几乎不使用的东西开始。同样,在我们将所有脚本移动到页面底部之前,让我们从不属于任何地方的脚本开始:正文中间的脚本。
其中一些脚本依赖于某些 Javascript 库,这使得优化性能变得非常困难。如果我们在不考虑这些依赖关系的情况下将这些脚本移动到页面底部,这会使我们面临破坏站点的风险。在 Shopify 主题中,正文中间的脚本包括位于任何 Liquid 文件中的任何 Javascript,布局 Liquid 文件除外。所以不要试图一次解决所有问题——选择一场战斗并从那里开始。
为了让事情变得更简单,选择一个页面并从您在开始 body 标签之后遇到的第一个脚本开始,该标签不在布局 Liquid 文件中。放松,这就像您第一次进入草地时玩口袋妖怪游戏一样。
初遇
你找到了你的第一个敌人——我的意思是,剧本。首先,我希望你拿出你的卷尺和放大镜,真正理解这个脚本在做什么。
基准脚本
除了现在我们将专门针对给定的脚本进行测量外,我们将完全按照之前对性能标记所做的操作,如以下示例所示:
<script>
window.performance.mark(window.markNames.menuScriptParse.start);
<!-- REST OF SCRIPT CODE -->
window.performance.mark(window.markNames.menuScriptParse.end);
</script>
上面的例子将测量这个特定脚本的解析持续时间。不要忘记在文件中添加新的性能标记名称theme.liquid
:
window.markNames = { | |
head: marks(‘parse_head’), | |
body: marks(‘parse_body’), | |
bodyLayout: marks(‘parse_body_layout’), | |
bodyEndScripts: marks(‘parse_body_end_scripts’), | |
menuScriptParse: marks(‘parse_menuScript’), | |
}; |
在控制台日志中,我们会看到类似这样的内容:

我们可以看到浏览器花了大约 12 毫秒来解析。这似乎还不错。关键是确定我们有多少。如果我们有 200 个这样的脚本在四处漫游,那就会增加两秒的持续时间。
查找脚本依赖项
在我们迁移这个脚本的位置之前,我们需要找到它的所有依赖项。这些可以在任何地方。以下是一些需要寻找的线索:
$('css_selector')
查询。- 函数调用无处可去。搜索声明此函数的位置。
{{ * }}
液体标签。
在脚本开头记录依赖关系。当我们开始移动代码时,这会派上用场。
<script> | |
// Dependencies | |
// – JQuery | |
// – assets/simplistic.js – getParameterByName | |
// – assets/cookies.js – window.Cookies | |
// – assets/lazysize.min.js – lazySizes.init | |
window.performance.mark(window.markNames.menuScriptParse.start); | |
// JS codes | |
</script> |
脚本中有Liquid标签怎么办
如果Liquid标签是全局标签,把这段代码移到theme.liquid
就可以了。如果 Liquid 标签与特定模板布局相关联,请使用以下内容包装脚本:
{% if template == 'collection' %}
<script>
<!-- REST OF SCRIPT CODE -->
</script>
{% endif %}
如果脚本代码涉及对某处代码存在的复杂依赖性,这就是您开始重构代码的地方,以便您可以将其移出 Liquid 文件。
移动该代码!
确定所有脚本依赖项后,就可以移动代码了。我们的目标是将所有代码移动到 body 标签的末尾。为了保持脚本执行的原始顺序(在大多数情况下很重要,除非你想出如何重构任何不依赖于顺序的东西),将它留在主体结束脚本的性能标记之后,如下所示:
<script>window.performance.mark(window.markNames.bodyEndScripts.start);</script> | |
<script> | |
// Main Menu Script | |
// JQuery | |
// assets/simplistic.js – getParameterByName | |
// assets/cookies.js – window.Cookies | |
// assets/lazysize.min.js – lazySizes.init | |
window.performance.mark(window.markNames.menuScriptParse.start); |
逐步对您的更改进行基准测试
移动代码后,通过检查您预期发生的事情是否已经发生来确保您没有破坏页面。
解析头
开始时间 |
解析
头 |
解析体
布局 |
解析体
结束脚本 |
第一的
画 |
改进空间(毫秒) | |
根据 | 1090 | 2560 | 2107 | 17 | 4805 | 1155 |
菜单脚本 | 946 | 2546 | 2046 | 24 | 4661 | 1169 |
与基本测量相比,我们可以看到正文布局的解析持续时间减少了,而正文结束脚本的解析持续时间增加了。我们知道菜单脚本的解析持续时间为 10 毫秒,因此这是预期的。第一道油漆也减少了,但现在下定论还为时过早。我们知道第一次绘制可以在浏览器完成解析头部的那一刻发生。
Room for improvement列是从解析头结束时延迟的毫秒数。这是一个很好的指标,可以告诉我们我们是在 50% 的收益还是 1% 的收益之间进行优化。
改进空间= First paint — (parse head start time + parse head duration)
冲洗并重复
现在,对您在正文中间遇到的每个脚本执行相同的操作。
解析磁头开始时间 | 解析头 | 解析正文布局 | 解析主体结束脚本 | 第一次油漆 | 改进空间(毫秒) | |
根据 | 1090 | 2560 | 2107 | 17 | 4805 | 1155 |
菜单脚本 | 946 | 2546 | 2046 | 24 | 4661 | 1169 |
脚本 1 | 891 | 2569 | 2041 | 34 | 4622 | 1162 |
脚本 2 | 928 | 2487 | 2039 | 78 | 4590 | 1175 |
脚本 3 | 972 | 2540 | 2060 | 80 | 4690 | 1178 |
脚本 4 | 1022 | 3147 | 2080 | 118 | 5066 | 897 |
脚本 5 1 | 909 | 2561 | 1553 | 118 | 4716 | 1246 |
脚本 6 | 923 | 2561 | 1428 | 128 | 4734 | 1250 |
脚本 7 | 951 | 2557 | 1416 | 124 | 4746 | 1238 |
脚本 8 | 921 | 2563 | 1325 | 152 | 4782 | 1298 |
脚本 9 | 978 | 2556 | 1308 | 138 | 4825 | 1291 |
脚本 10 2 | 1372 | 2568 | 1145 | 275 | 4052 | 112 |
这确实是一场毫秒之争。在将脚本移动到底部的过程中,进行了一些重构,使正文中间的脚本不依赖于脚本的位置。在上面的结果中,我们可以看到我们已经成功地减少了大约一秒的正文解析持续时间,并将一些解析转移到正文结束脚本。这使得首次绘制时间相对缩短了 8%,约为 320 毫秒。
5.收尾动作
到了最后boss的时候了:将头部的所有脚本移到身体的末尾,同时保持脚本顺序。
解析磁头开始时间 | 解析头 | 解析正文布局 | 解析主体结束脚本 | 第一次油漆 | 改进空间(毫秒) | |
根据 | 1090 | 2560 | 2107 | 17 | 4805 | 1155 |
所有脚本 | 977 | 1193 | 3938 | 161 | 2199 | 29 |
让我们了解这里发生了什么。我们将相当多的外部脚本请求从 head 标签移到了 body 标签。最初呈现阻塞的外部资源不再阻塞。这使得第一次油漆发生得更快。


我们有效地将开始渲染时间缩短了近 40%,而没有丢失任何脚本功能。?
您可能还喜欢: 4 个鲜为人知但功能强大的提高生产力的 Web 开发人员工具。
6.仔细检查
这样做永远不会有坏处。我们仅通过围绕文档移动脚本就获得了惊人的数字。通过网页测试运行它。就我而言,我发现了一个非常有趣的问题。


该页面的首次绘制时间不错,但在十秒标记之前变回空白。发生了什么?
事实证明,该网站实施了Google Optimize 的 A/B 实验框架中的防闪烁代码段。
我们可以选择解决这个问题。我们可以尝试将这个 Google Tag Manager 脚本移回头顶,看看效果如何。

老实说,我认为这不值得。它为第一次绘制增加了整整三秒,即使它是第一个下载的资源,只是为了有可能运行一个实验。有更好的方法来检测 A/B 实验,而无需将整个文档隐藏可能四秒钟,这正是这个防闪烁代码段正在做的事情。
这个问题实际上解释了为什么当我将脚本移出正文时我没有看到渐进的改进。它总是被这个防闪烁片段延迟。
通过 Shopify 合作伙伴计划发展您的业务
无论您是提供营销、定制还是网页设计和开发服务,Shopify 合作伙伴计划都会助您取得成功。免费加入并获得收益分享机会、发展业务的工具以及充满激情的商业社区。
为您的网页带来速度
成功!我们改进了开始渲染时间,所有这些都没有丢失我们需要保留的脚本。修复站点性能取决于我们所有人。随着 Shopify 不断提高店面的网站性能,每个开发人员也应该尽其所能来提高网站性能。它对每个人都有好处。
其他资源
- Addy Osmani 的Javascript 加载优先级
- Tali Garsiel 和 Paul Irish 的浏览器工作原理