tdoe
tdoe

Reputation: 11

Add client side javascript asset before end of body in nuxt

I have an image gallery in my nuxt site running in universal mode (deployed as static page) and would like to add blueimp gallery (a lightbox) to it.

There are several ways to place the Javascript (./static, ./plugins or ./assets). I'd prefer to use the assets folder since everything I put there is handled by webpack (compression, unique ID for long term client caching).

Since blueimp isn't important during rendering the page but only during interaction (clicking an image to open the lightbox), the javascript should be loaded just before the end of </body>

Another thing is, that I need blueimp only on a dedicated page, so if possible the blueimp-gallery.min.js and it's css shoudn't be served on the other pages.

JS

I got it running in yarn dev mode (which supports SSR) by including the script from the static folder in head and add body: true to it (to not use head ;) ) . In production (as a static page), it doesn't work. The parameter ssr: false seems to have no effect. Also adding <client-side> around the image component in the template doesn't help.

Question 1: I'd be glad if you could help me to rewrite this into a clientside function (maybe with another hook like created() or mounted() ?)

<script>
export default {
  head: {
    script: [
      {
        src: 'js/blueimp-gallery/blueimp-gallery.min.js',
        body: true,
        ssr: false,
      },
    ],
  },
},
</script>

CSS

I am able to load a css file from the static folder with the code below. Unfortunately this adds an extra request.

<template>
...
</template>

<script>
export default {
  head: {
    style: [{ src: "blueimp-gallery/css/blueimp-gallery.min.css", head: true }],
  },
}
</script>

Loading it with @import even fetches it from the assets folder and embeds it into the page which is nice, but it's being embedded into all pages.

<style>
@import '~/assets/styles/blueimp-gallery/css/blueimp-gallery.min.css';
</style>

Found another way here, which is probably the best: loading a css file from 'assets' directory in a NUXT layout

link: [{ rel: 'stylesheet', href: require('~/assets/blueimp-gallery/css/blueimp-gallery.min.css')}],

Question 2: Maybe you have an improved solution for embedding the CSS into a single page?

I'm a beginner, so if you have a solution please add some examples with a little context.

Upvotes: 1

Views: 3765

Answers (1)

Duckie
Duckie

Reputation: 31

As you have guessed, during SSR if the script is loaded it will be unable to find the DOM and will fail. There are a few approaches, but each has its own pros and cons.

Dynamically Loading Scripts in mounted()

This is an example posted by a user on the Nuxt issue board for dynamically loading jQuery. The key takeaway is checking the process.server value. I'd also recommend seeing if your users are likely to support the defer link attribute in the head Can I Use... Defer, which is generally a replacement for putting scripts in the body and remaining non-blocking.

export default {
  mounted() {
    if (!process.server && !window.jQuery) {
      const script = document.createElement("script");
      script.onload = this.onScriptLoaded;
      script.type = "text/javascript";
      script.src = "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.slim.min.js";
      document.head.appendChild(script);
    } else {
      this.onScriptLoaded();
    }
  },
  methods: {
    onScriptLoaded(event = null) {
      if (event) {
        console.log("Was added");
      } else {
        console.log("Already existed");
      }
      console.log(window.jQuery);
      window.jQuery("h1").append(` <span>(CDN script has loaded)</span>`);
    }
  }
}

Checking for Existing Solutions

A quick Google search yielded showed a couple of decently popular Vue components you can install that use blueimp gallery as a dependency: vue-gallery and Vue Blueimp Gallery. You may not like the components, however at a glance their source code is quite simple. When dealing with Vue plugins for NuxtJS, whenever you cannot find that they support SSR (and simple wrapper components such as these often don't), all you often need to do is make a new plugin with SSR disabled.

plugins/vue-gallery.js

import Vue from 'vue'
import VueGallery from 'vue-gallery'

Vue.component('VGallery', VueGallery)

nuxt.config.js

plugins: [{ src: '~/plugins/vue-gallery.js', ssr: false }]

CSS

You can import CSS rather easily in a couple of different ways using NuxtJS.

<script>
  import 'blueimp-gallery/css/blueimp-gallery.min.css';

  export default {
    data () { ... }
  }
</script>
<style scoped>
@import 'blueimp-gallery/css/blueimp-gallery.min.css';
</style>

You may also include the minified version relegated into your assets folder or wherever you like, but it's unnecessary as NuxtJS's production mode will handle asset minification. Note that the difference in the second one compared to yours is the scoped attribute. Without this tag any styles within it are site-wide and you should use scoped to keep styles within your components or pages whenever possible.

Upvotes: 1

Related Questions