下一代前端打包工具與tree-shaking

1、js模塊化打包概述

  隨着js模塊化規範AMD、CMD、commonJs的出現,模塊打包工具也在不斷的出現和演變,依次出現了r.js、browserify和webpack,過去的2015年就是webpack大行其道的一年,又隨着reactjs、es6的出現,webpack更是深刻人心,由於其人性化的特色和友好性,確實給前端模塊打包帶來了極大的方便。javascript

  不過今天並非重點講webpack,而是rollup,要了解webpack,能夠看個人另外一篇文章:http://ouvens.github.io/frontend-build/2015/04/01/webpack-tool.html ,在講rollup以前先來看看幾種以前的前端打包方案。css

2、js模塊化打包方案

  先區分下幾個不一樣概念:包管理工具(package manager)、模塊加載器(module loader),打包工具(bundler),包管理器指管理安裝js模塊的這類,例如npm、bower、jspm這些,模塊加載器指向requirejs、modjs、seajs這些,模塊加載器又主要遵循AMD、CMD、Commonjs三種規範,打包工具則指r.js、browserify、webpack這類。html

一、r.js

  在grunt結合requirejs的年代,r.js做爲通用標配的打包工具普世存在,固然如今應該也有些團隊在用。   r.js是requireJS的優化(Optimizer)工具,能夠實現前端文件的壓縮與合併,在requireJS異步按需加載的基礎上進一步提供前端優化,減少前端文件大小、減小對服務器的文件請求。先來分析一個官網的例子:前端

{
    appDir: '../www',
    baseUrl: 'js/lib',
    paths: {
        app: '../app'
    },
    dir: '../www-built',
    modules: [
        {
          //module names are relative to baseUrl
            name: '../common',
            include: ['jquery',
                      'app/lib',
                      'app/controller/Base',
                      'app/model/Base'
            ]
        },{
          //module names are relative to baseUrl/paths config
            name: '../page1',
            include: ['app/main1'],
            exclude: ['../common']
        },{
            //module names are relative to baseUrl
            name: '../page2',
            include: ['app/main2'],
            exclude: ['../common']
        }
    ]
}

若是這個文件配置命名爲build.js,在node環境下執行 node r.js -o build.js 就能夠壓縮合並須要的模塊。能夠見一下配置java

地址見:https://github.com/requirejs/example-multipage/blob/master/tools/build.jsnode

  這裏經過相對路徑和baseUrl路徑來進行文件依賴查找,因此通常咱們會設置一個baseUrl來指定要打包的js目錄,並將多個文件合成一個文件,並存放到指定的目錄下面。簡單的理解,他就是一個簡單的js依賴分析合併工具。那咱們來總結下它的特色:react

  • 能夠合併js,css,並結合其它工具壓縮,甚至對整個項目進行打包
  • 通常須要指定baseUrl
  • 須要將r.js放到開發目錄中
  • 依賴requireJs的AMD規範,CMD和CommonJs的場景不適用
  • 須要手寫配置

二、browserify

  Browserify 可讓你使用相似於 node 的 require() 的方式來組織瀏覽器端的 Javascript 代碼,不須要 define(function(require, exports, module) {...}) ,更符合CommonJS模塊化規範,而且可讓前端 Javascript模塊直接使用npm install的方式安裝模塊。browserify使用Esprima解析依賴, 生成的AST兼容Mozilla規範。jquery

npm install -g browserify
browserify greet.js > bundle.js //把 greet.js 及其所依賴的模塊文件打包成單個 bundle.js 文件。

看一個完整的例子:webpack

配置的js文件git

var fs = require('fs');
var domify = require('domify');
var insertCss = require('insert-css');
 
var css = fs.readFileSync(__dirname + '/badge.css', 'utf8');
insertCss(css);
 
var html = fs.readFileSync(__dirname + '/badge.html', 'utf8');
 
module.exports = Badge;
 
function Badge(opts) {
  if (!(this instanceof Badge)) return new Badge(opts);
  this.element = domify(html);
  if (opts.number) {
    this.setNumber(opts.number);
  }
}
 
Badge.prototype.setNumber = function (number) {
  this.element.querySelector('.number').textContent = number;
}
 
Badge.prototype.appendTo = function (target) {
  if (typeof target === 'string') target = document.querySelector(target);
  target.appendChild(this.element);
};

package.json

{
  "name": "badge",
  "version": "1.0.0",
  "private": true,
  "main": "badge.js",
  "browserify": {
  "transform": [ "brfs" ]
},
"dependencies": {
  "brfs": "^1.1.1"
  }
}

除了配置,咱們不得無論理一個依賴包的映射表,即package.json文件,這樣才能正常使用自定義的模塊

可見:

  • browserify更可能是爲了支持commonJs的規範
  • 可讓前端 Javascript模塊直接使用npm install的方式安裝
  • 使用機制稍微複雜,開發者須要關心的東西不少
  • 須要手寫打包配置和任務

三、webpack

  webpack以前也講到過http://ouvens.github.io/frontend-build/2015/04/01/webpack-tool.html 。這裏就直接總結一下webpack的特色:

  • 模塊來源普遍,支持包括npm/bower等等的各類主流模塊安裝/依賴解決方案
  • 模塊規範支持全面,amd/cmd/commonjs/shimming等徹底支持
  • 瀏覽器端足跡小,移動端友好,卻對熱加載乃至熱替換有很好的支持
  • 插件機制完善,實現自己實現一樣模塊化,容易擴展,支持es6,react等
  • 須要手寫配置

  對於這裏對多種模塊規範的支持,這裏講下webpack是怎麼封裝定義的,例如這裏是一個cookie的操做庫:

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define(['zepto'], factory);
    } else if (typeof exports === 'object') {
        module.exports = factory(require('zepto'));
    } else {
        root['Cookie'] = factory(root['Zepto']);
    }

})(this, function ($) {
  var exports = {
    init: function (){

    }
  };
    $.cookie = exports;
    return exports;
});

相信你們一看就懂,對於webpack的配置文件寫起來也是很長簡潔,這就是爲何webpack目前這麼受歡迎的緣由之一。

// webpack.config.js
module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'       
  }
};
固然也能夠這樣,webpack支持了coffee和jsx,喜歡玩react的同窗能夠試下。
// 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 can take parameters as a querystring
    ]
  }
};

四、fis3-packager-loader

  fis3-packager-loader(後面簡稱fpl)通常沒有單獨拿出來說,由於這個是結合fis3一塊兒使用的,fis3的單文件進行處理的流程依次爲:lint -> parser -> preprocessor -> standard -> postprocessor -> optimizer。這六個過程能夠經過配置插件來定義咱們最終想要的結果,而後進行package輸出。例如,

  lint 代碼校驗檢查,比較特殊,因此須要 release 命令命令行添加 -l 參數

  parser 預處理階段,好比 less、sass、es六、react 前端模板等都在此處預編譯處理

  preprocessor 標準化前處理插件

  standard 標準化插件,處理內置語法

  postprocessor 標準化後處理插件

  最終階段爲package打包階段,打包是依賴的插件fpl,fpl能夠對html、css、js進行分析打包,而且能打包分析html中引入的js,js中引入的css,功能十分強大,目前所在團隊通用的是fis3體系(以前用過grunt、gulp,發現依然不少不爽的地方)具體文檔能夠查看官網或個人另外一篇文章:

http://fis.baidu.com/ https://ouvens.github.io/frontend-build/2015/08/15/fis3-study-rearch.html

  fis3默承認以直接對咱們的html進行資源引入打包處理,將scss和js裏面的依賴關係層層遞進分析打包,並生成resource Map表,程序運行時經過resource Map來加載模塊。它的一個很大優點是不用咱們書寫構建任務和太多的配置。

<link rel="stylesheet" href="../../modules/sass/frozenjs.scss">
<script>
require.async(['zepto', 'frozen', './main'], function($, frozen, main) {
    main.init();
});
</script>

看下fis3-packager-loader使用幾個須要注意的地方:

  • 能夠方便的支持html,js,css的依賴引用打包
  • 已集成到fis3構建中,簡單配置後就可使用
  • 目前js支持同步打包,異步處理咱們也能夠本身作處理插件
  • 依賴fis3環境,支持commonjs規範
  • 不須要書寫任務配置,這點是很方便的

3、下一代前端打包工具

  再來看看下一代模塊打包工具rollup ( http://rollupjs.org/guide/ )和webpack2(這裏原理相同,不贅述了)。

  rollup是一個模塊打包工具:它能夠將多個ES6模塊轉化爲一個獨立的打包文件,打包後的模塊能夠是 ES六、CommonJS、ES5……中的任一種格式。Rollup打包JavaScript模塊具備兩個新的優點:

  一、ES6處處的模塊依然是可用的獨立模塊   如今很多前端團隊開始使用ES6 + babel + webpack的方式開發了,可是咱們依然只能這樣寫代碼,由於babel沒法爲咱們解析模塊加載的問題:

var utils = require( 'utils' );

var query = 'Rollup';
utils.ajax( 'https://api.example.com?search=' + query ).then( handleResponse );

使用rollup,咱們就能夠這樣使用了

import { ajax } from 'utils';

var query = 'Rollup';
ajax( 'https://api.example.com?search=' + query ).then( handleResponse );

  二、tree-shaking打包   經過名叫 「tree-shaking」 的技術使打包的結果只包括實際用到的 exports。Three-shaking 的關鍵在於依賴 ES6 模塊的靜態結構。「靜態結構」意味着在編譯時他們是可分解的,而不用執行它們的任何代碼,簡單理解是ES6導出的部分若是在其它模塊沒有調用,rollup在輸出時會直接把這部分做爲死碼刪除。死碼刪除有一個很大的優點,就是如今咱們能夠根據須要隨意地使模塊或大或小,而不用擔憂打包後的大小,由於工具能夠幫我篩選過濾,webpack 2也支持這一特性。例以下面兩個模塊:

// lib.js文件
export function foo() {
  console.log(1);
}
export function bar() {
  console.log(2);
}

// main.js文件
import {foo} from './lib.js';
console.log(foo());

  rollup打包合併處理後,新生成的文件main.js以下,lib.js中處處可是未被調用的模塊被移除了。

// main.js
function foo() {
  console.log(1);
}
console.log(foo());

再來總結下rollup:

  • 支持ES6模塊規範打包成其它任一格式規範
  • 支持tree-shaking方式打包
  • 方便接入構建,如gulp
  • 須要書寫配置任務

另外須要瞭解的是,webpack2也具備這兩大特性,不過webpack 2還在beta版,正式發佈後估計仍然會取代rollup的地位。

tree-shaking:http://www.2ality.com/2015/12/webpack-tree-shaking.html

面向將來打包:http://www.2ality.com/2015/12/bundling-modules-future.html

4、總結

  這裏總結下,目前ES6的前端開發者愈來愈多,雖然ES6在前端的應用仍須要babel等工具協助完成,rollup又爲ES6這一開發體系補上了一塊新的木板,另外構建打包工具的迭代更新速度很快,webpack 2也攜帶了tree-shaking技術、結合babel支持es6模塊打包出現,將來的團隊也要因勢而變,才能不斷髮展。

原文地址:https://ouvens.github.io/frontend-build/2016/01/20/next-generation-js-module-bunlder.html

相關文章
相關標籤/搜索