弁言
1. manually
之前,我新开一个网页项目,然后想到要用jQuery,我会翻开浏览器,然后找到jQuery的官方网站,点击谁人能干的“Download jQuery”按钮,下载到.js
文件,然后把它丢在项目目次里。在须要用到它的处所,如许用<script>
引入它:
<script src="path/to/jquery.js"></script>
2. Bower
厥后,我最先用[Bower][]如许的包治理东西。所以这个历程变成了:先翻开敕令行用bower
装置jQuery。
bower install jquery
再继承用<script>
引入它。
<script src="bower_components/jquery/dist/jquery.js"></script>
3. npm&Browserify
如今,我又有了新的挑选,大概是如许:
敕令行用npm
装置jQuery。
npm install jquery
在须要用到它的JavaScript代码里,如许引入它:
var $ = require("jquery");
没错,这就是运用npm的包的平常要领。但迥殊的是,这个npm的包是我们熟知的jquery
,而它将用在浏览器中。
[Browserify][],正如其名字所表现的行动那样,让底本属于服务器端的Node及npm,在浏览器端也可运用。
明显,上面的历程还没完毕,接下来是Browserify的事变(假定上面那段代码地点的文件叫main.js
):
browserify main.js -o bundle.js
末了,用<script>
援用Browserify天生的bundle.js
文件。
<script src="bundle.js"></script>
这就是依托Browserify竖立起来的第三挑选。
等下,如何比之前变庞杂了?
CommonJS作风的模块及依靠治理
实在,在这个看起来更庞杂的历程当中,require()
具有特殊的意义。
Browserify并不只是一个让你轻松援用JavaScript包的东西。它的症结才能,是JavaScript模块及依靠治理。(这才是为师的主业)
就模块及依靠治理这个题目而言,已有RequireJS[]这些优异的作品。而如今,Browserify又给了我们新的挑选。
Browserify参照了Node中的模块系统,商定用require()
来引入其他模块,用module.exports
来引出模块。在我看来,Browserify差别于RequireJS和Sea.js的处地点于,它没有出力去供应一个“运转时”的模块加载器,而是强调举行预编译。预编译会带来一个分外的历程,但对应的,你也不再须要遵照肯定划定规矩去加一层包裹。因而,相比较而言,Browserify供应的构造体式格局更简约,也更相符CommonJS范例。
像写Node那样去构造你的JavaScript,Browserify会让它们在浏览器里一般运转的。
装置及运用
敕令行情势
敕令行情势是官方贴出来的用法,由于看起来最简朴。
Browserify自身也是npm,经由过程npm的体式格局装置:
npm install -g browserify
这里-g
的参数示意全局,所以能够在敕令行内直接运用。接下来,运转browserify
敕令到你的.js
文件(比方entry.js
):
browserify entry.js -o bundle.js
Browserify将递归剖析你的代码中的require()
,然后天生编译后的文件(这里的bundle.js
)。在编译后的文件内,统统JavaScript模块都已兼并在一起且竖立好了依靠关联。末了,你在html里援用这个编译后的文件(喂,和弁言里的一样啊):
<script src="bundle.js"></script>
有关这个编译敕令的设置参数,请参照[node-browserify#usage][]。假如你想要做比较邃密的设置,敕令行情势能够会不太轻易。这类时刻,引荐连系Gulp运用。
+ Gulp情势
连系Gulp运用时,你的Browserify只装置在某个项目内:
npm install browserify --save-dev
发起加上背面的--save-dev
以保留到你项目的package.json
里。
接下来是gulpfile.js
的部份,下面是一个简朴示例:
var gulp = require("gulp");
var browserify = require("browserify");
var sourcemaps = require("gulp-sourcemaps");
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
gulp.task("browserify", function () {
var b = browserify({
entries: "./javascripts/src/main.js",
debug: true
});
return b.bundle()
.pipe(source("bundle.js"))
.pipe(buffer())
.pipe(sourcemaps.init({loadMaps: true}))
.pipe(sourcemaps.write("."))
.pipe(gulp.dest("./javascripts/dist"));
});
能够看到,Browserify是自力的,我们须要直接运用它的API,并将它加入到Gulp的使命中。
在上面的代码中,debug: true
是示知Browserify在运转同时天生内联sourcemap用于调试。引入gulp-sourcemaps
并设置loadMaps: true
是为了读取上一步获得的内联sourcemap,并将其转写为一个零丁的sourcemap文件。vinyl-source-stream
用于将Browserify的bundle()
的输出转换为Gulp可用的[vinyl][](一种假造文件花样)流。vinyl-buffer
用于将vinyl流转化为buffered vinyl文件(gulp-sourcemaps
及大部份Gulp插件都须要这类花样)。
如许设置好以后,直接运转gulp browserify
就能够获得效果了,能够像如许:
假如你的代码比较多,能够像上图如许一次编译须要1s以上,这是比较慢的。这类时刻,引荐运用[watchify][]。它能够在你修正文件后,只从新编译须要的部份(而不是Browserify底本的悉数编译),如许,只要第一次编译会花些时候,今后的马上变动革新则十分迅速。
有关更多Browserify + Gulp的示例,请参考[Gulp Recipes][]。
特征及扼要道理
运用Browserify来构造JavaScript,有什么要注意的处所吗?
要回复这个题目,我们先看看Browserify究竟做了什么。下面是一个比较细致的例子。
项目内如今用到2个.js文件
,它们存在依靠关联,其内容离别是:
name.js
:
module.exports = "aya";
main.js
:
var name = require("./name");
console.log("Hello! " + name);
然后对main.js
运转Browserify,获得的bundle.js
的文件内容是如许的:
(function e(t, n, r) {
// ...
})({
1: [function (require, module, exports) {
var name = require("./name");
console.log("Hello! " + name);
}, {"./name": 2}],
2: [function (require, module, exports) {
module.exports = "aya";
}, {}]
}, {}, [1])
//# sourceMappingURL=bundle.js.map
请先疏忽掉省略号里的部份。然后,它的构造就清楚多了。能够看到,团体是一个马上实行的函数([IIFE][]),该函数接收了3个参数。个中第1个参数比较庞杂,第2、3个参数在这里离别是{}
和[1]
。
模块map
第1个参数是一个Object,它的每一个key都是数字,作为模块的id,每一个数字key对应的值是长度为2的数组。能够看出,前面的main.js
中的代码,被function(require, module, exports){}
如许的构造包装了起来,然后作为了key1
数组里的第一个元素。相似的,name.js
中的代码,也被包装,对应到key2
。
数组的第2个元素,是另一个map对应,它示意的是模块的依靠。main.js
在key1
,它依靠name.js
,所以它的数组的第二个元素是{"./name": 2}
。而在key2
的name.js
,它没有依靠,因而其数组第二个元素是空Object{}
。
因而,这第1个庞杂的参数,携带了统统模块的源码及其依靠关联,所以叫做模块map。
包装
前面提到,原有的文件中的代码,被包装了起来。为何要如许包装呢?
由于,浏览器原生环境中,并没有require()
。所以,须要用代码去完成它(RequireJS和Sea.js也做了这件事)。这个包装函数供应的3个参数,require
、module
、exports
,恰是由Browserify完成了特定功用的3个症结字。
缓存
第2个参数险些老是空的{}
。它假如有的话,也是一个模块map,示意本次编译之前被加载进来的来自于其他处所的内容。现阶段,让我们疏忽它吧。
进口模块
第3个参数是一个数组,指定的是作为进口的模块id。前面的例子中,main.js
是进口模块,它的id是1,所以这里的数组就是[1]
。数组申明实在还能够有多个进口,比方运转多个测试用例的场景,但相对来说,多进口的状况照样比较少的。
完成功用
还记得前面疏忽掉的省略号里的代码吗?这部份代码将剖析前面所说的3个参数,然后让统统运转起来。这段代码是一个函数,来自于browser-pack项目的[prelude.js][]。使人不测的是,它并不庞杂,而且写有雄厚的解释,很引荐你自行浏览。
所以,究竟要注意什么?
到这里,你已看过了Browserify是如何事变的。是时刻回到前面的题目了。起首,在每一个文件内,不再须要自行包装。
你能够已很习气相似下面如许的写法:
;(function(){
// Your code here.
}());
但你已相识到,Browserify的编译会将你的代码封装在部分作用域内,所以,你不再须要本身做这个事变,像如许会更好:
// Your code here.
相似的,假如你想用"use strict";
启用严厉形式,直接写在表面就能够了,这示意在某个文件的代码范围内启用严厉形式。
其次,坚持部分变量作风。我们很习气经由过程window.jQuery
和window.$
如许的全局变量来访问jQuery如许的库,但假如运用Browserify,它们都应只作为部分变量:
var $ = require("jquery");
$("#alice").text("Hello!");
这里的$
就只存在于这个文件的代码范围内(自力的作用域)。假如你在另一个文件内要运用jQuery,须要根据一样的体式格局去require()
。
但是,新的题目又来了,既然jQuery变成了这类部分变量的情势,那我们熟习的种种jQuery插件要如何运用呢?
browserify-shim
你肯定熟习如许的jQuery插件运用体式格局:
<script src="jquery.js"></script>
<script src="jquery.plugin.js"></script>
<script>
// Now the jQuery plugin is available.
</script>
许多jQuery插件是如许做的:默许window.jQuery
存在,然后取这个全局变量,把本身增加到jQuery中。明显,这在Browserify的构造体式格局里是没法用的。
为了让如许的“不兼容Browserify”(实际上是不兼容CommonJS)的JavaScript模块(如插件)也能为Browserify所用,因而有了[browserify-shim][]。
下面,以jQuery插件[jquery.pep.js][]为例,请看browserify-shim的运用要领。
运用示例
装置browserify-shim:
npm install browserify-shim --save-dev
然后在package.json
中做以下设置:
"browserify": {
"transform": [ "browserify-shim" ]
},
"browser": {
"jquery.pep" : "./vendor/jquery.pep.js"
},
"browserify-shim": {
"jquery.pep" : { "depends": ["jquery:jQuery"] }
}
末了是.js
中的代码:
var $ = require("jquery");
require("jquery.pep");
$(".move-box").pep();
完成!到此,经由Browserify编译后,将能够一般运转这个jQuery插件。
这是一个如何的历程呢?
在本例中,jQuery运用的是npm里的,而jquery.pep.js运用的是一个本身下载的文件(它与许多jQuery插件一样,还没有宣布到npm)。检察jquery.pep.js
源码,注意到它用了如许的包装:
;(function ( $, window, undefined ) {
// ...
}(jQuery, window));
能够看出,它默许当前环境中已存在一个变量jQuery
(假如不存在,则报错)。package.json
中的"depends": ["jquery:jQuery"]
是为它增加依靠声明,前一个jquery
示意require("jquery")
,后一个jQuery
则示意将其命名为jQuery
(赋值语句)。如许,插件代码运转的时刻就能够一般找到jQuery
变量,然后将它本身增加到jQuery中。
实际上,browserify-shim的设置并不轻易。针对代码包装(只管都不兼容CommonJS,但也存在多种状况)及运用场景的差别,browserify-shim有差别的解决方案,本文在此只引见到这。
关于设置的更多申明,请参照browserify-shim官方文档[]。另外,假如你以为browserify-shim有些难以明白或许对它的道理也有兴致,引荐浏览[这篇Stack Overflow上的回复][]。
固然,关于已处理了CommonJS兼容的库或插件(比方已宣布到npm),browserify-shim是不须要的。
实在另有的更多transform
在前面browserify-shim的例子中,"browserify": {"transform": [ "browserify-shim" ]}
实际上是Browserify的设置。能够看出,browserify-shim只是Browserify的个中一种transform。在它以外,另有[许多的transform][]可用,离别应对差别的需求,使Browserify的系统更加完美。
比方,还记得本文弁言里的Bower吗?[debowerify][]能够让经由过程Bower装置的包也能够用require()
援用。npm和bower同为包治理东西,Browserify示意你们都是我的翅膀。
一点提醒
Browserify是静态剖析编译东西,因而不支持动态require()
。比方,下面如许是不能够的:
var lang = "zh_cn";
var i18n = require("./" + lang);
文档资料
有关Browserify更细致的申明文档,请看[browserify-handbook][]。
结语
我以为Browserify很风趣,它用了如许一个名字,让你以为它彷佛只是一个Node的浏览器端转化东西。为此,它还完成了Node中大部份中心库的浏览器端完成。但实际上,它走到了更远的处所,并在JavaScript模块化开辟这个主要的领域中,创立了一个全新的系统。
喜好CommonJS的简约作风?请尝试Browserify!
(从新编辑自我的博客,原文地点:http://acgtofe.com/posts/2015/06/modular-javascript-with-browserify)