How to make a dynamic import in Nuxt?
Asked Answered
S

2

5

In my nuxt component I want to use the ace editor:

import Ace from "ace-builds/src-noconflict/ace"

when the component is mounted I am doing the following:

this.editor = Ace.edit...

Obviously the window is not defined on the server on page reload. But unfortunately I just can't find a solution to fix this issue.

Is there a way to import a package on the mounted() hook? I already tried

const Ace = require("ace-builds/src-noconflict/ace")

But that doesn't quite seem to work. Do you have any ideas to solve this issue?

I already tried to register a plugin plugins/ace.js:

import Vue from "vue"
import Ace from "ace-builds/src-noconflict/ace"
Vue.use(Ace)

registered it in nuxt.config.js:

plugins: [
    { src: "~/plugins/ace", mode: "client" }
],

But how do I use Ace in my component now? It is still undefined...

Sensorium answered 3/6, 2021 at 15:58 Comment(2)
As for the "client side" topic: this part of the plugin documentation should helpPiecedyed
And you question how to access the plugin is answered herePiecedyed
A
4

Since the error was thrown during the import statement, I'd recommended using dynamic imports as explained in my other answer here.

async mounted() {
  if (process.client) {
    const Ace = await import('ace-builds/src-noconflict/ace')
    Ace.edit...
  }
},

From the official documentation: https://nuxtjs.org/docs/2.x/internals-glossary/context


EDIT: I'm not sure about Ace and it's maybe a drastic change but you may also give a look to vue-monaco which is elbow-to-elbow popularity wise (vanilla Monaco editor).

EDIT2: mounted actually only runs on the client so you could strip the process.client conditional. Meanwhile, I do let it here in case you want to run some logic in other hooks like created (which are run on both server + client). More info here.


EDIT3: not directly related to the question, but some packages expose a component which is only available on the client-side (no SSR support), in those cases you could import the component only on the client side and easily prevent any other errors.

Anorthic answered 3/6, 2021 at 16:12 Comment(7)
Unfortunately, that doesn't work. The error gets thrown due to the import statement, not due to Ace.editSensorium
Still, I have a minor question though. Since I am dynamically importing the package in my component now it only seems to work together with the plugin I stated in my inital question up above. So currently I am importing a package to my entire app even though I only need it in a single component. That doesn't look optimal :/Sensorium
When you do import (and not require), Webpack will handle the tree-shaking of the editor. Even more, you could load on a click or a specific event. Until it's done, it will not be available globally into your app. So, actually this is the optimal way (to my knowledge) to import it into a single specific component. Using it as a Nuxt plugin will be the solution to use it globally through your app. Dynamic import => if you need it, import it. Require => import the whole package in any case. require is the bad boy and the old way of doing things.Anorthic
Okay, so actually it should work by only importing it in the component, right? Somehow it doesn't make sense to me that I still need the nuxt plugin. Maybe I don't fully understand the concept of Nuxt here, but when I console.log("hello") in my nuxt plugin. It is logged on every route of my application. Therefor the package is imported on every single route (on page reload). That's what I meant with not optimal.Sensorium
Haha, sorry to have not explained this directly. You can totally ditch the plugin if you only want to use it locally in your component. Plugins are indeed imported globally at the start of your app. Local import in the component => local usage, Nuxt plugin => global usage. Both => non sense.Anorthic
Okay, so I still kind of have an issue here. 1. If I only import the package in a plugin (globally), without dynamically importing it inside the component aswell, then Ace is not defined. 2. If I only import it dynamically inside my component, then Ace is not defined. 3. If I import it like every other package on top, then I get window is not defined 4. Only if I import it dynamically and in the plugin I don't get the error. That just doesn't make sense to me. I hope you understand my struggle haha.Sensorium
Isn't the 2. a matter of scope? Try changing it to let Ace, and get it out of the mounted() scope and define it during the import if you're using it out of the mounted() hook.Anorthic
P
4

Nuxt Plugin

IMHO you were on the right track with the "plugin" solution. Only mistake was the Vue.use(Ace) part. This only works for vue plugins.

The plugin file could look somewhat like that:

import Ace from 'ace-builds/src-noconflict/ace'
import Theme from 'ace-builds/src-noconflict/theme-monokai'

export default ({ app }, inject) => {
  inject('ace', {
    editor: Ace,
    theme: Theme
  })
}

Then you could use this plugin and initiate the editor in a component this way:

<template>
  <div id="editor">
    function foo(items) {
    var x = "All this is syntax highlighted";
    return x;
    }
  </div>
</template>

<script>
export default {
  data () {
    return {
      editor: {}
    }
  },
  mounted () {
    this.editor = this.$ace.editor.edit('editor')
    this.editor.setTheme(this.$ace.theme)
  }
}
</script>
Piecedyed answered 3/6, 2021 at 18:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.