以前,我新开一个网页项目,然后想到要用jQuery,我会打开浏览器,然后找到jQuery的官方网站,点击那个醒目的“Download jQuery”按钮,下载到.js文件,然后把它丢在项目目录里。在需要用到它的地方,这样用<script>引入它:
<script src="https://www.jb51.net/path/to/jquery.js"></script>
2. Bower
后来,我开始用Bower这样的包管理工具。所以这个过程变成了:先打开命令行用bower安装jQuery。
bower install jquery
再继续用<script>引入它。
<script src="https://www.jb51.net/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="https://www.jb51.net/bundle.js"></script>
这就是依托Browserify建立起来的第三选择。
等下,怎么比以前变复杂了?
CommonJS风格的模块及依赖管理
其实,在这个看起来更复杂的过程中,require()具有非凡的意义。
Browserify并不只是一个让你轻松引用JavaScript包的工具。它的关键能力,是JavaScript模块及依赖管理。(这才是为师的主业)
就模块及依赖管理这个问题而言,已经有RequireJS和国内的Sea.js这些优秀的作品。而现在,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="https://www.jb51.net/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("https://www.jb51.net/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就可以得到结果了,可能像这样: