如何學習 Webpack

webpack-howto

Tip:css

本文是 webpack-howto 的原文,我以爲這篇文章寫得很是好,確實算是目前學習 webpack 入門的必讀文章。直接收錄之。node

本教程的目標

這是一本教你如何應用webpack到你的項目中的工具書。它包含了咱們在Instagram中用到的絕大多數的內容。react

個人建議:這個教程做爲你第一個webpack的文檔,學習完之後去看它的官方文檔,瞭解更詳細的說明。webpack

學習的前提

  • 你瞭解過相似browserifyRequireJS的東西
  • 你知道:
    • Bundle的拆分
      • 異步的加載
      • 打包images和css的這一類的靜態資源

1. 爲何選擇 webpack?

  • 它和browserify相似 可是它能夠把你的應用拆分紅多個文件。若是你的單頁應用裏有不少頁面,用戶只會下載當前訪問頁面的代碼。當他們訪問應用中的其餘頁面時,再也不須要加載與以前頁面重複的通用代碼。
  • 它能夠替代gulp和grunt 由於他能夠構建打包css、預處理css、編譯js和圖片等。

它支持AMD和CommonJS,以及其餘的模塊系統(Angular, ES6)。若是你不太熟悉如何使用,就用CommonJS吧。git

2. 對於習慣Browserify的人能夠這樣使用Webpack

下面的命令是等價的:es6

browserify main.js > bundle.js
webpack main.js bundle.js

然而,webpack要比Browserify強大。因此通常狀況下你須要創建一個webpack.config.js文件來配置webpack。github

// webpack.config.js
module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'
  }
};

這就是單純的JS,全部寫這個配置文件毫無壓力。web

3. 如何調用webpack

選擇一個目錄下有webpack.config.js文件的文件夾,而後運行下面的命令:npm

  • webpack 開發環境下編譯
  • webpack -p 產品編譯及壓縮
    • webpack --watch 開發環境下持續的監聽文件變更來進行編譯(很是快!)
    • webpack -d 引入 source maps

4. 編譯js

webpack能夠和browserify、RequireJS同樣做爲一個loader(加載工具)來使用。下面咱們來看下如何使用webpack去加載、編譯CoffeeScript和JSX+ES6。(這裏你必須先 npm install babel-loader coffee-loader):json

你也要看下babel-loader的介紹,它會做爲一個開發環境下的依賴加載到咱們的項目中(run npm install babel-core babel-preset-es2015 babel-preset-react)

// webpack.config.js
module.exports = {
  entry: './main.js', // 入口文件
  output: {
    filename: 'bundle.js' // 打包輸出的文件
  },
  module: {
    loaders: [
      {
        test: /\.coffee$/,  // test 去判斷是否爲.coffee的文件,是的話就是進行coffee編譯
        loader: 'coffee-loader'
      },
      {
        test: /\.js$/, // test 去判斷是否爲.js,是的話就是進行es6和jsx的編譯
        loader: 'babel-loader',
        query: {
          presets: ['es2015', 'react']
        }
      }
    ]
  }
};

若是你但願在require文件時省略文件的擴展名,只須要在webpack.config.js中添加 resolve.extensions 來配置。

// webpack.config.js
module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'
  },
  module: {
    loaders: [
      { test: /\.coffee$/, loader: 'coffee-loader' },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        query: {
          presets: ['es2015', 'react']
        }
      }
    ]
  },
  resolve: {
    // 如今你require文件的時候能夠直接使用require('file'),不用使用require('file.coffee')
    extensions: ['', '.js', '.json', '.coffee']
  }
};

5. Css樣式和圖片的加載

首先你須要用require()去加載你的靜態資源(named as they would with node's require()):

require('./bootstrap.css');
require('./myapp.less');

var img = document.createElement('img');
img.src = require('./glyph.png');

當你require了CSS(less或者其餘)文件,webpack會在頁面中插入一個內聯的<style>,去引入樣式。當require圖片的時候,bundle文件會包含圖片的url,並經過require()返回圖片的url。

可是這須要你在webpack.config.js作相應的配置(這裏仍是使用loaders)

// webpack.config.js
module.exports = {
  entry: './main.js',
  output: {
    path: './build', // 圖片和js會放在這
    publicPath: 'http://mycdn.com/', // 這裏用來生成圖片的地址
    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的圖片地址,圖片要小於8k,直接的url的地址則不解析
    ]
  }
};

6. 功能標識(Feature flags)

項目中有些代碼咱們只爲在開發環境(例如日誌)或者是內部測試環境(例如那些沒有發佈的新功能)中使用,那就須要引入下面這些魔法全局變量(magic globals):

if (__DEV__) {
  console.warn('Extra logging');
}
// ...
if (__PRERELEASE__) {
  showSecretFeature();
}

同時還要在webpack.config.js中配置這些變量,使得webpack可以識別他們。

// webpack.config.js

// definePlugin 會把定義的string 變量插入到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 會刪除全部無做用代碼,也就是說那些包裹在這些全局變量下的代碼塊都會被刪除,這樣就能保證這些代碼不會因發佈上線而泄露。

7. 多個入口文件

若是你有兩個頁面:profile和feed。若是你但願用戶訪問profile頁面時不加載feed頁面的代碼,那就須要生成多個bundles文件:爲每一個頁面建立本身的「main module」(入口文件)。

// webpack.config.js
module.exports = {
  entry: {
    Profile: './profile.js',
    Feed: './feed.js'
  },
  output: {
    path: 'build',
    filename: '[name].js' // name是基於上邊entry中定義的key
  }
};

在profile頁面中插入<script src="build/Profile.js"></script>。feed也同樣。

8. 優化通用代碼

Feed和Profile頁面存在大量通用代碼(好比React、公共的樣式和組件等等)。webpack能夠抽離頁面間公共的代碼,生成一個公共的bundle文件,供這兩個頁面緩存使用:

// 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' // 爲上面entry的key值
  },
  plugins: [commonsPlugin]
};

在上一步引入本身的bundle以前引入<script src="build/common.js"></script>

9. 異步加載

雖然CommonJS是同步加載的,可是webpack也提供了異步加載的方式。這對於單頁應用中使用的客戶端路由很是有用。當真正路由到了某個頁面的時候,它的代碼纔會被加載下來。

指定你要異步加載的 拆分點。看下面的例子

if (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 默認會從項目的根目錄下引入這些chunk文件。你也能夠經過 output.publicPath來配置chunk文件的引入路徑

// webpack.config.js
output: {
    path: "/home/proj/public/assets", // webpack的build路徑
    publicPath: "/assets/" // 你require的路徑
}

其餘

看一個真實的例子,看看他們是怎麼使用webpack。這是Pete Hunt在Instagram.com中談論webpack的視頻。

FAQ

webpack 不只僅是個modular

相比較browserify和browserify,在你的項目中大量的使用webpack插件才能體現出webpack的優點。當使用了插件後,代碼纔會被複寫。其他的都是默認加載。

相關文章
相關標籤/搜索