Improving the speed of a custom infinite scroll

2019-03-01 19:59发布

问题:

I have a custom infinite scroll that is working perfectly but it's really slow. Here is the script that handles the ajax request:-

function ga_infinite_scroll() {//trigger this on infinite scroll
  add_filter( 'woocommerce_get_price_html', 'ga_show_price' );//filter to fix price range
  if(empty($_POST['search_term'] )){
    $params = json_decode( stripslashes( $_POST['query'] ), true );
    $params['post_status'] = 'publish';
    $params['posts_per_page'] = get_option('posts_per_page');
    $params['post_type'] = 'product';
    $params['paged'] = $_POST['page'] + 1; // we need next page to be loaded

  }
  else{//search  logic here
      $search_query = json_decode( stripslashes( $_POST['search_posts'] ), true );
      $search_query['post_status'] = 'publish';
      $search_query['posts_per_page'] = get_option('posts_per_page');
      $search_query['paged'] = $_POST['page'] + 1;
      wc_set_loop_prop( 'total', $_POST['search_count'] );
      $params = $search_query;

  }

  ob_start();           
  query_posts( $params);

  if ( have_posts() ) {//product loop
        if ( wc_get_loop_prop( 'total' ) ) {
              while ( have_posts() ) {
                the_post();
                wc_get_template_part( 'content', 'product' );
              }
            }
    } 

    $data = ob_get_clean();
    die($data); 
    exit;
}
add_action( 'wp_ajax_ga_infinite_scroll', 'ga_infinite_scroll' );
add_action( 'wp_ajax_nopriv_ga_infinite_scroll', 'ga_infinite_scroll' );

Here's another post with my brief description of the problem Improving the performance of a custom developed scroll. Here is the code for ga_show_price.

    function ga_show_price( $price ) {


   global $post, $product, $reg_price_field_slug, $sale_price_field_slug, $user_currency, $wp_query,$wp_object_cache;


   if( count($product->get_children()) !== 0 ) {

       $variations = $product->get_children();
       $regularPriceList = [];
       $salePriceList = [];
       $lowestPrice;
       $salePrice;

     // die("here");
       if( $product->is_on_sale() ) {
           // NOTE: ADD caching HERE!!
           if( false === get_transient( 'sales_price' ) ) {
           foreach( $variations as $variation ) {
               array_push($salePriceList, get_post_meta( $variation, $reg_price_field_slug, true ) );
           }


           set_transient( 'sales_price', $salePriceList, 12 * HOUR_IN_SECONDS );
          }
          else{
            $salePriceList =  get_transient( 'sales_price');
          }
          $salePrice = min($salePriceList);
          $price = add_proper_decimal($salePrice);
           return get_woocommerce_currency_symbol() . $price . ' ' . $user_currency;

       } else {
           // NOTE: ADD caching HERE!!
           if( false === get_transient( 'reg_price' ) ) {
           foreach( $variations as $variation ) {
               array_push($regularPriceList, get_post_meta( $variation, $reg_price_field_slug, true ) );
           }

           set_transient( 'reg_price', $regularPriceList, 12 * HOUR_IN_SECONDS );
          }
          else{
            $regularPriceList =  get_transient( 'reg_price');
          }

           $lowestPrice = min($regularPriceList);
           $price = add_proper_decimal($lowestPrice);



           return get_woocommerce_currency_symbol() . $price . ' ' . $user_currency;
       }
   } else {
       $price = get_post_meta( $post->ID, $reg_price_field_slug, true );
       $price = add_proper_decimal($price); // pr( $price );

       if ( $price == '0.00' ) {
           return 'Call for Price';
       }

       return get_woocommerce_currency_symbol() . $price . ' ' . $user_currency;
   }

}

My javascript is here:-

jQuery(document).ready( function($) {
   var  url = window.location.origin + '/wp-admin/admin-ajax.php',
    canBeLoaded=true,
     bottomOffset = 2000; // the distance (in px) from the page bottom when you want to load more posts

    $(window).scroll(function(){
        var data = {
            'action': 'ga_infinite_scroll',
            'query': my_ajax_object.posts,
            'page' : my_ajax_object.current_page,
            //'search_results' : my_ajax_object.ga_search_results,
            'search_count' : my_ajax_object.ga_search_count,
            'search_posts': my_ajax_object.ga_search_posts,
            'search_term' : my_ajax_object.ga_search_term,
            'user_currency': my_ajax_object.user_currency,
            'reg_price_slug': my_ajax_object.reg_price_field_slug
        };


        if( $(document).scrollTop() > ( $(document).height() - bottomOffset ) && canBeLoaded == true ){

                $.ajax({//limit the ajax calls
                    url : url,
                    data:data,
                    type:'POST',                    
                    beforeSend: function( xhr ){
                        // you can also add your own preloader here
                        // you see, the AJAX call is in process, we shouldn't run it again until complete
                        //console.log(data.search_term);
                        $('#ajax-loader').show();  
                        canBeLoaded = false; 
                    },
                    success:function(data){
                        if( data ) {
                            $('#multiple-products .columns-3 .products ').find('li:last-of-type').after( data ); // where to insert posts

                            //console.log(url);
                            canBeLoaded = true; // the ajax is completed, now we can run it again
                            my_ajax_object.current_page++;
                            $('#ajax-loader').hide();
                        }
                        else{
                            $('#ajax-loader').html('End of products...').delay(1000).fadeOut(); 
                            return;
                        }

                    }
                });

        }
    });


    //setting if it's a search

});

Is there a way that i can use this woocommerce_get_price_html filter outside of the ajax request handling script(ga_infinite_scroll) as it's really costly in terms of speed to use it inside the ajax handling script? I tried using transients at the ga_show_price(). How to implement other types of caching here to increase the speed of the infinite scroll?

回答1:

So using transients is probably the best "simple" answer here without doing some major rework. However there's a couple issues with your ga_show_price() function.

So you want to always minimise the amount of database calls or long lengthy functions you call from your code to make things faster.

  1. Transients have GLOBAL names. So if you use something called sales_price for one product, as soon as you use it for another product it will still hold the value of the previous product. What you'll probably have to do is generate a unique name for all your transients. Something like: set_transient('price_'.$product->getSKU(), ...).

  2. $variations = $product->get_children(); - You're loading the $variations variable with all the children of the product, which probably takes quite a while and involves quite a few db calls, then if you already have a transient for this product, the variations are never used!. Only run this line if you dont already have a cached value for the product.

  3. A smaller issue, but you are calling get_transient twice every time you have a cached value. Once to check that it's not false, and then again to actually retrieve the value. Might seem like a small thing, but if you have 100+ products loading in, it adds up.

I like to do this with my transients:

 $value = get_transient('something');
 if ($value === false)
 {
    $value = some_long_calculation();
    set_transient('something', $value, ...);
 }

 //Now use $value here.
  1. Get even more aggressive with your caching. How often do items change from being on sale to not being on sale? Not more than once a day? Then just cache the entire function's calculation instead of first checking if it has a sales price or regular price.

A word of warning: Transients have a maximum length for their names and values so you cant store too much in them. Also they're designed for only storing a few values, not a price for every single product in your system.

If that is the case you're probably better off thinking about caching the value in a custom field for each product? You could attach hooks so that every time the product is updated, it updates the calculated price custom field automatically.



回答2:

@Mikepote's suggestions for ga_price increased the speed but editing the main product loop based on unique transient increased speed more. I hereby attach my code:-

  if( empty(get_transient('ga_loop_products_'.md5(serialize($params))))){ //using md5 and serialize(for 32digit) to assign a unique name to the given set of params



     query_posts( $params);

     ob_start(); 

     add_filter( 'woocommerce_get_price_html', 'ga_show_price' );//filter to fix price range

      if ( have_posts() ) {//product loop
            if ( wc_get_loop_prop( 'total' ) ) {
                  while ( have_posts() ) {

                    the_post();

                    wc_get_template_part( 'content', 'product' );
                  }
                }
        } 
        $data = ob_get_clean();
          // $ga_loop = get_transient('ga_loop_products_'.md5(serialize($params)));
          set_transient( 'ga_loop_products_'.md5(serialize($params)), $data, 24 * 60 ); // 1 day cache
      }
      else{


         $data=  get_transient('ga_loop_products_'.md5(serialize($params)));


      }

       wp_reset_query();