☰
Brunch 约定与默认行为完全指南:目录结构、CommonJS 模块包装、监视器与内置服务器
2026/10/6 2:16:26 网站建设 项目流程
  • 构建工具
  • 前端

【免费下载链接】brunch

🍴 Web applications made easy. Since 2011.

项目地址:https://gitcode.com/gh_mirrors/br/brunch
点击查看免费下载

Brunch 的核心设计哲学是"约定优于配置":项目只要遵循一套默认目录与命名约定,几乎零配置即可获得拼接、模块包装、增量编译、源码映射、文件监视与内置 HTTP 服务器等一整套前端构建能力。本文基于官方指南《Conventions and defaults》(packages/brunch-guide/content/en/chapter03-conventions-and-defaults.md)展开,结合仓库源码逐一剖析这些内置行为、对应的配置项与 CLI 选项,读完后你将能熟练使用brunch build/brunch watch --server,并通过paths、conventions、modules、server、watcher等配置精确改写默认行为,让 Brunch 适配自己的项目结构。

需要强调的是:本文描述的都是默认行为,没有任何一条是强制规定。遵循约定越多,你需要编写和维护的配置就越少;而几乎每一条约定都可以通过配置覆盖,以适应你的特定需求。

内置处理能力总览

Brunch 开箱即用地为你完成以下工作(运行brunch build的一次性构建,或brunch watch的监视模式):

  • 拼接(Concatenate):按类别(javascripts / stylesheets / templates)将源文件合并到你定义的 1 个或多个目标文件(joinTo);
  • 发布(Publish):将产物写入目标目录(默认public),同时把放在assets文件夹里的静态资源文件原样复制过去;
  • 模块包装(Wrap):在拼接阶段把相关 JS 源文件包装成CommonJS 模块(vendor目录除外);
  • 源码映射(Sourcemaps):为每个目标文件生成对应的 v3 多级 sourcemap,让你在浏览器开发者工具中直接调试构建链起点的原始源码,而不是运行时实际使用的拼接产物;
  • 监视(Watch):监听源文件与目录树的变更,任何相关改动都会触发一次增量构建更新(仅在brunch watch而非一次性构建模式下);
  • HTTP 服务器:提供比单纯静态文件服务更强的 HTTP 服务能力(仅在请求启动服务器时)。

拼接产物的具体形态取决于已安装的插件(例如 CoffeeScript、Sass、模板引擎插件),插件机制会在指南后续章节详述。下面先深入讲解这些默认行为本身。

配置文件:查找顺序与最小配置

Brunch 会在当前目录中按以下顺序查找第一个存在的文件作为配置:

  1. brunch-config.js(首选)
  2. brunch-config.coffee

历史上 Brunch 曾使用config.*这种过于通用的文件名,后来改为更明确的brunch-config.*。这一逻辑在源码中有直接体现:lib/utils/config.js中定义了DEFAULT_CONFIG_FILENAME = 'brunch-config',加载时会尝试brunch-config.js;若发现项目里仍存在config.coffee或brunch-config.coffee(Brunch 2.x 遗留),则会打印提示要求将其编译为 JS。

配置文件缺失时也有默认值

如果brunch-config.js不存在,lib/utils/config.js会合并一份最小默认配置:

module.exports = { files: { javascripts: { joinTo: 'app.js' }, stylesheets: { joinTo: 'app.css' } } };

也就是说,即使你完全不写配置,Brunch 也会默认把所有 JS 拼到app.js、把样式拼到app.css。配置文件也可以放在package.json的brunch字段中(源码tryToLoad中的 Case a)。

多环境覆盖(overrides)

指南提到"单一文件 + 按环境覆盖"已取代旧的指定配置文件方式。当前 CLI(lib/cli.js)仍保留-c, --config [path]选项用于指定配置文件路径,但更推荐的做法是在brunch-config.js中通过overrides字段区分环境:

module.exports = { files: { javascripts: {joinTo: 'app.js'}, stylesheets: {joinTo: 'app.css'} }, overrides: { production: { optimize: true, sourceMaps: false } } };

对应 CLI 选项为-e, --env [setting](可传逗号分隔的多个环境)和-p, --production(等价于--env production)。lib/utils/config.js的applyOverrides还会读取环境变量BRUNCH_ENV或NODE_ENV自动并入环境列表,且会为plugins.on/off做特殊的合并处理(对应源码注释中的 gh-826 问题修复)。仓库测试目录中的 test/fixtures/config-with-overrides.js 就是这一机制的验证用例。

目录约定:app / assets / vendor / public

默认情况下 Brunch 关注以下目录(均相对于配置文件所在目录解析):

目录作用
app整个源码库所在目录(除不适合 CommonJS 包装的第三方 JS 外),里面通常是一棵脚本、样式表与模板文件树
assets(通常是app/assets)其中的内容会被递归地原样复制到目标目录,不做任何处理
vendor内容与app一样参与拼接,但脚本文件不会被包装成模块;一般放不兼容模块包装的第三方库(如没有 UMD 加载器的库),或暂时仍需以全局变量方式使用的代码
_开头的文件任何文件名以_(下划线)开头的文件都被视为partial(局部文件),用于嵌入其他文件,因此不会单独处理
public默认的目标目录(与 Rack 等众多微型服务器/中间件的约定一致)

目录相关的自定义配置

上述行为全部可配置,源码默认值见 lib/utils/config-validate.js:

paths: { root: '.', public: 'public', watched: ['app', 'test', 'vendor'] // 默认还监视 test 目录 }, conventions: { ignored: [/\/_/, /vendor\/(node|j?ruby-.+|bundle)\//], // 下划线 partial + vendor 子目录 assets: /assets\//, vendor: /(^node_modules|vendor)\// }
  • paths.watched:一个路径数组,可自定义监听哪些源目录(旧的files.app、files.vendor等写法已移除,需改用paths.watched);
  • paths.public:目标目录;
  • conventions.assets/conventions.vendor:定义特殊处理目录的匹配规则,可以是正则表达式或函数;
  • conventions.ignored:定义不被独立处理的文件(即"忽略"文件)。

assets的复制逻辑在 lib/fs_utils/asset.js 中实现:找到assets约定目录后,把文件相对该目录的路径拼接到publicPath下作为目标路径,内容默认保持原样;is_ignored.js还会过滤掉点文件(dotfiles)、Emacs 缓存(~结尾)、__结尾文件等。此外paths.watched的默认值中已包含test目录,便于测试文件参与编译(可参考 test/fixtures/app/app.js 与 test/fixtures/public/ 的结构:源码在app,产物落到public/javascripts、public/stylesheets)。

CommonJS 模块包装:告别全局变量

模块化是正道。如果你的项目还在玩"全局变量"游戏、没有任何正式依赖管理,是时候改变思路了。指南成文时期正值模块格式之争,原生 ES6 模块最终胜出,而其形态与 Node 流行的CommonJS格式更接近;如今主流的同构 JS(isomorphic JS)方案(如 Browserify)也都是"按 Node 风格"打包代码供浏览器执行。

包装的默认行为

默认情况下,Brunch 会把你自己写的脚本文件(vendor中的除外)包装成CommonJS 模块:

  • 每个文件存在于一个闭包中,你显式声明的var、function因此都是模块私有的;
  • 文件内自动获得exports、module.exports与require(…);
  • 因此你可以放心地在文件顶部写"use strict";,不会把严格模式强加给第三方脚本。

从源码看,包装器定义在 lib/utils/modules.js,默认包装格式为:

require.register("模块名", function(exports, require, module) { // 你的代码 });

模块名默认由modules.nameCleaner决定,其默认实现是path => path.replace(/^app\//, ''),即剥掉app/前缀。判断"是否需要包装"的逻辑在 lib/fs_utils/source_file.js:_shouldBeWrapped要求文件是 JS 类型且不在 vendor 目录——这与指南的描述完全一致。

自定义包装

modules: { wrapper: 'commonjs', // 'commonjs' | false | 自定义函数 definition: 'commonjs', // 'commonjs' | false | 自定义函数 nameCleaner: path => path.replace(/^app\//, '') }
  • modules.wrapper:指定文件如何被包装(也可设为false彻底禁用包装);
  • modules.definition:指定运行时所需的模块注册器定义;
  • modules.nameCleaner:定义源文件路径如何映射为模块名。

注意:若npm.enabled为 true 而wrapper/definition均非commonjs,配置校验会直接报错(NPM_NOT_COMMONJS),因为 npm 依赖解析依赖 CommonJS 运行时。

Sourcemaps:调试原始源码的关键

任何发生在源文件与产物之间的处理步骤——拼接、压缩、编译——都会被 sourcemap 跟踪。每个目标文件都伴随一个匹配的 v3 多级 sourcemap 文件,让浏览器开发者工具等工具能直接显示并调试构建链起点的原始源文件,而不是运行时实际加载的目标文件。对于理智的调试体验,这几乎是必需品。

源码层面,lib/fs_utils/source_file.js 使用source-map包的SourceNode/SourceMapConsumer构建映射节点:包装器前缀、文件正文、包装器后缀分别被计入节点,且setSourceContent会把原始源码内容写入 map,因此即便构建链有多级转换(例如 CoffeeScript → JS → 拼接 → 压缩),浏览器也能一路回溯到最初的源文件。

自定义 sourcemap

sourceMaps: true // 默认值 // 其他可选值:false 禁用;'old' | 'absoluteUrl' | 'inline' 降级/改写生成方式

sourceMaps默认开启(lib/utils/config-validate.js中默认true);在production覆盖环境中默认被关闭(productionOverrideSchema中默认false)。指南对此的调侃是:"可以禁用或降级,但何必呢?"——除非构建产物体积或调试场景确有特殊要求,否则保持默认即可。

Watcher 监视器:增量、极速、可通知

Brunch 开箱即用地监视你的文件与目录树,一旦检测到变更就自动增量更新构建——这个更新极快。每次构建后,Brunch 都会输出一条详细日志,告诉你哪些源文件变了、哪些目标文件被更新、整个过程耗时多少。

监视模式由brunch watch命令触发(区别于一次性构建的brunch build)。从 lib/watch.js 可以看到底层实现:基于chokidar监听paths.watched+ 配置文件 + npm 组件文件;文件事件(add/change/unlink)进入FileList(lib/fs_utils/file_list.js),变更会被缓冲fileListInterval毫秒后合并为一次编译;若被监听的文件是配置文件本身(brunch-config.js或package.json),则会触发restartBrunch自动重载整个监视器——所以你改完配置甚至不用重启进程。

提醒:监视并非在任何平台都 100% 可靠(Windows 上偶有例外),可以通过下文设置尽量规避。

监视相关设置

fileListInterval: 65, // 两次变更检测之间的最小间隔(毫秒),用于合并连续变更 watcher: { usePolling: false, // 改用轮询检测:稍慢,但在个别平台上更可靠 awaitWriteFinish: false // 或 {stabilityThreshold: 50, pollInterval: 10} }
  • fileListInterval:源码默认 65(毫秒),FileList用它作为合并连续文件变更的时间窗,窗内所有变更会被视为同一次编译,从而避免频繁重建;
  • watcher.usePolling:切换底层变更检测技术为轮询模式,速度略慢但更可靠;
  • watcher.awaitWriteFinish:等待文件写完再触发编译,对编辑保存这类场景很有用(源码里true会被展开为{stabilityThreshold: 50, pollInterval: 10})。

桌面通知:错误发生时提醒你

你还可以在出错时收到系统通知(修改设置后 warning 与 info 级别也能通知),这样不必时刻盯着终端。这需要按操作系统安装通知工具(指南成文于 2015 年 4 月,如下步骤若失效请查阅所用通知模块的最新文档;Brunch 内部通过growlnpm 模块驱动通知):

  • macOS:安装 Ruby gemterminal-notifier:
$ sudo gem install terminal-notifier
  • Ubuntu:安装notify-send(来自libnotify-bin包):
$ sudo apt-get install libnotify-bin
  • Windows:安装 [Growl for Windows],再下载growlnotify并把二进制加入 PATH。

  • 所有系统:安装growlnpm 模块并跑一段测试代码验证:

$ npm install growl $ node -e "require('growl')('This is a test')"

通知行为可通过notifications配置调整:源码中它支持布尔值、级别数组或对象({app, icon, levels, notify}),lib/utils/config.js的setLoggyOptions会把levels映射到底层 loggy 库,并默认使用仓库的lib/logo.png作为通知图标。

watch 命令的完整 CLI 选项

从 lib/cli.js 可以看到brunch watch的完整选项:

brunch watch [path] -e, --env [setting] 指定一组覆盖设置 -p, --production 等价于 --env production -s, --server 为 public 目录在 localhost 上运行一个简易 HTTP 服务器 -n, --network 若指定了 --server,允许从网络访问 -P, --port [port] 若指定了 --server,指定监听端口 -d, --debug [pattern] 向 stdout 输出详细调试信息 -j, --jobs [num] 并行化构建 -c, --config [path] 指定 Brunch 配置文件路径 --stdin 监听 stdin,stdin 关闭时退出

注意:旧版的-p曾用于指定端口,新版改为-P(lib/cli.js中专门有checkForRemovedOptions对误用-p加数字的情况给出修正提示)。

内置 HTTP 服务器:3333 端口、pushState 与 CORS

Brunch 自带一个内置HTTP 服务器,可以静态地提供目标目录中的文件,让你用 HTTP 而非file://方式测试应用。这要求你运行在监视模式下。执行brunch watch --server后你将得到:

  • 在3333 端口上开启 HTTP 监听,/映射到你的目标目录(public);
  • 对目录 URL 或未知路径自动返回index.html(主要为了支持客户端pushState路由);
  • 附带CORS(跨域资源共享)响应头。

源码 lib/serve.js 印证了这一切:内置服务器用serve-handler实现,默认对**全部路径做 rewrite 到index.html(除非noPushState: true),并默认在响应头中加入Access-Control-Allow-Origin: *与Cache-Control: no-cache(除非noCors: true)。启动时会在终端打印app started on http://localhost:3333/之类的地址(-n/--network时列出网卡上的各 IPv4 地址)。

服务器相关配置

server: { port: 3333, // 默认端口 hostname: 'localhost', base: '', indexPath: 'index.html', run: false, // 由 --server 或配置开启 startupLogging: true, noPushState: false, noCors: false, stripSlashes: false // 也可以指定 path / command 来使用自定义服务器模块或外部命令 }
  • server是一个对象,可以修改每一项内置行为,或者"all-out"地指定你自己的自定义服务器模块(server.path指向导出startServer的模块,server.command则直接运行外部命令作为服务器);
  • CLI 选项-P(--port)可以不改配置直接换端口,-s/--server开启服务器,-n/--network允许网络访问;
  • 只有监视模式(persistent)下server.run才可能为 true(lib/utils/config.js的setConfigDefaults会在非持久模式下强制server.run = false)。

指南后续章节还会深入讲解服务器细节,甚至教你自己编写服务器(packages/brunch-guide-demos/7-custom-server就是一个brunch-server.js自定义服务器示例)。

插件加载约定:装进 node_modules 即被自动启用

插件是 Brunch 生态的扩展点(指南最后一章会详细探讨)。现在你只需要知道:使用一个 Brunch 插件,只需用 npm 安装它——它只要出现在node_modules与package.json中,就足以被 Brunch 检测并加载,并自动应用于它注册过的文件类型与环境。大多数 Brunch 插件被设计为无需任何配置即可直接可用。

从 lib/utils/plugins.js 的实现看,插件加载正是扫描项目package.json的依赖与开发依赖,过滤出符合 Brunch 插件约定的包(javascript-brunch、css-brunch这类基础插件在ignoredPlugins中被排除,避免重复处理),再按类型分组为 compilers / optimizers / linters 等。仓库的packages/addons/下汇集了大量现成插件,例如:

  • 语言编译:coffee-script-brunch、typescript-brunch、buble-brunch、less-brunch、sass-brunch、stylus-brunch;
  • 模板:handlebars-brunch、eco-brunch、jade-brunch、nunjucks-brunch;
  • 质量与优化:eslint-brunch、terser-brunch、clean-css-brunch、postcss-brunch;
  • 开发辅助:auto-reload-brunch、hmr-brunch、serve-brunch。

自定义插件启用策略

plugins: { on: ['plugin-name'], // 显式启用 off: ['plugin-name'], // 显式禁用 only: ['plugin-name'] // 只加载列表内的插件 }

你还可以通过plugins.<name>前缀的设置项对单个插件进行微调(例如plugins.autoReload.enabled)。组合overrides与plugins.on/off时,lib/utils/config.js的applyOverrides会对插件的启用/禁用列表做智能合并(同一插件不会同时出现在 on 与 off 中)。

结语

至此,你已经走完了本指南所有"总览层面"的内容:Brunch 的默认行为——按类别拼接、assets原样复制、CommonJS 包装、sourcemap 生成、增量监视、内置服务器与自动插件加载——以及覆盖它们的每一条配置入口(paths、conventions、modules、sourceMaps、fileListInterval、watcher、server、notifications、plugins)。是时候开始写真正的代码了!下一章 Starting from scratch(从零开始) 将带你进入具体的实操环节;如果你还没有跑过第一个项目,建议先回头看看 Getting started with Brunch(快速上手)。

「上一篇:快速上手 Getting started with Brunch • 下一篇:从零开始 Starting from scratch」

  • 构建工具
  • 前端

【免费下载链接】brunch

🍴 Web applications made easy. Since 2011.

项目地址:https://gitcode.com/gh_mirrors/br/brunch
点击查看免费下载
上一篇:career-ops 在 Windows 上执行 shell 脚本报 "syntax error near unexpected token"(CRLF 换行)怎么修复
下一篇:2025黑苹果终极指南:从零开始构建稳定macOS系统的完整解决方案

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询