前端模块及依靠治理的新挑选:Browserify

弁言

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》

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就能够获得效果了,能够像如许:

《前端模块及依靠治理的新挑选: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}。而在key2name.js,它没有依靠,因而其数组第二个元素是空Object{}

因而,这第1个庞杂的参数,携带了统统模块的源码及其依靠关联,所以叫做模块map。

包装

前面提到,原有的文件中的代码,被包装了起来。为何要如许包装呢?

由于,浏览器原生环境中,并没有require()。所以,须要用代码去完成它(RequireJS和Sea.js也做了这件事)。这个包装函数供应的3个参数,requiremoduleexports,恰是由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.jQuerywindow.$如许的全局变量来访问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

    原文作者:EdwardUp
    原文地址: https://segmentfault.com/a/1190000002941361
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞