daikiojm’s diary

ブログを書くときがやってきたどん!

Nuxt.jsでvuejs-datepickerを使うとdocument is not definedが発生する問題

前提として

Nuxt.js

以下は、Nnux.jsの公式から引用

Nuxtは、モダンな web アプリケーションを作成する Vue.js に基づいたプログレッシブフレームワークです。Vue.js 公式ライブラリ(vue、vue-router や vuex)および強力な開発ツール(webpack、Babel や PostCSS)に基づいています。 Nuxt の目標は、優れた開発者エクスペリエンスを念頭に置き、Web 開発を強力かつ高性能にすることです。

フレームワークによって設計やコーディングルールを矯正される面が大いにあるため、PureなVue.jsの導入を検討する際にも引き合いに出されることが多い印象。
最近開発を手伝っている案件でも、Nuxt.js(TypeScript)が採用されており、ちょくちょく触ることがある。

f:id:daikiojm:20200119214155p:plain
Nuxt.js

vuejs-datepicker

Vue Componentとしてdatepickerを提供するnpmモジュール。
Vue Component提供されるdatepickerの中ではダウンロード数も多く優勢。
個人的にも以前別のプロジェクトで使ったことがあり、シンプルなAPIが使いやすく気に入っていたこともあり、今回もNuxt.jsと組み合わせて導入することにした。
ただし、デフォルトのUIが結構簡素なものなので、カスタムCSSを当てて見た目は自前で作り込んで上げる必要がある。

f:id:daikiojm:20200119214009p:plain
Datepicker Examples

document is not defined

何も考えずvuejs-datepickerのドキュメントに沿って、対象のComponentにdatepickerをimportとして動作確認しようとすると、次のようなエラーが発生する。
どうでもいいが、Nuxt.jsのエラー画面はさすが優れた開発者エクスペリエンスを念頭に置き開発されたアプリケーションフレームワークと歌っているだけのことがある。

f:id:daikiojm:20200119213754p:plain
document is not defined

これは、クライアントサイドJavaScriptで動作することを前提としたモジュールをサーバーサイドレンダリングで動作させようとして、ブラウザオブジェクト(今回の場合documentオブジェクト)にアクセスしようとするも、失敗したことに起因するエラーっぽい。

大抵のVue Componentを提供するnpmモジュールではこの点は考慮されていないので、次で説明するように、Nuxt.jsのpluginを定義したうえで、一手間加えてやる必要がある。

解決方法

現状、上記の問題を解決する方法は次のような手順でdatepickerを使用すればよい。 最初から順を追って説明する。

インストール

yarn add vuejs-datepicker

Nuxt.jsプラグインの定義

/plugins/vue-datepicker.client.js を作成する。

次に、 vue-datepicker.client.js の内容を次のように定義する。

import Vue from 'vue';
import Datepicker from 'vuejs-datepicker';

Vue.component('date-picker', Datepicker);

Nuxt.jsプラグインの定義

コンポーネントで使用する

コンポーネントでdatapickerを使用する際には、次のように <client-only> コンポーネントを利用する。

<client-only>
  <date-picker
    placeholder="MM/DD/YYYY"
    format="MM/dd/yyyy"
    v-model="date"
  />
</client-only>

こうすることで、意図的にクライアントサイドレンダリングでdatapickerを利用するようにすることができる。

API: <client-only> コンポーネント

参考

https://github.com/charliekassel/vuejs-datepicker/issues/147