Vue.js - TECH PLAY - TECH PLAY

TECH PLAY

Vue.js

むベント

該圓するコンテンツが芋぀かりたせんでした

マガゞン

技術ブログ

はじめに NTT西日本の䞭川です。 本蚘事では、ブラりザのメむンスレッドを占有しないよう Web Worker を䜿っお重い凊理をバックグラりンドぞ逃がす方法を、実隓コヌド぀きで解説したす。シングルスレッドのJavaScriptでも、UXを萜ずさずに蚈算凊理ず描画を䞡立するための実装パタヌンをたずめたした。 本蚘事は2026幎3月時点の情報に基づきたす。 仕様曞やMDNなどを読んでも、「Workerに逃がす」ず口で蚀うのず、 画面䞊で動きの差を䞀床芋る のずでは、腹萜ちの深さがかなり違いたす。私自身も最初の頃はむベントルヌプの説明ず䜓感がなかなか結び぀かず、実隓甚の短い for を回しお初めお「占有」の意味が自分のものになった経隓がありたす。同じように手を動かしながら確認したい方に向け、デモを倚めに茉せおいたすので、ぜひ詊しおみおください。 蚭蚈思想やデバッグ手法など、コヌドの品質を高める方法はさたざたです。しかし、どれほどきれいなコヌドを曞いおも、ナヌザヌ䜓隓UXを損なう「画面のフリヌズ」が発生しおしたうず、アプリケヌションの䟡倀は倧きく損なわれおしたいたす。 JavaScriptはメむンスレッド䞊でUIずスクリプトが順番に凊理されるため、重い凊理をそのたた走らせるず描画が止たりたす。今回は、その限界を緩和する暙準機胜である Web Worker に焊点を圓おおご玹介したす。 察象読者 本蚘事が想定する察象読者は以䞋の通りです。 JavaScriptのむベントルヌプや実行コンテキストなど基瀎知識をお持ちの方 倧量デヌタの加工や耇雑な蚈算で、画面が䞀瞬カク぀いたり止たったりする課題を抱えおいる方 「JavaScriptはシングルスレッドだから重い凊理は避けるべき」ずいう前提の先にある、䞊行に近い実行モデルを知りたい方 目次 はじめに 察象読者 目次 1.背景・目的 2.スレッドずは 3.なぜ「画面が止たる」のかむベントルヌプの制玄 実隓メむンスレッドをブロックしおみる 結果 4.Web Workerずは䜕か 実装時の䞻なルヌル 5.【実践】Web Workerを導入しおフリヌズを和らげる ステップ1worker.js ステップ2index.htmlメむンスレッド偎 結果 補足メむンずWorkerのやり取りのコスト 6.よくあるナヌスケヌスず簡易コヌド䜕が起きるかをセットで理解する ケヌス1回転する「動き続けるUI」ず重い凊理ルヌプ 結果 ケヌス2進捗の逐次通知重い凊理のプログレスバヌ 結果 7.䜿い分けWorkerを䜿うべきずき・避けた方がよいずき 怜蚌ツヌルで凊理負荷を“枬定”しお可芖化する Workerが向いおいる凊理の䟋 Workerが向かない、たたは慎重になる凊理の䟋 8.さらに高床な掻甚転送可胜オブゞェクトTransferable 9.たずめ 執筆者 参考資料・出兞 商暙 1.背景・目的 モダンなWebアプリケヌションでは、ブラりザ䞊で実行されるロゞックが肥倧化し続けおいたす。䞀方で、ナヌザヌは滑らかなアニメヌションず即座のレスポンスを期埅しおいるケヌスが倚いです。 本蚘事の目的は、 メむンスレッドをUI応答に䜿い続け぀぀、重い蚈算をWorker偎ぞ委ねる ずいう構成を、実際にブラりザで動かしながら䜓感し、実装の型を身に぀けおいただくこずです。 業務のコヌドでも、いったんメむンに茉せた凊理を抱え蟌んだたた進め、 蚈枬で長いタスクがはっきり芋えおから Workerぞ切り出す、ずいう順番になりがちです。埌から盎せばよいのですが、手戻りが発生するずどんどん期間的な䜙裕がなくなっおいくケヌスが倚いず思いたす。だからこそ蚭蚈の早い段階で「メむンはUIず短い応答に寄せる」ずいう前提を共有しおおくず楜になる堎面が、私がこれたで経隓しおきた案件でも少なくありたせんでした。本蚘事は、その前提づくりのための土台ずしお敎理したした。 2.スレッドずは OSやCPUの厳密な定矩に螏み蟌むず長くなるため、 この蚘事を読むための最小限のむメヌゞ だけを解説したす。 簡易な説明ずなりたすので、あくたでむメヌゞず捉えおください。 スレッドthread 凊理の流れを運ぶ 䜜業ラむン のようなものです。レストランでいえば、調理や提䟛を進めるための「通路」に近いむメヌゞです。 シングルスレッド 同時に䜿える通路が1本だけ で、そこに仕事が順番に䞊ぶ状態です。JavaScriptの メむンスレッド は、基本この「1本の通路」䞊で動きたす。 メむンスレッド ブラりザでは、この1本の通路のうち、 画面の衚瀺やナヌザヌ操䜜クリック・入力などぞの反応を扱う䞻圹のラむン を指すこずが倚いです。本文では「メむン」ず略したす。 マルチスレッド耇数スレッド 耇数の通路を䞊行しお䜿える むメヌゞです。Web Workerは、メむンずは別のスレッド䞊でスクリプトを実行できたす。ただし WorkerからはDOMDocument Object Modelを盎接操䜜できない など、制玄がありたす。 この蚘事では「 重い蚈算をメむンの通路にずっず眮くず、画面の曎新が埌回しになる。だからWorkerずいう別の通路に逃がす 」ずいうむメヌゞを持っお読み進めおいただくず、埌の内容を理解する助けになりたす。 ※甚語の厳密な定矩や、ブラりザ実装ごずの差異は、参考資料のMDNや仕様曞で確認しおください。 3.なぜ「画面が止たる」のかむベントルヌプの制玄 JavaScriptの実行環境メむンスレッドは、 UIの描画曎新ずスクリプトの実行を、ひず぀のキュヌに乗ったタスクずしお順番に凊理する モデルが基本ですむベントルヌプ。 メむンスレッドずむベントルヌプの参考むメヌゞタスクキュヌから実行・重い凊理時のブロック ※図内の「rAF」ずは requestAnimationFrame() の略です。 䞊段は「順番埅ちのモデル」、䞋段は「 重い1件の間、ほかの仕事が先に進みにくい 」こずをむメヌゞした図です厳密なブラりザ内郚モデルではなく、説明甚の略図です。 䟋えば、数秒かかるルヌプ凊理をメむンスレッドで実行するず、その間は「次の描画」に回る前に長いタスク占有が発生したす。結果ずしお、アニメヌションやクリック応答が止たったように芋えたす。これがいわゆるメむンスレッドのブロックです。 実隓メむンスレッドをブロックしおみる 以䞋を index.html ずしお保存し、ロヌカルで開いお詊しおください file:// ではなく 簡易HTTPサヌバヌ で配信する方が挙動が安定したす。 <!doctype html> < html lang = "ja" > < head > < meta charset = "utf-8" /> < meta name = "viewport" content = "width=device-width,initial-scale=1" /> < title > メむンスレッドをブロックする実隓 </ title > </ head > < body > < h2 > メむンスレッドをブロックする実隓 </ h2 > < p id = "status" > 埅機䞭です。ボタンで重い凊理を開始したす。 </ p > < pre id = "log" style = "background:#f5f5f5;padding:8px;border-radius:4px;font-size:13px;" ></ pre > < div id = "box" style = "width:50px; height:50px; background:red; position:relative;" ></ div > < div > < small > このボタンでは、わざず「重い凊理」を走らせお、画面メむンスレッドが止たる感じを䜓感したす。 < br /> 走る蚈算は2぀です。 < br /> < br /> 1) < b > 环積和蚈算結果の衚瀺 </ b >< br /> 0〜n-1 の合蚈环積和を衚瀺したす。 < br /> < br /> 2) < b > 負荷をかけるルヌプフリヌズ䜓感甚 </ b >< br /> こちらはn回ルヌプしおCPUを䜿い、メむンスレッドを占有させるための凊理です。 < br /> < code > dummy </ code > はルヌプ䞭に曎新する䜜業甚の倉数で、倀そのものに意味はありたせん。 < br /> なお内郚的には32bit敎数ずしお曎新しおいるため、衚瀺は笊号なし0〜4,294,967,295に倉換しおいたす。 < br /> < br /> </ small > </ div > < button id = "runBtn" type = "button" > 重い凊理を実行フリヌズしたす </ button > < script > const statusEl = document . getElementById ( "status" ) ; const logEl = document . getElementById ( "log" ) ; const runBtn = document . getElementById ( "runBtn" ) ; let pos = 0 ; function animate () { pos = ( pos + 2 ) % 300 ; document . getElementById ( "box" ) . style . left = pos + "px" ; requestAnimationFrame ( animate ) ; } animate () ; function fmtTime ( d ) { return ( d . toLocaleTimeString ( "ja-JP" , { hour12 : false }) + "." + String ( d . getMilliseconds ()) . padStart ( 3 , "0" ) ) ; } function heavyTask () { logEl . textContent = "" ; runBtn . disabled = true ; statusEl . textContent = "次の描画フレヌムのあず、重い凊理を開始したす " ; requestAnimationFrame (() => { requestAnimationFrame (() => { const wallStart = new Date () ; statusEl . textContent = "実行䞭ですメむンスレッド占有䞭。赀いボックスの動きず、この文が曎新されない時間に泚目しおください。" ; const t0 = performance . now () ; const n = 1_000_000_000 ; let dummy = 0 ; for ( let i = 0 ; i < n ; i ++ ) { dummy = ( dummy + ( i & 7 )) | 0 ; } const dummyU32 = dummy >>> 0 ; const result = ( BigInt ( n - 1 ) * BigInt ( n )) / 2 n ; const elapsed = Math . round ( performance . now () - t0 ) ; const wallEnd = new Date () ; statusEl . textContent = "重い凊理が終わりたした。メむンスレッドが解攟され、ボックスが再び動き出したす。" ; logEl . textContent = "【画面ログ】\n" + "開始時刻: " + fmtTime ( wallStart ) + "\n" + "終了時刻: " + fmtTime ( wallEnd ) + "\n" + "所芁時間: " + elapsed + " ms\n" + "环積和 : " + result . toString () + "\n" + "ダミヌ倀笊号なし32bit: " + dummyU32 ; runBtn . disabled = false ; }) ; }) ; } runBtn . addEventListener ( "click" , heavyTask ) ; </ script > </ body > </ html > 結果 メむンスレッドをブロックしおみるの参考むメヌゞ ボタンを抌すず、赀いボックスの動きが止たりたす。これがメむンスレッドが長時間占有されおいる状態です。あわせお、 実行䞭は画面䞊郚の状態メッセヌゞが曎新されない フリヌズしおいるこず、 終了埌にログ欄ぞ開始・終了時刻ず所芁時間がたずめお衚瀺される こずも確認できたす。ルヌプ回数や端末性胜など、かかる時間は環境ごずに異なりたす。 所芁時間に衚瀺された時間分だけ、メむンスレッドが占有されおいたずいうこずになりたす。 4.Web Workerずは䜕か Web Workerは、Webアプリケヌションの メむン実行スレッドずは別のバックグラりンド䞊でスクリプトを動かす仕組み です実装はブラりザ䟝存ですが、「メむンずは別の䞊行の実行コンテキスト」ずいう理解で問題ありたせん。重い蚈算をWorkerに逃がすず、メむン偎はブロックされにくくなり、ナヌザヌの操䜜や画面の再描画を順に進めやすくなりたす。しかし、メむンずWorkerの間のメッセヌゞ凊理そのものもメむンスレッドで扱うため、通信のやりすぎは別のボトルネックになる可胜性があり、泚意が必芁です。 実装時の䞻なルヌル スクリプトはURLずしお枡しお起動する 別ファむルの .js を指すのが䞀般的ですが、Blobから生成したURLのように、ファむルを分けずに枡す方法もありたす。読み蟌むWorkerスクリプトは 呌び出し元ず同䞀オリゞン であるこずが基本で、クロスオリゞンで読み蟌む堎合は远加の条件CORSなどが必芁です。 DOM操䜜は䞍可 Workerの実行環境には window や document はなく、ペヌゞのDOMを盎接觊れたせん仕様䞊、Dedicated Workerのグロヌバルは WorkerGlobalScope 系です。 メッセヌゞでやり取りする  postMessage ず message むベントたたは addEventListener('message',
) でデヌタを受け枡ししたす。 䞍芁になったら終了する 郜床Workerを新芏䜜成する堎合は、凊理完了埌に terminate() するか、 1぀のWorkerを䜿い回す 蚭蚈にしたす攟眮するずリ゜ヌスを食い続けたす。 5.【実践】Web Workerを導入しおフリヌズを和らげる 先ほどの実隓を、Web Workerで曞き盎したす。同じフォルダに index.html ず worker.js を眮いおください。 ここで衚したいのは、「非垞に重いCPU蚈算= メむンスレッドを占有しがちなルヌプ」をWorkerぞ逃がすこずで、メむン偎のアニメヌション赀いボックスが止たらない、ずいう効果です。 この䟋で画面に出しおいる数倀环積和は、 「凊理が最埌たで完了した」こずを確認するための目印 です。 経過時間ではないこずに泚意しおください。 Worker甚のスクリプトは、 HTMLに <script src="worker.js"> ず曞いお読み蟌みたせん 。メむン偎の new Worker('worker.js') のずきに、ブラりザが別スレッド甚ずしお worker.js を取埗・実行したすメむン甚の <script> で読むず、Worker向けコヌドがメむンスレッドで動いおしたい意図ずずれたす。 ステップ1 worker.js // 重いルヌプを最埌たで回し切った「完了の蚌拠」ずしお倀をメむンぞ返す。 self .onmessage = function () { const n = 1_000_000_000 ; // ダミヌ蚈算CPUを䜿う32bitで回し続けお“走った痕跡”を残す let dummy = 0 ; for ( let i = 0 ; i < n ; i ++ ) { dummy = ( dummy + ( i & 7 )) | 0 ; } const dummyU32 = dummy >>> 0 ; // 結果の目印0〜n-1 の合蚈を BigInt で厳密に const sum = ( BigInt ( n - 1 ) * BigInt ( n )) / 2 n ; self . postMessage ({ n , sum : sum . toString () , dummy : dummyU32 }) ; } ; メむンに届く倀 䞊蚘の postMessage(result) の匕数が、メむン偎の message ハンドラでは e.data ずしお受け取れたす。 ステップ2 index.html メむンスレッド偎 先ほどの実隓コヌドのうち、 ボタンを抌したずきの凊理重いルヌプ郚分だけ をWorker呌び出しに差し替えた䟋です。ボタン連打でWorkerが増殖しないよう、 前回のWorkerは終了しおから 新しく起動しおいたす。ログ欄に出す e.data は、 ステップ1の result ず同じ倀 です。 <!doctype html> < html lang = "ja" > < head > < meta charset = "utf-8" /> < meta name = "viewport" content = "width=device-width,initial-scale=1" /> < title > Web Workerで蚈算を逃がす </ title > </ head > < body > < h2 > Web Workerで蚈算を逃がす </ h2 > < p id = "status" > ボタンでWorkerに蚈算を䟝頌できたす。 </ p > < pre id = "log" style = "background:#f5f5f5;padding:8px;border-radius:4px;font-size:13px;min-height:3em;" > 結果はここに衚瀺されたす </ pre > < div > < small > このボタンでは、重い蚈算をWorkerに任せお、画面メむンスレッドの描画が止たりにくくなるこずを䜓感したす。 < br /> Workerが返す倀は2぀です。 < br /> < br /> 1) < b > 环積和蚈算結果の衚瀺 </ b >< br /> 0〜n-1 の合蚈环積和を衚瀺したす。 < br /> < br /> 2) < b > ダミヌ倀笊号なし32bit </ b >< br /> Worker偎で負荷をかけるルヌプの䞭で曎新しおいる䜜業甚の倀です倀そのものに意味はありたせん。 < br /> </ small > </ div > < div id = "box" style = "width:50px; height:50px; background:red; position:relative;" ></ div > < br /> < button type = "button" id = "runBtn" > 重い凊理をWorkerぞ描画は動き続ける想定 </ button > < script > const statusEl = document . getElementById ( "status" ) ; const logEl = document . getElementById ( "log" ) ; const runBtn = document . getElementById ( "runBtn" ) ; let pos = 0 ; function animate () { pos = ( pos + 2 ) % 300 ; document . getElementById ( "box" ) . style . left = pos + "px" ; requestAnimationFrame ( animate ) ; } animate () ; let myWorker = null ; function heavyTask () { logEl . textContent = "" ; runBtn . disabled = true ; statusEl . textContent = "Workerぞ凊理を送りたした。蚈算䞭は画面の指瀺に埓い、赀いボックスの動きも芋おください。" ; if ( myWorker ) { myWorker . terminate () ; } try { myWorker = new Worker ( "worker.js" ) ; } catch ( err ) { statusEl . textContent = "Workerを起動できたせんでした。" ; logEl . textContent = "起動゚ラヌ: " + ( err && err . message ? err . message : String ( err )) + "\n" + "worker.js のパス、http:// で開いおいるか、ブラりザ蚭定などを確認しおください" ; myWorker = null ; runBtn . disabled = false ; return; } myWorker . onmessage = function ( e ) { // worker.js は { sum, dummy } の圢で返す想定ですが、コピペのズレ等で圢が違う堎合もあり埗るので // 衚瀺は防埡的に扱いたす。 const data = e . data ; const sum = data && typeof data === "object" ? ( data . sum ?? data . result ) : data ; const dummy = data && typeof data === "object" ? data . dummy : undefined ; const endTime = new Date () . toLocaleTimeString ( "ja-JP" , { hour12 : false , }) ; statusEl . textContent = "Workerから結果を受け取りたした" + endTime + "。" ; logEl . textContent = "环積和0〜999,999,999 の合蚈: " + sum + "\n" + "ダミヌ倀笊号なし32bit: " + ( dummy ?? "未取埗" ) ; myWorker . terminate () ; myWorker = null ; runBtn . disabled = false ; } ; myWorker . onerror = function ( e ) { statusEl . textContent = "Workerの読み蟌み、たたは実行で゚ラヌが発生したした。" ; logEl . textContent = "゚ラヌ: " + ( e && e . message ? e . message : "詳现䞍明" ) + "\n" + "worker.js が存圚しない、パスが違う、http:// で開いおいない、同䞀オリゞンでないなどの可胜性がありたす" ; myWorker . terminate () ; myWorker = null ; runBtn . disabled = false ; } ; myWorker . onmessageerror = function () { statusEl . textContent = "Workerずのメッセヌゞの受け枡しに倱敗したした。" ; logEl . textContent = "messageerror: Workerから受け取ったデヌタを埩元できたせんでした。" ; myWorker . terminate () ; myWorker = null ; runBtn . disabled = false ; } ; myWorker . postMessage ( null ) ; } runBtn . addEventListener ( "click" , heavyTask ) ; </ script > </ body > </ html > 結果 Web Workerを導入しおフリヌズを和らげるの参考むメヌゞ 先ほどずは異なり、ボタン抌䞋䞭も赀いボックスのアニメヌションが動き続け、数秒埌に ログ欄ぞ环積和0〜999,999,999の合蚈 が衚瀺されたす。状態メッセヌゞで、送信䞭・受信枈みの流れも远えたす。挙動はブラりザ実装・CPU負荷・他タブの状況により倉わるため、 可胜であれば手元で確認 しおください。 補足メむンずWorkerのやり取りのコスト メむンからWorkerぞデヌタを枡す凊理では、原則ずしお デヌタの耇補 が発生したす。巚倧なオブゞェクトを頻繁に埀埩させるず、通信そのものがボトルネックになるこずがあるため、泚意しおください。コピヌを避ける高床な手段は本蚘事の埌半で玹介したす。 6.よくあるナヌスケヌスず簡易コヌド䜕が起きるかをセットで理解する ここからは 1ファむルで詊せる 䟋です。 new Worker(URL.createObjectURL(blob)) により、別ファむルを眮かずにWorkerスクリプトを枡しおいたす本番ではファむル分割やバンドラ連携が䞀般的です。 各コヌドの盎埌に、 ブラりザ䞊でどう芋えるか・䜕が埗られるか をたずめおいたす。 ファむルを増やしたくないずきに Blob からWorkerを起動するのは、個人的にも手習い向きで重宝しおいたす。本番ずは蚭蚈が異なる点は埌述の通りですが、「たず挙動を掎む」には向いおいたす。 ケヌス1回転する「動き続けるUI」ず重い凊理ルヌプ 重いCPUルヌプは本来メむンを占有しやすいこずはこれたでの内容でお䌝えしおきたしたが、ここでは 回転する赀い四角 を眮き、 「止たらないメむンの描画凊理が生きおいる」 こずを䞀目で確認できるように画面䞊の「経過時間メむン」は、メむンが動いおいる間だけ増え続けるようにしおいたす。 Worker凊理完了時に出る 巚倧な数倀环蚈 は、ルヌプを最埌たで回し切った 完了の目印 です。 <!doctype html> < html lang = "ja" > < head > < meta charset = "utf-8" /> < meta name = "viewport" content = "width=device-width,initial-scale=1" /> < title > ケヌス1回転UIず重い凊理Worker </ title > </ head > < body > < h3 > 回転が止たらないか、泚目しおみおください </ h3 > < p > 赀い四角は < code > requestAnimationFrame </ code > で回転しおいたす。 </ p > < div > < small > この䟋は、重い蚈算をWorkerに任せおも「メむン偎の描画回転」が止たりにくいこずを䜓感するためのものです。 < br /> < br /> - 確認するポむント: < b > 赀い四角の回転 </ b > ず < b > 経過時間 </ b > が止たらないか < br /> - Workerが返す倀: < b > 环積和蚈算結果 </ b > ず < b > ダミヌ倀笊号なし32bit </ b >< br /> ※ダミヌ倀は負荷をかけるルヌプ䞭に曎新しおいる䜜業甚の倀で、倀そのものに意味はありたせん。 < br /> </ small > </ div > < div id = "spin" style = "width:48px;height:48px;background:#d33;margin:12px 0;transform-origin:center center;" ></ div > < p > 経過時間メむン: < span id = "tick" > 0 </ span > msフレヌムが進むほど増えたす </ p > < p id = "st" > 埅機䞭 </ p > < button type = "button" id = "run" > Workerで重いルヌプ玄2億ステップ </ button > < script > const spinEl = document . getElementById ( "spin" ) ; const tickEl = document . getElementById ( "tick" ) ; const st = document . getElementById ( "st" ) ; const runBtn = document . getElementById ( "run" ) ; let deg = 0 ; ( function spinLoop () { deg = ( deg + 4 ) % 360 ; spinEl . style . transform = "rotate(" + deg + "deg)" ; requestAnimationFrame ( spinLoop ) ; })() ; let t0 = performance . now () ; ( function tickLoop () { tickEl . textContent = String ( Math . round ( performance . now () - t0 )) ; requestAnimationFrame ( tickLoop ) ; })() ; runBtn . onclick = () => { runBtn . disabled = true ; st . textContent = "Workerで蚈算䞭 この間も回転ず経過衚瀺が止たらないはず" ; const src = [ "self.onmessage = () => {" , " const n = 200000000;" , " let dummy = 0;" , " for (let i = 0; i < n; i++) dummy = (dummy + (i & 7)) | 0;" , " const dummyU32 = dummy >>> 0;" , " const sum = (BigInt(n - 1) * BigInt(n)) / 2n;" , " self.postMessage({ steps: n, sum: sum.toString(), dummy: dummyU32 });" , "};" , ] . join ( "\n" ) ; const w = new Worker ( URL . createObjectURL ( new Blob ([ src ] , { type : "application/javascript" }) , ) , ) ; w . onmessage = ( e ) => { st . textContent = "完了Worker: ステップ数 " + e . data . steps + " の环積和 = " + e . data . sum + "ダミヌ倀・笊号なし32bit: " + e . data . dummy + "" ; runBtn . disabled = false ; w . terminate () ; } ; w . onerror = ( e ) => { st . textContent = "Worker゚ラヌ: " + e . message ; runBtn . disabled = false ; w . terminate () ; } ; w . postMessage ( null ) ; } ; </ script > </ body > </ html > 結果 ケヌス1回転する「動き続けるUI」ず重い凊理ルヌプの参考むメヌゞ このケヌスでは、ボタン抌䞋埌も 赀い四角の回転が途切れない ので、「重い凊理」をWorkerぞ逃がしたずきの䜓感が掎みやすいです。 「経過時間メむン」 が止たらず増え続けるのは、メむンがむベントルヌプず描画を進め続けおいる蚌拠ずなりたす。 最埌に衚瀺される 巚倧な环蚈 は、玄2億ステップ分のルヌプを終えた結果の 完了の目印 です。端末性胜により埅ち時間は倉わりたす。長すぎる堎合はWorker内の n を小さく調敎しおください。 ケヌス2進捗の逐次通知重い凊理のプログレスバヌ Worker内でルヌプを区切り、 䜕進んだか を䜕床かメむンぞ送り、画面䞊のプログレスバヌだけを曎新しおみたす。 <!doctype html> < html lang = "ja" > < head > < meta charset = "utf-8" /> < meta name = "viewport" content = "width=device-width,initial-scale=1" /> < title > ケヌス2進捗の逐次通知Worker </ title > </ head > < body > < div > < small > この䟋は、Workerから「いた䜕%たで進んだか」を䜕回か受け取り、プログレスバヌだけを曎新するサンプルです。 < br /> 芋どころは、重い蚈算䞭でも < b > バヌが段階的に䌞びる </ b > 点です。 < br /> ※最埌に返っおくる < code > work </ code > は、Worker偎の重いルヌプで䜜った「最終的な蚈算倀」です。 < br /> 凊理時間ではなく、あくたで「最埌たで蚈算が走り切った」こずを確認するために衚瀺しおいたす䞭身を解釈する必芁はありたせん。 < br /> </ small > </ div > < div id = "bar" style = "height:12px;width:0;background:#06c;" ></ div > < p id = "pct" > 0% </ p > < button type = "button" id = "run" > Workerで進捗付き凊理 </ button > < script > const runBtn = document . getElementById ( "run" ) ; runBtn . onclick = () => { if ( runBtn . disabled ) return; runBtn . disabled = true ; document . getElementById ( "bar" ) . style . width = "0%" ; document . getElementById ( "pct" ) . textContent = "0%" ; const src = ` self.onmessage = () => { const steps = 5; let work = 0; for (let s = 1; s <= steps; s++) { let x = 0; for (let i = 0; i < 80000000; i++) x += i % 7; work = x; self.postMessage({ progress: Math.round((s / steps) * 100) }); } self.postMessage({ done: true, work }); }; ` ; const w = new Worker ( URL . createObjectURL ( new Blob ([ src ] , { type : "application/javascript" }) , ) , ) ; w . onmessage = ( e ) => { if ( "progress" in e . data ) { const p = e . data . progress ; document . getElementById ( "bar" ) . style . width = p + "%" ; document . getElementById ( "pct" ) . textContent = p + "%" ; } if ( e . data . done ) { document . getElementById ( "pct" ) . textContent += " 完了 / work=" + e . data . work + "" ; runBtn . disabled = false ; w . terminate () ; } } ; w . onerror = function () { document . getElementById ( "pct" ) . textContent += " Worker゚ラヌ" ; runBtn . disabled = false ; w . terminate () ; } ; w . postMessage ( null ) ; } ; </ script > </ body > </ html > 結果 ケヌス2進捗の逐次通知重い凊理のプログレスバヌの参考むメヌゞ 青いバヌの幅が 20% → 40% → 
 → 100% のように段階的に䌞びるWorkerから進捗メッセヌゞが届くたびに曎新。 完了埌に「完了」が付く。内偎のルヌプ回数は端末に合わせお調敎しおください軜すぎるず䞀瞬で終わり、重すぎるず埅ち時間が長くなりたす。 実行䞭はボタンが無効化され、連打でWorkerが耇数起動しにくいようにしおいたす。 補足 進捗通知を现かくしすぎるず、メむンずWorkerの間のやり取りの回数が増え、かえっおオヌバヌヘッドになるこずがありたす。実務では䞀定間隔・䞀定チャンクごずに送るのが無難です。 7.䜿い分けWorkerを䜿うべきずき・避けた方がよいずき 怜蚌ツヌルで凊理負荷を“枬定”しお可芖化する ブラりザの怜蚌ツヌルを䜿うず凊理負荷を可芖化するこずができ、䞀局むメヌゞが持ちやすいです。 DevToolsを開く → Performance DevToolsを開く → Performance 蚘録開始Record→ サンプルのボタンを抌しお重い凊理を走らせる → 停止 蚘録開始Record→ サンプルのボタンを抌しお重い凊理を走らせる → 停止 タむムラむン䞊で、メむンスレッドの Long Task長いタスク や、描画Framesの詰たり方を確認する タむムラむン䞊で、Worker凊理が確認できる 「メむンで実行した堎合」ず「Workerに逃がした堎合」で、メむンスレッド䞊の長い塊の出方が倉わるのがポむントです。 ここたででWorkerの䟿利な動きに぀いお玹介しおきたしたが、 「䜕でもWorkerに投げればよい」わけではありたせん。 どんな機胜にも向き䞍向きがあり、Workerも䟋倖ではありたせん。 それぞれ䞀郚を玹介したす。 Workerが向いおいる凊理の䟋 数十䞇件以䞊の配列のフィルタリング・集蚈・倉換 画像やバむナリの加工、暗号凊理など、DOMに觊れない重い蚈算 むンタラクティブなUIの応答性を優先し、メむンスレッドの占有時間を短く保ちたい堎面 Workerが向かない、たたは慎重になる凊理の䟋 数ミリ秒で終わる軜い蚈算起動ずメッセヌゞのオヌバヌヘッドの方が倧きくなるこずがある DOMを頻繁に曎新する凊理Workerからは盎接操䜜できないため、結果をメむンぞ戻しお反映が必芁 巚倧なデヌタを高頻床で埀埩させる凊理コピヌたたは転送戊略の蚭蚈が必須 8.さらに高床な掻甚転送可胜オブゞェクトTransferable 巚倧なバむナリデヌタ型付き配列の裏偎のバッファなどをWorkerぞ送る堎合、デフォルトの耇補ではメモリず時間を消費したす。メッセヌゞを送るAPIのオプションで転送リストを枡すず、バッファの所有暩をWorker偎ぞ移し、コピヌを避けられる堎合がありたす転送埌、メむンスレッド偎ではそのバッファは利甚できなくなりたす。 高床な機胜ずなるため、ここではほんの少しだけ、䞀郚を玹介したす。 // メむン偎䟋100MBのバッファを転送 const buffer = new Uint8Array ( 1024 * 1024 * 100 ) . buffer ; myWorker . postMessage ({ type : 'binary' , payload : buffer } , [ buffer ]) ; // 転送埌、メむンスレッド偎では buffer はデタッチされ、觊れない想定になる Worker偎では、メむンが送ったデヌタの䞭身ずしお受け取りたす。甚途に応じお耇数スレッドで同じメモリを共有する仕組みを怜蚎する道もありたすが、 サむト党䜓のセキュリティ蚭定クロスオリゞン隔離などが必芁になっおくる ため、たずは同䞀オリゞンでさたざたな䜿い方を暡玢しおみるのが良いず思いたす。 9.たずめ Web Workerを䜿うず、JavaScriptのシングルスレッドずいう制玄のもずでも、メむンスレッドの占有時間を枛らし、UXず重い蚈算の䞡立を狙いやすくなりたす。 ボトルネックの特定 ブラりザの開発者ツヌルで、長いタスクがどこで発生しおいるかを把握する性胜分析の画面や、蚈枬甚のAPIを䜿う方法がありたす。 玔粋な蚈算の分離 画面の芁玠を盎接いじらない凊理をWorkerぞ切り出す。 メッセヌゞ蚭蚈 やり取りするデヌタのサむズず頻床を抑え、必芁なら埌述の「転送」による最適化で転送コストを䞋げる。 「ブラりザ向けJavaScriptは重い凊理に向かない」ず蚀われがちですが、Web Workerのような暙準機胜を適切に組み合わせれば、フロント゚ンドでも実甚的なアプリケヌションを構築できたす。 ルヌプ回数は端末の性胜などで䜓感が倉わりたす。動かしおみお「差が分かりにくい」ず感じたら、本蚘事の3節目のブロック実隓だけルヌプを半分にしおみたり、5節目のサンプルにおWorker偎はそのたた、ずいった 片偎だけ線集しおみるなどの比范 を詊しおみおください。私も蚘事を敎えるずきは、䜕床かこの比范に立ち返っお文蚀を盎したした。開発者ツヌルの性胜パネルを開いたたた読み返すず、長いタスクの芋え方もむメヌゞしやすくなるはずです。是非詊しおみおください。 執筆者 䞭川 拓哉NTT西日本 デゞタル革新本郚 デゞタル改革掚進郚所属 NTT西日本のWebアプリケヌションの開発・運営に埓事。 奜きな技術スタックTypeScript, Vue.js, GraphQL, Laravel 参考資料・出兞 本蚘事を執筆するにあたり、以䞋のサむト・資料を参考にしたした。 MDN Web Docs — Web Workers API MDN Web Docs — Worker MDN Web Docs — postMessage HTML Living Standard — postMessage with transfer WHATWG HTML — Safe passing of structured data 商暙 「JavaScript」は、Oracle Corporation およびその子䌚瀟の米囜およびその他の囜における商暙たたは登録商暙です。 「Google Chrome」は、Google LLC の商暙です。 蚘茉のその他の䌚瀟名・補品名は、それぞれ各瀟の商暙もしくは登録商暙です。
こんにちは、駅メモ開発チヌム゚ンゞニアの 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぀ありたす。 ぀目は 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), ぀目は 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/ビルドがどれくらい速くなるか実枬しおみたす。お楜しみに
れロからの開発で欠かせない「倖郚ラむブラリ」ず「ファむル構造」の理解に焊点を圓お、Vue.jsプロゞェクト䜜成の手順に沿っお解説したす。Vue.jsずNode.jsの圹割の違い、npm、Vite、Vue CLIずいった䞻芁なツヌルに぀いお説明し、Viteで䜜成されたプロゞェクトの基本的なファむル構成ずその圹割を具䜓的に瀺したす。開発の土台ずなる知識を固めるこずの重芁性を匷調したす。

動画

該圓するコンテンツが芋぀かりたせんでした

曞籍

おすすめマガゞン

蚘事の写真

SDV時代の「むネヌブラヌ」を目指す。パナ゜ニック オヌトモヌティブシステムズが远求する「゜フトりェア・ファヌスト」の本...

蚘事の写真

SDV時代の「むネヌブラヌ」を目指す。パナ゜ニック オヌトモヌティブシステムズが远求する「゜フトりェア・ファヌスト」の本...

蚘事の写真

死亡亀通事故れロぞ。アむサむトを支えるステレオ画像認識ず半導䜓内補の裏偎

新着動画

蚘事の写真

【解説】SNSで話題の「グラプンゞニアリング」の正䜓は / ルヌプ゚ンゞニアリングずの関係も解説

蚘事の写真

クラりドAIが䜿えない珟堎は、どうAIを"持぀"のか補造業の事䟋から孊ぶロヌカルAIFOCUSTUDIO

蚘事の写真

【ルヌプ゚ンゞニアリングずは】AIに自動で仕事を任せる前に決めるべき4぀のこず