Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Javaエンジニアのための Nodejs/Nuxt3入門

Javaエンジニアのための Nodejs/Nuxt3入門

今日、業務システムは様々な理由により Java がファーストチョイスになっていますが、フロントエンド技術の発展により Node.js を選ぶべき理由が増えてきています。

本資料は、Java エンジニアが Node.js/Nuxt3 を使えるようになるために最低限必要な知識を詰め込んだものです。Node.js から順を追って Nuxt3 にまでたどり付けるようになっています。
読んでみてこの部分は不足しているな、と思われる部分があればページを追加しようと思いますので X にて @hidekatsu_izuno までご意見いただければ幸いです。

4/6 Nuxt4 の話とNode.jsのEOLをGraalJSを使うことで伸ばす方法を追記しました。

Hidekatsu Izuno

March 30, 2024
Tweet

More Decks by Hidekatsu Izuno

Other Decks in Programming

Transcript

  1. なぜ Node.js ? Node.js:サーバーサイドで動作する JavaScript ランタイム ⚫Web 画面の作成はすでに JavaScript が主体

    ⚫ Java (に限らず多くの言語)はブラウザ上では直接動かせない(※1) ⚫ UIが高度になりテンプレートエンジンで HTML を出力し、動作だけ JavaScript で補うという作り方では対応しきれなくなった。 ⚫サーバーサイドでの使用にも優位性が出てきた ⚫ JavaVM に匹敵するパフォーマンス ⚫ 高速な起動時間(サーバーレス環境との親和性) ⚫ 多くのエッジ環境が Node.js と親和性の高い JavaScript 実行環境をサポート 3 3 ※1 そこの人。AppletもGWTもWASM版JVM もあるよ! とか突っ込まない
  2. 業務システム要件への適合性 4 4 長期互換性の担保 • JavaScriptの言語仕様 はECMAで国際標準 化されている • JavaScriptはブラウザ

    での動作が主となる ため、後方互換性に 相当の注意を払って 拡張されており、特 定ベンダの意向で互 換性が破壊されない エンジニアの確保 • 他言語の開発者で あってもWeb画面開 発では JavaScript を 使うため、誰でもあ る程度は言語仕様を 理解している 充実したライブラリ • PDF や EXCEL、DB接 続などエンタープラ イズで必須のライブ ラリも充実してきて おり、Java でなけれ ばという状況ではな くなった。 ⚫業務システムに安心して導入できる環境も整ってきた
  3. Node.js のメリット/デメリット ⚫メリット:クライアントとサーバーサイドを同一言語で書ける ⚫ ライブラリの共通化:日付や数値のフォーマットなどユーティリティ系の 処理をクライアントとサーバーで別々に用意しなくて良い ⚫ ツールや規約の共通化:コードチェックやプログラムのフォーマッタなど クライアントとサーバーで分けなくてよくなる ⚫デメリット:ランタイムのサポート期間が短い

    ⚫ LTS でも 3 年間弱(30カ月)しかサポートされない※1。Java だと配布元に よるが最大8年くらいサポートされることも多い。※2 ⚫ 最近は仕様変更も落ち着いてきており、非互換はほぼないため実際にはそ のまま動くが、ランタイム更新のための検証はやらざるをえない。 5 5 ※1 おそらく、Node.js が依存する V8 エンジンが外部システムであるため、バージョンの維持が難しいことに起因しているのでは ないかと思われる。Extended LTS を提供する会社が出てくるとよいのですが…… ※2 有償ですが、GraalJS を使うことで最大8年間のサポートを受けるという荒業もあります。
  4. なぜ React じゃないの? ⚫React は正しく理解しないと使えない ⚫ 使うには React の仕組みについてきちんとした理解が必須。多人数が関わり開発者の レベルが安定しない大規模業務システムでは導入が難しい。

    Vue.jsはHTMLの延長で 直観的に書け、比較的問題が起きにくい。 ⚫ 周辺ライブラリの選択が難しい。Vue.js だと Nuxt.js にエコシステムが集約されてお り周辺ライブラリは(良かれ悪しかれ)ほぼ一択。 ⚫Nuxt3 リリース後は安定している ⚫ Vue3リリース後、Vue2との非互換やNuxt3がなかなかリリースされないという事情も あり利用しずらい状況が続いたのは事実 ⚫ ただし、Vue3開発者のEvan Youも今後の開発では安定性を重視し、Vue2→Vue3 のような破壊的変更はしない方針はしないとのことなので安心できる ⚫ Nuxt4 のリリースが控えているが Semantic Versioning に従っているだけで、大きな 変更はない模様 ⚫ React 自体はそうでもないが周辺が落ち着かない ⚫ 先鋭的になってきた Next.js とまだ安定感のない Remix ⚫ CSSの取り扱いに至ってはもはや何が正解かよくわからない 8 8
  5. JavaScript とは ⚫元々は、ブラウザ上のHTMLを動的に操作するための簡易言語 ⚫ 1995年当時 Netscape 社に勤めていたブレンダン・アイク氏が Netscape Navigator 2.0

    に搭載する言語として開発。最初のバージョンは2週間ほどで作られた。※1 開発時は Mocha あるいは LiveScript という名前で呼ばれていたが、マーケティング 上の要請などもあり当時話題だった Java の簡易版として "Java"Script と名付けられた。 ⚫ プログラミング言語としては Java とはまったく別物だが、標準APIには Java から移 植されたものもあり、まったく無関係というわけでもない。 ⚫ その後 ECMA で ECMAScript として国際標準化(最新は ECMAScript 2023)。 JavaScript は Oracle の商標であり ECMAScript と呼ぶべきであるが、歴史的経緯もあ り今でも JavaScript と呼ばれている。 ⚫ 現在では、ブラウザに限らずサーバーサイドやエッジコンピューティング環境など 様々な環境で動作するようになっている ⚫ Node.js、Deno、Bun など ⚫ CloudFront Functions、Cloudflare Workers など 11 11 ※1) 経緯については「JavaScript の生い立ちを探る」が詳しい。現在も残る奇妙な動作(typeof null が "object" を返すな ど)は初期バージョンの名残で、アドホックな仕様が互換性の問題で変更できなくなったため。
  6. 最近の JavaScript 事情 ⚫ES2015 (ES6) 以降で標準的な書き方ががらりと変化 ⚫ 主要ブラウザでもすでにサポート済みです。 ⚫ 他の言語同等以上に便利な構文が増えた一方、知識のアップデートが必要です。

    12 12 var を使う let/const を使う 昔の書き方 今の書き方 変数の定義 prototype ベース class 構文ベース クラスの定義 無名関数 function () {} を使う アロー関数 () => を使う インライン関数 Object.assign を使う スプレッド構文 { … } を使う オブジェクト のコピー 長さを使ってループ 例: for (var i =0; i < array.length; i++) for-of 構文でループ 例: for (const item of array) 配列の列挙 + 演算子で結合する 例:"count: " + count テンプレートリテラルを使う 例: `count: ${count}` 変数を含む文字列 の構築 Promise を使う async / await を使う(ES2017) 非同期処理
  7. Node.js とは 14 14 ⚫ Chrome や Edge で使われている JavaScript

    エンジン V8 に ファイルI/Oなどサーバーサイドで必要となる機能を追加した JavaScript 実行環境です ⚫ インストールすると次の3コマンドが使えるようになる 【node コマンド】Java の java コマンドに相当 • 引数に指定した JavaScript のプログラムを実行します。 【npx コマンド】 • 引数に指定したNode.js 向けのツールをその場でダウンロードして実行します。 • 上記の例は gitignore がツール名で node 用の .gitignore を生成するものです。 npx gitignore node node ./sample.js 【npm コマンド】Java の mvn/gradle に相当 • Node.js に標準で付いてくるパッケージマネージャーです。npm は、多数のサブコマンドを持ちますが、上記の例は vue ライブラリをインストールする例です。詳細は後述。 • パッケージマネージャーとしては他にも yarn や pnpm もありますが、npm は標準で付属しますし、困ることはほぼ ないと思います。 npm install vue
  8. Node.js のインストール ⚫ Node.jsのインストール方法はいくつかありますが、Volta での管理がおすすめ※1 ⚫ Volta: Node.js用ランタイムマネージャー(Javaだと SDKMAN に相当)

    ⚫ apt でインストールされるものはバージョンが古いことが多くおすすめしま せん。 15 15 【Volta のインストール】 【Node.js のインストール】 【実行に使う Node.js の選択】 curl https://get.volta.sh | bash volta install node # 最新LTS版をインストール volta pin node@20 • Windows の場合はインストーラーを使います。ただし、WSL2 上で開発するなら上記の方法が使えます。 • Node.js は偶数バージョンがLTS (Long Time Support)の安定版(3年ごとにリリース)、奇数バージョンは不安定な 最新版という位置づけ。インストールすると、node, npx, npm の3コマンドが使えるようになります。 ※1) Node.jsのランタイムマネージャーとしては nvm、n、nodebrew なども有名ですし現在でも新たなツールが開発されている状 況ですが、Volta には高速な動作やWindows環境のサポートなど多くの利点があり現時点では一番おすすめできます。 • Volta を使うと複数の異なるバージョンをインストールし、特定のバージョンを使うよう固定することができます。
  9. npm による依存関係管理 ⚫npm はパッケージの依存関係管理などを行うコマンド ⚫ Java だと Maven や Gradle

    に相当するものです。 ⚫ プロジェクトルートに置かれた package.json にプロジェクト情報や依存関係を 定義します。どのようなパッケージがあるかは npmjs.com で調べることができます。 16 16 【package.json の生成】 • ウィザードに沿って回答すると package.json が生成されます。生成された後はテキストエディタで直接編集しても 構いません。 • package.json の項目の詳細はこのページを参考にしてください。 • 仕事用のプログラムは、ライセンスとして UNLICENSED を設定する(private: true を設定するとなお良い) npm init 【パッケージの追加】 • 第2引数に指定したパッケージをダウンロードし package.json の dependencies に追加します。パッケージ名@バー ジョンとすることで特定のバージョンをインストールすることもできます。 • フォーマッタなど開発時には使うが実行には使わないパッケージをインストールするときは、--save-dev (あるい は-D)オプションを付けることで依存関係を区別できます。devDependencies に追加されます。 npm install vue 【パッケージの削除】 npm unistall vue • 第2引数に指定したパッケージを削除し package.json 依存関係から取り除きます。
  10. npm によるコマンド実行 ⚫npm にはコマンドランナーの機能があります ⚫ build や test など良く使う処理をショートカットで呼び出すために使います ⚫

    プロジェクトで使うコマンドが明示できる 17 17 【コマンドの実行】 • 第2引数に指定したコマンドを実行します。 • 引数を渡したい場合は、-- を書き、その後ろに引数を渡します。※2 npm run target 【コマンドの定義(package.json)】 "scripts": { "target": "echo Hello world!" } • package.json 内の scripts という項目に呼び出し名と実行したいシェルスクリプト(Windows の場合はコマンドプロ ンプトのバッチスクリプト)を記述します。 • js のプログラムを実行したい場合は node ~.js と記述します。※1 ※1) node_modules/.bin にはパスが通っている状態となるため、依存関係にあるパッケージのコマンドはそのまま呼び出すことが できます。 ※2) –名前=値 を指定することで ${npm_config_名前} で参照することもできます。
  11. Node.js プロジェクトの構成 ⚫ npm install … を行うと次のファイル/フォルダが作られます※1 ⚫ package-lock.json ⚫

    コマンド実行時点でのパッケージの完全な依存関係が記録されるファイルです。 公開されているパッケージは常に新しいバージョンが公開されるため、ローカ ルと同じ環境を再現するために必要です。 ⚫ 通常は package.json と共に Git リポジトリに登録しておきます。 ⚫ node_modules/ ⚫ ダウンロードした依存関係にあるパッケージファイルが保管されます。 ⚫ Maven/Gradleではダウンロードされたパッケージは $USER/.gradle 以下のよう なグローバルに保存されますが、npm はすべてプロジェクトフォルダ配下の node_modules フォルダに保存されます。※2 ⚫ Git リポジトリに登録してはいけません。node_modules の内容は package.json と package-lock.json から復元できるので、単に npm install するだけです。 18 18 ※1) Git を使う場合は除外ファイルを指定する .gitignore を作成します。npx gitignore node で作成できます。 ※2) pnpm というパッケージマネージャを使うと、パッケージファイルを共有することでディスク容量の節約が可能です。
  12. Node.js のモジュール方式について ⚫ Node.js には歴史的経緯※1から CommonJS と ESM (ECMAScript Modules)

    という2つのモジュール方法が存在 ⚫ CommonJS: 古い方式。require 関数で外部モジュールを同期的にロードする。 ⚫ ESM: 標準に沿った新方式(ECMAScript モジュール)。非同期的にロードする。 ⇒ 課題: CommonJS と ESM は相互運用性に難がある ⚫ ファイル名だけからは呼び出し先のモジュールがどちらの方式を使っているか不明 ⚫ ロードタイミングが異なるため相互に呼び出しできない(場合がある) ⚫ デフォルトは CommonJS だが、今後は ESM に統一して書くことが望ましい ⚫ 拡張子を .js から .mjs に変えると ESM 扱い(.cjs にすると CommonJS) ⚫ package.json に "type": "module" を指定すると .js が ESM 扱いになる 19 19 ※1) 元々 JavaScript 単体には外部モジュールのロード方式がなく(HTMLのscriptタグでロード)Node.js が独自に require 方式を採用 し広まった。しかしその後、ECMA標準でESモジュールが定義されたため、相互運用性に問題が発生することになった。 const fs = require("fs") import * as fs from "fs"
  13. Node.js の標準モジュール ⚫Node.js では JavaScript 標準のクラス群だけではなく、サーバーサ イドで使う様々なモジュールが標準添付されています。 ⚫ ブラウザ向けのAPIも、互換性のため多くのものが移植されていますが、すべて が存在するわけではありません(例えば

    window オブジェクトは存在しない) 20 20 モジュール名 説明 node:buffer バイト配列関連機能 node:fs ファイルシステム操作 node:net ソケット通信 node:path ファイルパス編集 node:process プロセス関連機能(引数やカレントフォルダなど) node:util 各種ユーティリティ関数 node:zlib GZip 関連機能 【主要な Node.js 標準モジュール】 【Node.js に移植されているWeb標準モジュール例】 API名 オブジェクト名 説明 Console API console コンソール出力 Web Crypto API crypto 暗号化/復号化関連処理
  14. TypeScript とは ⚫JavaScript を型安全に扱うために Microsoft が開発した言語※1 ⚫ JavaScript は変数に型を書く必要がなく手軽に書けるが、型がある方が間違い が少なくなるため大規模開発では好まれる

    ⚫ 型推論の機能が充実しているため、対応もそこまで大変ではない ⚫ 型の指定が必要となるのは、おおむね未初期化変数、関数の引数、配列やオブジェ クトなどコンテナ型の要素型といった自明でないもののみ 22 22 // 未初期化変数 let value: number | undefined // 関数(戻り値は型推論される) function plusOne(arg: number) { return arg + 1 } // 配列要素 const array: string[] = [] // オブジェクト型 const obj: Record<string, any> = {} • 変数名に続けて、コロン(:)と型の 前を書きます(Java と異なり型が 変数名の右側に来ます) • 複数の型のいずれかが入る場合は 縦棒(|)でORを表現できます。 • 変数名に続けて、コロン(:)と型の 前を書きます(Java と異なり型が 変数名の右側に来ます) • 複数の型のいずれかが入る場合は 縦棒(|)でORを表現できます。 ※1) 開発者には C#やDelphi で有名なアンダース・ヘルスバーグも参加しており、ときおり C# 風の構文が見られる。
  15. TypeScript 特有の型 ⚫プリミティブ型※1などには専用の型名が用意されています 23 23 TypeScript 備考 any 任意の型を許容することを示します。 void

    値が返らないことを示します。関数の戻り値に使います。 boolean true/false に対応する型です。 number 数値に対応する型です。 string 文字列に対応する型です。 要素型[] 要素型の配列です。 { キー名: 要素型, … } 指定した要素型のキーを持つ object 型です。 キー名に ? を付けることで任意であることを示します。なお、 要素型に | undefined を付けるのと同じです。 { [key: キー型]: 要素型 } Record<キー型, 要素型> キー型と要素型を持つ object 型です。 (引数名: 引数型, …) => 戻り値型 関数型です。戻り値がない場合は戻り値型に void を指定しま す。 リテラル値 undefined、null、true、"文字列"、数値 などリテラル値はその 値しか取らない型名として使うことができます。 "yes" | "no" のようにリテラル値の OR を指定することで列挙型 のように扱うこともできます。 ※1) JavaScript では "abc" と new String("abc") は異なり、前者はプリミティブ型、後者はオブジェクト型の値となる。TypeScript で はそれぞれ string 型とString 型となるが、後者は基本的に使わないため通常はプリミティブ型の string だけを使用する。
  16. TypeScript 関連のファイル ⚫ tsconfig.json: TypeScript の設定ファイルです。 ⚫ Nuxt.js を使う場合は自動で生成されるため気にする必要はありません。 ⚫

    ~.ts: TypeScript のソースコードです。 ⚫ .mjs、.cjs に対応する .mts、.cts もありますがあまり使われていません。 ⚫ .ts のファイルを直接実行したい場合は、tsx などのツールが使用できます。 ⚫ ~.d.ts: JavaScript に外付けで型を付けるための型定義ファイルです。 ⚫ JavaScript で書かれたライブラリでも、外部に型定義を作ることができます。 ⚫ 多くの場合 @types/モジュール名 という名前で型定義ファイルだけのモジュールが 用意されているため、依存関係に加えるだけで型情報が使用できます。 24 24 npx tsx hello.ts npm install @types/node --save-dev
  17. Vue.js とは ⚫React、Angular と並ぶUI構築ライブラリのひとつ ⚫ Vite でも有名なエヴァン・ヨーが2014年に開発 ⚫ 現在の最新は Vue3(Vue2

    から Vue3 で仕組みが大きく変わった) ⚫ HTMLに近いテンプレート構文が特徴 26 26 <!DOCTYPE html> <html> <head> <script> window.onload = () => { const target = document.getElementById("target") target.innerText = "Loaded!" } </script> <style> #target { color: red; } </style> </head> <body> <div id="target">Loading...</div> </body> </html> <script setup lang="ts"> const target = ref("Loading...") onMounted(() => { target.value = "Loaded!" }) </script> <style> #target { color: red; } </style> <template> <div id="target">{{ target }}</div> </template> 【HTMLの例】 【Vue3の例】
  18. Vue ファイルの構造 ⚫ 基本は3つのタグだけ(この順番を推奨) ⚫ script: JavaScript コードを記述 ⚫ setup

    属性: 常に付ける。Vue3向け※2 に最適化された書き方で書ける ⚫ lang="ts" 属性: TypeScript を使う場合 ⚫ template: HTML テンプレートを記述 ⚫ プレースホルダは {{ 変数 }} で記述 ⚫ title や meta などヘッダ情報は書かな い。動的に変更する方法は後述 ⚫ style: CSS コードを記述 27 27 ※1) React のように JavaScript のみで書ける関数型コンポーネントを書くことも可能だが通常不要なので本資料では省略。 ※2) Vue3 には Vue2 互換の Options API とVue3で導入された Composition API の2通りの書き方があるが、Options API はもはや誰も 使っていないので、Composition API を使えば問題ない。本資料でも Options API は説明しない。 <script setup lang="ts"> const target = ref("Loading...") onMounted(() => { target.value = "Loaded!" }) </script> <template> <div id="target">{{ target }}</div> </template> <style> #target { color: red; } </style> ⚫ 正式には Vue シングルファイルコンポーネント: UI定義ができる拡張子 .vue のファイル ⚫ (誤解を恐れずに言えば)ブラウザ上で動くJSPみたいなもの
  19. Vue ファイルで使える構文 28 28 状況 記載箇所 構文 備考 変数(リアクティブ・オ ブジェクト)の定義

    script const 変数名 = ref(初期値) JavaScript 内は 変数名.value でア クセス。template内では省略可。 テキストの出力 template {{ 式 }} 属性の出力 template <div :属性名="式">…</div> 双方向バインディング template <input v-model="変数" /> 入力値の取得/設定に使用。 条件分岐 template <div v-if="式">…</div> <div v-else-if="式">…</div> <div v-else>…</div> タグを出力したくない場合は template タグを使用する。 ループ template <div v-for="要素 in 配列">… ループ(インデックス付き) template <div v-for="(要素, インデックス) in 配列">… イベントリスナー定義 script function リスナー名() { … JavaScript の関数を定義するだけ です。 イベントリスナー設定 template <div @イベント名="リスナー名">… ⚫ リアクティブ 値の変更に反応して、即座に結果 に反映する仕組み ⚫ リアクティブ・オブジェクトを使 うことで、再描画を指示しなくて も表示に反映できる。 ⚫ 配列やオブジェクトが入れ子に なっていてもOK <script setup lang="ts"> const count = ref(0) function add() { count.value++ } </script> <template> {{ count }} <button @click="add">Add</button> </template> 【Add ボタンで countを進める例】
  20. コンポーネント ⚫カスタムタグを作るための機能(JSP のカスタムタグと同種の機能) ⚫ 基本的には単なる .vue ファイル ⚫ 属性値(props※1)とイベント(emits)の受け渡しが可能※2 ⚫

    スロット(slot)で子要素の受け渡しも可能(名前付きスロット※3も可) 29 29 【コンポーネント定義】 <script setup lang="ts"> const props = withDefaults(defineProps<{ label: string, }>(), { label: "Default" }) const emits = defineEmits<{ (event: "click", value: MouseEvent): void, }>() </script> <template> <button @click="(event) => emits('click', event)" >{{ props.label }}<slot /></button> </template> Comp.vue 【コンポーネントの呼び出し】 <script setup lang="ts"> function onClick() { alert("Pushed!") } </script> <template> <Comp label="Push" @click="onClick" >Children</Comp> </template> ※1) props の値はリアクティブ・オブジェクトでないことに注意。props の変更に追随するには watchEffect で監視する必要があります。 ※2) input タグのように属性とイベントが紐づく双方向バインディングには defineModel というショートカットが用意されています。 ※3) <slot name="foo" /> と名前を指定することで <tag #foo>…</tag> の形式で差し込みができる。パラメータの受け渡しもできます。 属性値の 定義 属性値の 定義 イベント の定義 イベント の定義 スロット の定義 スロット の定義
  21. Vue.js の便利機能 ⚫ 式によるクラス/スタイル指定: オブジェクトや配 列を使ってクラスやスタイルを指定できます。 ⚫ 条件指定が簡単にできます。 ⚫ 普通の属性と組み合わても動作します。

    ⚫ 属性値の継承: コンポーネントの呼び出し側で 指定した属性はコンポーネント内のルート要素に 継承されます。 ⚫ defineOptions({ inheritAttrs: false }) で無効化できます。 ⚫ マルチルートコンポーネント: コンポーネントは複 数のルート要素を持てます。 ⚫ ただし、属性値の継承はできません。 ⚫ Teleport: HTML の挿入場所を移動させることが できます。 ※2 ⚫ 疑似ウィンドウを body タグの最後に挿入する、など ⚫ body だけでなく #ID でIDの指定も可能 30 30 <Teleport to="body"> <div>Last in Body</div> </Teleport> <template> <div>Root 1</div> <div>Root 2</div> </template> <Comp class="foo" /> ↓ <template> <div ※ここに class="foo" が付く >Comp</div> </template> <div class="base" :class="['text-red-100', { 'text-center': true }]" style="font-size: 1em" :style="{ 'color': 'green' }" >...</div>
  22. Nuxt.js とは ⚫ Vue.jsベースのフルスタックWebアプリケー ションフレームワーク ⇒ Java でいう Spring Boot

    みたいなもの ⚫ 現在の最新は Vue3 をサポートした Nuxt3 32 32 ⚫ Vue.js にルーティング、SSR/SSG、Web APIサーバ、プラグイン機構などを追 加したもの ⚫ ルーティング:pages/ 以下に置かれたパスに従い、リクエストを転送する ⚫ SSR(サーバーサイドレンダリング):初回アクセス時にサーバー側でページを生成 することでレスポンススピードを上げる仕組み※1 ⚫ SSG(静的サイト生成):ページをあらかじめ出力し、Webサーバーだけで動作可能 な静的サイトを生成する仕組み ⚫ Web API:高速でエッジでも利用できる H3 Web Framework が内臓されており、 server/api/ 以下に .ts/js ファイルを置くことで Web API の作成が可能 URLのパス ファイルパス / /pages/index.vue /example /pages/example.vue /sub/0001 /pages/sub/[id].vue ※各括弧を使うことでパス・パラメータを使うことができます。 ※1) SSR に対し、通常のブラウザ側でページを動的に生成する方式を CSR (クライアントサイドレンダリング)と呼びます。
  23. Nuxt.js のインストール ⚫nuxi: nuxt 用のセットアップツール※1 ⚫ npm install でもインストール可能ですが、通常はこのツールでプロジェクトごと作成 します。

    33 33 【nuxt プロジェクトの作成】 npx nuxi init 【nuxt バージョンの更新】 npx nuxi upgrade --force • Nuxt に新たなマイナーバージョンが出た場合には upgrade サブコマンドでバージョンを更新できます。 • ウィザードに沿って回答すると、Nuxt3 を使ったプロジェクトのひな形が生成されます(gradle init みたいなもの) ※1) Nuxt2 では create-nuxt-app が使われていましたが、Nuxt3 では nuxi を使います。 ⚫ プロジェクト作成後は、package.json に定義された各コマンドを使って開発します。 コマンド 説明 npm run dev 開発用サーバーを起動します。通常は、http://localhost:3000 で開きます。 npm run build .output に本番実行用のプログラムを出力します。 npm run generate .output/public にSSG用のプログラムを出力します。 npm run preview build 後の出力を使いプレビュー表示を行います。通常は、http://localhost:3000 で開きます。 ⚫ VSCode を使う場合は「Vue Official」拡張機能もインストールすることをお勧めします。
  24. Nuxt.js のフォルダ構成 ⚫ 多くのフォルダが生成されますが、用途がわかればさほど難しくありません。 34 34 用途 フォルダ 説明 UI

    pages/ 画面ごとの vue ファイルを配置します。 components/ コンポーネントを配置します。このフォルダにおかれたコンポーネントは、 import を書くことなく参照することができます。 リソースファイル assets/ 主にCSSやフォントファイル、画像といったリソースファイルを配置しま す。このフォルダ自体は公開されませんが、vue ファイルから参照するこ とでインライン化など最適化して出力します。 public/ favicon などそのまま公開する静的リソースを配置します。 UI用ユーティリ ティ関数 composables/ 追加のコンポーザブル関数を配置します。 utils/ 追加のユーティリティ関数を配置します。 Web API 用ユー ティリティ関数 server/api/ Web API 実行用のスクリプト(.ts/js)を配置します。 server/utils/ Web API 用のユーティリティ関数を配置します。 【nuxt プロジェクトのフォルダ】 【nuxt プロジェクトのファイル】 ファイル 説明 nuxt.config.ts Nuxt.js の設定ファイルです。設定値の詳細はここを参照。 app.vue 全画面の共通ファイルです。ローディング表示や共通CSSの設定などに使います。 error.vue エラー画面用のファイルです。
  25. Nuxt.js の便利機能 ⚫ レイアウト:ページのテンプレートとなるレイアウト を指定できます。※1 ⚫ layouts/ フォルダにレイアウトファイルに配置 し、各画面の definePageMeta

    でどのレイアウ トを適用するか指定します。 ⚫ useHead: head タグの中を動的に変更でき ます。 ⚫ title や meta タグ、外部 css/js ファイルの読み込 みなど head を動的に設定できます。 ⚫ html や body タグの属性も設定可能です。 ⚫ template 内に記載する別の方法も用意されてい ます。 ⚫ useId: SSRセーフなIDの生成ができます。 ⚫ input の for の値など画面内で一意となる値をコ ンポーネント内で安全に生成できます。 35 35 definePageMeta({ layout: 'custom' }) const title = ref("title") useHead({ title, meta: [{ name: "keywords", content: "sample" }], style: ["../custom.css"], htmlAttrs: { lang: "en" }, bodyAttrs: { tabindex: -1 }, }) ※1) コンポーネントでも代替できるパラメータも渡せるため、正直使いどころが微妙です。 ※2) Nuxt.js の項に書いていますが、Vue3 の機能です。 const id = useId()
  26. Web API の作成 ⚫ server/api/ の下に defineEventHandler を使った関数を置くと Web API

    が生える。 36 36 export default defineEventHandler(async (event) => { const query = getQuery(event) const body = await readBody(event) return { hello: 'world', query, body, } }) /api/hello ⇒ server/api/hello.ts ⚫ 検証ライブラリの Zod などを使った型検査もサポートされています。 import { z } from 'zod' const someSchema = z.object({ name: z.string().email(), }) export default defineEventHandler<{ query: z.infer<typeof someSchema>, body: z.infer<typeof someSchema>, }>(async (event) => { const query = await getValidatedQuery(event, someSchema.parse) const body = await readValidatedBody(event, someSchema.parse)
  27. Web API の呼び出し ⚫ useFetch: SWR※1 に従った Web API の呼び出しが可能

    ⚫ server/api の API を呼び出す場合は引数や戻り値の型チェックの恩恵が受けられる。 ⚫ 困ったところ: ⚫ useFetch は、新しいデータを取得するまでは過去のデータを表示することで速度を上 げる、というユースケースが想定されているが、業務システムでは過去のデータをそ もそも使ってほしくないことが多い。 ⇒ useFetch は使わずに onBeforeMount や onMounted で $fetch を使う必要がある※2 ⚫ $fetch: サーバでもクライアントでも使える fetch 関数 ⚫ イベント関数内で Web API を呼び出したい場合はこちらを使う ⚫ SWR を行わないので、意図しない動作が起きにくい 37 37 const { data, pending, error, refresh } = useFetch("/api/hello", { method: "POST", body: { name: "Name" }, }) const data = await $fetch("/api/hello", { method: "POST", body: { name: "Name" }, }) ※1) Stale-While-Revalidate キャッシュを表示しつつ、コンテンツ更新を確認し取得することで素早い表示を可能にする戦略 ※2) 後ほど vue-history-state の useRestorableState を使う方法を紹介します。
  28. Nuxt.js プロジェクトの実行 ⚫ 開発時は npm run dev だけで十分ですが、サーバ上で動作させるためにはビル ドの出力結果を使って起動する必要があります。 38

    38 # ビルドします。.output 以下に成果物が保存されます。 npm run build # 成果物とモジュールをコピーします cp -r .output/ $DIST cd $DIST # 起動します。 node .output/server/index.mjs 【SSR/CSR の場合】 【SSG の場合】 # 生成します。.output 以下に成果物が保存されます。 npm run generate # ページを公開サイトにコピーします。 cp -r .output/public/ $WWWROOT • build に --preset オプションを付けることで、Cloudflare などの環境に適した出力を行うことができます。 • SSG の場合 Web API は出力に含まれません。nuxt.config.ts に ssr: false を指定し build した後、.output/server/ のみを取り出すことで対応は可能です。
  29. Docker で Nuxt.js を使う ⚫Docker コンテナ化するには distoless を使うのがおすすめです。 ⚫ distoless:

    Google が提供する最小限のコンテナ実行環境 ⚫ NUXT_HOST を指定しないと外部からの接続を受け付けないので注意 FROM node:20 AS build-env COPY . /build WORKDIR /build RUN npm ci RUN npm run build FROM gcr.io/distroless/nodejs20-debian12 COPY --from=build-env /build/.output /app ENV NODE_ENV=production ENV NUXT_HOST=0.0.0.0 ENV NUXT_PORT=3000 WORKDIR /app CMD ["./server/index.mjs"] ビルドフェーズと実行フェーズを分 けることで不必要なモジュールが含 まれないようにします。 なお、npm ci は clean install の略で す。(≠continuous integration) ビルドフェーズと実行フェーズを分 けることで不必要なモジュールが含 まれないようにします。 なお、npm ci は clean install の略で す。(≠continuous integration) Nuxt.js の実行に必要なファイルはす べて .output フォルダに含まれてい ます。他のファイルをコピーする必 要はありません。 Nuxt.js の実行に必要なファイルはす べて .output フォルダに含まれてい ます。他のファイルをコピーする必 要はありません。
  30. セキュリティ上の注意点 ⚫Vue ファイルのコードは世界中に公開される ⚫ JSP はサーバー側で処理され結果だけが公開されますが、Vue.js(だけでなく React なども)を使う場合、コードはすべて公開された状態になります。秘匿すべき値が公 開されないよう今まで以上に注意する必要があります。 ⚫

    やりがちな失敗:アクセスキーの秘匿が必要な外部API呼び出しをクライアントサイ ドで行ってしまう。 ⇒ ブラウザからアクセスキーの秘匿が必要な外部API呼び出しは不可能なので、あ きらめて自サーバ内に Web API を作りその中からアクセスしましょう。 ⚫Vue ファイルとやり取りしたデータはユーザーに公開される ⚫ やりがちな失敗:Web API でユーザーマスタの検索結果を JSON 化しブラウザに送信、 Vue ファイル内で必要な項目だけ使い画面を表示させた。 ⇒ 画面上は見えていないが、開発者コンソールで API のレスポンスにはパスワード などが含まれていて情報漏洩が発生。 ⚫ 一方で対策されている問題もある ⚫ SSR でのアプリケーション内データ共有:Nuxt2 では Vuex というライブラリが使わ れていたが、正しく使わないと情報漏洩が発生する場合があった。Nuxt3 では useState コンポーザブルを使うことで安全に共有できます。 ⇒ とはいえ、ユーザー固有の情報は常に共有しない方に倒すのが無難。サーバ側で セッションに入れるなりしてキャッシュし都度リクエストすることを勧めたい。クラ イアントサイドはステートレスにするよう心掛けましょう。 40 40
  31. Node.js の便利な外部モジュール ⚫Nuxt.js の機能では不足する部分を埋めるライブラリを紹介します。 42 42 種類 モジュール名 説明 ユーティリティ

    lodash-es 便利なユーティリティ関数を提供 zod 有名 Validation ライブラリ(より軽量な valibot というモジュールもある) date-fns 日付関連ユーティリティ関数を提供 decimal.js Java の BigDecimal に相当する実数演算ライブラリ。JavaScript の Number は浮動 小数点であるため金額計算には使ってはいけません(計算せずに表現として使う だけであれば精度の範囲で使っても問題ありません) jtc-utils 和歴変換やシフトJISでのCSV/固定長入出力など固定長日本の業務システム向けの ユーティリティ機能群を提供(自作) UI補助系 body-scroll-lock 疑似ダイアログ表示時に後ろの画面を固定する PDF出力 pdf-lib PDF作成/編集ライブラリ。日本語も可(jsPDFも有名だが編集は不可) EXCEL出力 exceljs EXCEL作成/編集ライブラリ(sheet.js というモジュールもあるが機能制限があ る) DB接続 better-sqlite3 Sqlite3 接続ライブラリ(sqlite3 よりも高速で使いやすい) postgres PostgreSQL 接続ライブラリ(pg よりも使いやすく機能性も良い) mysql2 MySQL 接続ライブラリ prisma 最近よく使われている ORM フレームワーク。便利だが制約もあり業務システム で使うなら直接SQLを発行する方がよいかも。 ログ出力 pino 軽量なロギング・フレームワーク テスト vitest 高速に起動するユニットテストツール
  32. JTC-utils(自作ライブラリ) ⚫ Java での業務システム開発で必要になる機能が JavaScript では不足している ことが多く、必要に迫られ自作したものです。 43 43 【インストール】

    npm install jtc-utils 【機能】 種類 クラス/関数名 説明 CSV入出力 CsvReader CsvWriter Windows-31J、EUC-JP、CP930、CP939 BOM制御 に対応した async- await で使える CSV 入出力機能です。Web/Node.js 両対応しています。 固定長入出力 FixlenReader FixlenWriter Windows-31J、EUC-JP、CP930、CP939 BOM制御 に対応した async- await で使える固定長入出力機能です。Web/Node.js 両対応しています。 数値のパース/ フォーマット parseNumber formatNumber Java の DecimalFormat 相当の数値のパース/フォーマット関数です。 ロケールにも対応しています。 日時のパース/ フォーマット parseDate formatDate Java の DateTimeFormat 相当の日時のパース/フォーマット関数です。 ロケールや和歴変換にも対応しています。 日本で使う文字種の チェック・変換 toFullwidth toHalfwidth toHalfwidthAscii is/toHiragana is/toFullwidthKatakana is/toZenginKana isWindows31j 全角、半角、全銀カナ、Windows31J など日本特有の文字種に対する チェックや変換関数です。 • 詳細は GitHub リポジトリを参照してください。
  33. Nuxt.js の拡張モジュール ⚫ Nuxt.js を拡張するモジュールを紹介します。 44 44 種類 モジュール名 説明

    セキュリティ nuxt-security Nuxt.js 単独で OWASP 対応ができる拡張モジュール 国際化 @nuxtjs/i18n Nuxt.js で多言語に対応するための拡張モジュール UI系 @nuxtjs/tailwindcss Nuxt.js から tailwindcss を簡単に使えるようにするモジュール アイコンも別のモジュールではなく @iconify/tailwind が使えます。 テスト @nuxt/test-utils コンポーネントのテストやE2Eを行うためのユーティリティ群 画面遷移 vue-history-state 戻る/進むといったブラウザ遷移を考慮して状態を管理するモジュール(自作) 【モジュールの設定】 modules: [ "nuxt-security", "@nuxtjs/tailwindcss", "vue-history-state/nuxt", ], • npm install した後に nuxt.config.ts にモジュール設定の追加が必要です。 nuxt.config.ts
  34. vue-history-state(自作モジュール) ⚫ Vue.js で戻る/進む/リロードといった画面遷移アクションを正しく使えるようにする状 態管理モジュール ⚫ SPAとブラウザの標準動作の組み合わせでは、ブラウザの戻る/進む/リロードを行っ た際に JavaScript の値やスクロール状態がユーザーの望む状態と一致しない。より詳

    しい説明は Qiita の記事を参照 45 45 ⚫ vue-history-state では次の機能を提供します。 ⚫ 戻る/進む/リロードした際の前回状態の取得 ⚫ 戻る/進む/リロードした際のスクロール位置の復元 ⚫ ヒストリー上に保存された状態の編集/削除 ⚫ 画面遷移時の画面間での値の受け渡し ⚫ これらの機能によりグローバルな状態を持たせず、各 画面の状態と画面間での値の受け渡しというロー カルな状態管理を実現できます。※1 // データのバックアップと復元 const data = useRestorableState({ key: "value" // 初期値 }) // 遷移状態によって処理を変える例 const data = useRestorableState({ mode: "create", // 初期値 }, ({ visited, info }) => { if (!visited) { if (info?.mode === "update" || info?.mode === "delete") { return $fetch("api.example.com" ).then(res => ({ ...res, mode: info.mode, })) } } else { if (info?.refresh) { return $fetch("api.example.com") } } }) ※1) ひとつだけ欠点があるとすれば、戻る/進む/リロードといった遷移情報はブラウ ザ側にしか存在しないため SSR での fetch が行えません。パフォーマンスを意識する 場合は、用途によって useFetch と使い分ける必要があります。(SSR モードで使えな いという意味ではありません)