概要
Parcel で LP のような簡単なページを作る際に役立つかもしれない TIPS をまとめてみました。
私のサイトのトップページなどはまさに Parcel で作ったのですが、機能もへったくれもないので、そういうのを求めている方向けではありません。
$ npm i --save-dev parcel-bundler # `parcel` じゃないよ
小技
index.html を一階層上に移動する
.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"
}
http://localhost:1234/
が 404
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)
})
追記: Vue を使うなら Parcel より VueCLI を使った方が絶対良いです。
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 は便利なんですが、かゆいところに手が届かなくてやきもきしますね。ゼロコンフィグの宿命でしょうけど。
とはいえゼロコンフィグなバンドラという点ではメジャーな代替ツールもないようなので、期待はしています。