迎接到个人博客去看看: 戳着里
0. browserify是什么?
browserify是现在比较盛行的模块打包东西之一(别的一个webpack)
基于流式(stream)头脑设想
能够经由历程command line,也能够经由历程API来运用
仅处置惩罚javascript
模块化的逆历程,然则推进着模块化的更好生长
内置了一些node core module
node模块能够在浏览器端运用,是同构运用的有力兵器
1. 从demo提及
存在两个js: square.js、foo.js
// square.js
module.exports = function (a) {
return a*a;
}
// foo.js
var sq = require('./square');
console.log(sq(2));
接着经由历程API举行打包:
var browserify = require('../node-browserify');
var fs = require('fs');
var b = browserify(['./foo.js']);
b.require('./square.js', {
expose: 'square'
})
b.bundle().pipe(fs.createWriteStream('./bundle.js'));
获得一个能够在浏览器端运用的bundle.js:
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
'use strict';
var sq = require('./square');
console.log(sq(2));
},{"./square":2}],2:[function(require,module,exports){
"use strict";
module.exports = function (a) {
return a * a;
};
},{}]},{},[1]);
接下来剖析browserify是怎样做到的。
2. 总体设想
3. 详细剖析
3.1 输入(input)
browserfiy的输入内容团体上分为两类:file、transform function。file就是须要打包的文件,transform function用于对输入的file内容举行处置惩罚。比方:我们须要对coffeeScript文件举行打包,那末打包之前就须要将coffeeScript文件转换成javascript,然后才举行打包。
那末输入的接口都有哪些呢:
b = new Browserify(file, opts): 在实例化的时刻,将相关内容输入进去
b.require(file, opts): 指定能够在浏览器端require的文件
b.add(file, opts):实例化时未指定参数的状况下,能够运用该接口
b.external/exclude/ignore: 一些file的特别设置
b.transform(tr, opts): 指定transform function,用于对module举行转换
3.2 处置惩罚
b.pipeline是browserify内里的中心对象。经由历程这个对象,能够对module举行一系列的处置惩罚,这个对象具有以下特性:
整合了许多transform stream后,天生一个团体transform stream
带有label,经由历程label接见内部详细的transform stream
write进去的chunk分两种状况:带有file的对象,带有transform function的对象
伪代码:
// 建立pipeline
Browserify.prototype._createPipeline = function (opts) {
...
var pipeline = splicer.obj([
'record', [ this._recorder() ],
'deps', [ this._mdeps ],
'json', [ this._json() ],
'unbom', [ this._unbom() ],
'unshebang', [ this._unshebang() ],
'syntax', [ this._syntax() ],
'sort', [ depsSort(dopts) ],
'dedupe', [ this._dedupe() ],
'label', [ this._label(opts) ],
'emit-deps', [ this._emitDeps() ],
'debug', [ this._debug(opts) ],
'pack', [ this._bpack ],
'wrap', []
]);
...
return pipeline;
}
// pipeline write进去的chunk
Browserify.prototype.transform = function (tr, opts) {
...
var rec = {
transform: tr,
options: opts,
global: opts.global
};
// 带有transform function的对象
this.pipeline.write(transform);
...
}
Browserify.prototype.require = function (file, opts) {
...
var rec = {
source: buf.toString('utf8'),
entry: defined(opts.entry, false),
file: filename,
id: id
};
// 带有file的对象
this.pipeline.write(rec);
}
接下来针对个中症结的deps、pack举行剖析。
3.3 模块依靠的剖析
面临的题目:
在模块兼并前,首先要做的事情就是找出进口文件(entry file)的依靠以及依靠的依靠,比方在charpter1的demo中,进口文件foo.js仅依靠square.js
斟酌要对源代码举行转换
browserify经由历程module-deps来处理上述题目,经由历程下面的代码能够看到剖析的效果:
var browserify = require('../node-browserify');
var fs = require('fs');
var b = browserify(['./foo.js'], {
debug: true,
basedir: './'
});
b.require('./square.js', {
expose: 'square'
})
b.on('dep', function (row) {
console.log(row);
})
b.bundle().pipe(fs.createWriteStream('./bundle.js'));
输出的JSON效果为:
{
"entry":true,
"expose":false,
"basedir":"./",
"file":"/Users/lizhenhua/Documents/france/z-react/foo.js",
"id":1,
"order":0,
"source":"'use strict';\n\nvar sq = require('./square');\nconsole.log(sq(2));\n//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZvby5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLElBQUksRUFBRSxHQUFHLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FBQztBQUM3QixPQUFPLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDIiwiZmlsZSI6ImZvby5qcyIsInNvdXJjZXNDb250ZW50IjpbInZhciBzcSA9IHJlcXVpcmUoJy4vc3F1YXJlJyk7XG5jb25zb2xlLmxvZyhzcSgyKSk7XG4iXX0=",
"deps":{
"./square":2
},
"index":1,
"indexDeps":{
"./square":2
}
}
{
"id":2,
"source":"\"use strict\";\n\nmodule.exports = function (a) {\n return a * a;\n};\n//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNxdWFyZS5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLE1BQU0sQ0FBQyxPQUFPLEdBQUcsVUFBVSxDQUFDLEVBQUU7QUFDNUIsU0FBTyxDQUFDLEdBQUMsQ0FBQyxDQUFDO0NBQ1osQ0FBQSIsImZpbGUiOiJzcXVhcmUuanMiLCJzb3VyY2VzQ29udGVudCI6WyJtb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChhKSB7XG4gIHJldHVybiBhKmE7XG59XG4iXX0=",
"deps":{},
"file":"/Users/lizhenhua/Documents/france/z-react/square.js",
"index":2,
"indexDeps":{}
}
别的,在node端、browser端能够用到差别的代码,比方要求发送(参看superagent),node端运用http/https模块,而浏览器端则运用XMLHttpRequest对象。为了处理该题目,browserify经由历程在package.json中增加browser字段来制订浏览器运用的模块。
{
"name": "mypkg",
"version": "1.2.3",
"main": "main.js",
"browser": "browser.js"
}
在剖析的时刻,则须要离别运用resolve、browser-resolve来举行剖析。
3.4 模块的打包
应用browser-pack,将上述的json数据打包兼并成一个文件,browser-pack详细做了以下事情:
定义浏览器端可用的require症结词
到browser-pack中能够看到以下写好的代码片断:
(function outer (modules, cache, entry) {
// Save the require from previous bundle to this closure if any
var previousRequire = typeof require == "function" && require;
function newRequire(name, jumped){
if(!cache[name]) {
if(!modules[name]) {
// if we cannot find the module within our internal map or
// cache jump to the current global require ie. the last bundle
// that was added to the page.
var currentRequire = typeof require == "function" && require;
if (!jumped && currentRequire) return currentRequire(name, true);
// If there are other bundles on this page the require from the
// previous one is saved to 'previousRequire'. Repeat this as
// many times as there are bundles until the module is found or
// we exhaust the require chain.
if (previousRequire) return previousRequire(name, true);
var err = new Error('Cannot find module \'' + name + '\'');
err.code = 'MODULE_NOT_FOUND';
throw err;
}
var m = cache[name] = {exports:{}};
modules[name][0].call(m.exports, function(x) {
var id = modules[name][1][x];
return newRequire(id ? id : x);
},m,m.exports,outer,modules,cache,entry);
}
return cache[name].exports;
}
for(var i=0;i<entry.length;i++) newRequire(entry[i]);
// Override the current require with this new one
return newRequire;
})
2.将json source兼并在一起
能够对charpter1的效果举行剖析,除了上述的newRequire外,还包括以下构造内容:
(newRequire...)(source, cache, entry);
// source 代码
source = {
1:[
function(require,module,exports){
'use strict';
var sq = require('./square');
console.log(sq(2));
},
{"./square":"square"}
],
"square":[
function(require,module,exports){
"use strict";
module.exports = function (a) {
return a * a;
};
},
{}
]
};
// cache
cache = {}
// entry
entry = [1];
上述代码片断中:
source: 是一个map构造,key存在两种状况(默以为内部数字、显现expose出来的key)
source元素分为两部分:源代码(包括的function(require, module, exports) {…})、代码中存在的依靠
cache是缓存信息,防止再次读取souce
entry: 是打包代码进口文件的key
4. browserify的运用
在ES6没有周全支撑之前,browserify还会存在很长一段时间,还会有很强的生命力。相比较webpack,个人更喜好browserify,webpack给人的觉得是一直在设置、运用plugin,而且代码构造很庞杂,内里触及大批事宜,源码不容易浏览。而运用browserify给人觉得是在开辟,运用起来也较为天真,同时browserify的stream设想思绪给人更多启示,能够向其他方向(比方css兼并)迁徙。