Nuxt.js中出现"document is not defined"错误

31

我正在尝试在Vue组件中使用Choices.js。该组件编译成功,但随后触发了错误:

[vue-router]无法解析异步组件默认值: ReferenceError: document is not defined

在浏览器中,我看到:

ReferenceError document is not defined

我认为这与Nuxt.js中的SSR有关?我只需要让Choices.js在客户端上运行,因为我猜它只是一个客户端方面。

nuxt.config.js

build: {
  vendor: ['choices.js']
}

AppCountrySelect.vue

<script>
import Choices from 'choices.js'

export default {
  name: 'CountrySelect',
  created () {
    console.log(this.$refs, Choices)
    const choices = new Choices(this.$refs.select)
    console.log(choices)
  }
}
</script>

在经典的Vue中,这个代码可以正常工作,所以我还在努力适应如何让Nuxt.js以这种方式工作。

有任何想法我哪里做错了吗?

谢谢。

11个回答

46

当你开始一个Nuxt项目时,这是一个常见的错误 ;-)

Choices.js库仅适用于客户端!因此,Nuxt尝试从服务器端呈现,但从Node.js window.document不存在,因此您会遇到错误。
注意:window.document仅从浏览器呈现器可用。

Nuxt 1.0.0 RC7以来,您可以使用<no-ssr>元素仅允许您的组件在客户端中使用。

<template>
  <div>
    <no-ssr placeholder="loading...">
      <your-component>
    </no-ssr>
  </div>
</template>

可以查看官方示例:https://github.com/nuxt/nuxt.js/blob/dev/examples/no-ssr/pages/index.vue


更新:

Nuxt >= 2.9.0以来,您需要使用<client-only>元素而不是<no-ssr>

<template>
  <div>
    <client-only placeholder="loading...">
      <your-component>
    </client-only>
  </div>
</template>

了解更多,请参阅nuxt文档:https://nuxtjs.org/docs/2.x/features/nuxt-components#the-client-only-component


谢谢!你有什么想法可以让我在SSR中输出元素本身吗?也就是说,某些JS需要在客户端上运行以获取选择列表,但我仍然希望在服务器上呈现实际的选择列表。 - Michael Giovanni Pumo
4
<your-component />文件应该长什么样子?我的YourComponent.vue是这样的: <template><div></div></template> <script> import Plotly from 'plotly.js/dist/plotly' export default {} </script>,但它仍然以document is not defined结束。 - Michal Skop
2
插件: [ { src: '~/plugins/vue-notifications', mode: 'client' } ]我建议这个方案给那些像我一样仍在苦苦寻找解决方案的人。 - Jeff Bluemel
1
@JeffBluemel 您的解决方案救了我的一天。我不得不在我的 nuxt.config.js 中添加这个 { src: '~/plugins/vue-izitoast', mode: 'client' } - ManuEl Magak
2
@JeffBluemel,你的解决方案是我在苦苦寻找解决方案半天后唯一可用的。我真的尝试了互联网上能找到的所有方法。 - Imtiaz
如果您正在使用 Nuxt 版本大于 v2.9.0,请使用 <client-only> 替代 <no-ssr>。因为 <no-ssr> 已经在 Nuxt v2.9.0 中被弃用了。 - Kazmi

31

被接受的答案(虽然正确),对我来说太短了,无法理解和正确使用它,因此我写了一个更详细的版本。我正在寻找一种使用 plotly.js + nuxt.js 的方法,但应该与 OP 的问题 Choice.js + nuxt.js 相同。

MyComponent.vue

<template>
  <div>
    <client-only>
      <my-chart></my-chart>
    </client-only>
  </div>
</template>
<script>
export default {
  components: {
    // this different (webpack) import did the trick together with <no-ssr>:
    'my-chart': () => import('@/components/MyChart.vue')
  }
}
</script>

MyChart.vue

<template>
  <div>
  </div>
</template>
<script>
import Plotly from 'plotly.js/dist/plotly'
export default {
  mounted () {
    // exists only on client:
    console.log(Plotly)
  },
  components: {
    Plotly
  }
}
</script>

更新:在Nuxt v>2.9.0中,使用<client-only>标签代替<<no-ssr>,详见@Kaz的评论。


当我使用https://evodiaaut.github.io/vue-marquee-text-component时,这个方法对我很有效。通常no-ssr标签就足够了,但是在使用这个库时,我必须按照上述方式进行导入。@michal.你能解释一下这种导入方式背后的原因吗? - Baldeep Singh Kwatra
很遗憾,我记得我是通过试错来得到这个解决方案的。 - Michal Skop
1
如果您正在使用 Nuxt 版本大于 v2.9.0,请使用 <client-only> 代替 <no-ssr>。因为 <no-ssr> 已经在 Nuxt v2.9.0 中被弃用。 - Kazmi

14

您需要将它添加为插件,然后禁用SSR。

由于在服务器端没有定义文档和窗口,因此

您的nuxt.config.js应该如下所示:

plugins: [
  { src: '~/plugins/choices.js' } // both sides
  { src: '~/plugins/client-only.js', mode: 'client' }, // only on client side
  { src: '~/plugins/server-only.js', mode: 'server' } // only on server side
],

1
现在语法看起来像这样:{ src: '/plugins/client-only.js', mode: 'client' }, // 仅在客户端 { src: '/plugins/server-only.js', mode: 'server' } // 仅在服务器端 - Arif Ikhsanudin
感谢 @ArifIkhsanudin 的指针,我刚刚更新了答案。 - Yusuf Adeyemo

7

使用 mode: 'client' 添加 lightgallery.js 时遇到错误,似乎有帮助。

nuxt.config.js

 plugins: [
    { src: '~/plugins/lightgallery.js',  mode: 'client' }
  ],

plugins/lightgallery.js

import Vue from 'vue'
import lightGallery from 'lightgallery.js/dist/js/lightgallery.min.js'
import 'lightgallery.js/dist/css/lightgallery.min.css'

Vue.use(lightGallery)

ImageGallery.vue

<template>
  <section class="image-gallery-container">
    <div class="image-gallery-row">
      <div
        ref="lightgallery"
        class="image-gallery"
      >
        <a
          v-for="image in group.images"
          :key="image.mediaItemUrl"
          :href="image.mediaItemUrl"
          class="image-gallery__link"
        >
          <img
            :src="image.sourceUrl"
            :alt="image.altText"
            class="image-gallery__image"
          >
        </a>
      </div>
    </div>
  </section>
</template>

<script>
export default {
  name: 'ImageGallery',
  props: {
    group: {
      type: Object,
      required: true
    }
  },
  mounted() {
    let vm = this;

    if (this.group && vm.$refs.lightgallery !== 'undefined') {
      window.lightGallery(this.$refs.lightgallery, {
        cssEasing: 'cubic-bezier(0.680, -0.550, 0.265, 1.550)'
      });
    }
  }
}
</script>

7

我发现现在 no-ssr 被替换为,我正在使用 echart 并且遇到了相同的问题,但现在它可以正常工作了!

    <client-only>
        <chart-component></chart-component>
    </client-only>

1
<no-ssr>从Nuxt版本v2.9.0开始已经被弃用。 - Kazmi

6

这个帖子有点老了,但是我会在这里分享我的解决方法,希望可以帮到某些人。

最近我遇到了与 vue-star-rating 和其他几个插件相似的问题。


可以按照以下步骤进行操作,并根据插件名称、导入/使用设置进行调整:

  1. 进入插件文件夹,创建新的 js 文件,例如 vue-star-rating.js,然后编辑它以设置插件

import Vue from 'vue'
import VueStarRating from 'vue-star-rating'

Vue.component('vue-star-rating', VueStarRating); //<--- the name you used to register the plugin will be the same to use when in the component (vue-star-rating)

请前往您的 `nuxt.config.js` 文件并添加插件
  plugins: [{
      src: '~/plugins/vue-star-rating', // <--- file name
      mode: 'client'
    },
    //you can simply keep adding plugins like this:
    {
      src: '~/plugins/vue-slider-component',
      mode: 'client'
    }]
  1. 现在,您可以在应用程序的任何地方使用该插件。但是,为了实现这一点,您需要将其包装在容器<client-only>中。例如:
<client-only placeholder="loading...">
   <vue-star-rating />
</client-only>

注意:

您不需要在组件中本地导入任何内容,只需像上面那样使用即可解决问题。

请确保在步骤1和步骤3中以相同的方式命名插件,在这种情况下应该是vue-star-rating


5
<script>
import Choices from 'choices.js'

export default {
  name: 'CountrySelect',
  created () {
    if(process.client) {
      console.log(this.$refs, Choices)
      const choices = new Choices(this.$refs.select)
      console.log(choices)
    }
  }
}
</script>

我猜这应该会有所帮助,nuxt在服务器端渲染后会触及计算属性的内部,并定义window。


0

我试图在 created 钩子中访问 document,所以当我将逻辑从 created 钩子移动到 mounted 钩子时,我的问题得到了解决。


0

为了完整起见,值得一提的是,除了 Yusuf Adeyemo 的答案中使用的 对象语法(我更喜欢它将文件与使用方式分开),您还可以通过以下方式命名文件来设置插件仅在客户端或服务器端运行:

export default {
  plugins: [
    '~/plugins/foo.client.js', // only in client side
    '~/plugins/bar.server.js', // only in server side
    '~/plugins/baz.js' // both client & server
  ]
}

源代码:https://nuxtjs.org/docs/directory-structure/plugins/#client-or-server-side-only


0

除了这里提供的所有答案,您还可能会遇到一些其他不与SSR兼容的软件包(就像在您的情况下一样),这将需要一些技巧才能正常工作。这是我的答案的详细信息。

简而言之,您有时需要:

  • 使用process.client
  • 使用<client-only>标签(注意,它不会呈现但仍会执行内部代码)
  • 如果以后需要,使用动态导入,例如const Ace = await import('ace-builds/src-noconflict/ace')
  • 有条件地加载组件components: {[process.client && 'VueEditor']:() => import('vue2-editor')}

通过这些方法,您几乎可以涵盖每种可能的情况。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接