一. WebpackとLoader
Webpackは、すべての依存リソースを平等に扱い、異なる種類のリソース間の処理の差異をなくすことを目指しています:
Unlike most bundlers out there, the motivation behind Webpack is to gather all your dependencies (not just code, but other assets as well) and generate a dependency graph.
そして、その差異をなくす役割を担うのが Loader です。
Webpack: 何をインポートしようが、すべて JS モジュールに変換しろ。さもなくばエラーにするぞ。
Loaders: 了解。
設定構造
Entry
依存関係グラフの入り口。
entry: './src/index.js'
// または複数エントリの形式
entry: {
main: './src/index.js'
}
Output
出力成果物。
output: {
filename: 'main.js',
path: path.resolve('./build')
}
// 複数エントリの形式に対応
output: {
filename: '[name].js',
path: path.resolve('./build')
}
Loaders
依存関係プロセッサ。依存項目をインターセプトしてプリプロセスを行います。
例えばこのようなシナリオ:
// index.js file
import helpers from '/helpers/main.js';
// Hey Webpack! I will need these styles:
import 'main.css';
Webpackは CSS を認識できない(直接処理できない)ため、まずは Loader によって加工(プリプロセス)される必要があります。
一般的な Loader の設定:
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader' },
{ loader: 'less-loader' }
]
}
]
}
Loader の適用順序は less-loader, css-loader, style-loader です。
P.S. 設定ファイルで Loader とリソース型の関係を指定する以外に、リソースをインポートする際に loadername! プレフィックスを付けることもできます。例: import image from 'file-loader!./my-img.png'
Plugins
Loader では足りない、使いにくい、あるいは実現できない場合に、カスタムプラグインを使用して拡張します。
例えば extract-text-webpack-plugin は、スタイルルールが bundle に組み込まれる style-loader のデフォルト動作を変更し、CSS を収集して <link> タグで外部リソースとして参照させるために使用されます:
var ExtractTextPlugin = require('extract-text-webpack-plugin');
plugins: [
new ExtractTextPlugin('main.css')
]
html-webpack-plugin はエントリ HTML を生成するために使用されます:
var HTMLWebpackPlugin = require('html-webpack-plugin');
plugins: [
new HTMLWebpackPlugin()
]
内蔵の DefinePlugin は環境を区別するために使用されます:
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(ENV)
})
内蔵の UglifyJsPlugin はソースコードを圧縮するために使用されます:
new webpack.optimize.UglifyJsPlugin()
注意:似たような名前に uglifyjs-webpack-plugin がありますが、内蔵の UglifyJsPlugin とは細かなバージョンの違いがあります。詳細は Is webpack.optimize.UglifyJsPlugin() UglifyJsPlugin() ? を参照してください。
二. Loader
Loader は主に非 JS リソースの依存関係を処理するために使用されます:
webpack enables use of loaders to preprocess files. This allows you to bundle any static resource way beyond JavaScript.
Plugin との違い
Loader は特定の種類の依存関係の処理(解析、変換など)のみを担当し、Webpack が認識できないもの(各種非 JS 依存関係)を bundle 可能な JS に変換します。
Plugin はより強力で、Loader の出力をさらに加工したり、ビルドの前後に拡張を行ったりできます。
実体
A loader is a node module that exports a function. This function is called when a resource should be transformed by this loader. The given function will have access to the Loader API using the this context provided to it.
Loader は依存リソースを変換するための関数です。この関数は Loader API を通じて、ビルドプロセスのコンテキスト情報(ターゲットの元のリソース内容、前の Loader の出力、Loader の設定項目など)を取得したり、Webpack の内部メソッドを呼び出したり(例: this.resolve() による二次依存の処理)できます。
特徴
-
単一責任
-
組み合わせ可能
-
ステートレス
Loader が組み合わせ可能であることを目指しているため、単一責任(処理工程を柔軟に設定可能)かつステートレス(副作用による自由な組み合わせへの影響を避ける)であることが求められます。
三. 鎖状 Loader(Chaining loaders)
Webpack 1 では、Loader の適用順序を宣言する 3 つの方法をサポートしていました:
loaders: [
{ test: /\.scss$/, loader: 'style' },
{ test: /\.scss$/, loader: 'css' },
{
test: /\.scss$/, loader: 'autoprefixer',
query: { browsers: 'last 2 version' }
},
{
test: /\.scss$/, loader: 'sass',
query: { outputStyle: 'expanded' }
}
]
または
loaders: [
{
test: /\.scss$/,
loaders: [
'style',
'css',
'autoprefixer?browsers=last 2 version',
'sass?outputStyle=expanded',
]
}
]
または
loaders: [
{
test: /\.scss$/,
loader: 'style!css!autoprefixer?browsers=last 2 version!sass?outputStyle=expanded',
},
]
右から左へ適用されます(Grunt/Gulp のタスク定義順序とは逆)。関数の合成形式である style(css(file)) に相当し、パイプラインに似ています。例えば last!second!first は以下を意味します:
last Loader が最初に適用され、元のリソース内容を取得できます
second Loader は、直前に実行された Loader の実行結果を取得できます
first Loader が最後に適用され、JS モジュールとオプションの source map を返す必要があります
これは echo $resource_content | first | second | last に相当し、元のリソース内容を入力し、JS モジュール(CMD モジュールまたは ES モジュール)を出力します。その間に n 個の Loader を通過させることができます。
後のバージョンでは以下のように変更されました:
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1
}
},
{
loader: 'less-loader',
options: {
noIeCompat: true
}
}
]
ただし、互換性のために query 属性は残されており、 options のエイリアスとして存在しています。詳細は UseEntry を参照してください。
P.S. 鎖状 Loader に関する詳細は Chaining loaders を参照してください。
四. loader の例
例えば、 JSONC(JavaScript スタイルのコメント付き JSON)からコメントを削除し、JSON と同様にロードするシナリオを考えてみましょう:
// settings.json
{
// Format a file on save. A formatter must be available, the file must not be auto-saved, and editor must not be shutting down.
"editor.formatOnSave": false
}
デフォルトの JSON 依存関係処理はコメント付きをサポートしていません:
Module build failed: SyntaxError: Unexpected token / in JSON at position 0
この場合、既製品が見つからなければ、シンプルな Loader を自作することができます:
import { getOptions } from 'loader-utils';
import { parse as parseJSONC, stripComments } from 'jsonc-parser';
export default function parse(source) {
const opts = getOptions(this);
let json = stripComments(source);
if (opts.extRule) {
const filePath = this.resourcePath;
const matched = opts.extRule.test(filePath);
if (!matched) {
let errors = [];
json = parseJSONC(source, errors, {
disallowComments: true
});
if (errors.length) {
throw new Error(`Unexpected COMMENTS at ${filePath}`);
}
}
}
return `export default ${json}`;
}
Microsoft の jsonc-parser を使用して JSONC/JSON を解析し、さらに拡張子ルールのチェックを行うための設定項目 extRule をサポートしています:
{
test: /\.json$/,
use: {
loader: path.join(__dirname, './loaders/jsonc-loader.js'),
options: {
extRule: /.jsonc?$/i
}
}
}
P.S. 同期形式の Loader 以外に、コールバック注入形式の Asynchronous Loaders もあります。
P.S. その他の Loader API については The Loader Context を参照してください。
五. よく使われる loader
実際の利用シーンでは、多くの定番の組み合わせがあります。例えば:
-
style-loader!css-loader: アプリが依存する CSS を収集し、実行時に<style>タグでページに挿入します。 -
file-loader: マルチファイルを生成する方法(不思議なことに、これはメイン設定ではなく Loader で行われます)。 -
file-loader!html-loader: HTML をインポートし、テンプレートの置換などのプリプロセスを行ってから、出力ファイルを生成します。
公式では 7 種類の Loader が紹介されています:
ファイル
-
raw-loader: ファイル内容を直接取得します。
-
val-loader: JS コードをロードします。CMD モジュール形式である必要があります。
-
url-loader: file-loader と似ていますが、小さなファイルに対して Data URL を返すことをサポートしています。
-
file-loader: ファイルを
outputディレクトリにコピーし、相対 URL を返します。
JSON
-
json-loader: デフォルトで内蔵されており、JSON ファイルをロードします。
-
json5-loader: JSON 5 ファイル(ES5.1 JSON 構文)をロードしてトランスパイルします。
-
cson-loader: CSON ファイルをロードしてトランスパイルします。
トランスパイル
-
script-loader:
global環境で JS ファイルを実行します(scriptタグのように)。中のrequireは変換されません。 -
babel-loader: ES2015+ のコードをロードし、 Babel を使って ES5 にトランスパイルします。
-
buble-loader: ES2015+ のコードをロードし、 Bublé を使って ES5 に変換します。
-
traceur-loader: ES2015+ のコードをロードし、 Traceur を使って ES5 に変換します。
-
ts-loader または awesome-typescript-loader: TypeScript 2.0+ のコードをロードします。
-
coffee-loader: CoffeeScript のコードをロードします。
テンプレート
-
html-loader:
requireで引用された HTML 静的リソースを文字列としてエクスポートします。 -
pug-loader: Pug テンプレートをロードし、関数を返します。
-
jade-loader: Jade テンプレートをロードし、関数を返します。
-
markdown-loader: Markdown を HTML にコンパイルします。
-
react-markdown-loader:
markdown-parseパーサーを使って Markdown を React コンポーネントにコンパイルします。 -
posthtml-loader: HTML ファイルをロードし、 PostHTML を使って変換します。
-
handlebars-loader: Handlebars を HTML にコンパイルします。
-
markup-inline-loader: SVG/MathML ファイルの内容を HTML に埋め込みます。アイコンフォントや SVG に CSS アニメーションを適用する際に便利です。
スタイル
-
style-loader: モジュールの出力を style(タグ)として DOM に挿入します。
-
css-loader: CSS ファイルをロードして CSS を返します。imports をサポートしています。
-
less-loader: LESS ファイルをロードしてコンパイルします。
-
sass-loader: SASS/SCSS ファイルをロードしてコンパイルします。
-
postcss-loader: CSS/SSS ファイルをロードし、 PostCSS を使って変換します。
-
stylus-loader: Stylus ファイルをロードしてコンパイルします。
Lint チェックおよびテスト
-
mocha-loader: mocha を使ってブラウザ/NodeJS 環境でテストを行います。
-
eslint-loader: プリローダー。 ESLint を使って Lint チェックを行います。
-
jshint-loader: プリローダー。 JSHint を使って Lint チェックを行います。
-
jscs-loader: プリローダー。 JSCS を使ってコードスタイルチェックを行います。
-
coverjs-loader: プリローダー。 CoverJS を使ってテストカバレッジを確認します。
フレームワーク
-
vue-loader: Vue コンポーネントをロードしてコンパイルします。
-
polymer-loader: 設定可能なプリプロセッサを使って HTML と CSS を処理し、一般的なモジュールのように Web Components を
require()できるようにします。 -
angular2-template-loader: Angular コンポーネントをロードしてコンパイルします。
P.S. その他のサードパーティ Loader については awesome-webpack を参照してください。
コメントはまだありません