使用 Shopify 在 Hydrogen 站点上安装
一些先决条件和注意事项:
- 这是一般指南。所提供的代码可能需要进行调整才能与您的特定 Hydrogen 版本和配置兼容。
- Aimtell Shopify 插件必须通过 Shopify 后台安装。这是确保在 Aimtell 中正确设置 Shopify 网站并添加 Shopify 事件 Webhooks 所必需的。 除了下面的步骤 3 之外,还要完成此步骤。
- 必须上传 Service Worker,并在 Hydrogen 中安装标准 Aimtell 跟踪代码。 (有关安装选项,请参阅 Aimtell 文档)
- 跟踪第三方分析需要配置同意(有关详细信息,请参阅 Shopify 文档)
- Aimtell 仪表板中 Shopify 设置中的放弃浏览设置不会启用/禁用跟踪或更改 Hydrogen 的延迟; Hydrogen 的设置在“_aimtellShopifyWatchBrowse()”函数中定义(如下面的“AimtellJsTags.jsx”组件所示),并且应与仪表板中所需的设置相匹配:
javascript
// Shopify abandoned browse settings
// enable or disable browse abandonment tracking
var _aimtellShopifyAbandonedBrowse = true;
// flag to prevent multiple tracking events, defaults to false
var _aimtellShopifyBrowseAbandoned = false;
// convert minutes to milliseconds for setTimeout, default 10 minutes
var _aimtellShopifyAbandonedBrowseDelay = 10 * 60000;
var _aimtellTimeout;要在 Hydrogen 中设置 Aimtell Shopify 分析跟踪:
如果使用“Hydrogen”版本“2024.4.3”或更高版本,请跳到下一步,否则使用 Shopify 文档 配置分析跟踪。
按照步骤添加下面详述的自定义分析组件。
创建一个组件来订阅 Shopify 分析事件并触发相应的 Aimtell 事件(下例中的“AimtellJsTags.jsx”)。
jsx
import {useAnalytics} from '@shopify/hydrogen';
import {useEffect} from 'react';
export function AimtellJsTags({checkoutDomain}) {
const {subscribe, register} = useAnalytics();
// Register this analytics integration - this will prevent any analytics events
// from being sent until this integration is ready
const {ready} = register('Aimtell');
useEffect(() => {
// Check cart for items
function _aimtellShopifyCartChecker(cartData){
const cart = cartData;
// If there's no cart data and we can't get the cart token, return false
if(!cart){
return false;
}
const cartToken = cart.id.split('/').pop();
const products = cart.lines.edges;
// If there are items in cart, loop through each item
if(cart.totalQuantity > 0){
// Record cart information for add to cart features
if(!_aimtellGetCookie('_aimtellCartToken-' + cartToken)){
var _aimtellShopifyData = {};
_aimtellShopifyData.subscriber = _aimtellSubscriberID;
_aimtellShopifyData.cart = cartToken;
_aimtellShopifyData.idSite = _at.idSite;
_aimtellShopifyData.owner_uid = _at.owner;
_aimtellShopifyData.item_count = cart.totalQuantity;
// Grab the first item and use the variables
try {
var selected_options_query_string = '';
var first_product = products[0].node.merchandise;
if(first_product.selectedOptions.length > 0){
for (var i = 0; i < first_product.selectedOptions.length; i++) {
(i > 0) ? selected_options_query_string += '&' : selected_options_query_string += '?';
selected_options_query_string += first_product.selectedOptions[i].name + '=' + first_product.selectedOptions[i].value;
}
}
_aimtellShopifyData.variables = {};
_aimtellShopifyData.variables.price = (first_product.price.amount);
_aimtellShopifyData.variables.url = window.location.host + '/products/' + first_product.product.handle + selected_options_query_string;
_aimtellShopifyData.variables.icon = first_product.image.url;
_aimtellShopifyData.variables.title = first_product.product.title + ' - ' + first_product.title;
} catch (err) {
console.warn('[aimtell]' + err);
}
var postData = JSON.stringify(_aimtellShopifyData);
var postURL = _aimtellAPI+'/shopify/cookie';
// Fire off call to record data
var xmlhttp = new XMLHttpRequest();
xmlhttp.open('POST', postURL,true);
xmlhttp.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8');
xmlhttp.send(postData);
// Mark token as recorded
_aimtellSetCookie('_aimtellCartToken-' + cartToken, true, 1)
}
}
// Track each item in the cart as an event
for (var i = 0; i < products.length; i++) {
// Get the product id
var product_id = products[i].node.merchandise.id.split('/').pop();
// Only track each item once
if(product_id != undefined && !_aimtellGetCookie('_aimtellAddToCart-' + product_id)){
_aimtellTrackEvent('Item', 'Add To Cart', products[i].node.merchandise.product.title)
_aimtellSetCookie('_aimtellAddToCart-' + product_id, true, 1)
}
}
}
// Watch for browse abandonment
function _aimtellShopifyWatchBrowse(){
// Shopify abandoned browse settings
var _aimtellShopifyAbandonedBrowse = true; // Enable or disable browse abandonment tracking
var _aimtellShopifyBrowseAbandoned = false; // Flag to prevent multiple tracking events, defaults to false
var _aimtellShopifyAbandonedBrowseDelay = 10 * 60000; // Convert minutes to milliseconds for setTimeout, default 10 minutes
var _aimtellTimeout;
async function _aimtellGetTrackProduct() {
console.log('[aimtell] idle timer expired, tracking');
await _aimtellGetShopifyProductDetails(checkoutDomain).then(product_data =>{
if(product_data && typeof product_data == 'object'){
if (product_data.available && typeof product_data.details == 'object') {
_aimtellTrackEvent('Aimtell', 'Shopify-Page-Inactive', 'https://' + checkoutDomain + window.location.pathname, null, product_data.details);
// Remove event listeners for and set flag
_aimtellShopifyBrowseAbandoned = true;
const eventListeners = ['load', 'mousemove', 'keydown'];
eventListeners.forEach(function(event) {
window.removeEventListener(event, _aimtellResetTimer);
});
console.log('[aimtell] tracked browse abandon event');
}else{
console.log('[aimtell] product not available');
console.log('[aimtell] browse abandon event not tracked');
}
}else{
console.log('[aimtell] no product data available');
console.log('[aimtell] browse abandon event not tracked');
}
}).catch(error =>{
console.error('[aimtell] error fetching product details: ' + error);
console.log('[aimtell] browse abandon event not tracked');
});
}
async function _aimtellGetShopifyProductDetails() {
// Get product information and availability
var product_data = {};
product_data.available = false;
product_data.details = {};
try {
const product_availability_check = await fetch('https://' + checkoutDomain + window.location.pathname + '.js');
if (product_availability_check.ok) {
var product_availability = await product_availability_check.json();
product_data.available = product_availability.available;
if (product_data.available) {
const product_details_check = await fetch('https://' + checkoutDomain + window.location.pathname + '.json');
if(product_details_check.ok){
var product_details = await product_details_check.json();
if (product_details.product.image && product_details.product.image.src) product_data.details.icon = product_details.product.image.src;
product_data.details.price = product_details.product.variants && product_details.product.variants[0] && product_details.product.variants[0].price;
product_data.details.url = window.location.origin + window.location.pathname;
product_data.details.title = product_details.product.title;
}
}
}
} catch (error) {
console.error('[aimtell] error fetching product details: ' + error);
}
return product_data;
}
function _aimtellResetTimer() {
// Only run on product pages
if (window.location.href.indexOf('products') > 0) {
clearTimeout(_aimtellTimeout);
_aimtellTimeout = setTimeout(_aimtellGetTrackProduct, _aimtellShopifyAbandonedBrowseDelay);
}
}
if(_aimtellShopifyAbandonedBrowse && !_aimtellShopifyBrowseAbandoned){
console.log('[aimtell] Shopify browse abandonment enabled, with ' + _aimtellShopifyAbandonedBrowseDelay/60000 + ' minute delay');
// Add event listeners
const eventListeners = ['load', 'mousemove', 'keydown'];
eventListeners.forEach(function(event) {
window.addEventListener(event, _aimtellResetTimer);
});
}
}
// Make sure aimtell is loaded before tracking
function _aimtellTrackShopifyAnalytics(data, event, counter = 0){
counter++;
if(counter > 20){
// If Aimtell doesn't load after 20 attempts, return false
return false;
}
// Check if the necessary Aimtell dependencies are loaded
if (typeof _aimtellGetCookie === 'undefined' || typeof _aimtellTrackData == 'undefined') {
return setTimeout(() => _aimtellTrackShopifyAnalytics(data, event, counter), 250);
}
// Check the cart unless it's a product view since product pages also trigger the page viewed event
if(event !== 'product_viewed'){
_aimtellShopifyCartChecker(data.cart);
}
// Watch for browse abandonment on product pages
else {
_aimtellShopifyWatchBrowse();
}
return true;
}
// Subscribe to Shopify analytics events
subscribe('page_viewed', (data) => {
_aimtellTrackShopifyAnalytics(data, 'page_viewed');
});
subscribe('product_viewed', (data) => {
_aimtellTrackShopifyAnalytics(data, 'product_viewed');
});
subscribe('cart_viewed', (data) => {
_aimtellTrackShopifyAnalytics(data, 'cart_viewed');
});
subscribe('cart_updated', (data) => {
_aimtellTrackShopifyAnalytics(data, 'cart_updated');
});
// Mark the integration as ready
ready();
}, []);
return null;
}- 将组件导入到
root.jsx文件中。
jsx
import {AimtellJsTags} from './components/AimtellJsTags';确认已根据 Shopify 文档 配置同意(如果未设置,则不会触发分析事件)。
将“PUBLIC_CHECKOUT_DOMAIN”环境变量添加到“root.jsx”中的加载器数据对象(下例中的“checkoutDomain”)。请注意,这是您的 Shopify 网站结帐的 URL,而不是 Hydrogen 商店 URL。 (例如“https://yourshopifystore.myshopify.com”与“https://yourshopifystore.com”)
javascript
checkoutDomain: env.PUBLIC_CHECKOUT_DOMAIN,它将被传递到分析组件中,以获取废弃浏览脚本中的产品可用性和详细信息。 (参见Shopify 文档)
javascript
const product_availability_check = await fetch('https://' + checkoutDomain + window.location.pathname + '.js');- 添加“AimtellJsTags”组件作为“Analytics.Provider”组件的子组件(在“PageLayout”组件之后)。
jsx
<Analytics.Provider
cart={data.cart}
shop={data.shop}
consent={data.consent}>
<PageLayout {...data}>
{children}
</PageLayout>
<AimtellJsTags checkoutDomain={data.checkoutDomain} />
</Analytics.Provider>就是这样,你应该可以走了!如果您有任何疑问,请联系 support@aimtell.com。
