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

时间:2014-12-12 10:15:00

标签: javascript node.js build cross-browser browserify

我正在处理多个浏览器扩展程序/附加组件,这些扩展程序/附加组件通常至少需要在Chrome和Firefox中运行(有时也适用于Safari)。

最大的问题是保持DRY,另一方面保持源清洁。 从概念上讲,该项目通常包含以下部分:

  • Chrome的后台脚本
  • Firefox的后台脚本
  • 常见背景代码
  • Chrome的内容脚本
  • Firefox的内容脚本
  • 常用内容脚本代码
  • 其他脚本(例如:选项页面)。

为了减少代码重复,我有一个浏览器和预处理的内容脚本 它(在构建过程中删除其他特定于浏览器的部分)。

不幸的是,这会使内容脚本变得冗长而丑陋(并且难以发现)。

我想在我的项目中使用Browserify基本上用于整个JS代码。 仍然要这样做我需要一个解决方案来处理这种流程:

特定于浏览器的输入脚本 - >跨浏览器代码 - >浏览器特定的低级代码。

我会想象这种层次结构:

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

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

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

我希望使用Browserify的构建过程完全适合我 - 替换"根路径"低级代码取决于目标。

使用 package.json 进行黑客攻击是行不通的,因为我需要灵活数量的目标(甚至可能需要更深层次的依赖树)。

3 个答案:

答案 0 :(得分:2)

尝试使用factor-bundlepartition-bundle Browserify插件。它们都有助于将代码拆分为不同的条目文件和通用模块文件。 partition-bundle还包括可以异步加载不同包的脚本。

答案 1 :(得分:1)

执行此操作的一种可能方法是从代码中删除if / else require()次调用,而不是使用固定路径。

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

然后为每个浏览器运行单独的构建,使用browserify使用expose更改每个构建的路径解析内容。

所以在gulpfile.js中(例如,浏览器化API是重要的一点 - 您可以使用-r-x标志从shell执行相同操作,使用{ {1}}分隔require / expose值),为每个浏览器运行一次构建,每次都传入一个不同的: arg。

--browser=

对于常见的依赖项,您可以将它们捆绑到外部文件中,并将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

为方便起见,我通常在build.json脚本中放置构建链:

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'))
})

最后:

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

答案 2 :(得分:1)

这不是Browserify支持开箱即用的东西,但是如果你编写自定义源代码转换,理论上你可以实现你想要的东西。

作为替代方案,RaptorJS Optimizer提供的功能完全相同,您正在寻找开箱即用的功能。 (免责声明:我是这个工具的作者,它是我们在eBay上用于所有Node.js应用程序的工具)RaptorJS Optimizer允许您根据在优化期间启用的一组任意标志将一个模块重新映射到另一个模块。我们在eBay上使用此功能有条件地为不同的Web浏览器,设备,实验组等发送不同的代码。有关该功能的更多详细信息,请参阅:

仅供参考,RaptorJS Optimizer支持Browserify的所有功能,另外它还支持非JS依赖,异步加载,条件依赖,动态需求等。它仍然非常模块化,如Browserify,可以通过插件扩展教它如何处理新的依赖类型。与Webpack不同,RaptorJS Optimizer不会使CommonJS模块加载系统过载,因此仍然允许在Node.js和Web浏览器下运行代码。我们在eBay(和其他公司)的RaptorJS Optimizer上取得了很大的成功,所以我鼓励你查看它。