在单个JavaScript代码库中使用Browserify的多种风格/目标

我正在开发多个浏览器扩展插件,这些扩展插件通常至less需要在Chrome和Firefox中使用(有时也在Safari中)。

最大的问题是保持干爽,另一方面保持源头清洁。 从概念上讲,这个项目通常有以下几个部分

  • Chrome的后台脚本
  • Firefox的后台脚本
  • 共同的后台代码
  • Chrome的内容脚本
  • 内容脚本为Firefox
  • 常见的内容脚本代码
  • 其他脚本(例如:选项页)。

为了减less代码重复,我有一个浏览器的内容脚本,并在构build过程中对其进行预处理(删除其他浏览器特定的部分)。

不幸的是,这使内容脚本真的很长,很难看(很难擦掉)。

我想基本上使用Browserify在我的项目中的整个JS代码。 仍然要这样做,我需要一个解决scheme来处理这种stream量:

浏览器特定的入口脚本 – >跨浏览器代码 – >浏览器特定的低级代码。

我会想象这种层次结构:

- Entry scripts - Browser A - Browser B - ... - Common code - Low-level code - Browser A - Browser B - ... 

因此,例如,在构build过程中,我希望Browserify为浏览器A创build一个入口脚本,然后将其与通用代码和浏览器A的低级代码捆绑在一起。 这是在通用代码中没有这种切换的情况下完成的:

 if(isBrowserA()) { var lowLevelModule = require("../lowLevel/browserA/module"); } else { var lowLevelModule = require("../lowLevel/browserB/module"); } 

我希望Browserify的构build过程能够为我做到这一点 – 根据目标replace低级代码的“根path”。

package.json破解它不会工作,因为我需要灵活的目标数量(甚至可能更深的依赖树)。

尝试使用factor-bundle或分区捆绑的 Browserify插件。 他们都帮助将代码分成不同的入口文件和一个通用的模块文件。 分区捆绑包还包括启用asynchronous加载不同捆绑包的脚本。

一种可能的方法是从代码中删除if / else require()调用,而不是使用固定path。

 var lowLevelModule = require("../lowLevel/module") 

然后为每个浏览器运行一个单独的构build,使用browserify更改每个构build使用exposepathparsing的内容。

因此,在一个gulpfile.js (例如, gulpfile.js API是重要的一点 – 你可以使用-r-x标志来执行相同的浏览,使用:来分隔require / expose值),运行为每个浏览器构build一次,每次传入不同的--browser= arg。

 var browserify = require('browserify') var gulp = require('gulp') var gutil = require('gulp-util') var source = require('vinyl-source-stream') var browser = gutil.env.browser // browserA, or browserB, or... // You might want to configure paths up-front separately, just // hardcoding below for brevity. gulp.task('bundle-app', function() { var b = browserify('./entry/' + browser + '/module', {detectGlobals: false}) b.require('./path/to/lowLevel/' + browser + '/module', {expose: '../lowLevel/module'}) return b.bundle() .pipe(source('app.js')) .pipe(gulp.dest('./build')) }) 

对于常见的依赖关系,您可以将它们捆绑到一个外部文件中,并将external调用添加到您的浏览器特定包中:

 var commonModules = ['module1', 'module2'] gulp.task('bundle-common', function() { var b = browserify({detectGlobals: false}) commonModules.forEach(function(module) { b.require(module) }) return b.bundle() .pipe(source('common.js')) .pipe(gulp.dest('./build')) }) gulp.task('bundle-app', function() { var b = browserify('./entry/' + browser + '/module', {detectGlobals: false}) commonModules.forEach(function(module) { b.external(module) }) b.require('./path/to/lowLevel/' + browser + '/module', {expose: '../lowLevel/module'}) return b.bundle() .pipe(source('app.js')) .pipe(gulp.dest('./build')) }) 

为了方便起见,我通常会将构build链放在package.json脚本中:

 "scripts": { "build": "gulp bundle-common && gulp bundle-app --browser=browserA && gulp bundle-app --browser=browserB" } 

最后:

 npm run build 

这不是Browserify支持的开箱即用,但是如果您编写自定义的源代码转换,理论上可以达到您想要的效果。

另外, RaptorJS优化器提供了与开箱即用相同的function。 (免责声明:我是这个工具的作者,它是我们在eBay上为我们所有的Node.js应用程序使用的工具)RaptorJS优化器允许您根据一组在任何优化过程中启用的任意标志将一个模块重新映射到另一个模块。 我们在eBay上使用这个function来有条件地发送不同的代码给不同的浏览器,设备,实验组等等。关于这个function的更多细节,请看:

仅供参考,RaptorJS Optimizer支持Browserify的所有function,并支持非JS依赖,asynchronous加载,条件依赖,dynamic需求等。它仍然像Browserify一样非常模块化,可以通过插件进行扩展来教它如何处理新的依赖types。 与Webpack不同的是,RaptorJS Optimizer不会重载CommonJS模块加载系统,因此代码仍然可以在Node.js和Web浏览器下运行。 我们在eBay(以及其他公司)的RaptorJS Optimizer上取得了很大的成功,所以我鼓励你们检查一下。