Webpack2初識

本文爲騰訊移動分析MTA 產品UI工程師linji的分享。javascript

聚焦webpack2的部分目錄:css

溫故或知新

簡單地歸納--webpack是一個智能模塊依賴分析的打包工具,它經過入口js文件以及一系列的插件能把各類資源文件編譯、打包爲可發佈使用的靜態資源。如官網中的視圖:萬物皆模塊。
html

安裝

文章撰寫時webpack最新正式版版本號爲2.5.1。前端

命令行工具中安裝:java

# 全局安裝
npm install -g webpack
# 初始化項目
npm init
#安裝到項目目錄中
npm install --save-dev webpack複製代碼

快速開始

在根目錄建立以下文件:
entry.js -- 入口文件node

import fn from './fn';
fn();複製代碼

fn.js -- 被引用的模塊react

export default function fn() {
  document.write('I am a Function');
}複製代碼

webpack.config.js -- webpack配置文件webpack

module.exports = {
  entry: './entry.js',
  output: {
    filename: 'bundle.js'
  }
}複製代碼

index.html -- 靜態頁面git

<!DOCTYPE html>
<html>
  <body>
    <script src="./bundle.js"></script>
  </body>
</html>複製代碼

在項目目錄中執行命令 webpack,而後在瀏覽器打開上面的靜態頁面就能夠看到效果了es6

變化

如今簡單列出我認爲對比webpack1版比較明顯的變化

  1. ES6模塊的靜態導入(import等部分ES6語法的原生支持): 更快捷的初始化工程

  2. ES6模塊的動態導入:按官方文檔描述,這種動態導入方式正嘗試加入到ES的規範之中

    • 建立async.js
      export default function async() {
        console.log('異步引用');
      }複製代碼
    • entry.js 加入代碼

      import('./async').then(async => {
        async.default();
      });
      // 等價於
      /* require.ensure([], (require) => { const async = require('./async'); async.default(); }); */複製代碼

      import()返回的是promise實例,因此咱們還能夠經過Promise.all([import('./async')]).then(([async]) => {})的形式去引用多個模塊;不過,當你須要兼容IE8-的瀏覽器時,你須要額外導入promise-polyfill

      System.import()和import()方法做用相同,在本文撰寫期間的2.5.x版本前者依然可使用,但官方文檔有指出這個方法不建議使用

  3. 修復ugulifyJS壓縮css文件時誤刪除[dpr]問題:固然咱們能夠經過別的工具去代替

  4. 函數方式導出配置:能夠經過參數來代替設置複雜的環境變量

    • 修改 webpack.config.js, 並執行 webpack --env.dev
      module.exports = function(env) {
      return {
       entry: './entry.js',
       output: {
         filename: 'bundle.js'
       },
       // 新的「環境變量」使用方式 (此處新增了sourcemap)
       devtool: env.dev
         ? 'cheap-module-eval-source-map'
         : false
       };
      };複製代碼
  5. 配置更清晰簡潔, 更多查看後文配置「升級」
  6. Tree shaking:去除無用代碼,適用於文件目錄結構複雜的項目,更多查看後文瞭解 Tree shaking

配置「升級」

  • module.loaders --> modules.rules
    使用loader時須要把loader名字寫全,如 style 須要寫爲 style-loader

  • 移除 module.preLoaders, module.postLoaders
    如今經過module.rules.enforce屬性來配置

  • 再也不須要手動加入 DedupePlugin, OccurrenceOrderPlugin, json-loader, devServer.inline 等配置
    畢竟幾乎你們都須要用到它們

  • webpack-dev-server, extract-text-webpack-plugin, html-webpack-plugin 這三個經常使用的工具必須升級到2.x版本,不然沒法和webpack2做用

  • 合併分散的resolve配置 resolve.root, resolve.fallback, resolve.modulesDirectories
    經過配置好 resolve.modules 有助於提升定位引用文件的速度

    const path = require('path');
    module.exports = function(env) {
      return {
        // ...
        resolve: {
          modules: [
            path.resolve(__dirname, 'node_modules'),
          ],
        }
       };
    };複製代碼

下圖爲之前一次把個迷你項目從webpack1升級到webpack2的基本配置變動

瞭解TreeShaking

  • Tree shaking 示例
    修改fn.js

    // ...
    export function unused(){
      document.write('I am an unused Function');
    }複製代碼

    修改entry.js

    import fn, {unused} from './fn';
    fn();
    // ...複製代碼

    執行 webpack --env(這裏不使用env.dev來查看較爲美觀的打包代碼)後,咱們會發現生成的bundle代碼中出現以下片斷


    此時再執行 webpack --optimize-minimize --env ,打包的代碼通過壓縮處理後,unused()這個沒有被調用的函數被完全刪掉了。
    簡單而然,webpack2 中的 tree shaking就是一個按需打包模塊一種實現。但須要tree shaking生效,被引用的模塊必須遵循es6模塊導出的規範,詳看後文。

  • Tree shaking 起源
    在前端圈子中,該解釋 「起源」於 rollup.js 做者。
    Rollup中的 Tree-shaking 是無用代碼移除(DCE, dead code elimination)的一種實現。利用AST(Abstract Syntax Tree,譯做 抽象語法樹),找到被使用的代碼塊而後注入到輸出文件中。而Webpack2「原生」支持的ES6模塊靜態性引用,能夠幫助標記編譯後的多餘代碼,並在壓縮時"甩掉"它們。

    更多關於抽象語法樹的理解能夠經過在線抽象語法樹解釋工具去自行體驗

  • ES6模塊的靜態性的做用:

    1. DCE
    2. 快速定位代碼

      • 使用Commonjs去引用庫的時候,調用其子屬性會觸發對象屬性查表,這會下降編譯的效率
        const lib = require('lib'); lib.someFunc(); // 屬性查表

      • 而import的靜態特性使其跳過這個步驟;
        import * as lib from 'lib'; lib.someFunc(); // 靜態處理

    3. 提升變量校驗效率

    4. 爲macro(宏)作準備,現有macro庫 sweetjs.org/
  • Tree shaking 橫向對比
    babel-plugin-import 是一個類tree shaking的babel插件,它可以把 import { Button } from ‘antd’; 這種代碼轉換爲 import Button ‘antd/lib/button’; 的形式。

    • 優勢在於
      o 不硬性要求引用ES6模塊
      o 構建速度較快
    • 缺點在於
      x 需指定按需打包的模塊
      x 本身建立的模塊使用不便
      x 沒法把同一個文件裏的多個export分離
      x 配置錯誤將會致使依賴丟失

    現在的webpack2的tree shaking 主要適用範圍以下
    import {lastName} form 'name'; -- named import
    import name from 'name'; -- default import
    import * as all from 'name;
    const name = require('name');

    現在並非全部開源組件都提供ES6模塊的代碼,因此若是想最有效的去除多餘代碼,把 babel-plugin-import 和 webpack2 Tree shaking組合使用來打包壓縮生產環境的代碼爲當下最優選擇。

  • Webpack2 和 Babel 組合的短暫演變
    在使用webpack的同時,咱們大多都須要使用到babel來支持轉換ES六、ES七、TS等一系列語法糖,那一旦使用babel,務必要防止babel對import語法先行處理。爲了達到這個目的,babel的配置也經歷過比較明顯的轉變最終簡單易懂:

1-2速度以及打包大小對比

該速度對比數據爲一樣代碼每項執行5次後取最大時長的數據,代碼以下:

import react from 'react'; // react15.5.4 不提供ES6模塊
import {compose} from 'refux'; // redux3.6.0 提供ES6模塊複製代碼

速度

webpack1 webpack2
dev-server 啓動服務 2187ms 2252ms
dev-server 文件更改 41ms 27ms
production 1991ms 2057ms

打包大小

webpack1 webpack2
dev-server 1.38MB 1.75MB
production 53.3kB 48.5kB

因而可知,在開發過程當中,webpack2打包速度較快但需加載的代碼文件大小較大;生產環境代碼webpack2生成的文件體積更小(但這要求開發者遵循使用ES6模塊的寫法)

Webpack2優缺點彙總

我的最後總結的優缺點以下

  • 優勢
    o change文件打包效率提升
    o 升級快捷
    o 增強code splitting時chunk文件的錯誤定位(這個真心棒)
    o 官方文檔更加清晰
    o 和babel6完美契合
  • 缺點
    x 自帶的tree shaking對非ES6模塊和對象動態屬性無效
    x 動態import實現方式不標準,還須要 bable-plugin-syntax-dynamic-import
    x Chrome下devtool爲cheap-module-source-map等時源碼定
位有數行偏差
    x 後文中的Issue

    隨着webpack2版本迭代,以上缺點正慢慢改正

結語

我想不到任何一個不升級的理由,畢竟這是正式版了。:) 如文中有與實際不對等的狀況,務必告訴我好讓我能撇除誤解~~~

Issue

webpack2處理原生ES6 Module時,沒法使用babel-plugin-add-module-exports插件。該插件能讓babel6處理export時,如同babel5同樣,生成語句 module.exports = exports["default"];webpack2中export default轉換後爲 __webpack_exports__["default"] 而非 exports["default"],故執行代碼時會報錯。

參考

What's new in webpack 2

相關文章
相關標籤/搜索