まえがき
Nuxtは主にwebサービスなどに使う事場合がほとんどだと思いますが、
私が請ける仕事は4日とか1週間とかそういった納期で普通のサイトを作ることが多いので、(こちらの都合にもよりますが…)
Nuxtを採用すると高速にサイトが作れるので重宝しています。
わたしがNuxtで普通のサイトとして作る場合便利だなと思うところに、
- 一つのファイルにJSやCSSをかけるので干渉しにくい
- コンポーネントが切れるのでパーツ化しやすい
- イベント処理が簡単
- ページに入ったら、ページを去ったらなどの処理が楽
- Vuexが使えるのでStoreにぶちこめば状態が管理しやすい場合がある
- Vueのプラグインを使える(使えないものもある)
- キャッシュやアセット周りが楽
- 普通のサイトでもプラグイン入れればPWA使える
などなどといったことがあるので、静的サイトや一部API通信するサイトなどの場合Nuxtで最近は構築しています。
Nuxt(Vue)でTypeScriptを使うのは大変
最近はJavaScriptを使う場合、TypeScriptを検討することが多いと思います。
Reactの場合TypeScriptを使うことによって更に堅牢に、Angularの場合はそもそもTypeScriptが採用されています。
しかし、Nuxt(Vue)の場合はTypeScriptを使おうとするとけっこう大変です。(Vuexが大変)
以下、TypeScriptをTSと省略します。
Vuexによる状態管理を含む最高に快適な Vue.js + TypeScript の開発環境を目指す話
TypeScriptでNuxtアプリケーションを作成する際の覚書
上記が参考になると思いますが、ReactやAngularのようにTSでガチガチに固めようとしても結構ハードルが高いので、Vue 3に期待して今はカジュアルに使うことにしました。
普通のサイト制作でよくあること
- コンポーネントという概念がない
- ページでいろいろレイアウトが違う
- コンポーネント化できそうだと思ったらできなかった
- JS使う部分など、基本的には同じなのにページで若干処理が違う
ここらへんはあるあるではないでしょうか。
Atomic Designとか昨今のウェブアプリ制作などでは導入されているかと思いますが、地方では印刷もウェブもデザイナーさんが同じとかザラで、今のところ不動産などのサイト制作では紙のデータの流用がほとんどなので、そういったデザイナーさんにAtomic Designやコンポーネント思考をおすすめしても負担になるだけだと思っています。
かといって処理的に同じなものはまとめたい…
私のあった例
- ある地点からスクロールでついてくるバナーがページによって違う → ただ単に表示非表示のフラグたてるだけ
- トップページとある特集ページだけローディングを出したい → 4つのページだけ別々のローディングだしたい。ただ単に表示非表示のフラグたてるだけ
- バナーをクリックしたらページ別でモーダルを出したい → ただ単に表示非表示のフラグたてるだけ
なんか処理的には同じで、コンポーネントとして切ってもいいんですが、ifが多そうで、どうしたもんか…と思っていたのですが、継承ベースであれば処理はまとめられるのでは?と思ったらできたので共有します。
継承してみよう
継承を用いたサイト
僕のやるやる詐欺で1年経った作りかけ作品集サイトを参考にします。
https://salt.tanshio.net
https://github.com/tanshio/salt
(React の Next.js → Nuxt 1→ Nuxt 2などの変遷があったり、TSがテンプレを無理やり使っていたりなのですがスルーしてください…)
上のサイトはCanvas置き場なのですが、ページ行ったり来たりでtimer処理が挟んであったり、アニメーションを発動させたりと処理的には同じ感じだったので共通化させたいと思っていました。
ベースとなるファイル
base.ts
import { Component, Provide, Vue } from 'nuxt-property-decorator'
@Component({})
export default class Base extends Vue {
timer: number | null = null;
isShow: boolean = false
// 画面が作成されたときに実行
init() {
console.log('最初にやる共通処理')
}
onClick() {
this.isShow = true
}
leave() {
// ページから去るときにやる共通処理
console.log(this.timer)
if (this.timer) {
console.log('clear',this.timer)
cancelAnimationFrame(this.timer)
}
}
mounted() {
this.init()
}
beforeRouteEnter (to, from, next) {
next((vm) => {
// `vm` を通じてコンポーネントインスタンスにアクセス
})
}
beforeRouteLeave (to, from, next) {
this.leave()
next()
}
}
SFCファイル
<template>
<section class="container2">
<canvas id="canvas" ref="canvas"></canvas>
</section>
</template>
<script lang="ts">
import json from '~/static/data.json'
import { Component, Vue } from 'nuxt-property-decorator'
let debounce
if (process.browser) {
debounce = require('lodash-es/debounce').default
}
import { Polyline } from './Polyline'
import Base from '../Base'
@Component({})
export default class Effect05 extends Base {
counter: number = 0
SWING_WIDTH: number = 2
SWING_HEIGHT: number = 0.05
SPEED: number = 2
init() {
console.log(json)
console.log('this', this, this.$refs.canvas)
let polyline = new Polyline(this.$refs.canvas, this)
polyline.render()
}
}
</script>
mountedのなかでinitを発動させており、継承したファイル内でinitを上書きしています。
Vue以外でも使えるようにCanvasの処理は別ファイルにしているためthisをぶん投げているのですが、ここらへんはお作法的に駄目だと思うので参考にしないほうが良いかもしれません。(ちゃんと整形したオブジェクトを投げるなど)
利点
共通処理のロジックはベースファイルで作成しておき、もしページでやることが違う場合継承したファイル内でプロパティをはやしたりメソッドを上書きしてあげればいいので見通しが良いです。
型やメソッドなども補完されるので楽。
気になった点
mountedを上書きすればinitはいらないのでは?と考える方もいると思うのですが、ベースのmountedに処理を書いて、継承したファイル内でmountedを上書きすると、ベースと上書きしたmountedが2回発動します。
これは準備されているcreatedやbeforeRouteEnterなどでも起こります。自分的には2回発動するのが気持ち悪かったのでinitに処理を書くようにしました。
Nuxtで作ったサイトとか
昔作ったサイト
http://www.gk-cflt.com/
(今見たらGoogle Mapsがエラー吐いてたりしてますが…)
最近作ったサイト
公開してOKな案件があまりないですが、上記含めて10サイトぐらいはNuxtで作りました。
おわりに
Angularを使っていると継承でロジックをまとめることができたので、これをVueでも使いたいなと思っていました。
お作法的にどうなのか不明ですが、基本的な部分をまとめるという点では継承は僕にとってかなりスッキリする感じです。
今の所Nuxtだと何も考えないで普通のサイトが作れるので本当にNuxtには感謝しきれません。
来年もバンバン使っていきたいと思います。