寫在前面:css
文章翻譯自petehunt大神的petehunt/webpack-howto,做爲學習webpack的開始。fork了一份,翻譯後的在這裏https://github.com/zjzhome/webpack-howto/blob/master/README-cn.md,有些地方翻譯的很差,也但願在閱讀後對於很差的地方對出評論或者在github上提個issue。html
本文講述如何使用webpack將事情作好,包括了咱們在Instagram作的大多數事情,咱們沒作的固然沒有:)webpack
個人建議:以此文做爲學習webpack的開始,而後再閱讀官方的文檔說明。git
和browserify相似,但能夠將你的app劃分爲不一樣的文件。 若是你的單頁應用你有多個頁面,用戶只需下載當前頁的代碼,若是他們離開此頁瀏覽其餘頁面,不用再次下載那些通用的代碼。github
一般狀況下能夠替代grunt和gulp,由於webpack能夠構建和打包CSS,預處理CSS、能夠編譯爲JS的語言和圖片等等。web
webpack支持AMD和CommonJS以及其餘的模塊系統(Angular、ES6)。若是你不知道用哪一種,使用CommonJS。npm
下面的兩個命令是等價的:json
browserify main.js > bundle.js
webpack main.js bundle.js
不過,webpack比browserify更加給力,通常狀況下,咱們會建立webpack.config.js
來保證條理性。gulp
// webpack.config.js module.exports = { entry: './main.js', output: { filename: 'bundle.js' } };
這徹底是咱們熟悉的JS,因此隨意將揮灑你的代碼。bootstrap
切換到包含webpack.config.js
的目錄, 運行:
webpack中和browserify轉換器以及RequireJS中的插件等價的是loader
。下面展現瞭如何讓webpack支持加載CoffeeScript和Facebook JSX+ES6(你須要首先 npm install babel-loader coffee-loader )):
// webpack.config.js module.exports = { entry: './main.js', output: { filename: 'bundle.js' }, module: { loaders: [ { test: /\.coffee$/, loader: 'coffee-loader' }, { test: /\.js$/, loader: 'babel-loader' } ] } };
爲了在加載文件的時候沒必要生命文件擴展名,你必須加上 resolve.extensions 這個參數來告訴webpack該尋找哪些文件.
// webpack.config.js module.exports = { entry: './main.js', output: { filename: 'bundle.js' }, module: { loaders: [ { test: /\.coffee$/, loader: 'coffee-loader' }, { test: /\.js$/, loader: 'babel-loader' } ] }, resolve: { // you can now require('file') instead of require('file.coffee') extensions: ['', '.js', '.json', '.coffee'] } };
首先,在你的代碼中使用require()
來引入你的靜態資源。
require('./bootstrap.css'); require('./myapp.less'); var img = document.createElement('img'); img.src = require('./glyph.png');
當你引入CSS(或者less等),webpack會將CSS做爲字符串打包到JS中,而後 require() 會在頁面中插入 <style> 標籤。當引入圖片資源的時候,webpack會將圖片地址打包進JS, require() 返回圖片地址。
可是你要告訴webpack怎麼作:
// webpack.config.js module.exports = { entry: './main.js', output: { path: './build', // This is where images AND js will go publicPath: 'http://mycdn.com/', // This is used to generate URLs to e.g. images filename: 'bundle.js' }, module: { loaders: [ { test: /\.less$/, loader: 'style-loader!css-loader!less-loader' }, // use ! to chain loaders { test: /\.css$/, loader: 'style-loader!css-loader' }, {test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'} // inline base64 URLs for <=8k images, direct URLs for the rest ] } };
有些代碼咱們只想用在開發環境(好比日誌)或者內部的服務器(好比處於內測的未發佈產品特性)。在你的代碼裏,使用如下幾個變量:
if (__DEV__) { console.warn('Extra logging'); } // ... if (__PRERELEASE__) { showSecretFeature(); }
而後告訴webpack這些全局變量:
// webpack.config.js // definePlugin takes raw strings and inserts them, so you can put strings of JS if you want. var definePlugin = new webpack.DefinePlugin({ __DEV__: JSON.stringify(JSON.parse(process.env.BUILD_DEV || 'true')), __PRERELEASE__: JSON.stringify(JSON.parse(process.env.BUILD_PRERELEASE || 'false')) }); module.exports = { entry: './main.js', output: { filename: 'bundle.js' }, plugins: [definePlugin] };
而後你能夠在命令行裏使用BUILD_DEV=1 BUILD_PRERELEASE=1 webpack
命令構建,不過注意由於webpack -p
命令會去清除無用的代碼,全部抱在這些代碼塊裏的代碼都會被刪除,因此不要泄露私密特性或字符串。
假設你有一個用戶資料頁面和一個訂閱頁面,用戶在瀏覽資料頁面的你不想用戶下載訂閱頁面的代碼。因此要打多個包,爲每個頁面建立一個「主要模塊」(也叫做入口):
// webpack.config.js module.exports = { entry: { Profile: './profile.js', Feed: './feed.js' }, output: { path: 'build', filename: '[name].js' // Template based on keys in entry above } };
對於資料頁,在頁面插入 <script src="build/Profile.js"></script> ,訂閱頁面也是同樣。
訂閱頁面和資料頁面共用許多代碼(像React以及其餘的樣式和組件等)。webpack能夠抽出他們共用的代碼,而後打一個通用包在各個頁面緩存。
// webpack.config.js var webpack = require('webpack'); var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js'); module.exports = { entry: { Profile: './profile.js', Feed: './feed.js' }, output: { path: 'build', filename: '[name].js' // Template based on keys in entry above }, plugins: [commonsPlugin] };
在上一步的script標籤以前加上 <script src="build/common.js"></script> ,而後盡情享受免費的高速緩存吧。
CommonJS是同步的,可是Webpack提供一個方法來異步的指定依賴。這在客戶端路由中頗有用,每一個頁面都須要路由,你不想直到你真正須要他們的時候才下載相關功能。
指定你想異步加載的分割點,好比:
if (window.location.pathname === '/feed') { showLoadingState(); require.ensure([], function() { // this syntax is weird but it works hideLoadingState(); require('./feed').show(); // when this function is called, the module is guaranteed to be synchronously available. }); } else if (window.location.pathname === '/profile') { showLoadingState(); require.ensure([], function() { hideLoadingState(); require('./profile').show(); }); }
webpack會作接下來的工做,而且產生額外的塊文件,而後加載他們。
當你加載這些文件到html的script標籤,webpack假定他們在根目錄,你能夠經過 output.publicPath 來定義:
// webpack.config.js output: { path: "/home/proj/public/assets", //path to where webpack will build your stuff publicPath: "/assets/" //path that will be considered when requiring your files }
看一下真實的案例:一個成功的團隊是如何使用webpack的。這是Pete Hunt在全球開源大會上關於Intagram使用的webpack的討論:http://youtu.be/VkTCL6Nqm6Y。
webpack是嚴格模塊化的。和browserify和requirejs等替代選擇工具相比,webpack的偉大之處在於在構建過程當中他讓插件將本身注入到儘量多的地方。webpack不少看似內建的功能只是默認加載的插件,他們是能夠重寫的(好比CommonJS的require()語法分析器)。