隨着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
先區分下幾個不一樣概念:包管理工具(package manager)、模塊加載器(module loader),打包工具(bundler),包管理器指管理安裝js模塊的這類,例如npm、bower、jspm這些,模塊加載器指向requirejs、modjs、seajs這些,模塊加載器又主要遵循AMD、CMD、Commonjs三種規範,打包工具則指r.js、browserify、webpack這類。html
在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
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文件,這樣才能正常使用自定義的模塊
可見:
webpack以前也講到過http://ouvens.github.io/frontend-build/2015/04/01/webpack-tool.html 。這裏就直接總結一下webpack的特色:
對於這裏對多種模塊規範的支持,這裏講下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(後面簡稱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使用幾個須要注意的地方:
再來看看下一代模塊打包工具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:
另外須要瞭解的是,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
這裏總結下,目前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