Vueでキャンペーンサイトを作った話を自社の記事で投稿してしまい、同じネタを使おうとしたら、二重投稿禁止というのを知らなくて、焦りました。
似たような投稿するのもあれなので、なんか違う話題も書きます。
nuxt.js+expressのvue-cliテンプレートを使って、api+SSRのシングルページアプリケーション構成のマイクロサービスを作っています。
ちっちゃいページ+ちっちゃいAPIを使うとすごくいい感じにマイクロサービスを作ることができました。その良さを今回は語ります。
重要なものが最初から簡単に用意できる
webpackの設定は自分でローダーを設定すると柔軟に設定する代わりに割と手間が増えます。
特にVueでお決まりのアプリケーションを作りたいときに、下の要件は欲しいでしょう。
- シングルファイルフォーマットでの開発(vue-loaderの設定)
- linterの実行
- シングルページでのrouting
- buildファイルの圧縮
- ソースマップやHot Module Replacementを使える開発環境
nuxt.jsを使用するとこれらの機能をちょっと設定したり、もしくは最初から使用できる状態で開発を始めることができ、vueアプリケーションを作成することに集中することができます。更には、
- postcss-pluginの設定の追加(styleのクロスブラウザ対応など,必要なプラグインはnpm iでインストール)
- webpack-loaderの追加ができる(sass,pugなど)
- fetchの必要性がなければ、シングルページアプリケーションを静的ファイルとして吐き出せる
- ESの任意のバージョンを追加できる
- 最初からVuexが組み込まれている
など、手軽に、ものすごく便利な機能が簡単に使えます。
build: {
/*
** Run ESLint on save
*/
analyze: !isProd, // variable not related to nuxt.js
babel:{
presets:['es2015','stage-0'] //add-on of babel-core and require node modules
},
vendor: ['axios'],
postcss: [ //add-on of postcss and require node modules
require('postcss-nested')(),
require('postcss-responsive-type')(),
require('postcss-hexrgba')(),
require('autoprefixer')({
browsers: ['last 3 versions']
})
],
extend (config, ctx) {
if (ctx.dev && ctx.isClient) {
config.module.rules.push({
enforce: 'pre',
test: /\.(js|vue)$/,
loader: 'eslint-loader',
exclude: /(node_modules)/
})
}
}
}
もうこれDB必要ない静的ページは全部nuxt.jsを使って作れば早いんじゃないかってくらいの所感ですね。
nuxtサーバーでホストすることで、production環境でも使える
expressなしの構成でも、nuxt自体が内部でproduction環境でバックエンドサービスをホストするミドルウェアを持っているため、productionのコードをサーブするのも非常に楽です(nuxt build;nuxt
コマンドあるいは、最初から、templateで設定されているnpm startを使うだけ)。このとき、サーブされるコンテンツはすべて.nuxt
ディレクトリに置かれることになります。ブラウザ側でも使えるfetchをスクリプトの中に仕込むので、キャッシュがあればサーバーアクセスせず、なければバックエンドに問い合わせする仕組みが最初から備わっており、サーバとフロントエンドでコードを共有するための特別なコーディングは必要ないためとても簡単かつ便利に使えます。
また、nuxtはexpressのミドルウェアとしてrendererをモジュールインポートして噛ませることができるので、APIサーバ+コンテンツプロバイダの構成を簡単に作ることができます。以下はテンプレートの初期設定例です。
import express from 'express'
import { Nuxt, Builder } from 'nuxt'
import api from './api'
const app = express()
const host = process.env.HOST || '127.0.0.1'
const port = process.env.PORT || 3000
app.set('port', port)
// Import API Routes
app.use('/api', api)
// Import and Set Nuxt.js options
let config = require('../nuxt.config.js')
config.dev = !(process.env.NODE_ENV === 'production')
// Init Nuxt.js
const nuxt = new Nuxt(config)
// Build only in dev mode
if (config.dev) {
const builder = new Builder(nuxt)
builder.build()
}
// Give nuxt middleware to express
app.use(nuxt.render)
// Listen the server
app.listen(port, host)
console.log('Server listening on ' + host + ':' + port) // eslint-disable-line no-console
またこのとき、expressのから.nuxtのコンテンツをサーブするので、デプロイに必要なのは、express側で使用するmoduleをインストールするための、package.json、.nuxtとexpressのapiに関わるファイルと起動スクリプトだけです。うちはkubernetesでimage replaceによって、rolling updateを行っているため、Dockerfileを作成していて、buildを行うためのDockerfileは以下のように設定しています。
FROM node:9.2.0-slim
ENV NODE_ENV production
WORKDIR /app
COPY .nuxt /app/.nuxt
COPY ./build /app/build
COPY ./package.json /app/package.json
COPY ./package-lock.json /app/package-lock.json
RUN npm install
ENTRYPOINT ["npm","run","start"]
buildディレクトリは、express側のコードも圧縮buildしてからimageに追加させています。公式のテンプレートでは、バックエンド側はbackpackというサーバーサイドのアセットコンパイルをしてくれるwebpackの簡易版ライブラリを使用していたのでその流儀に従うようにしています。これによってbuild/main.jsに起動スクリプトが配置されます。たったこれだけの設定で、nuxt.jsアプリケーションのマイクロサービス化ができてしまいました。npmスクリプトは可用性のために以下のように少し変更を加えています。
"scripts": {
"dev": "backpack dev",
"build": "NODE_ENV=production nuxt build | backpack build && wait",
"start": "NODE_ENV=production HOST=0.0.0.0 PORT=8080 node ./build/main.js",
"generate": "nuxt generate",
"lint": "eslint --ext .js,.vue --ignore-path .gitignore .",
"precommit": "npm run lint"
}
このノウハウを発見したとき、かなり衝撃的でした。マイクロサービス+SPA+SSR構成のアプリケーションをまさか,こんな簡単に作れるとは思わなかったので、控えめに言っても最高でした。作成中のページのファーストビューも驚きの速さです。しかも、ミドルウェアを噛ませなければ、nuxt generate
でVueのSPA一ページづつを静的ファイルとして吐き出すことができるため、簡単なLPを作る際にレンダリング高速にしたいかつ、でも普通にhtml書くのも嫌なので、Vueを使って書きたいというニーズさえ満たしてくれます。
今の所実装でも困ってることにぶち当たっていないため、マイクロサービスを作る上で銀の弾丸となり得るかもしれないですね。