配合上一篇文章作的翻譯: https://github.com/petehunt/webpack-howtocss
這是 Webpack 怎麼作事情的烹飪書. 其中包含了咱們在 Instagram 作的大部分功能, 並且都是在用的功能.webpack
個人建議是: 把這個當成是 Webpack 的文檔開始學習, 而後看官方文檔的具體說明.git
他像 Browserify, 可是將你的應用打包爲多個文件. 若是你的單頁面應用有多個頁面, 那麼用戶只從下載對應頁面的代碼. 當他麼訪問到另外一個頁面, 他們不須要從新下載通用的代碼.github
他在不少地方能替代 Grunt 跟 Gulp 由於他可以編譯打包 CSS, 作 CSS 預處理, 編譯 JS 方言, 打包圖片, 還有其餘一些.web
它支持 AMD 跟 CommonJS, 以及其餘一些模塊系統, (Angular, ES6). 若是你不知道用什麼, 就用 CommonJS.npm
對應地:json
jsbrowserify main.js > bundle.js
jswebpack main.js bundle.js
Webpack 比 Browserify 更強大, 你通常會用 webpack.config.js
來組織各個過程:bootstrap
js// webpack.config.js module.exports = { entry: './main.js', output: { filename: 'bundle.js' } };
這僅僅是 JavaScript, 能夠隨意添加要運行的代碼.緩存
切換到有 webpack.config.js
的目錄而後運行:服務器
webpack
來執行一次開發的編譯webpack -p
for building once for production (minification)webpack -p
來針對發佈環境編譯(壓縮代碼)webpack --watch
來進行開發過程持續的增量編譯(飛快地!)webpack -d
來生成 SourceMapsWebpack 對應 Browsserify transform 和 RequireJS 插件的工具稱爲 loader
. 下邊是 Webpack 加載 CoffeeScript 和 Facebook JSX-ES6 的配置(你須要 npm install jsx-loader coffee-loader
):
js// webpack.config.js module.exports = { entry: './main.js', output: { filename: 'bundle.js' }, module: { loaders: [ { test: /\.coffee$/, loader: 'coffee-loader' }, { test: /\.js$/, loader: 'jsx-loader?harmony' } // loaders 能夠接受 querystring 格式的參數 ] } };
要開啓後綴名的自動補全, 你須要設置 resolve.extensions
參數指明那些文件 Webpack 是要搜索的:
js// webpack.config.js module.exports = { entry: './main.js', output: { filename: 'bundle.js' }, module: { loaders: [ { test: /\.coffee$/, loader: 'coffee-loader' }, { test: /\.js$/, loader: 'jsx-loader?harmony' } ] }, resolve: { // 如今能夠寫 require('file') 代替 require('file.coffee') extensions: ['', '.js', '.json', '.coffee'] } };
首先更新你的代碼用 require()
加載靜態資源(就像在 Node 裏使用 require()
):
jsrequire('./bootstrap.css'); require('./myapp.less'); var img = document.createElement('img'); img.src = require('./glyph.png');
當你引用 CSS(或者 LESS 吧), Webpack 會將 CSS 內聯到 JavaScript 包當中, require()
會在頁面當中插入一個 `<style>標籤. 當你引入圖片, Webpack 在包當中插入對應圖片的 URL, 這個 URL 是由
require()` 返回的.
你須要配置 Webpack(添加 loader):
js// webpack.config.js module.exports = { entry: './main.js', output: { path: './build', // 圖片和 JS 會到這裏來 publicPath: 'http://mycdn.com/', // 這個用來成成好比圖片的 URL filename: 'bundle.js' }, module: { loaders: [ { test: /\.less$/, loader: 'style-loader!css-loader!less-loader' }, // 用 ! 來鏈接多我的 loader { test: /\.css$/, loader: 'style-loader!css-loader' }, {test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'} // 內聯 base64 URLs, 限定 <=8k 的圖片, 其餘的用 URL ] } };
有些代碼咱們只想在開發環境使用(好比 log), 或者 dogfooging 的服務器裏邊(好比內部員工正在測試的功能). 在你的代碼中, 引用全局變量吧:
jsif (__DEV__) { console.warn('Extra logging'); } // ... if (__PRERELEASE__) { showSecretFeature(); }
而後配置 Webpack 當中的對應全局變量:
js// webpack.config.js // definePlugin 接收字符串插入到代碼當中, 因此你須要的話能夠寫上 JS 的字符串 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
會執行 uglify dead-code elimination, 任何這種代碼都會被剔除, 因此你不用擔憂祕密功能泄漏.
好比你用 profile 頁面跟 feed 頁面. 當用戶訪問 profile, 你不想讓他們下載 feed 頁面的代碼. 所以你建立多個包: 每一個頁面一個 "main module":
js// webpack.config.js module.exports = { entry: { Profile: './profile.js', Feed: './feed.js' }, output: { path: 'build', filename: '[name].js' // 模版基於上邊 entry 的 key } };
針對 profile, 在頁面當中插入 <script src="build/Profile.js"></script>
. feed 頁面也是同樣.
feed 頁面跟 profile 頁面共用不要代碼(好比 React 還有通用的樣式和 component). Webpack 能夠分析出來他們有多少共用模塊, 而後生成一個共享的包用於代碼的緩存.
js// 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' }, plugins: [commonsPlugin] };
在上一個步驟的 script 標籤前面加上 <script src="build/common.js"></script>
你就能獲得廉價的緩存了.
CommonJS 是同步的, 可是 Webpack 提供了異步指定依賴的方案. 這對於客戶端的路由頗有用, 你想要在每一個頁面都有路由, 但你又不像在真的用到功能以前就下載某個功能的代碼.
聲明你想要異步加載的那個"分界點". 好比:
jsif (window.location.pathname === '/feed') { showLoadingState(); require.ensure([], function() { // 語法奇葩, 可是有用 hideLoadingState(); require('./feed').show(); // 函數調用後, 模塊保證在同步請求下可用 }); } else if (window.location.pathname === '/profile') { showLoadingState(); require.ensure([], function() { hideLoadingState(); require('./profile').show(); }); }
Webpack 會完成其他的工做, 生成額外的 chunk 文件幫你加載好.
Webpack 在 HTML script 標籤中加載他們時會假設這些文件是怎你的根路徑下. 你能夠用 output.publicPath
來配置.
js// webpack.config.js output: { path: "/home/proj/public/assets", // path 指向 Webpack 編譯能的資源位置 publicPath: "/assets/" // 引用你的文件時考慮使用的地址 }
看一看真實環境當中一個成功的團隊怎麼使用 Webpack: http://www.tudou.com/programs/view/6KB6lNbVzhs/ .
這是 Peter Hunt 在 OSCon 大會關於 Instagram 如何使用 Webpack 的演講.
Webpack 是很是地模塊化. Webpack 的優秀之處在於相比 Browserify 跟 RequireJS 之類方案, 能讓插件將自身集成到編譯過程中的更多地方. 不少看起來像是編寫在覈心代碼的功能, 其實就是默認加載的插件, 他們能夠被覆蓋.(好比, CommonJS 的 require()
parser)