おとといに如何にWebのフロントのJSが絡むやつをやりたくないか長々と書き散らかしたのですが、文句を言ったところで何も生まないですし、進みもしないのでやりました。

やっている途中に、GruntでPolyfillのタスクを組むのが思ったよりめんどくさそう、というよりはGruntよくわかってない[1]し、このGruntのタスクの部分が使用しているテーマで最も挙動が謎なところで解析しておきたい、というよりは解析するくらいならもう書き換えた方がいいかというのもあったし、なにより今後のことを考えるとwebpackに切り替えておいた方がいいのは自明なのでそうすることにしました。

もともとのGruntタスクは細かい挙動は把握できてはいませんでしたが、大きくは下記のようなことをやっていました。

  • Sassのビルド、minify、ビルド後のファイルにキャッシュ対策用のハッシュ値を付与
  • JavaScriptのビルド、minify、ビルド後のファイルにキャッシュ対策用のハッシュ値を付与[2]
  • ビルド後のCSS、JavaScriptをEJSテンプレートにインジェクト

これらを全部webpackで行うようにしました。Gruntの時は一つにまとまっていたCSSとJSのタスクをwebpackでは別で行うことにしました。詳しい解説はちゃんと書こうと思うのですが、とりあえず今回やったことを雑に書きます。基本的に公式のドキュメントからコピペしまくればなんとかできます。

JavaScript向けの設定


まず、JavaScript向けのwebpack設定ですが、次のようにしました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
const path = require('path'); 
const randToken = require('rand-token');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

const ASSET_PATH = process.env.ASSET_PATH || '/assets/js/theme';
const HEXO_DIST_PATH = '../../public/assets/';

const token = randToken.generate(10).toLocaleLowerCase();

module.exports = {
entry: {
archive: path.join(__dirname, "source/_js", "archives.js"),
},
output: {
path: path.resolve(path.join(HEXO_DIST_PATH, "js/theme")),
publicPath: ASSET_PATH,
filename: `[name]-${token}.min.js`
},
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: "babel-loader",
}
]
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
filename: 'all-archives-js-template.html',
template: path.join(__dirname, "_resourceInjectTemplate", "all-archives-js.html")
})
]
};

まずASSET_PATHですが、仕方なしに/assets/js/themeというディレクトリにしました。本当は/assets/js/にしたかったのですが、そこには他のディレクトリが存在しておりCleanWebpackPluginが問答無用でoutput先のリソースを消してしまうためで仕方なしの処置です。

EJSのインジェクトはHtmlWebpackPluginで出力されたhtmlをEJS側からインクルードするというたぶんあんまり正攻法ではない力業で誤魔化しました。ファイルの出力先はoutputに依存してしまうのでHexoの生成後のディレクトリを指定して、EJSではそちらに生成されたものをインクルードしてます。これに関しては実はたぶんHexoの使い方を間違っていて、その間違った使い方に合わせたやりかたでやっているので、他の人が同じことをやる場合は出力先を調整しないとダメなんじゃないかと思います。

今回の分はあくまで一つのJSでしかやってないので増えたら…その時はその時でやり方を調べます…。

CSS向けの設定


CSS向けは下記のようにしました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
const path = require('path'); 
const HtmlWebpackPlugin = require('html-webpack-plugin')
const randToken = require('rand-token');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');

const ASSET_PATH = process.env.ASSET_PATH || '/assets/css/theme';
const HEXO_DIST_PATH = '../../public/assets/';

const token = randToken.generate(10).toLocaleLowerCase();

module.exports = {
entry: {
css: path.join(__dirname, "source/_css", "tranquilpeak.scss"),
},
output: {
path: path.resolve(path.join(HEXO_DIST_PATH, "css/theme")),
publicPath: ASSET_PATH,
},
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
url: false
}
},
'sass-loader'
]
}
],
},
optimization: {
minimizer: [
new OptimizeCssAssetsPlugin({})
],
},
plugins: [
new MiniCssExtractPlugin({
filename: `style-${token}.min.css`,
ignoreOrder: false,
}),
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
filename: 'style.html',
template: path.join(__dirname, "_resourceInjectTemplate", "style.html"),
cssPath: `/assets/css/theme/style-${token}.min.css`,
inject: false,
}),
]
};

ASSET_PATHはJSと同様の理由で/assets/css/themeにしました。

一番苦労したのがCSSのEJSへのインジェクトでHtmlWebpackPluginで生成されるHTMLはデフォルトで<head>...</head>が付与されてしまうのとpreloadが指定できないという問題がありました。

そのため、デフォルトのインジェクトはOFFにしてcssPathというのオプションを作成し、そこにはハッシュ値を付与した生成後のファイル名を指定しました。これをstyle.html側で受け取るようにしました。style.htmlは次のように書いてます。

1
2
<link rel="preload" href="<%= htmlWebpackPlugin.options.cssPath %>" as="style">
<link rel="stylesheet" href="<%= htmlWebpackPlugin.options.cssPath %>">

これで、実際には次のようなHTMLが生成されます。

1
2
<link rel="preload" href="/assets/css/theme/style-twhpyxune2.min.css" as="style">
<link rel="stylesheet" href="/assets/css/theme/style-twhpyxune2.min.css">

これをJSの時と同様にEJSでインクルードするようにしました。

Polyfill


.babelrcは次のようにしました。

1
2
3
4
5
6
7
8
{ 
"presets": [
["@babel/preset-env", {
"useBuiltIns": "entry",
"corejs": 3
}]
]
}

JSには次のように書きます。

1
2
import "core-js/stable"; 
import "regenerator-runtime/runtime";

で、今回調べていて、@babel/polyfillがbabel7.4から非推奨らしく、上記のやり方に変わるみたいなんですよ。あは~んと思った。

そもそもこのサイトような、いわゆるウェブログのような個人のブログとか文書がメインになるようなサイトでJavaScriptは不要というのが私の個人的な見解で、そういうこともあって、このサイトでは基本的に99.5%くらいのページでJavaScriptレスで動きますが、今回改修したアーカイブページのようなところでは必要になってきてしまうので仕方なしにやるわけですが、もう大変厳しい。

アーカイブページの方の実装は、とりあえずレベルなので段階的に改修したいと思います。後はサイト全体的な話ですがフォントサイズを大きめに変えたり、ページの幅を広げたりとかもしました。また、今回トップページの記事一覧を3カラムにしようかと思ったのですが、実際にやってみると配色の問題かいまいちしっくりこなかったので一旦保留にしました。これは色やマージンを調整しながら試してみたいと思います。

私の休日がJSで消滅しつつあります。Sincerely厳しいです。おわり。


  1. webpackもよくわかってないですが ↩︎

  2. そもそもJSに関しては少し前から撤廃していたのですが… ↩︎