大概幾個月前,剛接觸 gulp 的時候,經過 gulp 對前端工做流進行優化,在 gulpfile.js 文件中寫插件,編譯 less、stylus,壓縮 css、js 等等,感受工做效率獲得極大的提高,本來手動的東西,如今都是自動化了。javascript
可是,如今,學習 react 的時候,我又不得不來學習 webpack。webpack 最近火得不行(其實已經火了好久了),當下最熱門的前端資源模塊化管理和打包工具,它能把各類資源,包括 jxs、coffeeJS、less/sass,甚至圖片,看成模塊來加載和使用。一樣它須要一個 webpack.config.js 的配置文件,有專門針對於 css、js 和圖片等插件,在 js 中直接經過 require 來使用模塊,很方便。css
webpack 能夠將文件模塊按照依賴打包成方便使用的前端資源,還能夠將按需加載的模塊進行異步加載。html
ps:gulp/grunt 是前端構建工具,是自動化工具,能夠簡化前端流程;而 webpack 是前端模塊化方案。雖然兩者在某些功能上有共同點(好比壓縮代碼),但本質是不同的,好比如今還有基於 gulp 的 webpack 插件gulp-webpack。前端
前端模塊化是大勢所趨,隨着 webapp 的興起,瀏覽器的功能愈來愈強大,而一個單頁面在使用的過程當中會加載更多的 JavaScript 代碼,這給前端開發的流程和運行帶來了很大的挑戰。java
時下,已經有很多前端模塊化系統:node
script 標籤,這是最傳統的文件模塊,一個文件是一個模塊,react
<script src="./main.js"></script> <script src="./module.js"></script>
這些接口會暴露在全局做用下,弊端不少:webpack
全局做用域形成變量衝突git
文件只能按照 script 的順序進行加載程序員
開發人員必須主觀解決代碼庫和模塊的依賴關係
在大型的項目中,會形成資源文件難以管理,代碼庫混亂不堪
CommonJS
NodeJS 遵循的就是 CommonJS 規範,這種經過 require 和 module.exports 的方式很常見:
//main.js var module = require('./module.js') // dosomething module.exports = somevalue;
優勢是服務器端模塊便於重用,簡單易用,且 NPM 上有幾十萬個可使用的模塊包,缺點是這種加載方式屬於同步加載,不適用於瀏覽器,且不能非阻塞的加載多個模塊。關於 CommonJS 循環加載的原理,能夠看看這篇文章中的介紹 ES6模塊加載的實質 CommonJS 部分。
AMD CMD
AMD 是適合在瀏覽器中的異步加載的模塊,
define("module", ["dep1", "dep2"], function(d1, d2) { return someExportedValue; }); require(["module", "../file"], function(module, file) { /* ... */ });
CMD 規範和 AMD 規範很像,CommonJS 規範保持了很大的兼容。
還有就是 ES6 中的模塊加載系統,詳情請移步ES6模塊加載。
在上面的分析中,提到的都只是對 JavaScript 文件等加載,然而在前端的開發中還須要對圖片,樣式,字體文件和 html 模版等樣式的加載,webpack 可讓這一切成爲可能:
require("./style.css"); require("./style.less"); require("./template.jade"); require("./image.png");
在加載的過程當中,還能經過對靜態文件的分析,好比 css,把它內聯到 html 的 style 樣式中。
webpack 是集大成者,支持 CommonJS,AMD/CMD,還能對圖片,樣式等進行模塊化。
好比直接使用 CommonJS 語法:
var m1 = require('module1'); var m2 = require('module2'); //dosomething module.exports = function(){ m1(); m2(); }
經過 npm 全局安裝 webpack,npm install webpack -g
,也能夠在本地項目中安裝依賴 npm install webpack --save-dev
,前提要確保 npm init。
目錄下面有一個靜態頁面 index.html 和 JS 入口文件 entry.js,
<!-- index.html --> <html> <head> <meta charset="utf-8"> </head> <body> <script src="bundle.js"></script> </body> </html> // entry.js document.write('hello webpack');
而後使用命令 webpack entry.js bundle.js
就能夠生成 bundle.js 文件。
如今添加 module.js 文件,在 ertry.js 中引用:
//module.js document.write('hello module'); //ertry.js document.write(require('module'));
webpack 會分析每一個文件的入口,把依賴的相關文件都打包到 bundle.js。webpack entry.js bundle.js
這句話執行後,會先執行 entry.js,其餘文件,則只有在 require 的時候纔會加載。
還能夠經過插件來加載樣式模塊。目錄下添加一個 style.css 文件,安裝 css 和 style 模塊 npm install css-loader style-loader
:
/* style.css */ body { background: yellow; } // entry.js require("!style!css!./style.css"); document.write('hello webpack');
這個時候打包,刷新頁面,就能夠看到 index.html 中內聯的 css 樣式。
前面介紹的這種是命令行打包的方式,比較麻煩,通常都是寫一個 webpack 的配置文件,上面的配置文件能夠以下:
//webpack.config.js var webpack = require('webpack') module.exports = { entry: './entry.js', output: { path: __dirname, filename: 'bundle.js' }, module: { loaders: [ {test: /\.css$/, loader: 'style!css'} ] } }
module.loader 中 loader 可能有人會有疑惑,這種用感嘆號把模塊放開表示 css 文件加載多個模塊,加載的順序從右到左,先加載 css 模塊,再加載 style 模塊。在 entry.js 文件中就能夠簡化的寫成 require('./style.css')
。
配置文件的幾個比較重要的參數以下:
entry: 編譯過程的輸入
output: 編譯過程的輸出
module: 模塊module的處理方式
plugin: 配置文件的插件入口
resolve 配置文件其餘解決方案
output 表明輸出,path 表明路徑,filename 表明文件名。plugin 表示插件,有內置插件和擴展插件,
var webpack = require("webpack"); var ComponentPlugin = require("component-webpack-plugin"); module.exports = { plugin: [ //內置壓縮插件 new webpack.optimize.UglifyJsPlugin({ compressor: { warnings: false, }, }), // 擴展插件 ComponentPlugin() ] };
更多插件能夠去官網查看。
module 中最須要注意的就是 loaders,
test:正則表達式,用於匹配文件名(必須)
loader:須要加載的 loaders 列表,可用 ! 加載多個(必須)
include/exclude:手動添加必須處理的文件(文件夾)或屏蔽不須要處理的文件(文件夾)(可選);
query:爲loaders提供額外的設置選項(可選)
resolve 是配置的其餘解決方案,好比 resolve.alias 能夠定義模塊的別名,resolve.root 能夠定義絕對路徑。resolve.extensions 能夠省去加載文件的後綴名,即後綴名自動補全。可是必需要在前面加一個空的字符串,不然會致使沒法加載的狀況。
webpack-dev-server 是一個專門爲 webpack 服務的 nodejs 服務器,經過 npm install --save-dev webpack-dev-server
命令來安裝。
// webpack.config.js devServer: { contentBase: "./", //本地服務器所加載的頁面所在的目錄 colors: true, //終端中輸出結果爲彩色 historyApiFallback: true, //不跳轉 inline: true //實時刷新 }
Babel 是一個編譯 JavaScript 的平臺,它的功能很是強大,可用編譯 JSX,ES6,ES7,生成瀏覽器識別的 JavaScript 語言。須要安裝多個依賴:npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react
。
//webpack.config.js module: { loaders: [ { test: /\.js$/, exclude: /node_modules/, loader: 'babel', query: { presets: ['es2015','react'] } } ] },
webpack 提供熱更新,官網關於熱更新的介紹:hot-module-replacement-with-webpack,webpack-dev-server。
webpack-dev-server 自己就帶有熱更新功能,只須要在參數啓動參數重添加 webpack-dev-server --inline --hot
,若是嫌每次添加麻煩,可用在 package.json 中 script 設置 start 或在 webpack.config.js 中開啓。
這個插件主要是針對於 html 的,能夠自動生成 html 文件,尤爲當使用了 hash 以後,不用困擾因 hash 的變化帶來的問題。
var webpack = require('webpack') var htmlwebpackplugin = require('html-webpack-plugin') module.exports = { entry: './main.js', output: { path: __dirname, filename: 'bundle.js' }, plugins: [new htmlwebpackplugin()] }
有時候會由於 html 中必需要有一些特別的東西,不能直接生成,此時就須要配置模版:
module.exports = { plugins:[ new htmlwebpackplugin({ filename: 'hello.html', // 生成的文件 template: 'src/template.html' // 模版文件 }) ] }
更多配置信息,參考:
title: 頁面 title
filename: 輸出的 HTML 文件名,默認是 index.html
template: 模板文件路徑,支持加載器,好比 html!./index.html
inject: true | 'head' | 'body' | false ,注入全部的資源到特定的 template 或者 templateContent 中,若是設置爲 true 或者 body,全部的 javascript 資源將被放置到 body 元素的底部,'head' 將放置到 head 元素中。
favicon: 添加特定的 favicon 路徑到輸出的 HTML 文件中。
minify: {} | false , 傳遞 html-minifier 選項給 minify 輸出
hash: true | false, 若是爲 true, 將添加一個惟一的 webpack 編譯 hash 到全部包含的腳本和 CSS 文件,對於解除 cache 頗有用。
cache: true | false,若是爲 true, 這是默認值,僅僅在文件修改以後纔會發佈文件。
showErrors: true | false, 若是爲 true, 這是默認值,錯誤信息會寫入到 HTML 頁面中
chunks: 容許只添加某些塊 (好比,僅僅 unit test 塊)
chunksSortMode: 容許控制塊在添加到頁面以前的排序方式,支持的值:'none' | 'default' | {function}-default:'auto'
excludeChunks: 容許跳過某些塊,(好比,跳過單元測試的塊)
若是你感興趣,還能夠去看一看如何本身手動寫 webpack 插件。連接1連接2
有時候,一些函數只需在 dev 環境下運行,有些函數要在 product 環境下運行,經過設置 webpack 的 DefinePlugin 就能夠很輕鬆的幫助咱們實現,好比:
var webpack = require('webpack'); var devFlagPlugin = new webpack.DefinePlugin({ // __DEV__ 默認是 false,除非手動設置開發環境 __DEV__: JSON.stringify(JSON.parse(process.env.DEBUG || 'false')) }); module.exports = { entry: './main.js', output: { filename: 'bundle.js' }, plugins: [devFlagPlugin] };
// main.js document.write('<h1>Hello World</h1>'); if (__DEV__) { //若是開發環境 document.write(new Date()); }
此時有兩種運行方式,進入開發模式的運行方式:
# Linux & Mac $ env DEBUG=true webpack-dev-server # Windows $ set DEBUG=true $ webpack-dev-server
在接觸 webpack 以前,強烈建議先學習 ES6。其實,不少人都說前端變化太快,昨天還很火熱的框架,可能今天就被另外一個所取代。我以爲,正是這種快速的更新,讓那些喜歡學習,喜歡鑽研的程序員,得到了新生,新的活力。共勉!
webpack 中文官網
阮一峯 webpack-demos
webpack編譯流程漫談
webpack學習之路
入門Webpack,看這篇就夠了
一小時包教會 —— webpack 入門指南
歡迎來個人博客討論。