阿里云主机折上折
  • 微信号
Current Site:Index > Lazy loading and on-demand loading

Lazy loading and on-demand loading

Author:Chuan Chen 阅读数:1947人阅读 分类: HTML

The Concept of Lazy Loading and On-Demand Loading

Lazy Loading and On-Demand Loading are two commonly used techniques in front-end performance optimization. Lazy loading typically refers to delaying the loading of non-critical resources, such as images or videos, until they are about to enter the viewport. On-demand loading, on the other hand, focuses more on splitting code or modules, dynamically loading corresponding resources only when needed. Both techniques can effectively reduce initial page load time and improve user experience.

Implementation of Image Lazy Loading

Image lazy loading is the most common application scenario. Traditional image loading methods request all image resources at once, while lazy loading only loads images when they are about to appear in the viewport.

<img data-src="image.jpg" class="lazy" alt="Example Image">
document.addEventListener("DOMContentLoaded", function() {
  const lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));
  
  if ("IntersectionObserver" in window) {
    const lazyImageObserver = new IntersectionObserver(function(entries) {
      entries.forEach(function(entry) {
        if (entry.isIntersecting) {
          const lazyImage = entry.target;
          lazyImage.src = lazyImage.dataset.src;
          lazyImage.classList.remove("lazy");
          lazyImageObserver.unobserve(lazyImage);
        }
      });
    });
    
    lazyImages.forEach(function(lazyImage) {
      lazyImageObserver.observe(lazyImage);
    });
  }
});

This implementation uses the IntersectionObserver API to detect whether an image enters the viewport. When the image enters the viewport, the value of the data-src attribute is assigned to the src attribute, triggering the image load.

Route-Level On-Demand Loading

In single-page applications (SPAs), route-level on-demand loading can significantly improve the initial load speed. Taking React as an example:

const Home = React.lazy(() => import('./Home'));
const About = React.lazy(() => import('./About'));

function App() {
  return (
    <Router>
      <Suspense fallback={<div>Loading...</div>}>
        <Switch>
          <Route exact path="/" component={Home} />
          <Route path="/about" component={About} />
        </Switch>
      </Suspense>
    </Router>
  );
}

The React.lazy function allows dynamic importing of components, while the Suspense component provides a fallback UI during loading. When the user navigates to the /about route, the code for the About component is loaded.

Component-Level On-Demand Loading

For complex components in large applications, more granular on-demand loading can be implemented:

const loadModal = () => import('./Modal');

function App() {
  const [showModal, setShowModal] = useState(false);
  const [ModalComponent, setModalComponent] = useState(null);

  const openModal = async () => {
    const { default: Modal } = await loadModal();
    setModalComponent(<Modal />);
    setShowModal(true);
  };

  return (
    <div>
      <button onClick={openModal}>Open Modal</button>
      {showModal && ModalComponent}
    </div>
  );
}

In this approach, the Modal component and its dependencies are only loaded when the user clicks the button.

On-Demand Loading for Third-Party Libraries

Many large third-party libraries also support on-demand loading. Taking lodash as an example:

// Traditional method - import the entire library
import _ from 'lodash';

// On-demand loading method
import debounce from 'lodash/debounce';

button.addEventListener('click', debounce(() => {
  console.log('Debounced click event');
}, 300));

For modern libraries that support ES modules, dynamic imports can also be used:

const loadUtility = async () => {
  const { default: _ } = await import('lodash-es');
  // Use lodash functionality
};

Considerations for Lazy Loading

When implementing lazy loading, several key factors should be considered:

  1. Placeholder Design: Design appropriate placeholders for lazy-loaded elements to avoid layout shifts.
  2. Preloading Strategy: For resources that may be needed soon, a small amount of preloading can be done in advance.
  3. Error Handling: Network requests may fail, so retry mechanisms and fallbacks should be provided.
  4. SEO Impact: Search engines may not execute JavaScript, so critical content should not rely on lazy loading.
<div class="lazy-container">
  <img data-src="image.jpg" alt="Product Image" 
       class="lazy" 
       loading="lazy"
       onerror="this.src='fallback.jpg'">
  <div class="placeholder"></div>
</div>

Performance Optimization for On-Demand Loading

Combined with build tools like Webpack, further optimization for on-demand loading can be achieved:

// webpack.config.js
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'async',
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      automaticNameDelimiter: '~',
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  }
};

Such configurations help automatically split code chunks for more efficient on-demand loading.

Native Browser Support

Modern browsers natively support some lazy loading features:

<!-- Native image lazy loading -->
<img src="image.jpg" loading="lazy" alt="Example">

<!-- iframe lazy loading -->
<iframe src="content.html" loading="lazy"></iframe>

The loading attribute supports three values:

  • lazy: Delays loading
  • eager: Loads immediately
  • auto: Let the browser decide

Handling in Server-Side Rendering

Implementing lazy loading in SSR applications requires special handling:

// Dynamic import in Next.js
import dynamic from 'next/dynamic';

const DynamicComponent = dynamic(
  () => import('../components/hello'),
  { 
    loading: () => <p>Loading...</p>,
    ssr: false // Disable server-side rendering
  }
);

function Home() {
  return (
    <div>
      <DynamicComponent />
    </div>
  )
}

This approach maintains the benefits of SSR while achieving client-side on-demand loading.

Performance Monitoring and Tuning

After implementing lazy loading and on-demand loading, actual performance should be monitored:

// Use Performance API to monitor resource loading
const observer = new PerformanceObserver((list) => {
  list.getEntries().forEach((entry) => {
    console.log(`Resource: ${entry.name}, Load time: ${entry.duration}ms`);
  });
});

observer.observe({entryTypes: ["resource"]});

// Record the time from user interaction to load completion
button.addEventListener('click', () => {
  const start = performance.now();
  
  import('./module').then(() => {
    const loadTime = performance.now() - start;
    console.log(`Module load time: ${loadTime}ms`);
  });
});

This data can help further optimize loading strategies and split points.

Special Considerations for Mobile

In mobile network environments, special considerations are needed:

  1. More Aggressive Lazy Loading Thresholds: Start loading earlier to offset mobile network latency.
  2. Connection Type Awareness: Adjust strategies based on network type.
  3. Data Saver Mode: Respect user settings for data restrictions.
// Detect network conditions
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;

if (connection) {
  if (connection.effectiveType === 'slow-2g') {
    // More aggressive lazy loading strategy
  }
  
  connection.addEventListener('change', updateLoadingStrategy);
}

Combining with Caching Strategies

Reasonable caching strategies can improve loading performance for repeat visits:

// Cache on-demand loaded modules in Service Worker
self.addEventListener('fetch', (event) => {
  if (event.request.url.includes('/dist/') && 
      event.request.url.endsWith('.js')) {
    event.respondWith(
      caches.match(event.request).then((response) => {
        return response || fetch(event.request).then((response) => {
          const responseToCache = response.clone();
          caches.open('dynamic-cache').then((cache) => {
            cache.put(event.request, responseToCache);
          });
          return response;
        });
      })
    );
  }
});

This strategy ensures that after the first load, subsequent on-demand loads can be read directly from the cache.

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.