"document is not defined" 在 Nuxt.js

"document is not defined" in Nuxt.js

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

[vue-router] Failed to resolve async component default: 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 以这种方式工作。

对我哪里出错有任何想法吗?

谢谢。

这是启动 Nuxt 项目时的常见错误 ;-)

Choices.js 库仅适用于客户端!所以 Nuxt 尝试从服务器端渲染,但是 Node.js window.document 不存在,那么你有一个错误。
nb: 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

<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 将在服务器上呈现后触及 computed 的内部,并且 window 将被定义

接受的答案(虽然正确)太短,我无法理解和正确使用,所以我写了一个更详细的版本。我正在寻找一种使用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 的评论。

我发现现在 no-ssr 被替换了,我正在使用 echart 并且有同样的问题但是现在它工作了!

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

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

由于文档和 window 未在服务器端定义。

您的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
],

这个帖子有点旧,但我会把我的解决方案留在这里,也许有人会觉得它有用。

我最近在使用 vue-star-rating 和其他几个插件时遇到了类似的问题。


可以根据插件名称、导入/使用设置遵循和调整以下步骤:

  1. 转到您的插件文件夹并创建新的 js 文件,在本例中为 vue-star-rating.js,然后将其编辑为 setup the plugin:

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)
  1. 转到您的 nuxt.config.js 文件并添加 plugin:

  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

我在 lightgallery.js 添加模式时遇到此错误:'client' 似乎有帮助

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>

如果你还想做,文档对象可以这样取:

const d = typeof document === 'undefined' ? null : document

为了完整起见,值得一提的是,除了 (我更喜欢它,因为它将文件与其使用方式分开),您还可以将插件设置为仅在客户端或服务器端运行通过像这样命名文件:

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

除了此处的所有答案之外,您还可能会遇到一些与开箱即用的 SSR 不兼容的其他软件包(例如您的情况),并且需要一些技巧才能正常工作。这里是的详细信息。

TLDR 是您有时需要:

  • 使用process.client
  • 使用<client-only>标签(小心,它不会渲染但仍然执行里面的代码)
  • 如果以后需要,请使用动态导入,例如 const Ace = await import('ace-builds/src-noconflict/ace')
  • 有条件地加载一个组件components: { [process.client && 'VueEditor']: () => import('vue2-editor') }

有了所有这些,您几乎可以应对所有可能的情况。