21

I have a small app using Vue.Js (with webpack). I have a transition section. When the user clicks to a button the content going to change. It's a simple DIV element with an IMG child element. My problem is when the user clicks the button, the next image will be loaded in real-time and it's slow, so somehow I need to preload these images.

I have tried different approaches but couldn't achive the images be preloaded. The problems:

  • Only the first image is the part of the DOM, when page displayed, so the other images will be not loaded. Ok, it's the normal behavior.

  • I couldn't preload the images by using a simple for-loop on an array of images (Preloading images with JavaScript), because images will be requested by a unique query string (1) to each request. So the preloaded image names will not match with the requested.

  • I tried to load the images using require() and bind it to the IMG tag's src property. It's also not solved my problem, the images will be loaded in real-time after the click when the new DIV will be inserted into the DOM.

My simplified single file compontent (.vue) looks something like that:

<template>
    <!-- [FIRST] -->
    <div v-if="contentId == 0">
      <transition
        enter-active-class="animated fadeIn"
        leave-active-class="animated fadeOut"
        mode="out-in">
        <img :src="images.firstScreenShot" key="firstImage">
      </transition>
    </div>
    <!-- [/FIRST] -->

    <!-- [SECOND] -->
    <div v-else-if="contentId == 1">
      <transition
        enter-active-class="animated fadeIn"
        leave-active-class="animated fadeOut"
        mode="out-in">

        <img :src="images.secondScreenShot" key="secondImage">
      </transition>
    </div>
    <!-- [/SECOND] -->
</template>
<script>
    export default {
        data() {
            return {
                contentId: 0,
                images: {
                    firstScreenShot: require('./assets/first-screen-shot.png'),
                    secondScreenShot: require('./assets/second-screen-shot.png'),
                }
            }
        }
    }
</script>

I'm new to Vue.js, maybe my approach is not good. Please let me know what is the good approach!

kayess
  • 3,384
  • 9
  • 28
  • 45

3 Answers3

20

I quickly tried this out in the browser but it seems to work in console. You can use the Javascript Image object to pre-load images it seems:

https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/Image

You can create a new Image object like so:

const image = new Image();

Then you can assign a src value to that image:

image.src = 'test.jpg'

When you supply the src property with data immediately a network request will be fired by the browser which should be sufficient for the image to be loaded and cached. This way you don't have to insert these images into the DOM.

Someone correct me if I am wrong on this though, haven't tried to out fully.

Stephan-v
  • 19,255
  • 31
  • 115
  • 201
  • First of all, thank you for your answer! I tried this also, but interestingly it's also doesn't worked for me. I will try again this approach and I will write about the result... maybe I made a mistake... –  Jan 04 '18 at 15:57
  • Check to see if a network requests actually fires for the image through the chrome network tab. You can also try it out manually through the console for testing. – Stephan-v Jan 04 '18 at 15:58
  • @EdmundElmer I'm experiencing the same issue. Did you find a solution? – Mekeor Melire May 03 '18 at 01:26
  • 2
    You can also use `document.createElement('img').setAttribute('src', '/static/myimage');`but it's essentially the same, both work. – Daniel Jun 26 '18 at 22:48
  • Works for me. Note that each new image instance will preload one image, so it's necessary to create a `new Image()` for each image you want to preload. – samlandfried Feb 11 '19 at 18:02
5

If you are using the template to load the image with the tag you can make use of simple HTML to preload the images using rel="preload".

example:

<img :src="images.secondScreenShot" key="secondImage" rel="preload">
Fraccier
  • 780
  • 3
  • 11
  • 21
  • This does not work because as far as the DOM is concerned, the tag does not exist until you show it and put a src. At least for me in a for loop it does not work. Did you have a different context in mind in which this works? – Mig Sep 17 '20 at 07:57
  • I think what @Fraccier means is to create `img` elements via loop on pageload for each image URL later required and hide those elements indefinitely via CSS. Thus URLs are preloaded inside the browser cache and can be accessed by other elements likewise and automagically. Basically letting the browser do the heavy lifting. [This guy has done a similar approach, works fine for me](https://serversideup.net/how-to-preload-css-background-images/#Set%20Up%20Your%20Preload%20Element) – onewaveadrian Nov 21 '20 at 05:53
4

I create one function just send the image object you want to load via parameter.

in router

import Digital from '../../images/digital';
import { loadImage } from '../LoadImage';
...

const router = new Router({
  routes: [
    {
      path: '/',
      name: 'welcome',
      component: Welcome,
      beforeEnter(to, from, next) {
         loadImage(Digital);
      }
   }
]

my function

export function loadImage(imagesObject) {
  Object.keys(imagesObject).map((key, index) => {
    const img = new Image();
    img.src = imagesObject[key];
  });
}

Hope it help

TingSter
  • 61
  • 5