親バカエンジニアのナレッジ帳

webのエンジニアをやっており、日頃の開発で詰まったことや書き残しておきたいことを載せています。

vue-slickをNuxt.jsで使用した時の「window is not defined」エラー解消法

vue-slickを使用した場合のエラー

複数画像のスライド表示を簡単に実装できるJavascriptのプラグインであるslick.js。
これをVueやNuxtに実装する場合はvue-slickをインストールしてimportすれば簡単に実装できます。
ただここでNuxtに実装する場合は注意が必要です。
無邪気に

import VueSlick from 'vue-slick'

なんてしてしまうと、

window is not defined

のようなエラーが出てしまいます。

Vue.jsではこのようなエラーが出ることはないのになんで...と思ってしまうのですが、
エラーの原因はNuxt.jsではお馴染みのSSR(サーバサイドレンダリング)でDOM要素を操作しようとしていることに起因します。
このような場合、SSRの段階でimportしてしまったらその時点でエラーが発生してしまいます。

エラーの解消法

解消法1

ポイントとしては、SSRで読みこんではいけないので、「process.browser」の時にimportする必要があります。
記述すると以下のようなコードになります。

export default {
  components: {
    Slick: () => process.browser ? import('vue-slick') : null,
  },
}

components内にもimportは書けるのです。
あとはtemplate内に

<client-only>
  <slick>
   ・
   ・
   ・
  </slick>
</client-only>

のように記述すれば、クライアント側で読み込まれることになるためにエラーが発生しません。

解消法2

vue-slickをimportしたpluginsファイルを用意し、client側で読み込ませる方法もあります。
まずは以下のようにvue-slick.jsというpluginsファイルを作成します。

plugins/vue-slick.js

import Vue from 'vue'
import VueSlick from 'vue-slick'

Vue.use(VueSlick)

次にこれをnuxt.config.jsで読み込ませる設定を入れればOKです。
「mode: 'client'」と入れることで、クライアント側で読み込ませることができます。

nuxt.config.js

const config = {
  plugins: [
    { src: '~/plugins/vue-slick', mode: 'client' },
  ],
}

これもtemplate内では

<client-only>
  <slick>
   ・
   ・
   ・
  </slick>
</client-only>

のようにクライアント側で読み込ませるようにしましょう。
注意点ですが、この方法では「Vue.use(VueSlick)」とプラグインですることで、どこからでも呼び出せる状態になっています。
改めて使用したいコンポーネント内でimportをしないように注意してください。

Nuxtでは同様のエラーが発生するプラグインが多い

今回はvue-slickについてお話しましたが、他にも同様のエラーが発生するプラグインは多いです。
「window is not defined」や「Element is not defined」などのエラーが発生した場合は、DOMがない状態でDOM操作をしていないかを疑いましょう。
また、client-onlyで読み込む要素はSSRでは読まれないため、SEOに必要な要素は入れないようにしましょう。(どうしても入れなくてはいけないケースは多いと思いますが)