Nuxt.jsでプロジェクトをはじめるときにいつもやることが整理してみた。
モジュールが多いので、いろいろ設定が必要だけど、ヌケモレあったりするので。。
Nuxt.jsのバージョンは、2.5.1です。
いつもやることの概要
作成したいプロジェクトは以下な感じ
- TypeScript/Sassを使う、UIはBuefy/Bulma
- Firebase Hosting / Cloud Functionsを使ってSSR
- 英語版/日本語版と国際化したいので、nuxt-i18nも入れる
- SEOも気にするので、各種設定&各ページで個別にタイトルとかを設定
- Google Analyticsやsitemapなどももちろん
- Firebaseへのデプロイは、CircleCIで自動化したい
最終的な例は、GitHubにおいています。
お品書き
やることは以下の14個。ながい。。おおい。。
- プロジェクト作成
- ディレクトリ構成の変更
- TypeScriptの導入
- Sassの導入
- env用の設定ファイルを追加
- サイトマップ生成の設定を追加
- 国際化のためにnuxt-i18nを追加
- SEO関連の設定
- Google Analyticsの設定を追加する
- Buefyカスタマイズ用のSCSSを追加
- Page Transition用のSCSSを追加
- UglifyJsPluginを追加して、console.logを自動削除
- Firebase Functionsの設定してSSRできるようにする
- CircleCIの設定
1. プロジェクト作成(コミット: e5bf50b)
まずは、プロジェクト作成。npx create-nuxt-app
を使います
$ npx create-nuxt-app <プロジェクト名> npx: 379個のパッケージを12.001秒でインストールしました。 > Generating Nuxt.js project in ... ? Project name <プロジェクト名> ? Project description Nuxt.js Template using TypeScript, Sass and SSR on Firebase Cloud Functions ? Use a custom server framework none ? Choose features to install Axios ? Use a custom UI framework buefy ? Use a custom test framework none ? Choose rendering mode Universal ? Author name Memory Lovers ? Choose a package manager npm To get started: cd <プロジェクト名> npm run dev To build & start for production: cd <プロジェクト名> npm run build npm start
作り終えると、このコミット(e5bf50b)な感じに
2. ディレクトリ構成の変更(コミット: 4fbab23)
あとでfunctionsのディレクトリを追加するので、
app用のディレクトリ作成して、移動する
#!/bin/bash mkdir app mv assets/ app/ mv components/ app/ mv layouts/ app/ mv middleware/ app/ mv pages/ app/ mv plugins/ app/ mv static/ app/ mv store/ app/
ディレクトリを変えたので、nuxt.config.jsも変更
const config: NuxtConfiguration = {
mode: 'universal',
+ srcDir: "app",
3. TypeScriptの導入(コミット: c1106e0)
Nuxt.jsv2.5でTypeScriptサポートが強化されたので、だいぶ楽に。。
@nuxt/typescript
を入れるだけでOKらしい。(TypeScript Support - Nuxt.js)
$ npm install --save -D @nuxt/typescript # デコレータやvuexも使いたいので追加 $ npm install --save nuxt-property-decorator vuex-class # tsconifig.jsonを用意すると、Nuxtが設定を自動生成してくれる # なので、とりあえず、空ファイルを作成 $ echo "{}" > tsconfig.json # 一回、ビルドして、tsconifig.jsonを生成 $ npm run build
3.1. tsconfig.jsonをディレクトリ構成に合わせる(コミット: 8473099)
appディレクトリ配下に変更しているので、自動生成したtsconfig.jsonも合わせる
"paths": { "~/*": [ - "./*" + "./app/*" ], "@/*": [ - "./*" + "./app/*" ] },
3.2. nuxt.config.jsをnuxt.config.tsに変換(コミット: 8473099)
まずは、nuxt.config.jsをTypeScript化
長いので、差分はコミットを参照
3.3. 型定義の配置ディレクトリを追加(コミット: 90f3a1e)
mkdir app/types touch app/types/index.d.ts # Stateの型定義 touch app/types/state.d.ts # Vueの型定義の拡張 touch app/types/vue.d.ts
3.4. 今ある.vueファイルをTypeScriptに変換(コミット: 8dcffc3)
昔の記事や公式の記事を参考に、書き換える。
nuxt-community/nuxt-property-decoratorも参考になる
4. Sassの導入(コミット: 9cde2e1)
Sassの導入は、npm installするだけ
$ npm install --save-dev node-sass sass-loader
5. env用の設定ファイルを追加(コミット: 1cbc14a)
個人的な趣味で、開発用と本番用のenvを分けるときは、
nuxt.config.tsに書かず、別ファイルにしている。
# 開発用のenv $ touch env.development.js # 本番用のenv $ touch env.production.js
中身はこんな感じ。基本情報とかはenvに入れている
module.exports = { baseUrl: "http://localhost:3000", contactFormUrl: "TODO_YOUR_CONTACT_FORM_URL", policyUrl: "TODO_YOUR_PRIVACY_POLICY_URL", tosUrl: "TODO_YOUR_TERMS_OF_SERVICE_URL", twitterUrl: "TODO_YOUR_TWITTER_URL", firebaseConfig: "TODO_YOUR_FIREBASE_CONFIG", GA_ID: "TODO_YOUR_GOOGLE_ANALYTICS_ID" };
env.jsを読み込むようにnuxt.config.tsを変更
import NuxtConfiguration from '@nuxt/config' import pkg from './package.json'; + const environment = process.env.NODE_ENV || "development"; + const envSet = require(`./env.${environment}.js`); const config: NuxtConfiguration = { mode: 'universal', srcDir: "app", + /* + ** ENVIRONMENT PROPERTIES + ** See https://ja.nuxtjs.org/api/configuration-env + */ + env: envSet, +
6. サイトマップ生成の設定を追加(コミット: 0f3bb98)
Google Search Consoleにアップロードするようにsitemap.xmlをnuxt build
時に生成するように設定。
@nuxtjs/sitemapを利用すればOK!
まずは、インストール
$ npm install --save @nuxtjs/sitemap
nuxt.config.tsに設定を追加
const config: NuxtConfiguration = { modules: [ ... + // Doc: https://github.com/nuxt-community/sitemap-module + "@nuxtjs/sitemap", ], + + /* + ** Sitemap + */ + sitemap: { + path: "/sitemap.xml", + hostname: envSet.baseUrl, + generate: true + },
7. 国際化のためにnuxt-i18nを追加
国際化重要。日本語だけだと範囲が狭いので、英語には対応したい。。
なので、nuxt-i18nを使ってi18n対応できるようにする。
詳しい使い方は、別記事でも。
7.1. インストールして、設定を追加(コミット: 43108e2)
まずは、インストール
$ npm install --save nuxt-i18n # メッセージのJSONを配置するディレクトリを追加 $ mkdir app/assets/msg # メッセージファイルを追加 $ echo "{}" > app/assets/msg/common.json
nuxt.config.tsに設定を追加
const config: NuxtConfiguration = { modules: [ ... + // Doc: https://nuxt-community.github.io/nuxt-i18n/ + ['nuxt-i18n', { + parsePages: false, + locales: [ + { code: 'en', iso: 'en_US' }, + { code: 'ja', iso: 'ja_JP' }, + ], + defaultLocale: 'en', + vueI18n: { + fallbackLocale: 'en', + messages: require("./app/assets/msg/common") + }, + vueI18nLoader: true // vue-i18n-loaderを有効にする + }], // Doc: https://github.com/nuxt-community/sitemap-module "@nuxtjs/sitemap", ],
ポイント
- nuxt-i18nの設定が@nuxtjs/sitemapに反映されるように、先に書く。
- TypeScript+decoratorを使っている場合は、
parsePages: false
が必要。。
7.2. メッセージファイルを追加して、各.vueを国際化(コミット: afe0c96)
細かい変更は、コミット差分を参照。
メッセージファイルは、トランスノートというWebサービスを使って生成
7.3. メニューに言語変更を追加(コミット: c40d7bb)
英語と日本語を切り替えれるように、言語変換を追加。
詳細は、コミット差分を参照
8. SEO関連の設定
SEO的に各ページのtitleやdescriptionをちゃんとしたい&i18n使ってるのでLocaleにあわせて日英切り替えたい!
いろいろやってみたけど、Vue.jsのmixinを使うのが良さそうなので、
「Faviconなどの共通的なhead()
」と「titleやOGPなど各ページのhead()
を差し込む枠組み」を追加してく
各要素などは、過去の記事を参照 - SEO/OGP関連のmetaタグをまじめに対応しようとしてみた。 - くらげになりたい。
8.1. 共通的なhead()
(コミット: 96e82d8)
共通的なheadは、別ファイル(commonHead.js
)にまとめて、nuxt.config.tsでグローバルとして設定する。
詳細は、コミット差分を参照。
favicon一式は、favicon generatorで生成したものを、app/static
配下に配置した想定の設定。
8.2. mixinでpageごとに設定(コミット: 12a80ed)
mixinsはこんな感じ。mixinsのhead()
で各ページで設定するmetaタグを生成する。
各ページ個別の情報をheadInfo()
に書くと、HeadMixin
のhead()
がそっちを参照しながら、いい感じに。
import Vue from "vue"; import Component from "vue-class-component"; import { MetaInfo } from "vue-meta"; import { HeadInfo } from "~/types"; @Component export default class HeadMixin extends Vue { public headInfo(): HeadInfo { return {}; } public head(): MetaInfo { const i18nSeo = this.$nuxtI18nSeo(); const t = this.$t.bind(this); const info = this.headInfo(); const siteName: string = t("site_name") as string; const title: string = t(info.title || "site_name") as string; const description: string = t(info.description || "site_description") as string; const baseUrl: string = process.env.baseUrl || ""; const thisUrl: string = `${baseUrl}${this.$route.path}`; const ogImageUrl: string = `${baseUrl}${info.ogImagePath || "/ogp.png"}`; return { title: title, htmlAttrs: { prefix: "og: http://ogp.me/ns# fb: http://ogp.me/ns/ fb#", ...i18nSeo.htmlAttrs }, meta: [ { hid: "description", name: "description", content: description }, { name: "application-name", content: siteName }, // OGP / Social Meta Tag { property: "og:type", name: "og:type", content: "website" }, { property: "og:title", name: "og:title", content: title }, { property: "og:description", name: "og:description", content: description }, { property: "og:url", name: "og:url", content: thisUrl }, { property: "og:image", name: "og:image", content: ogImageUrl }, { property: "og:site_name", name: "og:site_name", content: siteName }, ...i18nSeo.meta ], link: [{ rel: "canonical", href: thisUrl }, ...i18nSeo.link] }; } }
そのままだと、$nuxtI18nSeo()
や$t
で型定義が足りてない!と、怒られるので、
tsconfig.json
やapp/types/vue.d.ts
にnuxt-i18nなどの定義を追加する。
HeadInfo
は、各ページで設定するパラメタを規定するために、独自に型定義を用意。
nuxt-i18nもseo関連のmetaタグを生成してくれるが、セルフマージが必要。。
なので末尾に...i18nSeo.htmlAttrs
などを追加
8.3. 各ページにmixinを導入(コミット: 12a80ed)
各ページはこんな感じ。mixins(HeadMixin)
でmixinsを継承して、
headInfo()
でページのtitleやdescriptionのkeyを設定。
- import { Component, Vue } from "nuxt-property-decorator"; + import { Component, Vue, mixins } from "nuxt-property-decorator"; + import HeadMixin from "~/mixins/HeadMixin"; + import { HeadInfo } from "~/types"; import Card from "~/components/Card.vue" @Component({ components: { Card } }) - export default class IndexPage extends Vue { + export default class IndexPage extends mixins(HeadMixin) { name:string = 'HomePage'; + public headInfo(): HeadInfo { + return { + title: "home_title", + description: "home_details" + }; }
9. Google Analyticsの設定(コミット: 51cc5db)
PVとかもちゃんと計測したいので、GoogleAnalyticsを使えるようにする。
@nuxtjs/google-analyticsを入れるだけでOK
$ npm install --save @nuxtjs/google-analytics
設定とかはコミット差分を参照。 過去の記事でも。
10. Buefyカスタマイズ用のSCSSを追加(コミット: 5b8372b)
Bulmaの$primaryの色とかはテーマとして変更したいので、 カスタム用のCSSを追加
ベースだけなので、ほぼ公式のまま。
- Customization | Buefy
11. Page Transitionを追加(コミット: b19e28e)
ページ遷移時のアニメーションもつけたいので、グローバルにCSSを追加
設定とかはコミット差分を参照。もちろん公式のガイドも。
12. UglifyJsPluginでconsole.logを自動削除(コミット: d485999)
個人的にbuild時に自動でconsole.logを削除してほしい。
なので、UglifyJsPlugin
を使って設定
13. Firebase Cloud Functionsの設定してSSRできるようにする
SPAだと、metaタグがいい感じならないので、SSRしたい。。
Firebase Hostingにstaticやassetsをおいて、それ以外はCloud FunctionsでSSRする
13.1. firebase init
で初期設定(コミット: fcbf5f7)
とりあえず、初期設定。HostingとFunctionを選択する
$ firebase init
13.2. Nuxtを呼び出す関数を用意して、rewriteを追加(コミット: 9f8d986)
ssr
というFunctionを追加。
Hostingのrewriteで基本すべてのリクエストをssr
に渡すようにする。
"rewrites": [ { "source": "**", "function": "ssr" } ],
headers
も合わせて設定しているが、キャッシュコントロールのため。不要ならなくてもOK
ssr関数自体は過去記事を参照。
- Nuxt2.5でFirebaseでSSRするCloud Functionsの書き方 - くらげになりたい。
13.3. functionsのpackage.jsonへ依存関係を追加(コミット: 31028f5)
ssr関数内で実行されるNuxtオブジェクトが実行できるように、
app/package.json
のdependenciesと同じパッケージを、functions/package.json
にも追加
13.4. buildの一連の流れをpackage.jsonに追加(コミット: 606cda6)
個人的にCloud FunctionsでSSRするときの肝っぽいところ。
一連の流れは、以下のような感じ。
nuxt build
でapp
配下をビルドし、.nuxt/
を作成functions/index.js
からNuxtを呼び出せるよう、.nuxt/
をfunctions配下に配置- Hostingでdeployするディレクトリとして、
public/
をクリーンして、新規作成 public/
にビルドしたうちのassets/
部分をコピー。app/static
もコピー- Hostingのdeployで
public/
ディレクトリをアップロード - Functionsのdeployで
functions/
ディレクトリをアップロード
よくハマったのは、2./4.のコピーする部分。
- Functionsは全部必要なので、そのまま配置、
- Hostingはjs/css/画像など、一部のみなので、選別して配置
特にHostingには優先順位があり、実際のファイルのほうがリライトよりも優先度が高いので、
/index.htmlがあると、ssr関数は全く呼び出されなくなる。。
その点を踏まえて、一連の流れをpackange.jsonのscriptに用意しておけば、
npm run build && npm run deploy
でデプロイできる
14. CircleCIの設定(コミット: 4cec55f)
めんどくさがりなので、masterにpushしたらデプロイしてほしい。。
なので、CircleCIで自動リリースできるように設定
詳細はコミット差分を参照。過去の記事も。
- CircleCIでFirebaseへ自動デプロイ(Hosting+Funcsions) - くらげになりたい。
以上!!
これでチェックアウトするだけで、初期構築の作業がほとんど完了するのでだいぶ楽になる!!
参考になる書籍
Nuxt.jsビギナーズガイド―Vue.js ベースのフレームワークによるシングルページアプリケーション開発
- 作者: 花谷拓磨
- 出版社/メーカー: シーアンドアール研究所
- 発売日: 2018/10/17
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
- 作者: 川口和也,喜多啓介,野田陽平,手島拓也,片山真也
- 出版社/メーカー: 技術評論社
- 発売日: 2018/09/22
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
- 作者: MIO
- 出版社/メーカー: シーアンドアール研究所
- 発売日: 2018/05/29
- メディア: Kindle版
- この商品を含むブログを見る
改訂新版 Vue.jsとFirebaseで作るミニWebサービス (技術の泉シリーズ(NextPublishing))
- 作者: 渡邊達明
- 出版社/メーカー: インプレスR&D
- 発売日: 2018/10/05
- メディア: オンデマンド (ペーパーバック)
- この商品を含むブログを見る
参考にしたサイト様
- SEO/mixin関連
- TypeScript/型定義関連
- TypeScript のサポート — Vue.js
- vue-router/types at dev · vuejs/vue-router · GitHub
- vue-i18n/types at dev · kazupon/vue-i18n · GitHub
- nuxt-i18n/types at master · nuxt-community/nuxt-i18n · GitHub
- Vue.jsとTypeScript - Qiita
- nuxt.js/examples/typescript at dev · nuxt/nuxt.js · GitHub
- [GitHub - nuxt-community/nuxt-property-decorator: Property decorators for Nuxt (base on vue-property-decorator)]