Viteのビルドで単一htmlを出力する「vite-plugin-singlefile」の紹介!あと、fileプロトコルとCORSエラーの話
初投稿です。初心者です。Viteのプラグインに良さげなやつがあった+日本語で検索しても単品での紹介記事が無かったので書いてみました。
書いてある内容や用語の使い方に誤りがあれば、ガンガン指摘して頂けると幸いです。CORS関係の部分があまり自信ないです。
- 対象読者:Viteを使ってJavaScriptのWebアプリを作っている人。
- 記事概要:Viteのビルドで単一htmlの出力を可能にする「vite-plugin-singlefile」の紹介。Vite標準のビルドで出力したhtmlをブラウザから直接開く(fileプロトコルでアクセス)するとエラーが出る理由(CORS関係)。
「vite-plugin-singlefile」とは
「vite-plugin-singlefile」とは、Viteのビルドで「全部のJavaScriptとCSSがインライン化されている、単一のhtmlファイル」の出力を可能にするプラグインです。
出力された単一のhtmlファイルは、ローカル環境のブラウザで直接開く(fileプロトコルでアクセスする)だけで、Webアプリとして動作させることが可能です! ランタイムをインストールしてターミナルでコマンドを打ったり、httpサーバーを準備してhttpプロトコルでアクセスしたりするといった、実行環境に関する事前準備が必要ないというメリットがあります!
こんな時に役立ちます!
・身内向けの簡単なWebアプリを、単一htmlファイルとしてお気軽に配布したい時
・httpサーバーを準備しなくても、ローカルのブラウザで動くWebアプリを作りたい時
・関連記事にあるGASのように、インライン化された単一htmlがデプロイに必要な時
※画像:ViteのデフォルトのVanilla+JavaScriptのプロジェクトでビルドした例
vite-plugin-singlefileの使用方法
0:必要な条件と注意事項
READMEの通りです。「vite-plugin-singlefile」のv2.0.1での情報です。
https://www.npmjs.com/package/vite-plugin-singlefile/v/2.0.1
条件
https://www.npmjs.com/package/vite-plugin-singlefile#what-doesnt-work
- エントリポイントとなるhtmlファイルが一つであること
複数のhtmlファイルを使用するWebアプリは対象外- Web History API、Cookie、WebXR Immersive Modeを使用していないこと
注意点
https://www.npmjs.com/package/vite-plugin-singlefile#caveats
public
内に配置した静的リソース(favicon
など)は、インライン化されない。(Vite本体の仕様)- SVG画像のインライン化はVite本体でサポートされていないため、
vite-svg-loader
を利用する必要がある。
1:プラグインのインストール
READMEの通りです。
https://www.npmjs.com/package/vite-plugin-singlefile#installation
- npmやyarnを使ってViteのプロジェクトにインストールします
- ビルド以外には使わないので
devDependencies
にインストールすればOK
npm install vite-plugin-singlefile --save-dev
yarn add vite-plugin-singlefile --dev
2:ビルドの設定
vite.config.js
かvite.config.ts
に、プラグイン使用の記述を追加します。
READMEにVue+TypeScriptの場合のサンプルがあります。
https://www.npmjs.com/package/vite-plugin-singlefile#how-do-i-use-it
Vanilla+JavaScriptの場合だと、このようになります(動作確認済み)。
import { defineConfig } from "vite";
//インストールした「vite-plugin-singlefile」をインポート
import { viteSingleFile } from "vite-plugin-singlefile";
export default defineConfig({
//pluginsに記述することでJSとCSSがインライン化された単一htmlを出力するように
plugins: [viteSingleFile()],
});
3:ビルドを実行
npm run build
やvite build
でビルドを実行します。初期設定のままの場合、./dist
のディレクトリに、全部のJavaScriptとCSSがインライン化された単一のindex.html
が出力されます。SVG画像のようにインライン化できないファイルがあると、それも出力されます。
fileプロトコル(file:///)とCORS
標準ビルドで出力したhtmlをfileプロトコルで開くとエラーが発生する理由
Viteの標準のビルドで出力されるhtmlファイルは、クロスオリジン要求を行いassets
のディレクトリに配置したJavaScript(ESM)やCSSを読み込むようなものです。ES Modules機能を使ったJavaScriptファイル(type="module"
)は、必ずCORS利用で読み込まれます。
通常のように、httpサーバーを立ち上げてhttpプロトコルでアクセスするなら、特に問題なくアプリとして動作させることができます。
例:Viteのサンプルで標準のビルドを行った時に出力されたhtmlの一部。
<script type="module" crossorigin src="/assets/index-Bd-pKGJy.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-Cz4zGhbH.css">
しかし、Viteのドキュメントのトラブルシューティングに記載されている通りで、ローカル環境のブラウザで直接開き、fileプロトコル(file:///)でアクセスすると、JavaScriptやCSSの読み込みで、CORSに関するエラーが発生してしまい、正常にアプリとして動作させることができません。
- Google Chrome(v125.0.6422.113)のコンソールでのエラーメッセージ例:fileプロトコルではクロスオリジン要求がサポートされていないため、CORSポリシー違反でエラーになります。公式の説明探したけど見つからなかった…。
Access to script at
'file:///C:/ViteProject/dist/assets/index-Cdp2L-o9.js'
from origin 'null' has been blocked by CORS policy:
Cross origin requests are only supported for protocol schemes:
http, data, isolated-app, chrome-extension, chrome, https, chrome-untrusted.
Access to CSS stylesheet at
'file:///C:/ViteProject/dist/assets/index-BAADmztE.css'
from origin 'null' has been blocked by CORS policy:
Cross origin requests are only supported for protocol schemes:
http, data, isolated-app, chrome-extension, chrome, https, chrome-untrusted.
- Firefox(v126.0.1)のコンソールでのエラーメッセージ例:クロスオリジン要求を行ったことによる、同一生成元ポリシー(SOP)違反でエラーになります。Firefoxの場合、fileプロトコルでのアクセス時の生成元(オリジン)は、そのhtmlファイルのみになります(MDN参照)。
https://developer.mozilla.org/ja/docs/Web/HTTP/CORS/Errors/CORSRequestNotHttp
クロスオリジン要求をブロックしました:
同一生成元ポリシーにより、
file:///C:/ViteProject/dist/assets/index-Cdp2L-o9.js にある
リモートリソースの読み込みは拒否されます (理由: CORS 要求が http でない)。
クロスオリジン要求をブロックしました:
同一生成元ポリシーにより、
file:///C:/ViteProject/dist/assets/index-BAADmztE.css にある
リモートリソースの読み込みは拒否されます (理由: CORS 要求が http でない)。
vite-plugin-singlefileでビルドしたhtmlがfileプロトコルで問題なく開ける理由
「vite-plugin-singlefile」によるビルドで生成した単一のhtmlファイルは、全部のJavaScriptやCSSが単一のhtmlファイルにインライン化されており、クロスオリジン要求による外部ファイルの読み込みを行うことがないため、ChromeのCORSポリシー・Firefoxの同一生成元ポリシー(SOP)に違反することも無いです。
従って、ローカル環境のブラウザからhtmlを直接開いて、fileプロトコルでアクセスしても、特に問題なく動作させることができるというわけです。
★参考にした&関連する記事
CORSや同一生成元ポリシー等
- 「vite-plugin-singlefile」は、この記事で述べられている「HTMLにファイルの内容を埋め込む (外部ファイル化を諦める。全ブラウザ対応)」を、Viteのビルドで実行するようなものです。
-
参考にしたCORSや同一生成元ポリシー(SOP)等の解説記事です。
とっても重要です。
https://qiita.com/ymbn/items/cc69dc3eccf5a13bbeed
https://qiita.com/Hirohana/items/9b5501c561954ad32be7
https://qiita.com/odas/items/9dadeb83598713f6b2e5
https://zenn.dev/syo_yamamoto/articles/445ce152f05b02
「vite-plugin-singlefile」の使用例
- Svelteで使用している事例
「vite-svg-loader」を利用したSVG画像の埋め込みも行っています。
- GASにClaspでデプロイする関係で使用している事例
インライン化された単一htmlが必要になるようです。
★関連するライブラリ
「vite-plugin-make-offline」
同様にViteで単一htmlをビルドするプラグインです。
「html-webpack-inline-source-plugin」
webpackで単一htmlをビルドするプラグインです。
Discussion