限界Webアプリ
数年前に作ったミニwebアプリがありました。
それは所謂アイコンメーカーで、好きなパーツを組み合わせてオリジナルのアイコンを作れるものでした。
まともなマークアップもスクリプトも書けない中、必死で作った思い出のアプリケーションです。
最近、ふとその時のコードを見たら目眩が止まらなくなりました。
pureすぎるphp
💩phpファイルでそのままルーティングしている。フレームワーク?何それおいしいの?
💩php7環境で動かない古のメソッドも使われていた
jQuery/scriptタグにベタ打ち
💩jQueryに頼りきったフロント
💩しかもscriptタグでphpにベタ打ちしていた。外部読み込みが存在しない世界。まさに限界集落。
💩JSのお作法も全くわかっていなかったので、動いているかどうかわからないスクリプトタグ・ファイルが多すぎる。胃が爆発する。
あまりにも辛い。
そこでNode.jsの勉強も兼ねてNode.js+Express+Webpack環境でrebornしました。
環境構築
とりあえずHello Worldしたい。
Node.jsはインストール済みで、今回はv10.15.3
で構築しました。
初期設定
$ mkdir reborn_app
$ cd reborn_app
$ npm init
# すべてenter, yes
Press ^C at any time to quit.
package name: (reborn_app)
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to /path/reborn_app/package.json:
{
"name": "reborn_app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
Is this OK? (yes) yes
サーバー・テンプレートのモジュール
サーバーサイドは安心と信頼のexpressを使います。
テンプレート言語はhtmlっぽくサクっと書きたい+触ったことがなかったのでejsを使ってみます。
$ npm install --save-dev express ejs
ejsはviews配下にあるテンプレートをレンダリングするので、
とりあえずindexを作っておきます
<html>
<head>
<title>hoge</title>
</head>
<body>
<!-- textはexpressから渡す変数 -->
<h1><%- text %></h1>
</body>
</html>
expressの設定
expressとテンプレ言語を揃えたのでexpressの設定をしていきます。
// expressを呼び出す
const express = require('express')
const app = express()
const port = 8080
// view engineをejsにする
app.set('view engine', 'ejs')
// listenメソッドでlocalhost:8080で開けるようにする
app.listen(port, () => {
console.log(`Server started on: localhost:${port}`)
});
// ルーティング設定
app.get('/', (req, res, next) => {
const text = 'Hello World!'
res.render('index', {text: text});
})
そしてコマンドラインで以下を叩きます
$ node server.js
https://localhost.8080/
アクセスするとHelloWorldできています。やったぜ。
webpackでbundleする
素jsをscriptで手作業読み込みは流石にしんどいので
webpackでbundleして読み込みを簡単にします
webpackの設定
この辺は公式ドキュメントとほぼ同じです。
$ npm install webpack webpack-cli --save-dev
const path = require('path')
module.exports = {
entry: './src/index.js', // エントリーポイント。使うmoduleまとめたやつ
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js' // 指定した名前で出力できる
}
}
モジュールはそれぞれexport default
で書き出し、
エントリーポイントでimport
して発火すればOK。
export default () => {
alert('🍣')
}
// import文を使って sushi.js ファイルを読み込む。
import sushi from './js/sushi.js'
// sushi.jsに定義されたJavaScriptを実行する。
sushi()
ただ、webpackコマンドを叩かないといつまでたってもbundleされないので
package.json
でbuildスクリプトを追加しておきましょう。
"scripts": {
"build": "webpack"
}
$ npm run build
あとはejsのscriptタグで読み込みます
<script src="js/bundle.js"></script>
サーバーを再起動すると無事にscriptを読み込めました。
$ node server.js

その他 快適開発ライフを送るために
できれば設定しておきたいものたち
ホットリロード
js更新したらbuild, バックエンド更新したらサーバー再起動を毎度行うのは辛すぎる…
とにかく楽にしたいのでホットリロード環境を整えます。
ソースは以下の記事を参考にさせていただきました。
expressにwebpack-dev-serverを組み込んでフロントエンドとサーバーサイドを同時にサクサク開発するハンズオン ~Auto Reloadで幸せに~
必要なモジュールを追加でインストールします
# webpack系
$ npm install --save-dev webpack-dev-server webpack-dev-middleware webpack-hot-middleware
# その他 express, フロント系
$ npm install --save-dev babel-watch @babel/core @babel/preset-env babel-loader core-js@3 @babel/polyfill
// output.pathに絶対パスを指定する必要があるため、pathモジュールを読み込んでおく
const path = require('path');
// 'production' か 'development' を指定
const MODE = "development";
// ソースマップの利用有無(productionのときはソースマップを利用しない)
const enabledSourceMap = MODE === "development";
// webpack設定
module.exports = {
mode: MODE,
devServer: {
contentBase: path.join(__dirname, '.'),
port: 8080,
host: `localhost`,
},
// メインとなるJavaScriptファイル(エントリーポイント)
// babelはES6適用するため
// hotreloadの設定を配列に追記している
entry: {
app:[
'@babel/polyfill',
'webpack-hot-middleware/client?reload=true&timeout=1000',
'./src/index.js'
]
},
// ファイルの出力設定
output: {
// 出力ファイルのディレクトリ名
path: path.join(__dirname, 'dist'),
// 出力ファイル名
filename: "bundle.js"
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: [{
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
'modules': 'false', //commonjs,amd,umd,systemjs,auto
'useBuiltIns': 'usage',
'targets': '> 0.25%, not dead',
'corejs': 3
}
]
]
}
}]
},
]
},
plugins: [
],
};
expressも追記します。ファイルの上部に以下を追記。
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const config = require('./webpack.config.js');
const devServerEnabled = true;
if (devServerEnabled) {
config.plugins.push(new webpack.HotModuleReplacementPlugin());
const compiler = webpack(config);
app.use(webpackDevMiddleware(compiler, {
publicPath: config.output.publicPath
}));
app.use(webpackHotMiddleware(compiler));
}
あとはpackage.jsonにスクリプトを追記して終了です。
これでnpm start run
するだけでホットリロードする世界が爆誕しました。
幸福度が高い。※ejsの更新は検知出来なかったので追々直したい
"scripts": {
"start:client": "webpack-dev-server --config webpack.config.js",
"start": "babel-watch ./server.js",
}
$ npm run start
Sass
CSSベタ書きは人権が消失するので、できればSassも使えるようにしたいです。
基本的にはnode-sassと各種loaderをインストールしてwebpackにぶちこむだけです。
ただ、そのままstyle-loaderを使うとjs処理が走ってからページにlinkrelを埋め込むので、
一瞬スタイルが当たる前の裸のDOMが出ることがあります。つら。
そのため、CSSはbundle.jsとは別に出力し、先に読ませるように設定していきます。
参考: https://github.com/webpack-contrib/mini-css-extract-plugin
npm install -D style-loader css-loader sass-loader node-sass mini-css-extract-plugin
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// module.rules
{
test: /\.scss/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
{
loader: 'css-loader',
options: {
url: false,
sourceMap: true,
importLoaders: 2
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
}
]
}
plugins: [
new MiniCssExtractPlugin({
// viewsで読み込む時のファイル名を指定する
filename: 'style.css',
}),
]
エントリーポイントで読み込むSassをimportします。
jsと同じように./src
配下にSassファイルを置けばホットリロードの対象になり、自動コンパイルしてもらえます。最高〜!
import './scss/style.scss'
あとはテンプレのheadにスタイルシートを貼ればOK。
<link rel="stylesheet" type="text/css" href="style.css">
最終的なディレクトリ構成
こんな感じになりました。
画像もbundleした方がいいのかなーと思いつつ、今回はあまり必要性を感じなかったのでassetsとして分けちゃいました😔
- node_modules
- assets // 静的ファイル 主に画像
- images
- src // webpackでbundleするもの
- js
hoge.js
- scss
_hoge.scss
style.scss
- index.js // webpackのエントリーポイント
- views
hoge.ejs
server.js
.gitignore
package-lock.json
package.json
webpack.config.js
あとはExpressとフロントでゴリゴリするだけなので割愛します
換装を完走した感想
サーバーサイドの苦手意識の緩和
ふだんサーバーサイドを触らない+phpしんどいので滅茶苦茶苦手なんですが、
Node.js+ExpressだとJSのお作法でデータ整形が出来るのでとっつきやすかったです。
jsが使えるの助かる…
webpackはえらい
今まで雰囲気でwebpackを使っていたんですが、
今回お作法やローダーの役割を考えながら学べたのがよかったです。
Sassの自動コンパイルもささっと作れるのが嬉しい。優しい世界。
振り返りは大事
今回は数年前のコードをリファクタしましたが、当時の自分よりは圧倒的成長を感じられたのでよかったです(小並感)
新しい技術をキャッチアップして強みを伸ばすのは大事ですが、たまに振り返ってクソコードをきれいに直すのも勉強になります。
そして数年後、今回作ったものをクソコードと罵る自分がいることを願います。