如何使用 Lighthouse 得分完美的分数构建一个电子商务网站
如今的消费者比以往任何时候都更加苛刻,尤其是在网上购物方面。这些体验必须让人感觉直观且快速。即使是 加载时间延迟 100 毫秒也会使转化率降低 7%。
我们使用 商品商店 (此处为源代码),它使用 Fresh 和 Shopify 的店面 API 构建,是服务器端渲染的 (SSR),包含一些 交互岛屿,并且部署在靠近用户的边缘。只发送客户端所需的内容可以使网站保持精简和快速,并获得完美的 Lighthouse 分数。
本教程介绍了如何使用 Fresh 和 Shopify 构建一个 Lighthouse 得分完美的分数的电子商务网站。
Fresh
我们使用了 Fresh,这是一个边缘优先的 Web 开发框架,默认情况下不会向客户端发送任何 JavaScript。在需要交互性的情况下,例如产品详情页面的图片轮播或购物车,Fresh 使用岛屿架构,在组件基础上发送客户端 JavaScript。
除了某些交互岛屿外,一切都作为静态 HTML 进行服务器端渲染。
对于商店后端,我们使用了 Shopify。它的店面 API 可以检索库存数据,跟踪用户的购物车,并处理结账和支付。
Fresh 使用文件系统路由系统。为了更好地理解它的工作原理,让我们看一下目录结构。
merch/
├── README.md
├── deno.json
├── dev.ts
├── fresh.gen.ts
├── import_map.json
├── main.ts
├── components
├── islands
│ ├── AddToCart.tsx
│ ├── Cart.tsx
│ └── ProductDetails.tsx
├── routes
│ ├── api
│ │ └── shopify.ts
│ ├── products
│ │ └── [product].tsx
│ ├── _app.tsx
│ └── index.tsx
├── static
│ ├── favicon.ico
│ └── logo.svg
└── utils
routes/
和 islands/
是两个主要子目录,其中包含服务器端渲染和岛屿的逻辑。
路由
routes/
文件夹中的每个 .tsx 文件都服务器端渲染一个页面或公开一个 API 端点。
首页或 https://merch.deno.com 是 index.tsx
。当发出请求时,边缘服务器在 handler
函数中从 Shopify 检索数据,然后通过 Home
组件进行渲染。
products/[product].tsx
文件动态生成一个服务器端渲染的产品页面。路径中 [product]
的值通过参数 ctx
(ctx.params.product
) 在 handler
函数中访问。例如,当对 https://merch.deno.com/products/sticker-sheet 发出请求时,handler
函数获取值 sticker-sheet
,从 Shopify 检索产品数据,然后通过 ProductPage
组件进行渲染。
最后,api/shopify.ts
公开更新购物车的编程访问权限。每次用户修改购物车时,请求都不会直接从用户的浏览器发送到 Shopify。相反,请求会通过此端点,然后由它处理并转发到 Shopify。(有关此内容的更多信息,请参见下面的 Shopify 部分。)
岛屿
电子商务网站不能完全是静态的,因为某些组件(如购物车)需要交互性。我们可以在 islands/
中添加这些交互式组件。
例如,在每个产品页面上,当用户单击箭头时,图像会旋转
此客户端渲染行为在 ProductDetails.tsx
中定义,我们在其中声明 changeImage
函数并将该函数绑定到 ProductDetails
组件中的 onClick
。
类似地,其他交互式元素包括添加到购物车和更新购物车,其客户端逻辑分别可以在 AddToCart.tsx
和 Cart.tsx
中找到。在每个岛屿组件中,我们定义函数并将它们绑定到 onClick
监听器。
请注意,为了管理客户端上的状态,我们从 preact/hooks
中导入并使用 useState
,这会在状态更改时触发重新渲染。
Shopify 店面 API
此店面与 Shopify API 交互的两种主要方式。
首先是从 Shopify 检索库存数据。这是一个简单的 graphql GET
请求,它在每个 /routes
组件文件中的 handler
函数中执行
// ./routes/index.tsx
const q = `{
products(first: 10) {
nodes {
id
handle
title
featuredImage {
url(transform: {preferredContentType: WEBP, maxWidth:400, maxHeight:400})
altText
}
priceRange {
minVariantPrice {
amount
currencyCode
}
maxVariantPrice {
amount
currencyCode
}
}
}
}
}`;
export const handler: Handlers<Data> = {
async GET(_req, ctx) {
const data = await graphql<Data>(q);
return ctx.render(data); // This function passes `data` to the component.
},
};
第二是更新 Shopify 的购物车。这分为三个部分
graphql
包装函数 (/utils/shopify.ts
),它向请求添加身份验证和其他相关标头- 一些
/utils/data.ts
中的辅助函数,它将查询抽象成人类可读的函数 - 我们的端点
/routes/api/shopify.ts
,它接收来自客户端的查询和输入(例如,当用户将产品添加到购物车时),并通过graphql
包装函数创建对 Shopify API 的请求,该函数向请求添加身份验证,并最终将其发送到 Shopify 的 API。
当用户与购物车进行交互时,相关的 islands/
组件将调用辅助函数(例如 addToCart
)。该函数将使用相应的查询和负载调用我们的 /api/shopify
端点,然后调用我们的 graphql
包装函数,该函数向请求添加身份验证,并最终将其发送到 Shopify 的 API。
图像优化
为了获得完美的 Lighthouse 分数,务必注意从服务器接收的每个文件的大小。
Shopify 的店面 API 可以 对我们的图像应用转换。在下面这段 graphql 查询代码片段中,我们请求 尺寸为 400x400、内容类型为 WEBP
的图像。
featuredImage {
url(transform: {preferredContentType: WEBP, maxWidth:400, maxHeight:400})
altText
}
将您的网站部署到靠近用户的服务器
您可能拥有最快的商店,但如果您的用户距离您的服务器数千英里,那么您网站的首次字节时间仍然会受到光速的影响。
为了进一步减少延迟,我们使用 Deno Deploy 在全球范围内将我们的商品商店托管在边缘。
每次有人访问我们的商店,最近的边缘服务器都会收到一个GET
请求,向Shopify请求相关数据,将其渲染成HTML,然后发送回来。凭借34个全球位置,任何用户距离您的网站都不会超过几毫秒。
看看在Shopify中更新产品后,我们的商店显示新产品图片的速度有多快
设置Deno Deploy就像连接您的GitHub、选择仓库和入口文件一样简单
每次您合并到主分支,它都会在几秒钟内在Deno Deploy上更新。
接下来是什么?
虽然构建网页变得更容易,但在某些方面它比以往任何时候都更加复杂。我们必须支持各种屏幕尺寸和互联网速度。虽然我们无法控制用户是在笔记本电脑上还是在隧道里的火车上,但我们可以控制从服务器发送的内容。
我们已经构建并开源了我们的商品商店,向每个人展示创建一个拥有完美 Lighthouse 得分的电子商务商店是多么简单(而且有趣!)。