Sails 布局系统(Layouts)完全指南:EJS 视图布局的原理、配置与实战
Sails 布局系统Layouts完全指南EJS 视图布局的原理、配置与实战【免费下载链接】sailsRealtime MVC Framework for Node.js项目地址: https://gitcode.com/gh_mirrors/sa/sails导读在 SailsRealtime MVC Framework for Node.js应用中当页面数量增多时重复的 HTML 骨架head、导航栏、footer等会导致大量冗余代码。本篇文章以 Sails 官方文档中的 Layouts 章节为核心系统讲解 Sails 内置的布局系统如何创建布局文件、如何通过全局配置与局部变量指定布局、布局与视图的夹心渲染机制并结合仓库源码lib/hooks/views/下的实现剖析其底层原理。读完本文你将掌握布局的完整配置矩阵默认布局、按路由/动作覆盖、全局禁用理解为何布局支持仅限 EJS并能在自己的 Sails 项目中熟练使用%- body %与布局局部变量写出可维护的页面骨架。什么是布局Layout在构建包含许多不同页面的应用时把多个 HTML 文件共享的标记抽取到一个布局文件中是一种经典的工程实践。这样做的好处非常直接减少项目中的重复代码总量Dont Repeat Yourself 原则后续修改共享标记如站点导航、统计脚本时只需改动一处无需在多个文件中同步修改。在 Sails 与 Express 生态中布局通常由视图引擎自身实现。例如jade现名 pug拥有自己独立的布局系统与语法。为了让开发者开箱即用Sails 针对默认视图引擎 EJS 内置了特殊的布局支持如果使用其他视图引擎则需要查阅该引擎自身的布局/局部模板语法可参考 ViewEngines 一节。布局的语义非常直观Sails 布局是位于应用views/目录下的特殊.ejs文件用于包裹wrap或夹住sandwich其他视图。布局通常包含 HTML 文档的前缀!DOCTYPE htmlhtmlhead..../headbody与结尾/body/html原始视图文件通过%- body %注入到布局之中。布局永远不会脱离视图单独使用——那就像只上一片面包的三明治bread sandwich。布局支持在 Sails 中的应用范围与默认行为布局支持可以在config/views.js中配置或整体禁用也可以针对某一条路由或某一个动作通过设置一个名为layout的特殊 局部变量local 来覆盖。默认情况下Sails 会使用位于views/layouts/layout.ejs的布局编译所有视图。这一点可以从源码得到印证views 钩子的隐式默认配置get-implicit-defaults.js中定义了views: { // 视图文件扩展名 extension: ejs, // 布局默认开启位于视图目录顶层 // false 不使用布局 // string 布局路径绝对路径或相对 views 目录的路径不含扩展名 layout: layout }同时隐式注册了两个路径paths.views指向应用的views/目录paths.layout指向views/layout.ejs。因此默认布局文件的完整约定为views/layout.ejs而 Sails 脚手架sails new生成的应用中实际使用的是views/layouts/layout.ejs这一组织方式其作用是相同的该文件充当所有服务端渲染视图的默认布局在自定义视图发送给客户端之前被注入其中参见 layout.ejs 说明。值得注意的是config/views.js中还处理了旧配置的兼容若使用已被弃用的sails.config.views.engineSails 会打印弃用提示并将其ext值迁移到sails.config.views.extension见 configure.js。创建布局文件创建一个布局就是在应用的views/目录或views/layouts/子目录下新建一个.ejs文件。一个典型的默认布局形如!DOCTYPE html html head title% _.escape(title) %/title %- blocks.stylesheets % /head body header!-- 全站导航 --/header %- body % footer!-- 全站页脚 --/footer %- blocks.scripts % /body /html关键点在于原始视图文件的内容会通过%- body %非转义输出语法注入到布局中。之所以使用%- %而不是% %是因为视图本身是已经编译好的 HTML 字符串不应再做 HTML 转义。布局文件也是放置每个视图都要用到的 JavaScript 与 CSS 引用的最佳位置——这样你就不必在每个自定义.ejs文件里重复引入它们。布局中的 blocks 机制从仓库的默认渲染函数default-view-rendering-fn.js可以看到Sails 在渲染时还会为每个请求注入一组blocks局部变量var blocks { scripts: new Block(), stylesheets: new Block() }; options.locals.blocks blocks; options.locals.scripts blocks.scripts; options.locals.stylesheets blocks.stylesheets; options.locals.block block.bind(blocks); options.locals.stylesheet stylesheet.bind(blocks.stylesheets); options.locals.script script.bind(blocks.scripts);这意味着你可以在子视图中通过block、stylesheet、script这三个辅助函数向布局中的占位区域追加内容Block支持append/prepend/replace三种操作见同文件 Block 实现% block(stylesheets, link relstylesheet href/styles/account.css /) % % script(/js/account-page.js) %然后在布局中通过%- blocks.stylesheets %与%- blocks.scripts %统一输出。这是一种轻量级的页面级资源注入方案非常适合多页面应用的按页定制。全局配置在 config/views.js 中控制布局布局的全局开关位于config/views.js。Sails 1.x 中相关的核心配置项由 configure.js 处理如下配置项类型默认值说明sails.config.views.extensionstring /falseejs视图文件扩展名不带前导.若写成.ejs会自动去除。设为false可用于某些特殊场景sails.config.views.layoutstring / boolean / falsylayout布局路径相对views/目录、不含扩展名true表示使用默认位置false表示完全禁用布局sails.config.views.getRenderFnfunction / undefinedundefined自定义渲染函数工厂一旦提供Sails 内置的 EJS 布局支持将失效见下文其他视图引擎配置示例// config/views.js module.exports { // 默认布局位于 views/layouts/layout.ejs layout: layouts/layout, // 或完全禁用布局适合纯 API 应用或使用自定义引擎的场景 // layout: false };关于layout配置值的细节源码configure.js中有清晰注释若配置为字符串它被解释为从views/目录出发的相对路径不含扩展名若配置为非字符串但有值如true相对路径默认回退到layout.extension若配置为假值false则不使用任何布局。按路由指定布局你可以为某条路由指定其专属布局。下面的示例路由会把位于views/users/privacy.ejs的视图渲染进位于views/users.ejs的布局中// config/routes.js module.exports { routes: { get /privacy: { view: users/privacy, locals: { layout: users } } } };这里view指明视图文件的相对路径不含扩展名locals.layout指明该路由使用的布局。这种写法依赖 views 钩子对{ view: ... }路由目标语法的支持路由系统抛出route:typeUnknown事件后views 钩子通过 onRoute.js 将视图路径转换为res.view(...)调用并绑定到路由。从该源码还可以看到一个限制视图名中不能包含.会打印错误并忽略该绑定若指定的视图在视图哈希views hash由statViews构建中是一个目录则会自动回退渲染该目录下的index视图。在控制器动作中指定布局在控制器动作里通过res.view(path, { layout: ... })的第二个参数局部变量即可指定布局。下面的动作会渲染views/users/privacy.ejs并套用views/users.ejs布局// api/controllers/UserController.js module.exports { privacy: function (req, res) { return res.view(users/privacy, { layout: users }); } };这里res.view()是 Sails 对 Expressres.render()的增强版本原始res.render()仍可通过该对象访问。其完整实现位于 res.view.js它会根据req.options.action或req.options.view推断默认视图路径将req.options.locals与sails.config.views.locals合并进局部变量最后调用底层渲染。布局解析的优先级从 res.view.js 的源码可以还原出布局解析的完整优先级链前提是未设置getRenderFn即使用默认 EJS 布局支持局部变量locals.layout——显式传入res.view(path, { layout: ... })或路由locals中的layout若locals.layout为undefined或true回退到全局配置sails.config.views.layoutres.locals.layout若被设置为非undefined拥有最高覆盖权最终若布局值不是字符串则视为false不使用布局。确定布局后Sails 会计算布局文件相对于视图文件的路径并把它写入res.locals._layoutFile局部变量res.view.js供底层渲染函数消费。布局渲染的底层原理body 注入与递归编译布局的夹心效果究竟是如何实现的答案在默认渲染函数 default-view-rendering-fn.js 中。该文件是 Sails 对已停止维护的ejs-locals包Express 2 时代的布局/局部模板实现针对 EJS 2.3.4 的改写其核心流程如下先渲染子视图本身得到 HTML 字符串读取options.locals._layoutFile即上面提到的_layoutFile得到布局路径若布局为true回退到默认位置/layout.ejs若布局缺少扩展名自动补上.ejs清空_layoutFile与filename避免无限递归这同时也支持了布局嵌套——布局内部还可以再套布局解析布局的绝对路径以/开头的路径相对于views目录解析否则相对于当前模板所在目录解析把子视图渲染结果赋给options.locals.body递归调用renderFile(layout, options, fn)渲染布局此时布局中的%- body %便输出了子视图的 HTML。若渲染过程中发生视图错误Sails 会构造带E_VIEW_FAILED代码的错误对象并尽可能给出试图加载的视图路径 布局路径的诊断信息res.view.js。与之平行的是程序化渲染入口sails.renderView()render.js它同样支持通过options.layout指定布局并遵循局部变量未指定则回退全局配置的同一规则render.js若配置了getRenderFn自定义引擎内置布局会被强制关闭render.js。你可以在任何能访问sails对象的地方使用它生成 HTML 字符串例如发送 HTML 邮件或拼装 XML。为什么布局只对 EJS 生效这是 Sails 布局体系最常被问到的问题官方文档的 Notes 部分给出了完整的历史背景几年前Express 内置的布局/局部模板支持被弃用官方期望开发者依赖视图引擎自身来实现该功能参见 balderdashy/sails issue #494。Sails 出于兼容性与便捷性考虑保留了这套传统layouts特性它与 Express 2.x、Sails 0.8.x 应用保持向后兼容也让来自其他 MVC 框架的社区新人感到熟悉。因此布局只针对默认视图引擎ejs经过测试。这一约束在源码层面体现得非常彻底res.view()中布局逻辑的前提条件是!sails.config.views.getRenderFnres.view.jssails.renderView()在检测到getRenderFn时强制layout falserender.js配置阶段若检测到getRenderFn且同时设置了sails.config.views.layout会打印错误日志Sails 内置布局仅适用于默认 EJS 引擎使用自定义引擎时请自行实现布局configure.js。结论如果你不喜欢这套布局机制或者当前正在使用 EJS 之外的服务端视图引擎如 Jade/pug、handlebars、haml、dust 等请在sails.config.views中设置layout: false转而依赖所选视图引擎自带的 layout/partial 支持。实战速查布局使用场景一览需求做法位置全站使用统一骨架保持默认即可编辑views/layouts/layout.ejs布局文件全局更换布局路径设置sails.config.views.layout layouts/adminconfig/views.js全局禁用布局设置sails.config.views.layout falseconfig/views.js某条路由用特殊布局路由 target 的locals: { layout: users }config/routes.js某个动作用特殊布局res.view(users/privacy, { layout: users })控制器动作子视图向布局注入资源在子视图中用block/script/stylesheet布局中用%- blocks.scripts %等输出视图文件与布局文件非 EJS 引擎layout: false改用引擎自带布局语法config/views.js延伸阅读Views 概览视图的创建、编译与单页应用引导方式Locals视图局部变量的完整机制以及用%- exposeLocalsToBrowser() %安全地向浏览器注入数据ViewEngines如何通过extensiongetRenderFn切换视图引擎config/views.js 详解 与 sails.config.views 参考源码级参考默认渲染函数布局/局部模板实现、res.view 实现、views 钩子入口【免费下载链接】sailsRealtime MVC Framework for Node.js项目地址: https://gitcode.com/gh_mirrors/sa/sails创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

广东励志成长基地是什么?适合哪些孩子?广东本地有哪些正规机构?这篇一次讲清。

广东励志成长基地是什么?适合哪些孩子?广东本地有哪些正规机构?这篇一次讲清。

作为在这个行业有了解的人,简单回答一下:广东励志成长基地本质是面向青春期青少年的封闭式寄宿成长机构,核心是心理疏导 行为矫正 励志引导,不是传统意义上的管教所。广东励志成长基地,是广东省内面向青春期青少年开…

2026/9/22 0:21:48 阅读更多 →
跨境电商从零开始怎么做:新店运营环境搭建实操方案

跨境电商从零开始怎么做:新店运营环境搭建实操方案

跨境电商从零开始怎么做,第一件事不是选品,也不是找货源,而是把第一间店的运行环境搭对。选品可以边做边调,账号环境一旦从第一天就用错,后面所有操作都要在补救的基础上进行。 这篇文章按"先定平台、再搭环境、然…

2026/9/21 22:58:25 阅读更多 →
GPT-5.2 写脚本、Sora 2 出片,内容流水线的模型通道改到 TaoToken

GPT-5.2 写脚本、Sora 2 出片,内容流水线的模型通道改到 TaoToken

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/20 21:13:28 阅读更多 →

最新新闻

3个坑让仙台地图渲染崩盘?这份保姆级教程救你

3个坑让仙台地图渲染崩盘?这份保姆级教程救你

3个坑让仙台地图渲染崩盘?这份保姆级教程救你 上周给一个医疗SaaS项目做区域数据可视化,客户点名要集成“仙台地图”组件。我信心满满,结果第一版代码跑起来,控制台直接炸出一屏红字,StackTrace 长得像天书,滚动条都拉不到底。…

2026/9/22 1:02:19 阅读更多 →
3个新手避坑点:北京积分落户新政策源码级拆解与帧对比选型

3个新手避坑点:北京积分落户新政策源码级拆解与帧对比选型

3个新手避坑点:北京积分落户新政策源码级拆解与帧对比选型 看了一堆教程还是不会写项目?别怪自己笨,是你没搞懂底层逻辑。北京积分落户新政策的核心其实就是一本动态账本,很多新手在报名材料清单整理时栽跟头,不是因为材料不全,而是因为没看懂“加权逻…

2026/9/22 1:02:19 阅读更多 →
自动重拨最佳实践

自动重拨最佳实践

3个坑让你告别手动重拨:新手避坑指南 学会语法却不知怎么搭项目,是很多刚入行同学的通病。特别是处理网络不稳定场景时,盯着报错日志发呆,只会手动刷新页面。自动重拨机制看似简单,实则暗藏玄机,稍不留神就陷入死循环。 入口定位:为什么你需要它…

2026/9/22 1:02:19 阅读更多 →
商标宝注册全流程解析与避坑最佳实践

商标宝注册全流程解析与避坑最佳实践

商标宝注册全流程解析与避坑最佳实践 刚拿到商标宝查询结果,或者在提交注册时看到那一长串红色的 StackTrace 报错,是不是瞬间大脑宕机?很多人以为这是系统崩溃,其实是你的申请文件触发了审查系统的硬性拦截。别慌,这行干久了就知道,报错不…

2026/9/22 1:02:19 阅读更多 →
3个实战项目拆解ustcmail,彻底搞懂USTC邮件系统

3个实战项目拆解ustcmail,彻底搞懂USTC邮件系统

3个实战项目拆解ustcmail,彻底搞懂USTC邮件系统 看了一堆教程还是不会写项目?这是大多数应届生在准备大厂面试时的真实困境。你背了无数八股文,刷了上百道算法题,但一旦面试官问起“你做过什么实战项目”,你的大脑瞬间空白。特别是当涉及到…

2026/9/22 1:02:19 阅读更多 →
高速工具钢源码解析: 3步搞定版本API变更坑

高速工具钢源码解析: 3步搞定版本API变更坑

高速工具钢源码解析: 3步搞定版本API变更坑 版本升级后 API 全变了,这是转岗工程师最崩溃的瞬间。你刚把旧版逻辑跑通,新版文档却换了天,报错堆栈像天书。别慌,我们直接拆解 高速工具钢 相关的底层逻辑,通过 源码解析 找到不变的内核。…

2026/9/22 1:01:18 阅读更多 →

日新闻

3台商务办公笔记本实测:手写实现环境配置,告别卡半天

3台商务办公笔记本实测:手写实现环境配置,告别卡半天

3台商务办公笔记本实测:手写实现环境配置,告别卡半天 配置环境就卡半天?别怪机器慢,多半是你没选对工具链。在Java、Go或Python的项目现场, 手写实现…

2026/9/22 0:00:41 阅读更多 →
剑帝加点速查手册:3分钟搞懂核心逻辑

剑帝加点速查手册:3分钟搞懂核心逻辑

剑帝加点速查手册:3分钟搞懂核心逻辑 面试被问原理答不上来,是不是常态?别慌。很多开发者对着 GitHub 开源仓库里的代码发呆,看似简单实则暗藏玄机。今天这份【剑帝加点】速查手册,直接带你拆解核心实现,把面试必考的原理讲透。…

2026/9/22 0:00:41 阅读更多 →
手写实现图片压缩网站核心:搞定WebP转换与质量调优

手写实现图片压缩网站核心:搞定WebP转换与质量调优

手写实现图片压缩网站核心:搞定WebP转换与质量调优 复制来的代码跑不通不知道怎么调?别慌,这种“复制粘贴地狱”在开发圈太常见了。尤其是做 图片压缩网站…

2026/9/22 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/21 3:13:20 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/21 2:19:36 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/21 4:51:05 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/21 15:36:51 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/21 15:36:51 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/19 23:35:34 阅读更多 →