Webpack 怎麼用

配合上一篇文章作的翻譯: https://github.com/petehunt/webpack-howtocss


這份教程的目的

這是 Webpack 怎麼作事情的烹飪書. 其中包含了咱們在 Instagram 作的大部分功能, 並且都是在用的功能.webpack

個人建議是: 把這個當成是 Webpack 的文檔開始學習, 而後看官方文檔的具體說明.git

預備條件

  • 你懂 Broserify, RequireJS 或者相似的打包工具
  • 你注重這些東西:

    • 代碼分包
    • 異步加載
    • 靜態資源(圖片, CSS)的打包

1. 爲何用 webpack?

  • 他像 Browserify, 可是將你的應用打包爲多個文件. 若是你的單頁面應用有多個頁面, 那麼用戶只從下載對應頁面的代碼. 當他麼訪問到另外一個頁面, 他們不須要從新下載通用的代碼.github

  • 他在不少地方能替代 Grunt 跟 Gulp 由於他可以編譯打包 CSS, 作 CSS 預處理, 編譯 JS 方言, 打包圖片, 還有其餘一些.web

它支持 AMD 跟 CommonJS, 以及其餘一些模塊系統, (Angular, ES6). 若是你不知道用什麼, 就用 CommonJS.npm

2. Webpack 給 Browserify 的同窗用

對應地: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, 能夠隨意添加要運行的代碼.緩存

3. 怎樣啓動 webpack

切換到有 webpack.config.js 的目錄而後運行:服務器

  • webpack 來執行一次開發的編譯
  • webpack -p for building once for production (minification)
  • webpack -p 來針對發佈環境編譯(壓縮代碼)
  • webpack --watch 來進行開發過程持續的增量編譯(飛快地!)
  • webpack -d 來生成 SourceMaps

4. JavaScript 方言

Webpack 對應 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'] 
  }
};

5. 樣式表和圖片

首先更新你的代碼用 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
    ]
  }
};

6. 功能開關

有些代碼咱們只想在開發環境使用(好比 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, 任何這種代碼都會被剔除, 因此你不用擔憂祕密功能泄漏.

7. 多個進入點(entrypoints)

好比你用 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 頁面也是同樣.

8. 優化共用代碼

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> 你就能獲得廉價的緩存了.

9. 異步加載

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 的演講.

FAQ

Webpack 看起來不是模塊化的?

Webpack 是很是地模塊化. Webpack 的優秀之處在於相比 Browserify 跟 RequireJS 之類方案, 能讓插件將自身集成到編譯過程中的更多地方. 不少看起來像是編寫在覈心代碼的功能, 其實就是默認加載的插件, 他們能夠被覆蓋.(好比, CommonJS 的 require() parser)

相關文章
相關標籤/搜索