ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Brunch 约定与默认行为完全指南:目录结构、CommonJS 模块包装、监视器与内置服务器

Brunch 约定与默认行为完全指南:目录结构、CommonJS 模块包装、监视器与内置服务器 构建工具前端【免费下载链接】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 会在当前目录中按以下顺序查找第一个存在的文件作为配置brunch-config.js首选brunch-config.coffee历史上 Brunch 曾使用config.*这种过于通用的文件名后来改为更明确的brunch-config.*。这一逻辑在源码中有直接体现lib/utils/config.js中定义了DEFAULT_CONFIG_FILENAME brunch-config加载时会尝试brunch-config.js若发现项目里仍存在config.coffee或brunch-config.coffeeBrunch 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指南提到单一文件 按环境覆盖已取代旧的指定配置文件方式。当前 CLIlib/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.jspaths: { 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.watchedpaths.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格式更接近如今主流的同构 JSisomorphic 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 → 拼接 → 压缩浏览器也能一路回溯到最初的源文件。自定义 sourcemapsourceMaps: 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进入FileListlib/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-notifierUbuntu安装notify-send来自libnotify-bin包$ sudo apt-get install libnotify-binWindows安装 [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 监听 stdinstdin 关闭时退出注意旧版的-p曾用于指定端口新版改为-Plib/cli.js中专门有checkForRemovedOptions对误用-p加数字的情况给出修正提示。内置 HTTP 服务器3333 端口、pushState 与 CORSBrunch 自带一个内置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才可能为 truelib/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点击查看免费下载相关推荐TachideskSuwayomi-Server数据目录完全指南默认位置、目录结构与 rootDir 自定义配置TachideskSuwayomi Server数据目录完全指南默认位置、目录结构与 rootDir 自定义配置 Tachidesk Server即 S后端VuePress 目录结构与默认页面路由完全指南VuePress 目录结构与默认页面路由完全指南 本文以 VuePress 官方文档《目录结构》为骨架结合本仓库 gh_mirrors/vu/vuepres前端文档SSRHIXL C 接口全解析昇腾单边通信库核心 API、初始化配置与实战指南HIXL C 接口全解析昇腾单边通信库核心 API、初始化配置与实战指南 本文以 CANN hixl 开源仓库中的 HIXL C 接口文档 https构建工具前端上一篇career-ops 在 Windows 上执行 shell 脚本报 syntax error near unexpected tokenCRLF 换行怎么修复下一篇2025黑苹果终极指南从零开始构建稳定macOS系统的完整解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进