一. 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 Module,不然就胡鬧(報錯)
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 進一步加工 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 拿到 bundle 過程中的一些上下文資訊(比如目標原始資源內容或前一個 loader 的輸出、loader 配置項等),以及調用 Webpack 內部方法(例如透過 this.resolve() 處理二級依賴)
特點
-
職責單一
-
可組合
-
無狀態
因為希望 Loader 可組合,所以要求其職責單一(操作工序可靈活配置),且無狀態(避免副作用影響隨意組合)
三. 鏈式 Loader(Chaining loaders)
Webpack 1 支持透過 3 種方式聲明 Loader 的應用順序:
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 的 task 定義順序相反),相當於函數組合形式的 style(css(file)),與管道類似,例如 last!second!first 表示:
last Loader最先应用,能够拿到原始资源内容
second Loader能够拿到前一个执行的Loader的返回结果
first Loader最后应用,要求返回一个JS Module和可选的source map
相當於 echo $resource_content | first | second | last,輸入原始資源內容,輸出 JS Module(CMD 模塊或 ES 模塊),中間可以流經 n 個 Loader
在後來版本中換成了:
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1
}
},
{
loader: 'less-loader',
options: {
noIeCompat: true
}
}
]
但出於相容性考慮,query 屬性還保留著,並且作為 options 的別名存在,具體見 UseEntry
P.S. 關於鏈式 loaders 的更多資訊,請查看 Chaining loaders
四. loader 示例
比如,有個場景是要去掉 JSONC(JSON with JavaScript style comments)中的註釋,像加載 JSON 一樣加載 JSONC,例如:
// 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}`;
}
借助 MS 的 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:收集 App 依賴的 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:加載並用 PostHTML 轉換 HTML 文件
-
handlebars-loader:把 Handlebars 編譯成 HTML
-
markup-inline-loader:把 SVG/MathML 文件內容塞進 HTML,用 icon font 或給 SVG 應用 CSS 動畫時很有用
樣式
-
style-loader:把模塊輸出作為 style(標籤)插入 DOM
-
css-loader:加載 CSS 文件返回 CSS,支持 imports
-
less-loader:加載並編譯 LESS 文件
-
sass-loader:加載並編譯 SASS/SCSS 文件
-
postcss-loader:加載並用 PostCSS 轉換 CSS/SSS 文件
-
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,支持像引入一般模塊一樣
require()Web Components -
angular2-template-loader:加載並編譯 Angular 元件
P.S. 更多第三方 loader,見 awesome-webpack
暫無評論,快來發表你的看法吧