この記事は Sansan Advent Calendar 2019 の12日目の記事です。
今年に入ってから Nuxt.js を使い始めましたがとっても便利で、今まで React 派でしたがこれがあるから Vue っていいなと思いました。
みなさんも Nuxt 使っているでしょうか。そして、モダンにかっこよく使えているでしょうか。
僕はあんまりモダンじゃ無い、泥臭くやることを強いられた部分があったので、もしも同じことで悩む人のためにいくつか紹介したいと思います。
コンテンツは
- Component (Typescript) 内で外部 jQuery を利用する
- SPA + Producitonモードでエラーthrow時にエラー画面へ飛ばす
- ServerMiddlewareでCORSを無視してAPIリクエストする
の3点です。
前提
以下で動作確認
- Nuxt.js は v2.10
- Typescriptを使用
- Componentの作成には vue-property-decorator を使用
また、今回のコード全部入りはGitHubで公開しています。
1. Component 内で外部 jQuery を利用する
これは比較的普通に使えるネタかもしれません。
Component 内で jQuery を import して、$
で使用します。
型定義を準備
- package をインストール
npm i -D @types/jquery
-
tsconfig.json
のtypes
を以下のように追記します。
{
// "@types/jquery" を追加!
"types": ["@types/jquery", "@types/node", "@nuxt/types"]
}
Component を書く
こんな感じ
<template>
<div>
<button @click="buttonClick" class="continue">
Click me
</button>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
// Ref: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/jquery#authoring-type-definitions-for-jquery-plugins
declare const $: JQueryStatic
// このページだけで外部jQueryを利用する書き方
// Ref: https://ja.nuxtjs.org/faq/#%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E3%81%AA%E8%A8%AD%E5%AE%9A
@Component({
head() {
return {
script: [{src: 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js'}]
}
}
})
export default class extends Vue {
buttonClick() {
$('button.continue').html('Next Step...')
}
}
</script>
これで使えます。
2. SPA モードでもエラーが起きた時にエラーページへ飛ばす
これは今回書く中でも特にやってみたら動いた系のネタなので Production レベルでは使わないか、使うにしても Nuxt のバージョンをしっかり固定しましょう
Nuxt では、layout/error.vue
を置くことで、404 にアクセスされた時や予期せぬエラーが起きた時に、そのエラーページを表示してくれる機能があります。 (参考)
ですが、SPA モードかつ production build の場合、Component でエラーが throw されるとこのエラーページへ行かなかったので、無理やり制御しました。
エラーページを準備
これは layout/error.vue
を追加するだけです。
<template>
<div>
<h1>エラーが発生しました</h1>
<nuxt-link to="/">
ホーム
</nuxt-link>
</div>
</template>
error を制御する plugin を追加する。
Vue.config.errorHandler を使ってエラーをキャッチして、nuxt インスタンスのerror()
を呼び出します。
この部分が、ソースを追っかけていて「これで行けるんじゃね?」ってやったら動いた系なので取り扱い注意です。
import Vue from 'vue'
import { NuxtApp } from '@nuxt/types/app'
Vue.config.errorHandler = (err, vm) => {
const $nuxt = vm.$root as NuxtApp
$nuxt.error(err)
}
nuxt.config.ts にも忘れず追加します。
plugins: ['~/plugins/error-handler.ts'],
これで、Component 内でエラーが起きた時に error.vue へ飛ぶようになります。
ちなみに SPA Production モードの挙動は build した後にnuxt-ts start
で確認できます。
エラーを起こすためのお試しComponentはこちら
3. CORS を無視して API にリクエストするための Proxy サーバーを立てる
backend がまだ準備できてない等の理由で、開発中、どうしてもクロスオリジンな API にリクエストしたい場合がないでしょうか。まあ、普通は無いと思います。
Nuxt には ServerMiddleware という機能があり、例えば /api
という serverMiddleware を定義すると
http://localhost:3000/api
というエンドポイントを作ることができます。
これを活用して、あたかも同じサーバーのエンドポイントなのに、裏では外部 API をリクエストする、みたいなことを実現できます。
(追記) proxy moduleを使う
これですが、公式ドキュメントにある通り@nuxtjs/proxyを使うことで簡単に実現できます。
proxy: {
'/api': {
target: 'http://example.com/', // Nuxtを起動しているサーバーから見たエンドポイントを記載する
pathRewrite: {
'^/api': '/'
}
}
}
上記の記載をすると、 /api
へのアクセスでexample.comへリクエストが飛びます。
以下は、追記前の冗長なやり方です。
外部 API へリクエストするサーバーを書く
- リクエストを受けて
- パスやパラメータを解析して
- そのパスとパラメータそのまま外部 API にリクエストする
みたいなサーバーを node.js で書きます。これは正直どんな実装でも良いのですがサンプルを載せておきます。
const express = require('express')
const request = require('request')
const app = express()
// この場合 localhost:8080/api/~~ とリクエストするとこのserverが受ける
const rootPath = '/api'
// 実際のエンドポイントを入力
const actualEndpoint = process.env.ENDPOINT || 'https://example.com/v1'
const requestWrapper = async (options) => {
const result = await new Promise((resolve, reject) => {
request(options, (err, response) => {
if (err) {
reject(err)
}
if (!response) {
reject(err, 'Nothing response')
}
resolve(response)
})
})
return result
}
// 全部のリクエストを受けて外部APIへ飛ばす
app.all('/*', async (req, res) => {
console.log(`original url: ${req.originalUrl}`)
const options = {
url: actualEndpoint + req.originalUrl.replace(rootPath, ''),
method: req.method,
qs: req.query,
json: req.body
}
console.log('request with following options.')
console.log(options)
const response = await requestWrapper(options)
res.status(response.statusCode).send(response.body)
})
// for Nuxt.js server middleware
module.exports = {
path: rootPath,
handler: app
}
express とか依存関係がある場合は、npm install
も忘れずに!
nuxt.config に serverMiddleware を追加する
serverMiddleware に先ほど追加したパスを指定します。
mode: 'spa',
serverMiddleware: ['~/server/'],
これで、以下のように書くと、上のサーバーへリクエストし、レスポンスを受け取ることができます。
this.response = await this.$axios.$get('/api/hoge')
こんなの2度と使うのか・・・わからないけど以上です!
取り扱いにはくれぐれも注意しましょう。