【更新日 : 】
【簡易版】Viteでコーダーのコーディング環境(HTML(ejsライク:ハンドルバー化)・Sass・JS)を作る
- Category:
- 開発環境
※本記事はejsを利用していません。
代わりにHTMLファイルをejsと同等の機能を追加する(ハンドルバー化する)プラグイン vite-plugin-handlebars を利用しています。
ejsは以下プラグイン vite-plugin-ejs を利用すると実装できるようです。
2025.01.17 更新箇所
ビルド時のファイル名調整に用いるコードで参照している「assetInfo.name」が非推奨になったため「assetInfo.names[0]」に修正しました。
本記事は構築する手順とファイル構成だけをまとめた記事です。
各設定の詳細を知りたい場合は以下の【詳細版】をご参照ください。
Viteでコーダー向けの環境構築をしたサンプルです。
通常のコーディングと同じ感覚(HTML(ejsライク)・Sass・JS)でコーディングできる設定を目指しました。
Viteの環境構築にはターミナル(Mac)やコマンドプロンプト(Windows)を用います。
本記事はこれらの基礎知識があることを前提とした記事です。
Viteは「HTML、SCSS、JS」で1セットなので、特定のファイルのみビルドしたい場合は別のツールを利用してください。
node.js v16.16.0 で動作確認をしています。
不具合が出る場合は実行環境のバージョンを合わせてください。
動作確認はmacです。windowsでは未検証ですので予めご了承ください。
この記事のViteでできること
- 複数のHTMLページ生成
- HTMLファイルをejsのように扱う(HTMLをハンドルバー化してejsの代替とする)
- ビルド後のHTMLファイルを整形(任意追加)
- SCSSの書き出し(PostCSSによるオプション設定)
- autoprefixer:ベンダープレフィックスの追加
- postcss-sort-media-queries:メディアクエリをソートして1つにまとめる
- css-declaration-sorter:プロパティ順のソート(smacss)
- postcss-purgecss:CSSファイルから未使用のスタイルを削除する
- postcss-normalize-charset:先頭にcharset追加
- publicに内包したサブJS(特定のページのみ追加したいJS)の圧縮(任意追加)
この記事のViteでしない(できない)こと
画像の圧縮はしない
プロジェクトで利用する固定画像は個別にコントロールしたい場合もあるので、コーディング環境に一括設定はしない派です。
Viteでは vite-plugin-imagemin というプラグインが存在しているようですので、そちらを利用すれば設定可能だと思います。
本記事では扱いませんので必要な方は以下をご参照ください。
【2024.08.06更新】Viteの外で利用できる画像圧縮ツールとしてsharpの実装記事を公開しました。
buildコマンド実行後に圧縮処理を組み合わせるなど、工夫次第でViteと共存させることも可能かと思います。
ベース環境の構築
ベース環境の構築には以下の記事を参考にさせていただきました。
Vite は Node.js >=14.6.0 のバージョンが必要ですので事前にインストールしておいてください。
プロジェクト作成
プロジェクトを作成したいディレクトリで以下を実行すると、①〜③の質問が始まるので順番に入力・選択してください。その後は④、⑤と続けて実行してください。
npm init vite@latest
①プロジェクト名(構築するプロジェクトの名称)を入力
? Project name: › vite-project
②利用するフレームワークを選択:vanillaを選択
? Select a framework: › - Use arrow-keys. Return to submit.
❯ vanilla
vue
react
preact
lit
svelte
③テンプレートのバリエーションを選択:vanillaを選択
? Select a variant: › - Use arrow-keys. Return to submit.
❯ vanilla
vanilla-ts
④作成したプロジェクトへ移動
cd vite-project
⑤作成したプロジェクトに初期インストール
npm install
ここまで実行すると「npm run dev」「npm run build」が実行できるようになります。
想定しているディレクトリ構成
初期インストールを実行するとViteのサンプルファイル一式が出力さていますが不要なものは全て削除します。
本記事では開発ファイルを src/で管理する構成です。
存在していないファイルは以降の手順を進めながら新規作成してください。
■プロジェクトディレクトリ
┣ dist (ビルドしたファイルが出力される場所)
┣ package.json (プロジェクトのjsonファイル)
┣ postcss.config.cjs (PostCSSの設定ファイル)
┣ vite.config.js (viteの設定ファイル)
┣ .jsbeautifyrc (任意:HTML整形プラグインjs-beautifyの設定ファイル)
┃
┣ node_modules (編集不要:自動生成されるコアファイル格納場所)
┣ package-lock.json (編集不要:インストールしたパッケージ情報などが記載されている)
┃
┗ src
┣ index.html
┣ xxx.html (複数ページを追加する場合)
┃
┣ components (HTMLのコンポーネントパーツを格納)
┃ ┗ header.html
┃ xxx.html ...
┃
┣ js (メインのモジュールJSファイルを格納)
┃ ┗ main.js
┃
┣ public (Viteの変換対象外のディレクトリ。distに中身がそのままコピーされます。)
┃ ┗ assets (そのまま移動させたいファイルを必要に応じて格納していく)
┃ ┣ fonts
┃ ┃ ┗ xxx.woff2 ...
┃ ┣ js
┃ ┃ ┗ xxx.js ...
┃ ┗ images
┃ ┗ xxx.jpg ...
┃
┗ scss
┣ style.scss
┗ (各記法に合わせたディレクトリ構成)
各モジュールやプラグインのインストール
Sass
npm install -D sass
PostCSS + プラグイン (不要なものは適宜省いてください。)
npm install -D postcss autoprefixer postcss-sort-media-queries css-declaration-sorter @fullhuman/postcss-purgecss postcss-normalize-charset
HTMLファイルをejsのように扱う (ハンドルバー化する) vite-plugin-handlebars
npm install -D vite-plugin-handlebars
HTML整形プラグインjs-beautifyをインストール
npm install -D js-beautify
package.json
browserslistは環境に合わせて書き換えてください。
esbuildの実行コマンドは、対象となるsrc/public/assets/js/にJSファイルが存在しないとコンソール上でエラーが出ます。
Viteのビルドコマンドとは独立してるのでベースのビルドは問題なく動作しますが、
不要は場合はesbuildの実行コマンド自体を削除してください。
devDependencies”の各バージョンは記事執筆時のものですので、package.jsonをコピペで利用する場合は更新がないか確認をしてください。
package.json
{
"name": "プロジェクト名",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build && html-beautify dist/**/*.html && esbuild src/public/assets/js/*.js --bundle --minify --outdir=dist/assets/js/",
"preview": "vite preview"
},
"devDependencies": {
"@fullhuman/postcss-purgecss": "^5.0.0",
"autoprefixer": "^10.4.13",
"css-declaration-sorter": "^6.3.1",
"js-beautify": "^1.14.7",
"postcss": "^8.4.21",
"postcss-normalize-charset": "^5.1.0",
"postcss-sort-media-queries": "^4.3.0",
"sass": "^1.57.1",
"vite": "^4.0.0",
"vite-plugin-handlebars": "^1.6.0"
},
"browserslist": [
"last 3 versions",
"> 5%",
"Firefox ESR",
"not dead"
]
}
vite.config.js
handlebars用のページ情報(pageData)はサンプルですので不要なら省いてください。
vite.config.js
import { defineConfig } from 'vite';
import { resolve } from 'path';
//handlebarsプラグインimport
import handlebars from 'vite-plugin-handlebars';
// HTMLの複数出力を自動化する
//./src配下のファイル一式を取得
import fs from 'fs';
import path from 'path';
const files = [];
function readDirectory(dirPath) {
const items = fs.readdirSync(dirPath);
for (const item of items) {
const itemPath = path.join(dirPath, item);
if (fs.statSync(itemPath).isDirectory()) {
// componentsディレクトリを除外する
if (item === 'components') {
continue;
}
readDirectory(itemPath);
} else {
// htmlファイル以外を除外する
if (path.extname(itemPath) !== '.html') {
continue;
}
// nameを決定する
let name;
if (dirPath === path.resolve(__dirname, 'src')) {
name = path.parse(itemPath).name;
} else {
const relativePath = path.relative(path.resolve(__dirname, 'src'), dirPath);
const dirName = relativePath.replace(/\//g, '_');
name = `${dirName}_${path.parse(itemPath).name}`;
}
// pathを決定する
const relativePath = path.relative(path.resolve(__dirname, 'src'), itemPath);
const filePath = `/${relativePath}`;
files.push({ name, path: filePath });
}
}
}
readDirectory(path.resolve(__dirname, 'src'));
const inputFiles = {};
for (let i = 0; i < files.length; i++) {
const file = files[i];
inputFiles[file.name] = resolve(__dirname, './src' + file.path );
}
/*
この形を自動的に作る
input:{
index: resolve(__dirname, './src/index.html'),
list: resolve(__dirname, './src/list.html')
}
*/
//HTML上で出し分けたい各ページごとの情報
const pageData = {
'/index.html': {
isHome: true,
title: 'Main Page',
},
'/list.html': {
isHome: false,
title: 'List Page',
},
};
//cssとjsファイルに更新パラメータ追加
const htmlPlugin = () => {
return {
name: 'html-transform',
transformIndexHtml(html) {
// npm run build のときのみ動作させる
if(process.env.NODE_ENV !== 'production') {
return;
}
//更新パラメータ作成
const date = new Date();
const param = date.getFullYear() + date.getMonth() + date.getDate() + date.getHours() + date.getMinutes() + date.getSeconds();
// CSSファイルにパラメータを追加(httpsから始まる外部リンクは除外)
let setParamHtml = html.replace(/(?=.*<link)(?=.*css)(?!.*https).*$/gm, match => {
return match.replace(/\.css/, '.css?' + param);
});
// JSファイルにパラメータを追加して変更内容を返す(httpsから始まる外部リンクは除外)
return setParamHtml.replace(/(?=.*<script)(?=.*js)(?!.*https).*$/gm, match => {
return match.replace(/\.js/, '.js?' + param);
});
}
}
}
export default defineConfig({
server: {
host: true //IPアドレスを有効化
},
base: './', //相対パスでビルドする
root: './src', //開発ディレクトリ設定
css: {
preprocessorOptions: {
scss: {
api: 'modern-compiler' //scssのコンパイラーを新しいAPIに変更
}
}
},
build: {
outDir: '../dist', //出力場所の指定
rollupOptions: { //ファイル出力設定
output: {
assetFileNames: (assetInfo) => {
let extType = assetInfo.names[0].split('.')[1];
//Webフォントファイルの振り分け
if (/ttf|otf|eot|woff|woff2/i.test(extType)) {
extType = 'fonts';
}
if (/png|jpe?g|svg|gif|tiff|bmp|ico/i.test(extType)) {
extType = 'images';
}
//ビルド時のCSS名を明記してコントロールする
if(extType === 'css') {
return `assets/css/style.css`;
}
return `assets/${extType}/[name][extname]`;
},
chunkFileNames: 'assets/js/[name].js',
entryFileNames: 'assets/js/[name].js',
},
input:
//生成オブジェクトを渡す
input: inputFiles,
},
},
/*
プラグインの設定を追加
*/
plugins: [
handlebars({
//コンポーネントの格納ディレクトリを指定
partialDirectory: resolve(__dirname, './src/components'),
//各ページ情報の読み込み
context(pagePath) {
return pageData[pagePath];
},
}),
htmlPlugin()
],
});
postcss.config.cjs
npm install時に省いたものは削除してください。
postcss.config.cjs
module.exports = {
plugins: {
autoprefixer: {},
'postcss-sort-media-queries': {},
'css-declaration-sorter':{order:'smacss'},
'@fullhuman/postcss-purgecss': {
content: ['./src/**/*.html','./src/js/**/*.js'],
//除外設定 https://purgecss.com/safelisting.html
safelist: ['hoge']
},
}
}
.jsbeautifyrc
js-beautifyの設定ファイルです。各自の環境に合わせて設定してください。
.jsbeautifyrc
{
"html": {
"indent_size": 2,
"unformatted": ["svg", "pre"]
}
}
main.jsの中身を削除
CSSのimport設定やテストコードは全て不要なので削除してください。
main.js
//空にしてください。
index.htmlにlinkタグを追加・パスの修正
CSSを通常のコーディングと同じようにlinkタグで読み込ませます。
変更したディレクトリに合わせて相対パスでSCSSのまま設定します。(buildすると自動的にCSSへ置き換わります。)
※lang属性はjaに変更し、初期に入っているfavicon設定は不要なので削除しています。
※main.jsも同じように相対パスを修正してください。
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
<!-- CSSの読み込みを追加 -->
<link rel="stylesheet" href="./scss/style.scss">
</head>
<body>
<div id="app"></div>
<!-- 相対パスを修正 -->
<script type="module" src="./js/main.js"></script>
</body>
</html>
開発を開始する
以下のコマンドを実行すると開発サーバが起動するのでコーディングを開始できます。
npm run dev
開発したプロジェクトをビルドする
以下のコマンドを実行するとdist/ディレクトリに公開用のファイル一式が書き出されます。
npm run build
エラーが出た場合はコンソール上に詳細が書いてあるので、しっかり確認すると殆どの原因はすぐに特定できると思います。
よくありそうなエラーは、パスの記述ミス、vite.config.jsやpostcss.config.cjsファイルなどの構文エラー、存在しないファイルを参照しているなどが考えられます。
CSSでfont-familyに日本語を設定する場合の注意点
Viteに内蔵された機能でCSSを圧縮すると日本語文字列をUnicodeエスケープシーケンスに変換(例:\65e5\672c\8a9e)しますが、変換されるとWindows環境などでfont-family設定が効かなくなる場合があり、これを回避するには別途設定が必要です。
詳細は以下の記事にまとめましたので、必要に応じて追加で設定をしてください。
Tailwind CSSを導入する
ViteでTailwind CSSを導入する方法を以下の記事にまとめました。
Tailwind CSSの最適化(リセットCSSの削除、変数の削減、独自拡張)や、最適化した独自styleを混在させることができるのか検証しています。
UnoCSSを導入する
ViteでUnoCSSを導入する方法を以下の記事にまとめました。
Tailwind CSSとの互換性があり、拡張性にも優れていて高速に動作するツールです。なるべくTailwind CSSと同じ使用感で導入することを目指しました。