Angular - TECH PLAY - TECH PLAY

TECH PLAY

Angular

イベント

該当するコンテンツが見つかりませんでした

マガジン

技術ブログ

本記事は 2年目ウィーク 2日目の記事です。 📈 1日目 ▶▶ 本記事 ▶▶ 3日目 ✒️ はじめに 仮想スクロールの仕様 仮想スクロールのハマりポイント バグの原因 親コンポーネント 子コンポーネント 解決法 おわりに はじめに こんにちは、カロリーメイトがそれなりに好きな入社2年目の小宮山です。 重いページの読み込みを待つ時間って地味にストレスですよね。 フロントエンドエンジニアとしては、ユーザ体験を損ねないためにも画面表示の時間は短く抑えたいものです。 Angularで大量のデータを画面に表示する際、パフォーマンスを向上させるために通常スクロールから仮想スクロールに移行することはよくある…
はじめに こんにちは、セーフィーに新卒で入社し、サーバーサイドエンジニアをしている古谷です。 入社後、同期の坂上さんと一緒に VApp(Vulnerable Application) という、意図的に脆弱性を仕込んだ学習用Webアプリケーションを作りました。社内のセキュリティ学習を目的に始めたプロジェクトでしたが、作ってみると 「Webフレームワークを使っていても、脆弱な実装をしないように気をつけなければならない」 という、当たり前のようで意外と実感しにくいことを実装ベースで確かめられました。 この記事では、VAppを紹介しつつ、それを通して 自分の中でセキュリティとの向き合い方がど
こんにちは、駅メモ!開発チームエンジニアの id:hayayanai です! 最近、 VoidZero から Vite+ がリリースされました。 Vite+ は Vite 8、Vitest、Oxlint、Oxfmt などを統合した「Web のための統合ツールチェーン」です。 駅メモ!は現在 Vue + Vite 7 + Vitest + ESLint(チーム独自ルール有り)+ Prettier + Stylelint で開発されており、Vite+ のツールはまだ導入していません。 AI で開発が高速化した現代、数十倍速いと謳う Vite+ のツールチェーンは気になります。 ただ、そもそも各ツールがどれくらい Vue 対応しているのか、どう設定すれば良いのかがわからなかったので、テンプレを使って確認することにしました。 Vite+ 経由の vp create vue と従来の pnpm create vue@latest で生成されるプロジェクト設定を比較し、Vue プロジェクト特有の注意点を整理します。 検証環境 プロジェクトの作成 Vite+ 経由 従来の create-vue 生成される設定ファイルの比較 Vite+ プロジェクトの構成 従来の create-vue プロジェクトの構成 共通: eslint.config.ts 共通: Lint 実行順 Linter 比較: Oxlint vs ESLint 検証用コンポーネント Oxlint の結果 ESLint の結果 CSS/Style Lint について 型チェックの違い テスト Formatter 比較: Oxfmt vs Prettier Vue SFC のフォーマット対応 全体比較表 まとめ Linter 型チェック Formatter CSS Lint 検証環境 vp v0.1.16 (Vite+) create-vue v3.22.2 (pnpm create vue@latest) Node.js v24.14.1 pnpm v10.33.0 プロジェクトの作成 Vite+ 経由 vp create vue vue-viteplus ◇ Which package manager would you like to use? pnpm ◇ pnpm@10.33.0 installed ◇ Which agents are you using? Claude Code ◇ Which editor are you using? VSCode ◇ Set up pre-commit hooks to run formatting, linting, and type checking with auto-fixes? Yes Generating project… Running: pnpm dlx create-vue ┌ Vue.js - The Progressive JavaScript Framework │ ◇ Project name (target directory): │ vue-viteplus │ ◇ Use TypeScript? │ Yes │ ◇ Select features to include in your project: │ Vitest (unit testing), Linter (error prevention), Prettier (code formatting) │ ◇ Select experimental features to include in your project: │ Replace Prettier with Oxfmt │ ◇ Skip all example code and start with a blank Vue project? │ No Scaffolding project in /Users/yanai/project/vue-viteplus... │ └ Done. ✔ Merged vue-viteplus/.oxlintrc.json into vue-viteplus/vite.config.ts ✔ Merged vue-viteplus/.oxfmtrc.json into vue-viteplus/vite.config.ts Wrote agent instructions to CLAUDE.md Rewrote imports in 4 files ✔ Merged staged config into vue-viteplus/vite.config.ts ◇ Dependencies installed ◇ Code formatted ◇ Scaffolded vue-viteplus 出力を見ると Running: pnpm dlx create-vue とあり、内部で create-vue を呼んでいることが分かります。create-vue でプロジェクトを生成した後に、Vite+ が以下の変換をかけています。 .oxlintrc.json → vite.config.ts の lint ブロックにマージ .oxfmtrc.json → vite.config.ts の fmt ブロックにマージ vite / vitest の import パスを vite-plus に書き換え pre-commit フック( vp staged )の設定を統合 つまり vp create vue は create-vue のラッパーで、生成物を Vite+ 向けに変換しているだけのようです。 従来の create-vue pnpm create vue@latest vue-create-vue ┌ Vue.js - The Progressive JavaScript Framework │ ◇ Use TypeScript? │ Yes │ ◇ Select features to include in your project: │ Vitest (unit testing), Linter (error prevention), Prettier (code formatting) │ ◇ Select experimental features to include in your project: │ none │ ◇ Skip all example code and start with a blank Vue project? │ No Scaffolding project in /Users/yanai/project/vue-create-vue... │ └ Done. こちらは従来通りのシンプルな Scaffold です。create-vue 側でも「Replace Prettier with Oxfmt」の選択肢が出ますが、今回は Oxfmt との比較のため Prettier を選びました。 生成される設定ファイルの比較 以降のコードブロックは主要部分の抜粋です。 Vite+ プロジェクトの構成 package.json { " scripts ": { " dev ": " vp dev ", " build ": " run-p type-check \" build-only {@} \" -- ", " build-only ": " vp build ", " type-check ": " vue-tsc --build ", " test:unit ": " vp test ", " lint ": " run-s lint:* ", " lint:oxlint ": " vp lint . --fix ", " lint:eslint ": " eslint . --fix --cache ", " format ": " vp fmt src/ " } , " devDependencies ": { " eslint ": " ^10.1.0 ", " eslint-plugin-vue ": " ~10.8.0 ", " eslint-plugin-oxlint ": " ~1.57.0 ", " eslint-config-prettier ": " ^10.1.8 ", " vite ": " catalog: ", " vite-plus ": " catalog: ", " vitest ": " catalog: " } } vite.config.ts: import { defineConfig } from "vite-plus" import vue from "@vitejs/plugin-vue" export default defineConfig( { staged : { "*" : "vp check --fix" , } , fmt : { semi : false , singleQuote : true , } , lint : { plugins : [ "eslint" , "typescript" , "unicorn" , "oxc" , "vue" , "vitest" ] , env : { browser : true } , categories : { correctness : "error" } , options : { typeAware : true , typeCheck : true } , } , plugins : [ vue() ] , } ) defineConfig を 'vite-plus' からインポートしていて、Vite の設定に加え lint (Oxlint)、 fmt (Oxfmt)、 staged (pre-commit フック)の設定が1つのファイルにまとまっています。 vite と vitest は、 pnpm-workspace.yaml の catalog: により @voidzero-dev のものに解決されています。 従来の create-vue プロジェクトの構成 package.json { " scripts ": { " dev ": " vite ", " build ": " run-p type-check \" build-only {@} \" -- ", " build-only ": " vite build ", " type-check ": " vue-tsc --build ", " test:unit ": " vitest ", " lint ": " run-s lint:* ", " lint:oxlint ": " oxlint . --fix ", " lint:eslint ": " eslint . --fix --cache ", " format ": " prettier --write --experimental-cli src/ " } , " devDependencies ": { " eslint ": " ^10.1.0 ", " eslint-plugin-vue ": " ~10.8.0 ", " eslint-plugin-oxlint ": " ~1.57.0 ", " eslint-config-prettier ": " ^10.1.8 ", " oxlint ": " ~1.57.0 ", " prettier ": " 3.8.1 ", " vite ": " ^8.0.3 ", " vitest ": " ^4.1.2 " } } .oxlintrc.json: { " plugins ": [ " eslint ", " typescript ", " unicorn ", " oxc ", " vue ", " vitest " ] , " env ": { " browser ": true } , " categories ": { " correctness ": " error " } } .prettierrc.json: { " $schema ": " https://json.schemastore.org/prettierrc ", " semi ": false , " singleQuote ": true , " printWidth ": 100 } 共通: eslint.config.ts 前述の通り vp create vue は内部で create-vue を実行しているため、eslint.config.ts は両プロジェクトで同一です。 import { defineConfigWithVueTs, vueTsConfigs, } from "@vue/eslint-config-typescript" import pluginVue from "eslint-plugin-vue" import pluginVitest from "@vitest/eslint-plugin" import pluginOxlint from "eslint-plugin-oxlint" import skipFormatting from "eslint-config-prettier/flat" export default defineConfigWithVueTs( { name : "app/files-to-lint" , files : [ "**/*.{vue,ts,mts,tsx}" ] } , ...pluginVue.configs[ "flat/essential" ], vueTsConfigs.recommended, { ...pluginVitest.configs.recommended, files : [ "src/**/__tests__/*" ] } , ...pluginOxlint.buildFromOxlintConfigFile( ".oxlintrc.json" ), skipFormatting ) ただし、Vite+ プロジェクトではこの eslint.config.ts に落とし穴があります。 Vite+ の公式ガイド では .oxlintrc.json の使用は推奨されておらず、 vite.config.ts の lint ブロックへ設定を集約する方針です。実際、 vp create vue で .oxlintrc.json は vite.config.ts へマージされた後に削除されています。 しかし eslint.config.ts の buildFromOxlintConfigFile(".oxlintrc.json") はそのまま残っています。存在しないファイルを参照すると eslint-plugin-oxlint: could not find oxlint config file: .oxlintrc.json と警告が出て空配列を返すため、ルール重複の無効化が効きません。 つまり、Oxlint と ESLint で同じ違反が重複報告される状態になります。 回避策は2つあります。 1つ目は vite.config.ts から lint ブロックを直接インポートする方法です。 eslint.config.ts は TypeScript ですから、 vite.config.ts の default export から .lint を取り出して buildFromOxlintConfig に渡せます。 // eslint.config.ts import viteConfig from './vite.config' // 変更前: ファイルが存在しないため機能しない ...pluginOxlint.buildFromOxlintConfigFile( ".oxlintrc.json" ), // 変更後: vite.config.ts の lint ブロックをそのまま渡す ...pluginOxlint.buildFromOxlintConfig(viteConfig.lint), 2つ目は vite.config.ts の lint ブロックを削除し、 .oxlintrc.json に設定を一本化する方法です。 vite-plus の issue によると、現状の実装では .oxlintrc.json 等の専用設定ファイルが優先され、 vite.config.ts はフォールバックとして使われます。 .oxlintrc.json があればそちらが読み込まれます。 { " plugins ": [ " eslint ", " typescript ", " unicorn ", " oxc ", " vue ", " vitest " ] , " env ": { " browser ": true } , " categories ": { " correctness ": " error " } , " options ": { " typeAware ": true , " typeCheck ": true } } eslint.config.ts の修正が不要で済みますが、Vite+ の「 vite.config.ts に集約する」方針とは外れます。 共通: Lint 実行順 2026年4月時点で、create-vue は Oxlint をデフォルトで同梱しています。前述の package.json にある通り、 pnpm lint ( run-s lint:* )で lint:oxlint → lint:eslint の順に直列実行されます。 create-vue 側では eslint-plugin-oxlint が .oxlintrc.json を読み取り、Oxlint と重複する ESLint ルールを自動で無効化してくれます。 # Vite+ vp run lint # → vp lint . --fix ... Oxlint(vp経由) # → eslint . --fix --cache ... ESLint(直接呼び出し) # create-vue pnpm lint # → oxlint . --fix ... Oxlint(直接呼び出し) # → eslint . --fix --cache ... ESLint(直接呼び出し) vp lint は Oxlint だけを実行する組み込みコマンドで、ESLint は Vite+ に統合されていません。 そのため、テンプレートでは ESLint を eslint コマンドで直接呼ぶ構成になっています。 Vite+ のタスクランナーを活用したい場合は、 vite.config.ts の run.tasks に定義を移行すると良さそうです。 run.tasks で定義したタスクはデフォルトでキャッシュが有効なため、入力ファイルに変更がなければ再実行がスキップされます。 なお、 run.tasks のタスク名は package.json の scripts と重複できないため、移行する場合は package.json 側の lint スクリプトを削除します。 // package.json の lint 関連スクリプトを run.tasks に移行する例 run: { tasks: { lint: { command: 'vp lint . --fix && eslint . --fix --cache' , input: [{ auto : true } , '!.eslintcache' ] , } , } , } , eslint の --cache を使うと .eslintcache が書き出され、 vp run がそれを入力の変更と見なしてタスクキャッシュがヒットしません。 input で '!.eslintcache' を指定し、キャッシュファイルを変更検知の対象外にすることで併用できます。 キャッシュ機能については ESLint ではなくタスクランナー側のもので十分かもしれませんが、 --cache の有無による差異は今回未検証です。 Linter 比較: Oxlint vs ESLint 検証用コンポーネント 検証用に、意図的に Lint 違反を仕込んだ Vue コンポーネントを用意しました。 < script setup lang = "ts" > import { ref } from "vue" // unused expression (correctness) const x = 1 x // prefer-as-const (typescript) let y = "hello" as "hello" const items = ref ([ { id : 1 , name : "Apple" } , { id : 2 , name : "Banana" } , ]) </ script > < template > <!-- v-for without :key --> < li v- for = "item in items" > {{ item.name }} </ li > <!-- v-if and v-for on same element --> < div v- for = "item in items" v-if= "item.id > 0" :key= "item.id" > {{ item.name }} </ div > </ template > < style scoped> .unused-class { color : redd; } </ style > Oxlint の結果 # Vite+ vp lint src/components/LintTest.vue x eslint(no-unused-expressions): Expected expression to be used , - [src/components/LintTest.vue: 6 : 1 ] 5 | const x = 1 6 | x : ^ `---- x typescript-eslint(prefer-as-const): Expected a ` const ` assertion instead of a literal type annotation. , - [src/components/LintTest.vue: 9 : 20 ] 8 | // prefer-as-const (typescript) 9 | let y = 'hello' as 'hello' : ^^^^^^^ ` ---- Found 0 warnings and 2 errors. Finished in 369ms on 1 file with 132 rules using 10 threads. # create-vue pnpm exec oxlint -c .oxlintrc.json src/components/LintTest.vue ...(同一の 2 件) Found 0 warnings and 2 errors. Finished in 24ms on 1 file with 116 rules using 10 threads. Oxlint は <script> 内の違反を検出しましたが、 <template> / <style> の問題はスルーされています。Oxlint は .vue ファイルの <script> ブロックしか Lint しないためです。 oxc の互換性ページ にもある通り、Vue/Svelte/Astro 等のフレームワークでは script ブロックのみが対象です。 vue プラグインを有効にしても、script ブロック内の Vue 関連ルール(ref の使い方など)しか動きません。 SFC テンプレートの Lint 対応は oxc#15761 で追跡されていますが、まだ実装されていません。 また、Oxlint には ESLint の JS プラグインを読み込む JS plugins 機能もありますが、eslint-plugin-vue は動きません。 JS plugins の 制限事項 に「Custom file formats and parsers (e.g. Svelte, Vue, Angular)」は未対応と明記されています。 eslint-plugin-vue はカスタムパーサー( vue-eslint-parser )で .vue ファイル全体をパースしてテンプレートの AST をルールに渡す仕組みのため、この制限に該当しています。 ルール数の差(132 vs 116)は、 typeAware: true で型情報を使ったチェック(floating promise の検出等)が追加されるためです。 なお、Vite+ テンプレートの typeCheck: true は Vue プロジェクトでは実質的に使えないようです。 vp lint src/ のようにディレクトリを指定すると .ts ファイルもチェック対象になります。 しかし、 .ts から .vue をインポートしている箇所で tsgo がモジュール解決に失敗し TS2307: Cannot find module エラーが出ます。 上の検証でファイルを直接指定しているのはこの問題を回避するためです。 ESLint の結果 # Vite+(eslint-plugin-oxlint が機能していない) vp exec eslint src/components/LintTest.vue src/components/LintTest.vue 6 : 1 error Expected an assignment or function call and instead saw an expression @typescript-eslint/no-unused-expressions 9 : 5 error 'y' is never reassigned. Use 'const' instead prefer-const 9 : 5 error 'y' is assigned a value but never used @typescript-eslint/no-unused-vars 9 : 20 error Expected a `const` instead of a literal type assertion @typescript-eslint/prefer-as-const 19 : 3 error Elements in iteration expect to have 'v-bind:key' directives vue/require-v-for-key 22 : 30 error The 'items' variable inside 'v-for' directive should be replaced with a computed property that returns filtered array instead. You should not mix 'v-for' with 'v-if' vue/no-use-v-if-with-v-for ✖ 6 problems ( 6 errors, 0 warnings) # create-vue(eslint-plugin-oxlint が正常動作) pnpm exec eslint src/components/LintTest.vue src/components/LintTest.vue 9 : 5 error 'y' is never reassigned. Use 'const' instead prefer-const 9 : 5 error 'y' is assigned a value but never used @typescript-eslint/no-unused-vars 19 : 3 error Elements in iteration expect to have 'v-bind:key' directives vue/require-v-for-key 22 : 30 error The 'items' variable inside 'v-for' directive should be replaced with a computed property that returns filtered array instead. You should not mix 'v-for' with 'v-if' vue/no-use-v-if-with-v-for ✖ 4 problems ( 4 errors, 0 warnings) Vite+ 側は Oxlint で検出されている no-unused-expressions と prefer-as-const も ESLint から報告されて6件。前述の通りルール重複の無効化が効いていません。 create-vue 側は eslint-plugin-oxlint が正常動作し、Oxlint との重複ルールが ESLint 側で無効化されるため4件。 どちらも、 eslint-plugin-vue により <template> 内の Vue 固有の問題も検出されています。 CSS/Style Lint について 検証用コンポーネントの <style> に color: redd; というタイポを仕込みましたが、Oxlint でも ESLint でも引っかかりませんでした。 どちらのテンプレートも CSS の Lint は対象外のようです。 Vue 公式のツーリングガイドの Linting セクション でも案内されているのは eslint-plugin-vue による JavaScript/テンプレートの Lint だけで、CSS/Style の Lint には触れていません。 CSS の Lint が必要なら、これまで同様 Stylelint と Vue プラグインを別途入れることになりそうです。 型チェックの違い pnpm type-check はどちらも vue-tsc --build で同じです。 Vite+ 側は vite.config.ts に typeAware: true (型認識 Lint ルールの有効化)と typeCheck: true (tsgo 経由の型チェック同時実行)の設定があります。 ただし前述の通り tsgo は .vue を読めないため、 .vue の型チェックには引き続き vue-tsc が必要です。 テスト どちらも Vitest です。 Vite+ ではインポートパスが 'vitest' から 'vite-plus/test' に、コマンドが vitest から vp test に変わりますが、設定内容やテストの書き方は同じです。 Formatter 比較: Oxfmt vs Prettier Oxfmt は Prettier との出力互換を謳っており、JavaScript/TypeScript の conformance test を 100% パスしています。 Vue SFC のフォーマット対応 <template> と <style> もフォーマットできるのか気になったため、わざと崩した Vue ファイルで試しました。 <!-- フォーマット前 --> < template >< div class = "foo" >< p v-if= "true" > hello </ p ></ div ></ template > < style scoped>.foo{ color : red ; font-size : 16px ; display : flex ; justify-content : center }</ style > <!-- フォーマット後(Oxfmt / Prettier どちらも同一の結果) --> < template > < div class = "foo" >< p v-if= "true" > hello </ p ></ div > </ template > < style scoped> .foo { color : red ; font-size : 16px ; display : flex ; justify-content : center ; } </ style > Oxfmt でも Prettier でも <template> と <style> をフォーマットでき、出力結果は同一でした。乗り換えて問題なさそうです。 公式ドキュメント によると Prettier の約30倍の速度とのこと。小規模プロジェクトだと体感差はありませんが、大規模プロジェクトでは差が出そうです。 全体比較表 項目 Vite+ ( vp create vue ) create-vue ( pnpm create vue@latest ) Linter Oxlint ( vp lint ) + ESLint Oxlint + ESLint ESLint 設定 同一 (ただし eslint-plugin-oxlint の修正が必要) 同一 Vue template lint ESLint 経由で対応 ESLint 経由で対応 CSS lint なし なし typeCheck tsgo が .vue を読めず実質使えない なし テスト Vitest ( vp test ) Vitest Formatter Oxfmt ( vp fmt ) Prettier (Oxfmtも案内される) ビルド Vite ( vp build ) Vite ( vite build ) 設定の統合 vite.config.ts に集約 個別ファイル ( .oxlintrc.json , .prettierrc.json ) Pre-commit フック .vite-hooks/pre-commit → vp staged なし (要別途設定) まとめ vp create vue と pnpm create vue@latest で生成されるプロジェクトを比較した結果をまとめます。 Linter Oxlint は .vue の <script> ブロックしか Lint できない <template> や <style> は対象外 Vue template の Lint には引き続き ESLint(eslint-plugin-vue)が必要 これは Vite+ でも create-vue でも同じ create-vue は Oxlint をデフォルトで同梱している ESLint 連携の落とし穴 Vite+ テンプレートでは .oxlintrc.json がマージ後に削除されることへの対応が無く、ルール重複の無効化が壊れる vite.config.ts の lint ブロックを import するか、 .oxlintrc.json に一本化することで対処できる Lint 実行 どちらも Oxlint → ESLint の直列実行 型チェック typeCheck: true は Vue プロジェクトでは実質使えない tsgo が .vue をインポートした .ts ファイルで TS2307 エラーを出すため .vue の型チェックには引き続き vue-tsc が必要 Formatter Oxfmt は Prettier と同一の出力 <template> / <style> もフォーマットできる Prettier からの乗り換えで困ることはなさそう CSS Lint どちらのテンプレートも対象外 必要なら Stylelint を別途入れることになる Vite+ を選ぶメリットは Linter/Formatter 単体の差分よりも、 vite.config.ts への設定一元化と vp コマンドによる統合にありそうでした。 Vue 固有の Lint や型チェックについてはまだ ESLint + vue-tsc 頼りなため、Oxlint の Vue テンプレート対応や tsgo の .vue サポートが整えば、設定が楽になりそうです。 今回はテンプレートの設定比較だけでしたが、気になるのはやはり実際のプロジェクトでの速度差です。 次回は Vue ファイルが約2000個存在する駅メモ!のフロントエンドで、Lint/Format/ビルドがどれくらい速くなるか実測してみます。お楽しみに!

動画

書籍