Skip to content

使用 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 分析跟踪:

  1. 如果使用“Hydrogen”版本“2024.4.3”或更高版本,请跳到下一步,否则使用 Shopify 文档 配置分析跟踪。

  2. 按照步骤添加下面详述的自定义分析组件。

  3. 创建一个组件来订阅 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;
}
  1. 将组件导入到 root.jsx 文件中。
jsx
import {AimtellJsTags} from './components/AimtellJsTags';
  1. 确认已根据 Shopify 文档 配置同意(如果未设置,则不会触发分析事件)。

  2. 将“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');
  1. 添加“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