Shopify主题应用扩展中Liquid代码段异步渲染问题求助
问题:无法将JavaScript变量赋值给Shopify Liquid并实现异步渲染
我调用后端API后想渲染Shopify产品,但没法把JavaScript变量赋值给Shopify Liquid文件。以下是我的代码:
<script async defer> const BACKEND_SHOPIFY_URL="/apps/client/upsells"; (async()=>{ try { const response = await fetch(`${BACKEND_SHOPIFY_URL}?position={{ position }}`); const data= await response.json(); {% assign result= "SOMETHING HERE" %} // 需要把result赋值为data } catch(err) { console.log(err,'error'); } })(); </script>
我试过各种方法都没成功,想请教:
- 如何实现异步渲染?
- 了解到fetch过滤器,但不确定怎么在主题应用扩展中导入?
- 既然JavaScript是客户端语言,Liquid是服务端语言,有没有办法重新渲染特定组件?
解决方案
核心原理说明
Liquid是服务端渲染语言,在服务器生成页面HTML时就已经执行完毕;而JavaScript是客户端语言,要等页面加载到浏览器后才会运行。两者完全不在同一执行阶段,所以不可能直接把客户端JS变量赋值给Liquid变量,必须换思路实现需求。
方案1:客户端JS动态渲染DOM
这是最直接的异步渲染方案,步骤如下:
- 在页面上预留一个用于渲染产品的容器
- 调用API拿到数据后,用JS动态生成产品的HTML结构,插入到容器中
示例代码:
<!-- 预留容器 --> <div id="upsell-products-container"></div> <script async defer> const BACKEND_SHOPIFY_URL = "/apps/client/upsells"; const container = document.getElementById('upsell-products-container'); (async () => { try { const response = await fetch(`${BACKEND_SHOPIFY_URL}?position={{ position }}`); const data = await response.json(); // 动态生成产品卡片HTML(结构可参考主题现有产品卡片) let productsHtml = ''; data.forEach(product => { productsHtml += ` <div class="product-card"> <a href="${product.url}"> <img src="${product.image.src}" alt="${product.title}"> <h3>${product.title}</h3> <span class="price">${product.price}</span> </a> </div> `; }); container.innerHTML = productsHtml; } catch(err) { console.log(err, 'error'); container.innerHTML = '<p>推荐商品加载失败</p>'; } })(); </script>
方案2:使用Liquid的fetch过滤器(服务端获取数据)
Liquid自带的fetch过滤器是服务端请求工具,和客户端JS的fetch不是一回事,它能在页面渲染阶段就获取API数据,不需要客户端异步请求。
在主题应用扩展中可以直接使用,无需额外导入,示例:
{% comment %} 服务端调用API获取数据 {% endcomment %} {% assign api_url = '/apps/client/upsells?position=' | append: position %} {% assign upsell_data = api_url | fetch %} {% assign upsell_products = upsell_data | json %} {% comment %} 用Liquid直接渲染产品 {% endcomment %} <div class="upsell-products"> {% for product in upsell_products %} <div class="product-card"> <a href="{{ product.url }}"> <img src="{{ product.image.src }}" alt="{{ product.title }}"> <h3>{{ product.title }}</h3> <span class="price">{{ product.price }}</span> </a> </div> {% endfor %} </div>
注意:此方案要求后端API允许Shopify服务器发起请求(无CORS限制、认证逻辑兼容服务端调用)。
重新渲染特定组件的方法
页面加载后无法重新执行Liquid代码,要重新渲染组件只能通过客户端JS实现:
- 方法一:重新调用API获取数据,再重新生成DOM结构替换原有内容
- 方法二:提前用
<template>标签存储Liquid生成的产品卡片模板,后续用JS克隆模板并填充新数据
示例模板方式代码:
<!-- 预存产品卡片模板 --> <template id="product-card-template"> <div class="product-card"> <a href=""> <img src="" alt=""> <h3></h3> <span class="price"></span> </a> </div> </template> <!-- 渲染容器 --> <div id="upsell-products-container"></div> <script async defer> const BACKEND_SHOPIFY_URL = "/apps/client/upsells"; const container = document.getElementById('upsell-products-container'); const template = document.getElementById('product-card-template'); async function renderUpsellProducts(position) { try { const response = await fetch(`${BACKEND_SHOPIFY_URL}?position=${position}`); const data = await response.json(); // 清空容器 container.innerHTML = ''; // 克隆模板并填充数据 data.forEach(product => { const card = template.content.cloneNode(true); card.querySelector('a').href = product.url; card.querySelector('img').src = product.image.src; card.querySelector('img').alt = product.title; card.querySelector('h3').textContent = product.title; card.querySelector('.price').textContent = product.price; container.appendChild(card); }); } catch(err) { console.log(err, 'error'); container.innerHTML = '<p>推荐商品加载失败</p>'; } } // 首次渲染 renderUpsellProducts({{ position }}); // 后续需要重新渲染时调用:比如用户切换位置后 // renderUpsellProducts(newPosition); </script>
内容的提问来源于stack exchange,提问作者Sanjiv Gautam
相关产品推荐
相关产品推荐

