Tag: Lazy

Lazy Load Routes in Vue with webpack Dynamic Comments

The way routing works in JavaScript is usually that you specify which relative URL pattern you want for which component to render. So for /about you want the <About /> component to render. Let’s take a look at how to do this in Vue/Vue Router with lazy loading, and do it as cleanly as possible. I use this little tip all the time in my own work.

A repo that includes everything covered in this post is available on GitHub.

You’ve probably seen Vue routes (URLs) like this:

import Vue from 'vue' import VueRouter from 'vue-router'  import Home from '../views/Home.vue' import About from '../views/About.vue' import Login from '../views/Login.vue'  Vue.use(VueRouter)  const routes = [   { path: '/', name: 'Home', component: Home },   { path: '/about', name: 'About', component: About },   { path: '/login', name: 'Login', component: Login } ]  const router = new VueRouter({   routes })  export default router

That will load the <Home /> component at the / route, the <About /> component at the /about route, and the <Login /> component at the /login route.

That doesn’t do a very good job of code splitting though, since all three of those components will be bundled together rather than loaded dynamically as needed.

This is a terminal screenshot with a dark purple background with text in white green and blue. The content shows the final build after compiling.

Here’s another way to do the same, only with code splitting with dynamic import statements and webpack chunk names:

const routes = [   {     path: '/',     name: 'Home',     component: () => import(/* webpackChunkName: "Home" */ '../views/Home.vue')   },   {     path: '/about',     name: 'About',     component: () => import(/* webpackChunkName: "About" */ '../views/About.vue')   },   {     path: '/login',     name: 'Login',     component: () => import(/* webpackChunkName: "Login" */ '../views/Login.vue')   } ]

This is perfectly fine and doesn’t have any major downsides, other than being a bit verbose and repetitive. Since we’re awesome developers, let’s do a bit of abstraction to help, using an array that we’ll .map over.

const routeOptions = [   { path: '/', name: 'Home' },   { path: '/about', name: 'About' },   { path: '/login', name: 'Login' } ]  const routes = routeOptions.map(route => {   return {     ...route,     component: () => import(`@/views/$ {route.name}.vue`)   } })  const router = new VueRouter({   routes })

Now we’ve reduced the use of the component key by using the route name as param in the import function.

But what happens if we want to set the chunk name?

As far as I know, you can’t have dynamic comments in JavaScript without some kind of build step. So, we are sacrificing comments (webpackChunkName) in favor of having to write less code in this case. It’s entirely up to you which you prefer.

Just kidding, let’s fix it.

As of webpack 2.6.0 , the placeholders [index] and [request] are supported, meaning we can set the name of the generated chunk like this:

// ...  const routeOptions = [   { path: '/', name: 'Home' },   { path: '/about', name: 'About' },   { path: '/login', name: 'Login' } ]  const routes = routeOptions.map(route => {   return {     ...route,     component: () => import(/* webpackChunkName: "[request]" */ `../views/$ {route.name}.vue`)   } })  const router = new VueRouter({   routes })

Nice! Now we have all the power, plus dynamically loaded routes with named chunks. And it works with Vue 2 and Vue 3. You can check it out by running npm run build in the terminal:

This is the same terminal screenshot as before, but with three of the JavaScript built files highlighted with the words "Named Chunks" annotated beside them.
See that? Now the components are chunked out… and the build did all the naming for us!

Buuuuut, we can still take this one step further by grouping the lazy loaded routes into named chunks rather than individual components. For example, we can create groups that group our most important components together and the rest in another “not so important” group. We merely update the webpack chunk name in place of the [request] placeholder we used earlier:

const routes = [   {     path: "/",     name: "Home",     component: () =>       import(/* webpackChunkName: "VeryImportantThings" */ "../views/Home.vue")   },   {     path: "/about",     name: "About",     component: () =>       import(/* webpackChunkName: "VeryImportantThings" */ "../views/About.vue")   },   {     path: "/login",     name: "Login",     component: () =>       import(/* webpackChunkName: "NotSoImportant" */ "../views/Login.vue")   },   {     path: "/contact",     name: "Contact",     component: () =>       import(/* webpackChunkName: "NotSoImportant" */ "../views/Contact.vue")   } ];

Now our four components are groups into two separate chunks.

The same terminal with different compiled JavaScript files.

There you have it! A technique for lazy loading routes in Vue, plus some ideas for how to name and group them together at build.


The post Lazy Load Routes in Vue with webpack Dynamic Comments appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , ,

Lazy Loading Images in Svelte

One easy way to improve the speed of a website is to only download images only when they’re needed, which would be when they enter the viewport. This “lazy loading” technique has been around a while and there are lots of great tutorials on how to implement it.

But even with all the resources out there, implementing lazy loading can look different depending on the project you’re working in or the framework you’re using. In this article, I’ll use the Intersection Observer API alongside the onLoad event to lazy load images with the Svelte JavaScript framework.

Check out Tristram Tolliday’s introduction to Svelte if you’re new to the framework.

Let’s work with a real-life example

I put this approach together while testing the speed on a Svelte and Sapper application I work on, Shop Ireland. One of our goals is to make the thing as fast as we possible can. We hit a point where the homepage was taking a performance hit because the browser was downloading a bunch of images that weren’t even on the screen, so naturally, we turned to lazy loading them instead.

Svelte is already pretty darn fast because all of the code is compiled in advance. But once we tossed in lazy loading for images, things really started speeding up.

This is what we’re going to work on tofgether. Feel free to grab the final code for this demo from GitHub and read along for an explanation of how it works.

This is where we’ll end up by the end:

Let’s quickly start up Svelte

You might already have a Svelte app you’d like to use, but if not, let’s start a new Svelte project and work on it locally. From the command line:

npx degit sveltejs/template my-svelte-project cd my-svelte-project npm install npm run dev

You should now have a beginner app running on http://localhost:5000.

Adding the components folder

The initial Svelte demo has an App.svelte file but no components just yet. Let’s set up the components we need for this demo. There is no components  folder, so let’s create one in the src folder. Inside that folder, create an Image folder — this will hold our components for this demo.

We’re going to have our components do two things. First, they will check when an image enters the viewport. Then, when an image does enter, the components will wait until the image file has loaded before showing it.

The first component will be an <IntersectionObserver> that wraps around the second component, an <ImageLoader>. What I like about this setup is that it allows each component to be focused on doing one thing instead of trying to pack a bunch of operations in a single component.

Let’s start with the <IntersectionObserver> component.

Observing the intersection

Our first component is going to be a working implementation of the Intersection Observer API. The Intersection Observer is a pretty complex thing but the gist of it is that it watches a child element and informs us when it enters the bounding box of its parent. Hence images: they can be children of some parent element and we can get a heads up when they scroll into view.

While it’s definitely a great idea to get acquainted with the ins and outs of the Intersection Observer API — and Travis Almand has an excellent write-up of it — we’re going to make use of a handy Svelte component that Rich Harris put together for svelte.dev.

We’ll set this up  first before digging into what exactly it does. Create a new IntersectionObserver.svelte file and drop it into the src/components/Image folder. This is where we’ll define the component with the following code:

<script>   import { onMount } from 'svelte'; 
   export let once = false;   export let top = 0;   export let bottom = 0;   export let left = 0;   export let right = 0; 
   let intersecting = false;   let container; 
   onMount(() => {     if (typeof IntersectionObserver !== 'undefined') {       const rootMargin = `$  {bottom}px $  {left}px $  {top}px $  {right}px`; 
       const observer = new IntersectionObserver(entries => {         intersecting = entries[0].isIntersecting;         if (intersecting && once) {           observer.unobserve(container);         }       }, {         rootMargin       }); 
       observer.observe(container);       return () => observer.unobserve(container);     } 
     function handler() {       const bcr = container.getBoundingClientRect(); 
       intersecting = (         (bcr.bottom + bottom) > 0 &&         (bcr.right + right) > 0 &&         (bcr.top - top) < window.innerHeight &&         (bcr.left - left) < window.innerWidth       ); 
       if (intersecting && once) {         window.removeEventListener('scroll', handler);       }     } 
     window.addEventListener('scroll', handler);     return () => window.removeEventListener('scroll', handler);   }); </script> 
 <style>   div {     width: 100%;     height: 100%;   } </style> 
 <div bind:this={container}>   <slot {intersecting}></slot> </div>

We can use this component as a wrapper around other components, and it will determine for us whether the wrapped component is intersecting with the viewport.

If you’re familiar with the structure of Svelte components, you’ll see it follows a pattern that starts with scripts, goes into styles, then ends with markup. It sets some options that we can pass in, including a once property, along with numeric values for the top, right, bottom and left distances from the edge of the screen that define the point where the intersection begins.

We’ll ignore the distances but instead make use of the once property. This will ensure the images only load once, as they enter the viewport.

The main logic of the component is within the onMount section. This sets up our observer, which is used to check our element to determine if it’s “intersecting” with the visible area of the screen. It also attaches a scroll event to check whether the element is visible as we scroll, and then it’ll remove this listener if we’ve determined that it is viable and that once is true.

Loading the images

Let’s use our <IntersectionObserver> component to conditionally load images by wrapping it around an <ImageLoader> component. Again, this is the component that receives a notification from the <IntersectionOberserver> so it knows it’s time to load an image.

That means we’ll need a new component file in components/Image. Let’s call it ImageLoader.svelte. Here’s the code we want in it:

<script>   export let src   export let alt 
   import IntersectionObserver from './IntersectionObserver.svelte'   import Image from './Image.svelte'    </script> 
 <IntersectionObserver once={true} let:intersecting={intersecting}>   {#if intersecting}     <Image {alt} {src} />   {/if} </IntersectionObserver>

This component takes some image-related props — src and alt — that we will use to create the actual markup for an image. Notice that we’re importing two components in the scripts section, including the <IntersectionObserver> we just created and another one called <Image> that we haven’t created yet, but will get to in a moment.

The <IntersectionObserver> is put to work by acting as a wrapping around the soon-to-be-created <Image> component. Check out those properties on it.  We are setting once to true, so the image only loads the first time we see it.

Then we make use of Svelte’s slot props. What are those? Let’s cover that next.

Slotting property values

Wrapping component, like our <IntersectionObserver> are handy for passing props to the children it contains. Svelte gives us something called slot props to make that happen.

In our <IntersectionObserver> component you may have noticed this line:

<slot {intersecting}></slot>

This is passing the intersecting prop into whatever component we give it. In this case, our <ImageLoader> component receives the prop when it uses the wrapper. We access the prop using let:intersecting={intersecting} like so:

<IntersectionObserver once={true} let:intersecting={intersecting}>

We can then use the intersecting value to determine when it’s time to load an <Image> component. In this case, we’re using an if condition to check for when it’s go time:

<IntersectionObserver once={true} let:intersecting={intersecting}>   {#if intersecting}     <Image {alt} {src} />   {/if} </IntersectionObserver> 

If the intersection is happening, the <Image> is loaded and receives the alt and src props. You can learn a bit more about slot props in this Svelte tutorial.

We now have the code in place to show an <Image> component when it is scrolled onto the screen. Let’s finally get to building the component.

Showing images on load

Yep, you guessed it: let’s add an Image.svelte file to the components/Image folder for our <Image> component. This is the component that receives our alt and src props and sets them on an <img> element.

Here’s the component code:

<script>   export let src   export let alt 
   import { onMount } from 'svelte' 
   let loaded = false   let thisImage 
   onMount(() => {     thisImage.onload = () => {       loaded = true     }   })  
 </script> 
 <style>   img {     height: 200px;     opacity: 0;     transition: opacity 1200ms ease-out;   }   img.loaded {     opacity: 1;   } </style> 
 <img {src} {alt} class:loaded bind:this={thisImage} />

Right off the bat, we’re receiving the alt and src props before defining two new variables: loaded to store whether the image has loaded or not, and thisImage to store a reference to the img DOM element itself.

We’re also using a helpful Svelte method called onMount. This gives us a way to call functions once a component has been rendered in the DOM. In this case, we’re set a callback for thisImage.onload. In plain English, that means it’s executed when the image has finished loading, and will set the loaded variable to a true value.

We’ll use CSS to reveal the image and fade it into view. Let’s give set an opacity: 0 on images so they are initially invisible, though technically on the page. Then, as they intersect the viewport and the <ImageLoader> grants permission to load the image, we’ll set the image to full opacity. We can make it a smooth transition by setting the transition property on image. The demo sets the transition time to 1200ms but you can speed it up or slow it down as needed.

That leads us to the very last line of the file, which is the markup for an <img> element.

<img {src} {alt} class:loaded bind:this={thisImage} />

This uses class:loaded to conditionally apply a .loaded class if the loaded variable is true. It also uses the bind:this method to associate this DOM element with the thisImage variable.

Let’s hook it all up!

Alright, it’s time to actually use our component. Crack open the App.svelte file and drop in the following code to import our component and use it:

<script>   import ImageLoader from './components/Image/ImageLoader.svelte'; </script> 
 <ImageLoader src="OUR_IMAGE_URL" alt="Our image"></ImageLoader>

Here’s the demo once again:

And remember that you’re welcome to download the complete code for this demo on GitHub. If you’d like to see this working on a production site, check out my Shop Ireland project. Lazy loading is used on the homepage, category pages and search pages to help speed things up. I hope you find it useful for your own Svelte projects!


The post Lazy Loading Images in Svelte appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,
[Top]

Lazy Loaded Prefill Embeds

Lemme sum this up:

  • CodePen has Embedded Pens. Build a Pen on CodePen, embed it on any other site.
  • We also offer Prefill Embeds, which remove that first step. With Prefill Embeds, the Pen doesn’t need to exist on CodePen at all. You pass in the code and settings you want to appear in the embed via code blocks sitting on the page (progressive enhancement!). Now that code can live in your Git repo or database, which might offer a more desirable level of control.
  • Stephen Shaw details how these Prefill Embeds can be created on-demand through user interaction (e.g. click a “Run this code” button) so they are only there when a user wants to see them. This is a super lightweight way to add optional interactivity to any blog post or documentation.

I’ll put an example right here in this blog post:

<h1>Hello from HTML</h1>
html {   background: black;   color: white;   text-align: center; }  h1::after {   content: " / CSS!"; }
document.querySelector("h1").innerText += " / JavaScript";

All that code lives right here in this blog post, and the Embedded Pen iframe doesn’t load until you click to load it, which you might do if you’re interested in seeing that code run.

Direct Link to ArticlePermalink


The post Lazy Loaded Prefill Embeds appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,
[Top]

Native Image Lazy Loading in Chrome Is Way Too Eager

Interesting research from Aaron Peters on <img loading="lazy" ... >:

On my 13 inch macbook, with Dock positioned on the left, the viewport height in Chrome is 786 pixels so images with loading="lazy" that are more than 4x the viewport down the page are eagerly fetched by Chrome on page load.

In my opinion, that is waaaaay too eager. Why not use a lower threshold value like 1000 pixels? Or even better: base the threshold value on the actual viewport height.

My guess is they chose not to over-engineer the feature by default and will improve it over time. By choosing a fairly high threshold, they ran a lower risk of it annoying users with layout shifts on pages with images that don’t use width/height attributes.

I think this unmerged Pull Request is the closest thing we have to a spec and it uses language like “scrolled into the viewport” which suggests no threshold at all.

Direct Link to ArticlePermalink

The post Native Image Lazy Loading in Chrome Is Way Too Eager appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]

Tips for rolling your own lazy loading

You may have heard (or even issued the call) that “we can just use lazy loading!” when looking for a way to slim down a particularly heavy web page.

Lazy loading is a popular technique for gradually requesting images as they come into view, rather than all at once after the HTML of the page has been parsed. It can reduce the initial page weight, and help us hit our performance budgets by requesting images when they’re needed.

It can be effective. But it also comes with some baggage of its own. We’ll get to that! In fact, Rahul Nanwani did an extensive write-up that hits several lazy-loading methods and illustrates just how complex some are.

In this post, we’ll look at an implementation that’s already been covered in brief detail in this post by Preerhi. We’re going to expand on that so you can add your own implementation of lazy loading to your site site as I’ve done on this little demo site.

We’ll cover these topics along the way:

(more…)

, , ,
[Top]

Native Lazy Loading

IntersectionObserver has made lazy loading a lot easier and more efficient than it used to be, but to do it really right you still gotta remove the src and such, which is cumbersome. It’s definitely not as easy as:

<img src="celebration.jpg" loading="lazy" alt="..." />

Addy Osmani says it’s coming in Chrome 75:

The loading attribute allows a browser to defer loading offscreen images and iframes until users scroll near them. loading supports three values:

  • lazy: is a good candidate for lazy loading.
  • eager: is not a good candidate for lazy loading. Load right away.
  • auto: browser will determine whether or not to lazily load.

I’ll probably end up writing a WordPress content filter for this site that adds that attribute for every dang image on this site. Hallelujah, I say, and godspeed other browsers.

Easy lazy loading of images will have the biggest impact on the site as a whole, but lazy loaded iframes will be even bigger for the individual sites that use them. I’m into it.

I hope this pushes along the need for native aspect ratios as well, since a major reason for that is preventing content reflow from things loading later. We do have ways now, though.

The post Native Lazy Loading appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]