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

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

Nuxtでfunction()から始まるようなJavascript構文を呼び出し

function()から始まるような構文

サイト制作でJavascriptのライブラリを使う昨今、サイト表示時点で外部にアクセスするような、
function()から始まるような構文を使うことはよくありますよね。
例えば流入数を計測するためのお決まりのタグや、Typekitなどのフォントを呼び出すJavascript構文など。

このような構文はクライアント側(ブラウザ側)ので実行する分には問題ないのですが、
SSR(サーバサイドレンダリング)を行うNuxtでは一工夫必要になります。

なぜならSSRはその名の通りサーバサイドの処理のため、
windowやdocumentのようなクライアントサイドで使用するオブジェクトを呼び出そうとすると以下のようなエラーが発生するのです。

ReferenceError: window is not defined
ReferenceError: document is not defined

モジュールを使って呼び出し

nuxtでは上記の悩みを解決するようなモジュールが多数用意されています。
例をいくつか挙げると

Google Tag Managerを使いたい時

@nuxtjs/google-tag-managerというモジュールをインストールすれば解決です。

npm install --save @nuxtjs/google-tag-manager

インストール後は、以下のようにnuxt.config.jsに設定すれば動作します。

modules: [
  ['@nuxtjs/google-tag-manager', { id: 'GTM-xxxxxx', pageTracking: true }]
]

超絶簡単!

Google Fontsを使いたい時

nuxt-webfontloader
をインストールすれば解決です。

npm install --save nuxt-webfontloader

インストール後は、以下のようにnuxt.config.jsに必要なフォントを指定すれば動作します。

modules: [
  'nuxt-webfontloader'
],
webfontloader: {
  google: {
    families: ['Noto+Sans+JP']
  }
},

その他の方法

他にも様々な種類がありますが、どうにもならない時は一例として以下のようなやり方があります。
今回はTypekitのJavascript呼び出しを例にとります。

まずは関数を実行するメソッドが書かれたvueファイルを用意。

Typekit.vue

<script>
let typekit = {
  methods: {
    load () {
      (function(d) {var config = {kitId: 'XXXXXXX',scriptTimeout: 3000,async: true},h=d.documentElement,t=setTimeout(function(){h.className=h.className.replace(/\bwf-loading\b/g,"")+" wf-inactive";},config.scriptTimeout),tk=d.createElement("script"),f=false,s=d.getElementsByTagName("script")[0],a;h.className+=" wf-loading";tk.src='https://use.typekit.net/'+config.kitId+'.js';tk.async=true;tk.onload=tk.onreadystatechange=function(){a=this.readyState;if(f||a&&a!="complete"&&a!="loaded")return;f=true;clearTimeout(t);try{Typekit.load(config)}catch(e){}};s.parentNode.insertBefore(tk,s)})(document)
    }
  }
}

export default typekit
</script>

あとはbeforeCreate ()などで呼び出し処理を入れれば問題ありません。

beforeCreate () {
  typekit.methods.load()
}

ただしここで注意!
上記の書き方ではだめなのです。
なぜならbeforeCreateはサーバサイドでも実行されるため、再び以下のようなエラーが発生するのです。

ReferenceError: document is not defined

よってprocess.clientのif文を入れてサーバサイドで実行されるようにしましょう。

beforeCreate () {
  if (process.client) {
    typekit.methods.load()
  }
}

nuxtのSSRはかなり強力な武器ですが、サーバサイド・クライアントサイドの処理を意識する必要があるという点が大変ですね。