尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Express 视图渲染与 EJS 模板引擎:从服务端渲染到可复用模板的完整实战指南

Express 视图渲染与 EJS 模板引擎:从服务端渲染到可复用模板的完整实战指南 Express 视图渲染与 EJS 模板引擎从服务端渲染到可复用模板的完整实战指南【免费下载链接】curriculumThe open curriculum for learning web development项目地址: https://gitcode.com/GitHub_Trending/cu/curriculum导读本文围绕 Node.js/Express 应用中的视图层展开系统讲解如何用 EJS 模板引擎在服务端生成包含真实数据的 HTML 页面涵盖环境配置、% %/% %/%- %语法、res.render渲染流程、locals作用域机制、include模板复用以及静态资源托管。读完本文你将掌握在 Express 项目中从零搭建 EJS 视图体系的能力并理解模板引擎相对客户端渲染SPA的核心差异为后续构建留言板、表单处理等实战项目打下基础。从 SPA 到服务端渲染为什么需要模板引擎在之前的大量项目中构建的往往是 SPA单页应用服务器返回的 HTML 文件本质上是空的页面通过一个script标签让客户端用 JavaScript 渲染。而现在课程的重点转向在服务器端渲染 HTML——即由服务器创建真正包含页面内容的 HTML 文件。视图Views是应用中面向用户的部分在本课程中即指 HTML 文件。尽管在更早的项目里服务器已经会向用户发送 HTML 文件但那些文件是静态的而我们的许多使用场景要求视图随数据动态变化。因此我们使用模板引擎template engine来创建视图。正如其名我们在代码库中编写模板文件当响应服务器请求时模板会被转换成 HTML。模板文件中定义的任何变量都会被替换为真实数据。此外我们还可以在模板文件中插入条件判断和/或循环逻辑例如在用户登录后渲染其用户名——这是纯 HTML 无法做到的。当然客户端 JavaScript 仍然可以通过script标签添加不过本课程暂时不会在客户端做太多花哨的事。本课程选用 EJSEmbedded JavaScript作为模板引擎。EJS 的语法与 HTML 非常相似相比其他模板引擎学习曲线相对较低——你可以把它理解为在 HTML 中嵌入 JavaScript。为什么不用 React这里有一个很自然的问题为什么不直接用 React 做视图层原因在于课程尚未教授 React Server Components它与目前所了解的 React 差别很大单独配置并不像 EJS 这类模板引擎那样简单而如果使用一个预配置但高度抽象的全栈框架前后端界限会被模糊不利于专注学习后端基础。因此本课程选用 EJS 作为视图渲染方案。在 Express 项目中配置 EJS安装 EJS在项目终端中执行npm install ejs该命令会把ejs添加到项目的dependencies中。接着在项目根目录下创建一个名为views的子文件夹用于存放所有模板文件。设置视图引擎与视图目录要让应用知道用 EJS 作为模板引擎以及去哪里找视图文件需要在app.js中设置两个应用属性// app.js app.set(views, path.join(__dirname, views)); app.set(view engine, ejs);同时在文件靠顶部的位置导入 Node 的 Path CommonJS 模块// app.js const path require(node:path);app.set(views, ...)告诉 Express 模板文件存放在哪个目录这里是用path.join拼出的绝对路径避免相对路径在不同启动目录下的歧义app.set(view engine, ejs)将默认视图引擎注册为 EJS此后res.render(index)会被自动解析为views/index.ejs。完成以上两步后Express 便知道渲染视图时去/views子目录中查找模板并用 EJS 引擎进行编译。在路由中渲染视图创建一个模板文件views/index.ejs!-- index.ejs -- html body % message % /body /html然后在app.js的路由中渲染它// app.js app.get(/, (req, res) { res.render(index, { message: EJS rocks! }); });理想情况下路由应该定义在独立的 router 文件中但本课为了聚焦视图层直接在app.js中定义。启动服务器并访问/路由浏览器中应显示EJS rocks!在浏览器开发者工具中检查 HTML可以看到页面的结构与我们写的 EJS 模板完全一致只是message变量被替换成了它的值。当请求命中/路由时res.render(index, { message: EJS rocks! })就是发送响应的那一行。由于我们已经定义了views与view engine两个应用属性res.render的第一个参数会被解析为在指定文件夹中寻找名为 index 的模板第二个参数则是要提供给该模板的变量对象。EJS 核心语法三种标签的语义与差异EJS 中%与%标签允许我们在模板中使用 JavaScript从而编写条件语句、for循环以及使用变量。要输出一个变量的值则使用%标签。下面是一个包含数组与循环逻辑的快速示例% const animals [Cat, Dog, Lemur, Hawk] % ul % animals.map((animal) { % li% animal %s are cute/li % }) % /ul三种标签的核心区别标签作用输出是否转义% ... %执行 JavaScript 逻辑循环、条件、定义变量不产生输出—% ... %输出变量/表达式的结果是转义为 HTML 实体%- ... %输出变量/表达式的结果否原样输出 HTML第三个标签%-raw 输出在本文后续的include与未转义输出场景中会反复用到。需要注意的是转义是重要的安全机制在 表单与数据处理 一课中仓库专门用About Me文本域的例子演示了 XSS 风险——如果用户输入scriptalert(Hacked!);/script而被%- %原样输出浏览器会直接执行这段脚本而% %会把等特殊字符转义成lt;等 HTML 实体使攻击代码变得无害。这也解释了为什么include这类本意就是要输出 HTML 片段的场景才应使用%- %。locals视图中的数据作用域在上面的示例中模板文件是如何知道message变量的当我们渲染视图时EJS 可以访问我们传给res.render的对象中的任何属性以及 Express 的 res.locals 对象上的任何属性。res.locals的典型用途是在某个中间件函数中把值放进 locals但直到中间件链的后面才调用res.render时需要把值传递给视图。EJS 会把这些属性存储在一个名为locals的对象中你可以在视图中访问它。与浏览器中的全局window对象类似你可以通过locals.message访问message变量也可以直接写message。% locals.message % !-- 与 % message % 等价 --警惕未定义变量的引用错误如果尝试访问模板中未在res.render的 locals 参数或res.locals中定义的变量可能引发引用错误reference error。例如尝试访问未定义的foo变量时locals.foo会返回undefined而直接写foo则会抛出引用错误。可以在index.ejs中先输出locals.foo验证再替换为直接输出foo对比!-- index.ejs -- html body % message % !-- replace the below with the output of just foo -- % locals.foo % /body /html实践建议当变量可能不存在时优先通过locals.xxx访问并用条件判断兜底避免模板渲染中途崩溃。利用 locals 做全局视图数据res.locals的能力在仓库后续课程中有更深的体现。在 身份认证基础 一课中展示了通过自定义中间件向所有视图注入currentUser的做法app.use((req, res, next) { res.locals.currentUser req.user; next(); });这样所有视图都能直接使用currentUser无需在每个控制器中手动传递——登录页模板甚至直接用它做条件渲染% if (locals.user) { %显示欢迎语否则显示登录表单。这正是locals作为跨中间件、跨视图的数据总线的典型用例。可复用模板用 include 组合页面你可能希望在不同页面间共享网页组件比如侧边栏或页头。要在页面中插入这类组件需要使用include命令。它需要传入被插入的文件名以及可选的要传递的数据对象。先创建一个名为navbar.ejs的导航栏组件!-- navbar.ejs -- nav ul % for (let i 0; i links.length; i) { % li a href% links[i].href % span % links[i].text % /span /a /li % } % /ul /nav注意导航栏期望一个links变量。要把数据传入导航栏就在res.render()渲染模板时把它作为 locals 变量传入。修改app.js定义一个links对象并在/路由处理函数中传给render// app.js const links [ { href: /, text: Home }, { href: /about, text: About }, ]; app.get(/, (req, res) { res.render(index, { links: links }); });然后修改之前创建的index.ejs删除% message %那行改用include引入导航栏并传入links!-- index.ejs -- html head titleHomepage/title /head body %- include(navbar, {links: links}) % /body /html启动服务器即可看到导航栏被渲染进index.ejs。可复用模板可以用于在所有页面中包含页头和页脚。注意这里include使用的是原始输出标签%-目的是避免 HTML 输出被二次转义。include 的动态列表渲染再用include以另一种方式动态渲染变量列表。在app.js的links变量下方新增users数组并在渲染index时一并传入// app.js const links [ { href: /, text: Home }, { href: /about, text: About }, ]; const users [Rose, Cake, Biff]; app.get(/, (req, res) { res.render(index, { links: links, users: users }); });在views目录下新建视图user.ejs!-- views/user.ejs -- li% user %/li然后在index.ejs中添加如下代码块!-- index.ejs -- ul % users.forEach((user) { % %- include(user, {user: user}); % % }); % /ul如果一切正常渲染index.ejs时 Rose、Cake 和 Biff 三个名字都会出现在页面上。views 目录内的嵌套子目录include的路径支持views目录内的嵌套子目录。例如把views/user.ejs移动到views/users/user.ejs那么index.ejs中的引用路径也要相应从user改为users/user!-- index.ejs -- ul % users.forEach((user) { % %- include(users/user, {user: user}); % % }); % /ul在 表单与数据处理 一课中仓库正是用views/partials/errors.ejs存放错误提示 partial并在createUser.ejs、updateUser.ejs等多个视图中通过%- include(partials/errors.ejs) %复用——这是partials 目录 include组织可复用视图组件的成熟模式。托管静态资源CSS、图片与前端脚本使用 EJS 托管静态资源的方式与此前直接使用 HTML 时类似可以在模板文件的head中通过link标签引入外部文件。关键在于应用需要知道从哪里提供资源。假设express已安装在app.js中设置以下两行// app.js const assetsPath path.join(__dirname, public); app.use(express.static(assetsPath));express.static()是一个启用静态资源服务的中间件函数我们告诉它以public目录作为资源根目录。在public目录根下创建styles.css/* public/styles.css */ body { color: red; }要在index.ejs中引入styles.css在head中设置link标签!-- index.ejs -- head link relstylesheet href/styles.css /head此时index.ejs页面中的文字就会显示为红色。静态资源机制让视图可以在保留服务端动态渲染能力的同时正常加载 CSS、图片和客户端脚本。视图层在 Express 请求处理链中的位置将视图渲染放在更大的请求处理语境中看在 控制器 一课中控制器controller扮演终极中间人的角色——它知道要向模型问哪些问题、想把哪个视图渲染并送回浏览器但让模型去处理数据的重活、让视图自己去拼装 HTML。res.render正是控制器与视图之间的桥梁它接收视图模板名与数据对象渲染出完整的 HTML 响应。这也呼应了本文开头的核心转变视图不再是空的 HTML 客户端 JS而是服务端根据控制器提供的数据动态生成的页面。之后在 迷你留言板项目 中这一模式被直接落地res.render(index, { title: Mini Messageboard, messages: messages })把消息数组传给模板在views目录的 index 模板中循环渲染每一条消息的用户、文本与日期——这正是本课res.render locals 循环输出的综合演练。实战练习通读 EJS 官方文档重点理解各标签的转义行为与include的完整参数形式。阅读 Express 官方的模板引擎资源。该资源示例使用 Pug语法不同但信息可作为本课的有益补充。为本课一直在开发的应用新增一个 about 页面视图并使其在/about路由上渲染。创建一个可复用的 footer 模板并在应用的所有路由中渲染它。知识自检与在客户端渲染的 SPA 相比模板引擎的用途是什么如何为 Express 项目配置 EJS%与%标签的区别是什么如何在控制器回调中渲染一个视图模板如何被包含进其他模板【免费下载链接】curriculumThe open curriculum for learning web development项目地址: https://gitcode.com/GitHub_Trending/cu/curriculum创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表