‘Nuxt.js’ タグのアーカイブ

murave

SPAでのキャッシュの闇

というか自分の失敗なんですけど。
今回の話は具体的にはNuxt2系でクライアント側でキャッシュしてみてのやらかしです。ひとことで言うと

「自分でちゃんと後始末しなきゃだめ」

仕組みはこうです。

  • Vuexにキャッシュ用のstateを用意。
  • キャッシュ用stateが空の場合はサーバーからデータを取得してセット。
  • キャッシュ用stateに値がセットされていればそれを返す。

(ほぼ)変更が無い値の場合、これで無駄なサーバーからのデータ取得をなくすことができます。
「翌日に反映されれば問題ない」位のものまでこの方式で扱っていました。

ある朝、トラブル発生の連絡がきました。前日に設定した値について反映されている端末とされていない端末があるとのこと。

こういう場合はだいたいキャッシュが原因です。

キャッシュってのはそもそもトラブルの元です。

でもパフォーマンスのためにはやっちゃうんだ。

お気づきですね。キャッシュの後始末をするのをすっかり忘れてました。毎日ブラウザが終了・起動されていると思ってはいけないのです。

業務で使用されているPCについては終業時にスリープさせて帰るということはあまりないので反映されていたのですが、タブレットの場合は基本的にブラウザ上がりっぱなしですので古いキャッシュがずっと残りっぱなしになっていたのです。

Webアプリの開発中は習慣で頻繁にブラウザをリロードするのでそれで消えるんですよね(言い訳)

んで後始末を忘れてるのに気づかずにリリースして事故ったり(闇)

以上です。

murave

Nuxt.js 開発サーバーでのSSEがランダムポートになって困ってた

説明しよう!

  • Nuxt.js で開発やってると開発サーバーを使うでしょ?
  • プログラム修正したら自動的にビルドしてくれて素敵!
  • そして画面を自動リロードしてくれて親切!

なんです。

ところで、Nuxt Screens Module(https://github.com/nuxt/screens)がローディング表示のためにSSE(Server-Sent Events)を使ってまして、最近、使用するポートがランダムになりました。

この変更は開発中のパフォーマンス問題への対策らしいです。良いことです。
しかし特定ポートをフォーワードして開発している人なんかは困ったことになります。
僕とか。

Nuxt.jsのdevでのsseがランダムなポート使うようになってるようなんだけど情報が見当たらない。ポートフォワードしてるので困る。— murave (@murave) June 19, 2020

とあるプロジェクトで

[Firebase] Cloud Functionsで消耗したくない人のために、開発環境のベストプラクティスをまとめていったらDockerに行き着いた話(随時更新)
https://qiita.com/pannpers/items/244a7e3c18d8c8422e4f

を参考にDockerベースで開発環境をつくってまして、必要なポートをフォワードしてゴニョゴニョやってるのでSSEのポートをフォワードしてないと

というふうにコンソールにエラーが出続けることになります。文字起こししときましょう。

GET http://localhost:45377/sse net::ERR_CONNECTION_REFUSED
このときのポートは45377ですが、ランダムに変わります(頻繁に、ではないですが)

つらい。

固定ならフォワーディングを増やせばいいだけなのですがランダムはつらい。
範囲指定でのフォワーディングも試しましたがリソース不足でコンテナが死んだりで断念。

本質的じゃないところでひっかかって半日を費やしたのちにメモを残して諦めることにしたのだった— murave (@murave) June 19, 2020

と敗北宣言したのですが、時間ができたので再調査したところ同じ目にあってる方(JasonCubicさん)を発見しました。

https://github.com/nuxt/nuxt.js/pull/7286#issuecomment-646853851

I am running nuxt dev in a docker container because I have a driver that needs some special setup. Using this different port spams cors errors into my console. How can I disable this?

ですよねー。そしてこの方、自己解決されたようです。

Update: If anyone else is trying to turn off the loading screen its in nuxt.config.js build.loadingScreen: false

なるほど。loadingScreen自体を無効化したのか。
自動リロードしてくれても変更が反映されないことがたまにあって手動リロード癖がついてるし、いい気がする(敗北感はあるけど)

nuxt.config.jsから抜粋

module.exports = {
// 略
build: {
// 略
loadingScreen: false
}
}
こうするとエラー表示はでなくなりました。そして画面の自動リロード自体は動きました。
本当にローディング表示のためだけだったのね。自動リロードにも影響すると思ってた。

ローディング表示、なくてもこまらないので無効化でOK。

murave

CORSに@nuxtjs/proxy

Nuxt.js でspaの開発やってたら普通は開発サーバーを使いますよね。
例えばyarnをパッケージマネージャーに採用している場合だと

$ yarn run dev
yarn run v1.21.1
$ nuxt

╭───────────────────────────────────────────────╮
│ │
│ Nuxt.js v2.12.2 │
│ Running in development mode (spa) │
│ │
│ Listening on: http://127.0.0.1:3000/ │
│ │
╰───────────────────────────────────────────────╯

ℹ Preparing project for development 13:54:32
ℹ Initial build may take a while 13:54:32
✔ Builder initialized 13:54:32
✔ Nuxt files generated 13:54:32

✔ Client
Compiled successfully in 25.76s

ℹ Waiting for file changes 13:55:00
ℹ Memory usage: 241 MB (RSS: 451 MB) 13:55:00
ℹ Listening on: http://127.0.0.1:3000/
といった感じ。通常だとポートは3000になります。

サーバー側APIは別のポートで提供することになるので別オリジンになり、そのままだと開発中にAPIアクセスができません。

セキュリティ上の理由から、ブラウザーは、スクリプトによって開始されるオリジン間 HTTP リクエストを制限しています。例えば、 XMLHttpRequestや Fetch API は同一オリジンポリシーsame-origin policyに従います。つまり、これらの API を使用するウェブアプリケーションは、そのアプリケーションが読み込まれたのと同じオリジンに対してのみリソースのリクエストを行うことができ、それ以外のオリジンの場合は正しい CORS ヘッダーを含んでいることが必要です。

「オリジン間リソース共有 (CORS)」(https://developer.mozilla.org/ja/docs/Web/HTTP/CORS)

からの引用です。

運用時には必要なくても開発時にはCORS対応が必要になる、ということが発生するわけです。
どうしたら?

「そうだね @nuxtjs/proxy だね」と公式さんは言いました(適当)

「クロスオリジンリソースを共有するには?」(https://ja.nuxtjs.org/faq/http-proxy/)

から導入や詳細については辿っていただくとして、自分の設定などをご紹介します。

前提条件として、cross-envで環境毎に設定を切り替えています。
この件についてはNuxt.js 悩み解決の小ネタ集をご参照ください。

nuxt.config.js から関係箇所抜粋

const environment = process.env.NODE_ENV || 'development'
const envSet = require(`./.env.${environment}.js`)

export default {
// (略)
axios: {
proxy: true
},
proxy: {
// 開発中はAPIサーバーとポートが異なるのでその対処のため(CORS対策)
'/api/': {
target: envSet.server.domain,
pathRewrite: { '^/api/': envSet.server.apiDir }
}
},
// (略)
開発環境用の設定ファイル .env.development.js

module.exports = {
server: {
domain: 'http://127.0.0.1',
apiDir: '/api/'
}
}
てな設定で開発時は http://127.0.0.1/api/ の下にAPIアクセスしCORSについてはよろしくやってくれます。

使用方法は /api/all というパスのAPIへのアクセスだとこんな感じ

const res = await this.$axios.get('/api/all')
おまけ
前の例ではややこしくなるのが嫌でpathRewriteで変に書き換えが起きないようにしていますが、APIが実は /api/rev2/ の下で提供されていた場合には設定ファイルを以下のよう修正すれば対応できます。

module.exports = {
server: {
domain: 'http://127.0.0.1',
apiDir: '/api/rev2/'
}
}
いい感じですね。

murave

Nuxt.js 悩み解決の小ネタ集

ここ1ヶ月程Nuxt.js + FirebaseでSPAのシステムを作っていました。

短期構築がご要望でリアルタイム性が求められる内容。Firebaseに向いていると判断、提案したところ技術構成は自由にやらせていただけることになりました。
別件で一昨年末あたりからずっとバックエンドはLaravel、フロントエンドは素のVue.js + Vuex + Vue Routerで開発やってるんですが、PWA化したほうがよさそうだったこともあってNuxt.js(以降Nuxt)使ってみようかなってことで採用。

Nuxt、便利ですね。特にプラグインとミドルウェアが素敵!

Firebase(今回使用したのはAuthentication、Firestore、Hosting、Functions)との相性もバッチリでした。

ですが初めてということもありいろいろと悩んだりもするわけで…
それらをどう解決したか?の小ネタ集です(というか今回の落とし所、かな)

cross-envで環境を切り替える

設定ファイルの扱いをどうするか?という悩みです。

バックエンドにFirebaseを使用していることもあり、クライアント側にapiKey等の設定をもたせる必要がありました。当然ながら開発用の設定と本番用の設定は異なります。
開発者の勘を働かせて(大げさ)envというキーワードでモジュールを調査。結果、cross-envを採用しました。

Qiitaにきっちりまとめてくださっている方がいらっしゃったので助かりました

Nuxtでcross-envを使い環境ごとに環境変数を分ける

今回のpackege.jsonのscriptsは以下のように開発・動作確認用Firebaseプロジェクトへのデプロイ用のbuild:staging、顧客練習用のbuild:trainingを追加しています。

"scripts": {
"dev": "nuxt",
"build": "nuxt build",
"build:staging": "cross-env NODE_ENV=\"staging\" nuxt build",
"build:training": "cross-env NODE_ENV=\"training\" nuxt build",
"start": "nuxt start",
"generate": "nuxt generate",
"lint": "eslint --ext .js,.vue --ignore-path .gitignore .",
"precommit": "npm run lint"
},

Nuxtでは通常のnuxt buildを行うとNODE_ENVはproductionになりますのでbuildをそのまま本番用としています。

インスタンスをプラグインで初期化する

ライブラリインスタンスの初期化をどこで行うか?という悩みです。

例えばFirebaseを利用するにはFirebaseプロジェクト用設定での初期化が必要ですが、プラグインの仕組みを利用して初期化するのが常道のようです。

確かに実行タイミング的に適していますね。

plugins/firebase.js

import firebase from 'firebase/app'
import 'firebase/auth'
import 'firebase/firestore'

if (!firebase.apps.length) {
firebase.initializeApp(process.env.firebase)
}
export default firebase
利用側では初期化したインスタンスをimportして利用します。

store/index.js

import firebase from '~/plugins/firebase'

export const strict = false

const db = firebase.firestore()
// 以下略
Vuexの厳格モードをオフにするせっかく用意されている厳格モード、ご指導いただくべき?という悩みです。

Vuexの厳格モードは人類には厳しすぎる!というのが私の感想です。

前掲の利用側コードにありますがVuexの厳格モードは export const strict = false でオフにしました(開発中も)

ドキュメントに「本番環境で厳格モードを有効にしてデプロイしてはいけません! 」とあることですし、気にしない。

問題なく開発できる方は厳格モードを活用してください。

共通処理をプラグイン化する

同じ処理をバラバラ書くのは避けたいもの。どこにまとめるべき?という悩みです。

プラグインに落ち着きました。さまざまなインジェクションも可能ですし、いろいろな場所で使う処理をあつかうのに便利です。他で再利用することは無いようなプロジェクト独自の処理もプラグインに書いています。

plugins/forApp.js

import Vue from 'vue'
import { cloneDeep } from 'lodash'
import firebase from '~/plugins/firebase'

const forApp = {
testA: function(a) {
return a
},
testB: function() {
return this.testA('b')
}
}
Vue.prototype.$forApp = forApp
export default forApp
のような感じでインジェクションとエクスポートを行えばvueファイル内ではthis.$forApp.testA(‘ええじゃないか’)みたいに使えますし、前掲のfirebase.jsのようにimportして使用することもできます。

こんなもんかな。
もっと良い方法があったら教えてくださいね。

murave

Nuxt.jsのbuild時にconsole.logを削除する

小ネタ集を書いていて分けておいたほうがよさそうだと思ったヤツ、その2。

nuxt buildでproductionビルドするときにconsole.logが削除されると良くないです?
良いですよね?なのでminifyを行うモジュールにはそういう機能が載っているようです。

Nuxt.jsのproduction時にconsole.logを削除する – くらげになりたい。

との情報を見つけました。ありがたい!

ですが、デフォルトのterser-webpack-pluginではなくuglifyjs-webpack-pluginに差し替えて実現されているとのこと。
この情報をもとに調査・実験をすすめたところterserのままでも可能でしたのでnuxt.config.jsのbuildの該当箇所を提示します(“nuxt”: “^2.4.0”)

module.exports = {
// 略
build: {
// 略
terser: {
terserOptions: {
compress: {
drop_console:
process.env.NODE_ENV === 'production' ||
process.env.NODE_ENV === 'training'
}
}
}
}
}
この設定では本番のproduction、顧客練習用に追加したtrainingのbuildの際にはconsole.xを削除されます。
その他(例えば私の場合はローカル開発用のdevelopmentや動作確認デプロイ用に追加したstaging)では削除されず、コンソールにメッセージが出力されます。

※ production、training、stagingなどについては「Nuxt.js 悩み解決の小ネタ集」のcross-envの項目をご参照ください。