做者介紹:趙鵬,美團點評點餐團隊成員javascript
webpack是一個模塊打包器(module bundler),提供了一個核心,核心提供了不少開箱即用的功能,同時它能夠用loader和plugin來擴展。webpack自己結構精巧,基於tapable的插件架構,擴展性強,衆多的loader或者plugin讓webpack顯得很複雜。
webpack經常使用配置包括:devtool、entry、 output、module、resolve、plugins、externals等,本文主要介紹下webpack經常使用的loader和plugincss
webpack容許咱們使用loader來處理文件,loader是一個導出爲function的node模塊。能夠將匹配到的文件進行一次轉換,同時loader能夠鏈式傳遞。html
通常loader的使用方式分爲三種:
1:在配置文件webpack.config.js中配置vue
module.exports = {
module: {
rules: [
{
test: /\.txt$/,
use: 'raw-loader'
}
]
}
}複製代碼
2:經過命令行參數方式java
webpack --module-bind 'txt=raw-loader'複製代碼
3:經過內聯使用node
import txt from 'raw-loader!./file.txt';複製代碼
好比下面配置,能夠匹配.scss的文件,分別通過sass-loader、css-loader、style-loader的處理。sass-loader
轉化sass爲css文件,而且包一層module.exports成爲一個js module。style-loader
將建立一個style標籤將css文件嵌入到html中。css-loader
則處理其中的@import和url()。jquery
module.exports = {
module: {
rules: [
{
test: /\.scss$/,
use:[
{loader:'style-loader'},
{loader:'css-loader',options:{sourceMap:true,modules:true}},
{loader:'sass-loader',options:{sourceMap:true}}
],
exclude:/node_modules/
}
]
}
}複製代碼
vue-loader、coffee-loader、babel-loader
等能夠將特定文件格式轉成js模塊、將其餘語言轉化爲js語言和編譯下一代js語言file-loader、url-loader
等能夠處理資源,file-loader能夠複製和放置資源位置,並能夠指定文件名模板,用hash命名更好利用緩存。url-loader
能夠將小於配置limit大小的文件轉換成內斂Data Url的方式,減小請求。raw-loader
能夠將文件已字符串的形式返回imports-loader、exports-loader
等能夠向模塊注入變量或者提供導出模塊功能,常見場景是:expose-loader
:暴露對象爲全局變量module.exports = function(content) {
this.cacheable && this.cacheable();
this.value = content;
return "module.exports = " + JSON.stringify(content);
}複製代碼
webpack的plugin比loader強大,經過鉤子能夠涉及整個構建流程,能夠作一些在構建範圍內的事情。webpack
UglifyJsPlugin
,壓縮和混淆代碼。CommonsChunkPlugin
,提升打包效率,將第三方庫和業務代碼分開打包。ProvidePlugin
:自動加載模塊,代替require和importnew webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
})複製代碼
html-webpack-plugin
能夠根據模板自動生成html代碼,並自動引用css和js文件extract-text-webpack-plugin
將js文件中引用的樣式單獨抽離成css文件DefinePlugin
編譯時配置全局變量,這對開發模式和發佈模式的構建容許不一樣的行爲很是有用。new webpack.DefinePlugin({
PRODUCTION: JSON.stringify(true),
VERSION: JSON.stringify("5fa3b9"),
BROWSER_SUPPORTS_HTML5: true,
TWO: "1+1",
"typeof window": JSON.stringify("object")
})複製代碼
HotModuleReplacementPlugin
熱更新git
webpack 內置的DllPlugin
和DllReferencePlugin
相互配合,前置第三方包的構建,只構建業務代碼,同時能解決Externals屢次引用問題。DllReferencePlugin引用DllPlugin配置生成的manifest.json文件,manifest.json包含了依賴模塊和module id的映射關係github
babili-webpack-plugin、transform-runtime 、transform-object-rest-spread
optimize-css-assets-webpack-plugin
不一樣組件中重複的css能夠快速去重
webpack-bundle-analyzer
一個webpack的bundle文件分析工具,將bundle文件以可交互縮放的treemap的形式展現。compression-webpack-plugin
生產環境可採用gzip壓縮JS和CSShappypack
:經過多進程模型,來加速代碼構建
const os = require('os');
let HappyPack = require('happypack');
let happyThreadPool = HappyPack.ThreadPool({size: os.cpus().length});
exports.plugins = [
new HappyPack({
id: 'jsx',
threadPool: happyThreadPool,
loaders: [ 'babel-loader' ]
}),
new HappyPack({
id: 'coffeescripts',
threadPool: happyThreadPool,
loaders: [ 'coffee-loader' ]
})
];
exports.module.loaders = [
{
test: /\.js$/,
loaders: [ 'happypack/loader?id=jsx' ]
},
{
test: /\.coffee$/,
loaders: [ 'happypack/loader?id=coffeescripts' ]
},
]複製代碼
好比咱們能夠在構建生成文件時,將全部生成的文件名生成到filelist.md的文件中
webpack會將compilation.assets的內容生成文件,因此能夠在構建中利用它生成咱們想要的文件。
function FileListPlugin(options) {}
FileListPlugin.prototype.apply = function(compiler) {
compiler.plugin('emit', function(compilation, callback) {
var filelist = 'In this build:\n\n';
for (var filename in compilation.assets) {
filelist += ('- '+ filename +'\n');
}
compilation.assets['filelist.md'] = {
source: function() {
return filelist;
},
size: function() {
return filelist.length;
}
};
callback();
});
};
module.exports = FileListPlugin;複製代碼
好比咱們能夠在html-webpack-plugin生成文件後刷新頁面,完成熱更新效果。
var webpack = require('webpack')
var webpackConfig = require('./webpack.config')
var compiler = webpack(webpackConfig)
var hotMiddleware = require('webpack-hot-middleware')(compiler, {
log: () => {}
})
compiler.plugin('compilation', function (compilation) {
compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
hotMiddleware.publish({ action: 'reload' })
cb()
})
})複製代碼
好比咱們能夠在構建完成後,打開一個提示窗口。
class Notifier {
apply(compiler) {
compiler.plugin("done", (stats) => {
const pkg = require("./package.json");
const notifier = require("node-notifier");
const time = ((stats.endTime - stats.startTime) / 1000).toFixed(2);
notifier.notify({
title: pkg.name,
message: `WebPack is done!\n${stats.compilation.errors.length} errors in ${time}s`,
contentImage: "https://path/to/your/logo.png",
});
});
}
}
module.exports = Notifier;複製代碼
Tapable.prototype.plugin = function plugin(name, fn) {
if(Array.isArray(name)) {
name.forEach(function(name) {
this.plugin(name, fn);
}, this);
return;
}
if(!this._plugins[name]) this._plugins[name] = [fn];
else this._plugins[name].push(fn);
};複製代碼
plugin方法將插件對應的方法加入一個數組中、註冊到事件(name)上,等待apply的時候串行調用/觸發
Compilation中作了不少事情,處理編譯過程。所對應的方法,如addEntry ,buildModule,processModuleDependencies,createChunkAssets,seal等
webpack的設計思想、插件機制值得咱們深刻學習和交流,還有一些特性,好比tree shaking,scope hoisting……
參考文章: