前端模块及依赖管理的新选择:Browserify
引言1. manually以前,我新开一个网页项目,然后想到要用jQuery,我会打开浏览器,然后找到jQuery的官方网站,点击那个醒目的“Download jQuery”按钮,下载到 <script src="path/to/jquery.js"></script> 2. Bower后来,我开始用[Bower][]这样的包管理工具。所以这个过程变成了:先打开命令行用 bower install jquery 再继续用 <script src="bower_components/jquery/dist/jquery.js"></script> 3. npm&Browserify现在,我又有了新的选择,大概是这样: 命令行用 npm install jquery 在需要用到它的JavaScript代码里,这样引入它: var $ = require("jquery"); 没错,这就是使用npm的包的一般方法。但特别的是,这个npm的包是我们熟知的 [Browserify][],正如其名字所体现的动作那样,让原本属于服务器端的Node及npm,在浏览器端也可使用。 显然,上面的过程还没结束,接下来是Browserify的工作(假定上面那段代码所在的文件叫 browserify main.js -o bundle.js 最后,用 <script src="bundle.js"></script> 这就是依托Browserify建立起来的第三选择。 等下,怎么比以前变复杂了? CommonJS风格的模块及依赖管理其实,在这个看起来更复杂的过程中, Browserify并不只是一个让你轻松引用JavaScript包的工具。它的关键能力,是JavaScript模块及依赖管理。( 就模块及依赖管理这个问题而言,已经有RequireJS[]这些优秀的作品。而现在,Browserify又给了我们新的选择。
Browserify参照了Node中的模块系统,约定用 像写Node那样去组织你的JavaScript,Browserify会让它们在浏览器里正常运行的。 安装及使用命令行形式命令行形式是官方贴出来的用法,因为看起来最简单。 Browserify本身也是npm,通过npm的方式安装: npm install -g browserify 这里 browserify entry.js -o bundle.js Browserify将递归分析你的代码中的 <script src="bundle.js"></script> 有关这个编译命令的配置参数,请参照[node-browserify#usage][]。如果你想要做比较精细的配置,命令行形式可能会不太方便。这种时候,推荐结合Gulp使用。 + Gulp形式结合Gulp使用时,你的Browserify只安装在某个项目内: npm install browserify --save-dev 建议加上后面的 接下来是 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的任务中。 在上面的代码中, 这样配置好之后,直接运行
如果你的代码比较多,可能像上图这样一次编译需要1s以上,这是比较慢的。这种时候,推荐使用[watchify][]。它可以在你修改文件后,只重新编译需要的部分(而不是Browserify原本的全部编译),这样,只有第一次编译会花些时间,此后的即时变更刷新则十分迅速。 有关更多Browserify + Gulp的示例,请参考[Gulp Recipes][]。 特性及简要原理使用Browserify来组织JavaScript,有什么要注意的地方吗? 要回答这个问题,我们先看看Browserify到底做了什么。下面是一个比较详细的例子。 项目内现在用到2个
module.exports = "aya";
var name = require("./name"); console.log("Hello! " + name); 然后对 (function e(t,n,r) { // ... })({ 1: [function (require,module,exports) { var name = require("./name"); console.log("Hello! " + name); },{"./name": 2}],2: [function (require,exports) { module.exports = "aya"; },{}] },{},[1]) //# sourceMappingURL=bundle.js.map 请先忽略掉省略号里的部分。然后,它的结构就清晰多了。可以看到,整体是一个立即执行的函数([IIFE][]),该函数接收了3个参数。其中第1个参数比较复杂,第2、3个参数在这里分别是 模块map第1个参数是一个Object,它的每一个key都是数字,作为模块的id,每一个数字key对应的值是长度为2的数组。可以看出,前面的 数组的第2个元素,是另一个map对应,它表示的是模块的依赖。 因此,这第1个复杂的参数,携带了所有模块的源码及其依赖关系,所以叫做模块map。 包装前面提到,原有的文件中的代码,被包装了起来。为什么要这样包装呢? 因为,浏览器原生环境中,并没有 缓存第2个参数几乎总是空的 入口模块第3个参数是一个数组,指定的是作为入口的模块id。前面的例子中, 实现功能还记得前面忽略掉的省略号里的代码吗?这部分代码将解析前面所说的3个参数,然后让一切运行起来。这段代码是一个函数,来自于browser-pack项目的[prelude.js][]。令人意外的是,它并不复杂,而且写有丰富的注释,很推荐你自行阅读。 所以,到底要注意什么?到这里,你已经看过了Browserify是如何工作的。是时候回到前面的问题了。首先,在每个文件内,不再需要自行包装。 你可能已经很习惯类似下面这样的写法: ;(function(){ // Your code here. }()); 但你已经了解到,Browserify的编译会将你的代码封装在局部作用域内,所以,你不再需要自己做这个事情,像这样会更好: // Your code here. 类似的,如果你想用 其次,保持局部变量风格。我们很习惯通过 var $ = require("jquery"); $("#alice").text("Hello!"); 这里的 然而,新的问题又来了,既然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插件是这样做的:默认 为了让这样的“不兼容Browserify”(其实是不兼容CommonJS)的JavaScript模块(如插件)也能为Browserify所用,于是有了[browserify-shim][]。 下面,以jQuery插件[jquery.pep.js][]为例,请看browserify-shim的使用方法。 使用示例安装browserify-shim: npm install browserify-shim --save-dev 然后在 "browserify": { "transform": [ "browserify-shim" ] },"browser": { "jquery.pep" : "./vendor/jquery.pep.js" },"browserify-shim": { "jquery.pep" : { "depends": ["jquery:jQuery"] } } 最后是 var $ = require("jquery"); require("jquery.pep"); $(".move-box").pep(); 完成!到此,经过Browserify编译后,将可以正常运行这个jQuery插件。 这是一个怎样的过程呢? 在本例中,jQuery使用的是npm里的,而jquery.pep.js使用的是一个自己下载的文件(它与很多jQuery插件一样,还没有发布到npm)。查看 ;(function ( $,window,undefined ) { // ... }(jQuery,window)); 可以看出,它默认当前环境中已存在一个变量 实际上,browserify-shim的配置并不容易。针对代码包装(尽管都不兼容CommonJS,但也存在多种情况)及使用场景的不同,browserify-shim有不同的解决方案,本文在此只介绍到这。 关于配置的更多说明,请参照browserify-shim官方文档[]。此外,如果你觉得browserify-shim有些难以理解或者对它的原理也有兴趣,推荐阅读[这篇Stack Overflow上的回答][]。 当然,对于已经处理了CommonJS兼容的库或插件(比如已经发布到npm),browserify-shim是不需要的。 其实还有的更多transform在前面browserify-shim的例子中, 比如,还记得本文引言里的Bower吗?[debowerify][]可以让通过Bower安装的包也可以用 一点提示Browserify是静态分析编译工具,因此不支持动态 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) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- C引用在函数范围内创建的实例
- swift设置状态栏为白色
- Access to the path "Library\UnityAssemblies&
- ruby-on-rails – Rails accepted_nested_attributes_for回
- Vue结合原生js实现自定义组件自动生成示例
- c# – 在asp.net mvc核心中绑定一个Guid参数
- iphone – 如何通过UIImageView(Objective-C)显示UILabel?
- Oracle sql优化必知――表的访问
- ios – swift UITabbaritem颜色
- c# – 使用Newtonsoft.Json反序列化DbGeometry