Edited at

Parcel のどうということはない小技集


概要

Parcel で LP のような簡単なページを作る際に役立つかもしれない TIPS をまとめてみました。

私のサイトのトップページなどはまさに Parcel で作ったのですが、機能もへったくれもないので、そういうのを求めている方向けではありません。

$ npm i --save-dev parcel-bundler # `parcel` じゃないよ


小技


index.html を一階層上に移動する

Parcel は何でもかんでも dist フォルダにファイルをビルドしちゃいますが、 index.html だけ上の階層に移動したい、というケースがあるかもしれません。私はありました。

しかし Parcel にそんな気の利いた機能はないので、 mv コマンドで移動する必要があります。

が、そのままだと index.html 内部の相対パスが壊れてしまうため、ビルドする際には --public-url オプションを付ける必要もあります。

例えば、 /top/index.html/index.html に移動したい場合は以下のようになるかと。

"scripts": {

"dev": "parcel ./src/*.html --no-cache --no-source-maps --out-dir dist/top --target browser",
"build": "parcel build ./src/*.html --no-cache --no-source-maps --out-dir dist/top --public-url ./top --target browser && mv dist/top/index.html dist/index.html"
}

ちなみに parcel ./src/index.html とした場合は http://localhost:1234/ でページが表示されますが、 parcel ./src/*.html とワイルドカードを使った場合は http://localhost:1234/index.html と明示的にファイル名を指定しなければページは表示されません。


古いビルドファイルを削除する

Parcel でビルドすると top.44a62113.js, top.9666360a.js, ... というように古いファイルが際限なく残り続けます。

これはもちろんキャッシュ対策であって正しい挙動なんですが、いやそんな残さんでええよ?という時は rm コマンドを npm-scripts の prebuild かビルドコマンドの前に書くと良さげです。

"scripts": {

"build": "rm dist/top/* ; parcel build ..."
}

なお、こちらによると parcel-plugin-clean-dist とか parcel-plugin-clean-easy というプラグインもあるようですが、使う必要あるんでしょうか?わかりません。


Sass を使う

どこにでもある情報ですが、 .scss ファイルを JavaScript で import して Parcel を走らせれば自動的に sass パッケージが読み込まれ、 CSS に変換されます。

( Parcel には Auto-install 機能があるので、手動で npm install するのは避けた方が良いかな、と。)

import './index.scss'

ちなみに Normalize CSS や Reset CSS も JavaScript で import すれば勝手にインストールされます。

import 'normalize.css'

import 'reset-css'

上二つの Sass 版を使いたい場合は npm i --save normalize-scss reset-css してから Sass で import する必要がある、のかな? Parcel 関係ないけど。

@import '../node_modules/normalize-scss/sass/normalize';

@include normalize();
@import '../node_modules/reset-css/sass/reset';


Autoprefixer を使う

まず、 package.json と同階層に postcss.config.js を作り、以下のようなコードを書きます。

module.exports = {

plugins: {
autoprefixer: {}
}
}

次に package.json に browserlist を追記すれば、 autoprefixer がインストールされ、プリフィクスが付与されます。 display: flex; なんかで試すと良くわかりますね。

"browserslist": [

"cover 99.5%"
]

なお、 postcss.config.js に browserlist を書くと、下記のようなエラーが表示されるようになりました。

Replace Autoprefixer browsers option to Browserslist config.

Use browserslist key in package.json or .browserslistrc file.


Babel を使う

やっぱりどこにでもある情報ですが、 package.json と同階層に .babelrc を作り、以下のようなコードを書けば babel-core と(上記の例では) babel-preset-env がインストールされるので楽ちんです。

{

"presets": [
[
"env", {
"targets": {
"browsers": [ "> 0.1%" ]
}
}
]
]
}

なお、 async await などを使って Uncaught ReferenceError: regeneratorRuntime is not defined というエラーが表示された場合は、 .babelrc にプラグインを追記すると良いようです。

{

"presets": [ ... ],
"plugins": [
[
"@babel/plugin-transform-runtime", {
"corejs": 2
}
]
]
}

あるいは Polyfill を import します。

import 'babel-polyfill'

他にも対応策はいくつかありますね。


テンプレートエンジンを使う


Pug (Jade)

お察しの通り、拡張子を .pug にすれば自動的に pug がインストールされ、使えるようになります。


Vue

こちらも npm install は不要です。

JavaScript で import Vue from 'vue' などと書けば vue と関連プラグインがインストールされます。

import Vue from 'vue'

import Footer from './footer.vue'

new Vue({
el: '#footer',
render: h => h(Footer)
})


Google Analytics を( Vue で)使う

テンプレートエンジンを使わない場合は直に書けば良いのですが、 Vue を使っている場合は vue-analytics を使うと便利です。これも Parcel 関係ありませんが…。

npm i --save vue-analytics で手動インストール後、任意の箇所に下記コードを記述するだけ。

import Vue from 'vue'

import VueAnalytics from 'vue-analytics'

Vue.use(VueAnalytics, { id: 'UA-XXXXXXXXXX-X' }) // 🔥 TODO: ここ変えて
Vue.$ga.page()

ページが複数ある場合は Vue.$ga.page({ page: window.location.pathname }) などとすると良いでしょう。

Vue Router も設定できます…が、 SPA で Parcel を使うのが適切かどうか怪しいので割愛します。


勝手にインストールしてくれるけど、勝手にアンインストールはしてくれない

あるパッケージが不要になった際は、忘れずに npm uninstall しましょう。

とは言え Babel や Vue のように、一回の import で 2 つも 3 つもパッケージをインストールされると管理しきれなくなるので、折を見て棚卸しをするべきかもしれませんね。


ファイルパスをスクリプト内で読み込む

JavaScript 内から画像ファイルなどを直接読み込む際は、 Parcel によって変換されたファイルパスを参照する必要があります。

しかしキャッシュバスター付きのファイルパスは予測できません。そこで import を使い、ファイルパスを取得します。

例えば、「 ./images フォルダ内の全ファイルを画像としてひとつずつ読み込む」コードは以下のようになるでしょう。

import 'babel-polyfill'

import images from './images/*.*'

const main = async () => {
console.time()
await loadAllImages()
console.timeEnd()
}

const loadAllImages = async () => {
for (let imageName in images) {
for (let extensionName in images[imageName]) {
const filePath = images[imageName][extensionName]
const imageEvent = await loadImage(filePath)
console.log(filePath)
}
}
}

const loadImage = filePath =>
new Promise((resolve, reject) => {
const image = new Image()
image.onload = resolve
image.onerror = reject
image.src = filePath
})

main()

もちろん import images from './images/*.png' というように拡張子を明示することも可能ですが、拡張子を明示した場合としない場合とで返されるオブジェクトの構造が異なることに注意してください。


おわりに

Parcel は便利なんですが、かゆいところに手が届かなくてやきもきしますね。ゼロコンフィグの宿命でしょうけど。

とはいえゼロコンフィグなバンドラという点ではメジャーな代替ツールもないようなので、期待はしています。