ちゅんラヂのビルドが必要になった理由
ソースファイルが肥大化してメンテナンス性が悪化してしまいました。
この悪化したソースファイルを機能ごとにモジュール化して、メンテナンス性を改善させるリファクタリング作業の進行中です。
モジュール化の短所は細切れになったモジュールファイルのI-O回数が増えることです。結果としてちゅんラヂが開く待ち時間の悪化を招いてしまいます。
このトレードオフの関係を改善する為に、エントリーポイントとなるソースファイルに依存関係にあるモジュールファイルをまとめる作業を行い、数あるソースファイルを読み込むI-Oオーバーヘッドの改善を行っています。
モジュール化で分割したソースファイルを、バンドルしてデプロイするって妙な関係!?、これ、メンテナンス性が大幅に向上し、デプロイ後のI-Oオーバーヘッドが削減できる関係です。
なお、ビルド環境の整備でGitHUBにて公開する事も考えましたが、長年の更新を経てモジュール構成とソースコードが汚いので恥ずかしい。^^;
HTTP/2環境の普及と、Brotli圧縮が普及した現在は、このビルド作業を行わなくてもI-Oオーべーヘッドは改善されているはずです。
それでもビルドしてバンドルしておけばWebサーバー側のオーバーヘッドを減らす効果は有るはずです。
JavaScriptのモジュール化
・JavaScriptを機能単位にモジュール分割
・npmで外部ライブラリを利用
※エントリーポイントになるJavaScriptファイルでimport、モジュールのJavaScriptファイルでexport
CSSのモジュール化
CSSをブロック単位にモジュール分割してメンテナンス性を改善
※エントリーポイントになるCSSファイルでモジュールとなるCSSファイルを@import
イメージファイルのバンドル
放送局選局欄のアイコン数が多く、そのアイコン読み込みのI-Oオーバーヘッドが大きくなるのを改善する為に、選局データ内にBase64データで保有しています。
放送局アイコン数以外はHTMLやCSS内にBase64データとして抱え込んでいます。
※Base64でイメージファイルは大きくなりますが、Webサーバー側のBrotli圧縮により転送量増加は軽微です。
1. 開発環境ディレクトリ構成
▼ちゅんラヂ開発環境のルートディレクトリ ├◆git ※gitディレクトリ ├─ package.json │ ※当開発環境の情報 ├◆node_modules │ ※Webpackが導入される │ ※ちゅんラヂで使っている外部モジュール ├─ package-lock.json │ ※npmのバンドル依存情報 ├◆dist ││ ※distributeの略 ││ ※リリースはこのディレクトリから │├─ tunein2radio.html │├─ tunein2player.html │├─ …… ││ ※HTML自体は単純なのでビルド対象にしていない │├◆js │││※バンドル後のJavaScript ││├─ tunein2radio.js ││├─ tunein2player.js ││└─ …… │├◆lib │││※バンドル対象外ライブラリ等 ││└─ dash.js │├◆css │││※バンドルしたCSS ││├─ tunein2radio.css ││└─ tunein2player.css │├◆data ││├─ tunein2modified5.json │││※プリセットデータの更新判定用 ││└─ tunein2data5.json ││ ※プリセットデータ │├◆img │││※将来的にsrc配下に移動してバンドルする? ││├─ xxxxxx.png ││├─ …… │:: ├◆src ││※バンドル前のソースコード │├◆js │││※バンドル前のJavaScriptソースコード ││├─ main.tunein2radio.js ││├─ main.tunein2player.js ││├─ …… ││└◆ modules ││ ├─ 各種JSモジュール ││ ├─ …… │: : │├◆css │││※バンドル前のCSSソースコード ││├─ main.tunein2radio.css ││├─ main.tunein2player.css ││└◆ modules ││ ├─ 各種cssモジュール ││ ├─ …… │: : │ ※configはHTMLの機能単位毎(バンドル単位)で作成 ├─ webpack.config.tunein2rado.js ├─ webpack.config.tunein2player.js ├─ webpack.config.tunein2.css.js ※CSSは共通部分があるので一括複数CSSをビルド :
2. パッケージ環境(package.json)
-
項目要約
-
package.jsonちゅんラヂ開発環境のpackage.jsonの内容
{ "name": "ちゅんラヂ", "version": "05.18.00", "description": "サイマルラジオ受信アプリ", "private": true, "scripts": { "t2radio_dev": "webpack --mode development -c ./webpack.config.tunein2radio_js.js", "t2radio": "webpack --mode production -c ./webpack.config.tunein2radio_js.js", "t2player_dev": "webpack --mode development -c ./webpack.config.tunein2player_js.js", "t2player": "webpack --mode production -c ./webpack.config.tunein2player_js.js", "t2readme_dev": "webpack --mode development -c ./webpack.config.tunein2readme_js.js", "t2readme": "webpack --mode production -c ./webpack.config.tunein2readme_js.js", "t2css": "webpack --mode production -c ./webpack.config.tunein2.css.js" }, "keywords": [], "author": "booskanium", "license": "MIT", "devDependencies": { "autoprefixer": "^10.4.7", "css-loader": "^6.7.1", "cssnano": "^5.1.10", "mini-css-extract-plugin": "^2.6.0", "postcss": "^8.4.14", "postcss-loader": "^7.0.0", "sass": "^1.52.1", "sass-loader": "^13.0.0", "webpack": "^5.72.1", "webpack-cli": "^4.9.2", "webpack-fix-style-only-entries": "^0.6.1", "webpack-remove-empty-scripts": "^0.8.0" }, "dependencies": { "hls.js": "^1.6.16", "mse-audio-wrapper": "^1.4.4" }, "browserslist": [ "last 2 versions", "> 1%", "not dead" ] }各外部モジュールのバージョンはWindows 7で動かせる事を前提としています。
-
環境設定手順
node.jsとnpmが先にインストールされているものとします。
以下の操作は、プロンプト画面でカレントディレクトリーをちゅんラヂ開発環境のルートディレクトリにしてある事。
-
Node.jsのインストール
node.jsはwebpackの実行環境でもあります。
npmはnode.jsに同梱されています。
node.jsのインストール方法はこちらを参照
-
package.jsonの初期作成
npm config set init.author.name "booskaniium" npm config set init.author.license "MIT" npm init -y
既定のnpm configのキー値としてinit.author.nameとinit.author.licenseを設定
package.jsonの初期作成 -
package.jsonをちゅんラヂ用に調整
以下のpackage.jsonのキー値を調整する。
"name": "ちゅんラヂ",
"version": "9.99.99",
"description": "ちゅんラヂ",
"private": true,
・"name": "ちゅんラヂ",
・"description": "ちゅんラヂ",
プロジェクト名である"ちゅんラヂ" と記述
・authorやlicenseは事前にconfigのキー値でセットした内容になっているかを確認
npm config set init.author.name "開発者名"
npm config set init.author.email "開発者メールアドレス"
・"license": "GNU LGPLv3"
ちゅんラヂのlicenseに打ち替えています。
さらに、Privete開発でnpmに公開しないので
-
外部モジュールをインストール
HLSを受信する外部モジュールと、コンテナ載せ替えをを行う外部モジュールをインストール
npm install --save hls.js npm install --save mse-audio-wrapper
hls.js HLS配信を受けてMSE経由でAUDIO要素に流し込むモジュール
mse-audio-wrapper opus/oggをopus/webmに載せ替えるモジュール
-
Webpsckをインストール
npm install -D webpack webpack-cli
"devDependencies": { "webpack": "^5.72.0", "webpack-cli": "^4.9.2" }
3. JavaScriptバンドル環境
Webpackでバンドルするプロジェクトの環境を整える手順
落書き人はhtmlファイル毎のJavaScriptを個々にバンドルします。
そして開発(development)モードでバンドルしてテスト、テストが完了したら本番(production)モードでバンドルして再確認してリリースという手順です。
以下はWindows環境での説明です。
-
手順要約
-
JavaScriptはちゅんラヂ(UI部分)とちゅんプレイヤー(受信機能部分)を別HTMLになっており、JavaScriptのそれぞれ別々です。
その2つの部分を個別にビルドする様にしています。
-
webpack.config.tunein2radio.js
ちゅんラヂ(UI)部分のビルドconfig
const path = require('path'); const webpack = require('webpack'); const pjson = require('./package.json'); const config = { entry: path.resolve(__dirname, "./src/js/main.tunein2radio.js"), output: { path: path.resolve(__dirname, 'dist/js'), filename: 'tunein2radio.js' }, plugins: [ new webpack.BannerPlugin({ banner: ` /*! * ${pjson.name} v${pjson.version} * (c) ${pjson.author} * license: ${pjson.license} Copyright (c) 2018-2026 booskanium Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */`, raw: true }) ] }; module.exports = (env, argv) => { if (argv.mode === 'development') { config.mode = 'development'; config.devtool = 'source-map'; } if (argv.mode === 'production') { config.mode = 'production'; } return config; };
-
webpack.config.tunein2player.js
ちゅんプレイヤー(受信機能部分)のビルドconfig
const path = require('path'); const webpack = require('webpack'); const pjson = require('./package.json'); const config = { entry: path.resolve(__dirname, "./src/js/main.tunein2player.js"), output: { path: path.resolve(__dirname, 'dist/js'), filename: 'tunein2player.js' }, plugins: [ new webpack.BannerPlugin({ banner: ` /*! * ${pjson.name} v${pjson.version} * (c) ${pjson.author} * license: ${pjson.license} Copyright (c) 2018-2026 booskanium Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */`, raw: true }) ] }; module.exports = (env, argv) => { if (argv.mode === 'development') { config.mode = 'development'; config.devtool = 'source-map'; } if (argv.mode === 'production') { config.mode = 'production'; } return config; };
4. CSSバンドル環境
CSSをバンドルする環境を整える手順
CSSを機能別にファイル分割してメンテナンス性を高めました。その結果としてCSSファイルが細分化してしまい、ページを開く時の読み込みリクエスト数が増えてしまいました。そこでCSSファイルもWebpackでバンドルする事にしました。
なおHTMLファイル中にバンドルしたCSSを埋め込まずに、CSSファイルを別ファイルにしています。
下記の手順でローダーやプラグインをインストールして、configを記述しました。
各ローダーやプラグインのGitHUBを参照すると、バンドルの大凡な手順が理解できますのでリンクを張っときます。
-
手順コマンド要約 ?
-
css-loaderのインストールnpm i -D css-loaderhttps://github.com/webpack-contrib/css-loader
JSファイル内にCSSを文字列として取り込むローダー。 -
sass sass-loaderのインストールnpm i -D sass sass-loadersass
This package is a distribution of Dart Sass, compiled to pure JavaScript with no native code or external dependencies. It provides a command-line sass executable and a Node.js API.
sass-loader
Loads a Sass/SCSS file and compiles it to CSS. -
postcss postcss-loader autoprefixer cssnanoをインストールnpm i -D postcss postcss-loader autoprefixer cssnanoCSSの解析で、変数とミックスインをサポートし、将来のCSS構文、インライン画像などをトランスパイルする。
PostCSS
postCSS is a tool for transforming styles with JS plugins. These plugins can lint your CSS, support variables and mixins, transpile future CSS syntax, inline images, and more.
PostCSS is used by industry leaders including Wikipedia, Twitter, Alibaba, and JetBrains. The Autoprefixer PostCSS plugin is one of the most popular CSS processors.
postcss-loader
Loader to process CSS with PostCSS.
autoprefixer
PostCSS plugin to parse CSS and add vendor prefixes to CSS rules using values from Can I Use. It is recommended by Google and used in Twitter and Alibaba.
cssnano
cssnano is a modern, modular compression tool written on top of the PostCSS ecosystem, which allows us to use a lot of powerful features in order to compact CSS appropriately.
-
mini-css-extract-plugin webpack-remove-empty-scriptsをインストールnpm i -D mini-css-extract-plugin webpack-remove-empty-scripts
npm i -D webpack-fix-style-only-entriesmini-css-extract-plugin
This plugin extracts CSS into separate files. It creates a CSS file per JS file which contains CSS. It supports On-Demand-Loading of CSS and SourceMaps.
ebpack-remove-empty-scripts
The plugin remove empty scripts generated by usage only a style (css/scss/sass/less/stylus) without a js script in entry.
webpack-fix-style-only-entries
This is a small plugin developed to solve the problem of having a style only entry (css/sass/less/stylus) generating an extra js file.
-
webpack.config.jsを名前を変えて準備
webpack.config.tunein2.css.jsのconfig
//webpack.config.tunein2radio.css.js const path = require('path'); const webpack = require('webpack'); const pjson = require('./package.json'); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const FixStyleOnlyEntriesPlugin = require("webpack-fix-style-only-entries"); const sass = require("sass"); const cssnano = require("cssnano"); const autoprefixer = require("autoprefixer"); const MODE = process.env.MODE || process.env.NODE_ENV || "development"; const ENABLED_SOURCEMAP = MODE === "development"; const TARGET_DIR = path.resolve(__dirname, './src/css'); module.exports = { mode: MODE, target: ["web", "es5"], entry: { radio: `${TARGET_DIR}/main.tunein2radio.css`, player: `${TARGET_DIR}/main.tunein2player.css` }, output: { path: path.resolve(__dirname, 'dist/css'), filename: 'dummuy[name].js' }, module: { rules: [ { test: /\.css|\.c(a|c)ss/, use: [ MiniCssExtractPlugin.loader, { loader: "css-loader", options: { // url() を require() に変換しない url: false, // development の時だけ source map を出力する sourceMap: MODE === "development", // css-loader の前に loader を 2つ (postcss, sass) 実行する importLoaders: 2, }, }, { loader: "postcss-loader", options: { postcssOptions: { plugins: [ [ cssnano, { // コメントを削除する preset: [ "default", { discardComments: { removeAll: true } }, ], }, ], [autoprefixer, { grid: true }], ], }, }, }, { loader: "sass-loader", options: { // dart-sass を使用する implementation: sass, }, }, ], }, ], }, plugins: [ new FixStyleOnlyEntriesPlugin(), //不要なJSファイルを除外する new MiniCssExtractPlugin({ //CSSを圧縮るする filename: "tunein2[name].css", }), ], }上記ではエントリーとなる2つのCSSを一括してビルド&バンドルでして、それぞれ別のファイルに出力しています。
一括してビルドする理由は、CSSのモジュールのソースコードで共有している部分があるからです。
複数のエントリーとアウトプットに対応する記述の指定箇所は、
・entry:でビルドする複数の「プロパティ名:エントリーCSSファイル」を指定
・それが順番(rule:定義の逆順)にビルド&バンドルされる
・ビルド&バンドル中の「[name]」部分に処理中のentryのプロパティ名が代入される
上記config実行用のScript
package.jsonのscript欄に以下のコマンドを記述"t2css": "webpack -c ./webpack.config.tunein2.css.js",
バンドルコマンドは「npm run t2css」
SCSS,SASSのバンドル可能
SCSS,SASSのトランスパイルを行っていますのでSCSSであってもバンドル可能です。
PostCSSのバンドル可能
PostCSSのパーサーも組み込んでありますので、新しいCSS3もバンドル可能です。
4. イメージファイルはBase64形式で
ちゅんラヂは放送局アイコンのイメージデータが多く、gifやjpegファイルで保有すると、そのi-oオーバーヘッドが大きくなってしまい、開く時の待ち時間が馬鹿になりません。
放送局アイコン
選局プリセットファイルの中にbase64形式で保有しています。
プリセットファイルのサイズは大きくなりますが、多くのプリセット分のイメージファイルを個別に読み込むオーバーヘッドがなくなり結果として、ちゅんラヂを開く時の待ち時間が減ります。
その他イメージデータ
HTMLやCSSの中にBase65形式で抱え込んでいます。
メージファイルを個別に読み込むオーバーヘッドがなくなり結果として、ちゅんラヂを開く時の待ち時間が減ります。