前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket

# koa2加载模板引擎

30 秒速记

  • 模板引擎 = 模板文件 + 数据 → HTML 字符串,Koa 里用 koa-views 接入,ctx.render() 渲染
  • ctx.render 是异步的,必须 await,否则 Koa 先返回 404
  • 用户内容用转义输出(EJS 的 <%= %>),这是防 XSS 的第一道关
  • 模板名不能直接用用户输入,ctx.render(ctx.query.page) 这种写法要拦
  • 渲染报错交给统一错误中间件,生产别把模板路径和堆栈返回给前端

模板引擎干的事很简单:拿一份带占位符的 HTML 模板,填进数据,生成最终的 HTML 返回给浏览器。 在 Koa 里一般用 koa-views 中间件把模板引擎接进来,它会在 ctx 上挂一个 render 方法。最常见的坑是忘了 await ctx.render(),中间件函数提前结束,Koa 发现没有 body 就直接回了 404。安全上要记住两点:用户输入的内容用转义输出,模板名别拿用户参数拼。

理解「koa2加载模板引擎」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 middleware 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

Koa 接模板引擎的标准写法:

const Koa = require('koa')
const views = require('koa-views')
const path = require('path')
const app = new Koa()

// 统一错误处理:放最前面,包住后面所有中间件
app.use(async (ctx, next) => {
  try {
    await next()
  } catch (err) {
    ctx.status = err.status || 500
    ctx.body = process.env.NODE_ENV === 'production' ? '服务器内部错误' : err.stack
    ctx.app.emit('error', err, ctx)
  }
})

app.use(views(path.join(__dirname, 'view'), { extension: 'ejs' }))

app.use(async (ctx, next) => {
  ctx.state.user = { name: 'Tom' } // 所有模板都能直接用 user
  await next()
})

app.use(async (ctx) => {
  await ctx.render('index', { title: 'hello koa2' }) // 少了 await 会返回 404
})

app.listen(3000)
@前端进阶之旅: 代码已经复制到剪贴板

几个要点:模板目录用 __dirname 拼绝对路径,不依赖启动目录;错误中间件放最前面,因为 Koa 的洋葱模型里只有外层能 catch 到内层抛出的异常;模板名写死在代码里,不从请求参数里取。EJS 自带编译缓存选项 cache,开发时关掉方便改模板立即生效,生产打开避免每次请求重复编译。

面试官追问

追问 1页面一直返回 Not Found,但模板文件明明存在,最可能漏了什么?
参考回答

漏了 await。ctx.render 返回 Promise,不 await 的话中间件已经结束,Koa 看 body 为空就回 404。写成 await ctx.render('index', data)。

追问 2模板里用了 product.title,接口偶尔没返回 product,页面直接 500,怎么兜?
参考回答

模板里访问 undefined 的属性会抛错。数据层先保证结构,缺了就走 404 或降级数据;模板里也可以写 product?.title,EJS 支持现代 JS 语法。错误要被统一错误中间件接住,别把堆栈吐给用户。

追问 3多租户想按 ctx.query.theme 选模板目录,直接拼路径有什么问题?
参考回答

用户能传 ../../xxx 去加载不该加载的文件。先用白名单把参数映射成固定主题名,不在名单里就用默认主题。

追问 4全局要用的 user,每个 render 都传一遍太麻烦,有更好的写法吗?
参考回答

在前面的中间件里写 ctx.state.user = user,koa-views 会把 ctx.state 合并进模板变量,模板里直接用 user。

追问 5现在都前后端分离了,服务端模板还有用吗?
参考回答

有。内容页、营销页、后台小工具这类交互少、要首屏 HTML 和 SEO 的场景,用模板直出成本最低。交互复杂的应用才值得上 React / Vue 加 SSR 框架。

# koa2加载模板引擎

# 快速开始

30 秒速记

  • 三步:装 koa-views + ejs → app.use(views(dir, { extension: 'ejs' })) → await ctx.render('index', data)
  • 目录结构:index.js 加一个 view/ 目录放 .ejs 文件
  • render 只传模板名,不带扩展名,koa-views 按 extension 补全
  • 跑起来访问 localhost:3000,右键查看源码,能看到数据已经填进 HTML

快速开始就三步:装依赖、注册 koa-views、在中间件里 await ctx.render。 目录结构也很简单,入口 index.js 旁边放一个 view 文件夹,模板都放里面。注册时告诉 koa-views 模板目录和扩展名,之后 render('index') 就会去找 view/index.ejs。跑起来以后我会右键看一下页面源代码,确认标题这些已经是填好的文字,而不是 <%= title %> 原样输出,这说明渲染发生在服务端。

理解「快速开始」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 module 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

面试官追问

追问 1页面源码里直接显示 <%= title %>,没被替换,为什么?
参考回答

模板没走引擎,多半是被当静态文件发出去了,比如 koa-static 的目录里也有这个文件。或者 extension 和文件后缀对不上,koa-views 没找到引擎。检查请求到底命中了哪个中间件。

追问 2模板文件想用 .html 后缀,又想用 EJS 语法,怎么配?
参考回答

views(dir, { map: { html: 'ejs' } }),意思是 .html 文件交给 ejs 渲染,render('index') 就会找 index.html。

追问 3从别的目录启动服务,报找不到模板,怎么改?
参考回答

模板目录要用 path.join(__dirname, 'view'),别写 './view',相对路径是按启动目录算的。

追问 4改了模板刷新没变化,要重启才生效,为什么?
参考回答

开了模板缓存。开发环境关掉缓存,生产再打开;或者开发时用 nodemon 监听 .ejs 文件自动重启。

# 安装模块

30 秒速记

  • koa-views 是适配层,负责在 ctx 上挂 render;ejs 才是真正解析模板的引擎,两个都要装
  • npm install --save koa-views ejs,npm 5 以后 --save 是默认行为,可以省略
  • 依赖必须写进项目的 package.json,全局装的包 require 找不到
  • koa-views 后续版本迁到了 @ladjs/koa-views,新项目装之前看一眼当前维护的包名

接 EJS 要装两个包:koa-views 负责把模板渲染能力接到 Koa 上,ejs 负责真正解析模板语法。 koa-views 本身不认识任何模板语法,它根据扩展名去找对应的引擎来渲染,所以只装它会报找不到引擎。两个包都要装在项目里,写进 package.json,换台机器 npm install 就能复现。npm 5 之后 install 默认就会写进 dependencies,--save 可写可不写。

# 安装koa模板使用中间件
npm install --save koa-views

# 安装ejs模板引擎
npm install --save ejs
@前端进阶之旅: 代码已经复制到剪贴板

面试官追问

追问 1只装了 koa-views 没装 ejs,会怎样?
参考回答

渲染时报找不到 ejs 模块。koa-views 只是个调度层,具体引擎要单独装。

追问 2同事说他电脑全局装过 ejs,项目里就不用装了,对吗?
参考回答

不对。require 不会去全局目录找包,他本机能跑可能是上层目录碰巧有 node_modules。依赖一定要写进项目 package.json,CI 和别人才装得上。

追问 3--save 和 --save-dev 该用哪个?
参考回答

--save。koa-views 和 ejs 是运行时依赖,生产环境渲染页面要用,放 devDependencies 的话

基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket
完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

koa2加载模板引擎|Nodejs专题系列

# koa2加载模板引擎

30 秒速记

  • 模板引擎 = 模板文件 + 数据 → HTML 字符串,Koa 里用 koa-views 接入,ctx.render() 渲染
  • ctx.render 是异步的,必须 await,否则 Koa 先返回 404
  • 用户内容用转义输出(EJS 的 <%= %>),这是防 XSS 的第一道关
  • 模板名不能直接用用户输入,ctx.render(ctx.query.page) 这种写法要拦
  • 渲染报错交给统一错误中间件,生产别把模板路径和堆栈返回给前端

模板引擎干的事很简单:拿一份带占位符的 HTML 模板,填进数据,生成最终的 HTML 返回给浏览器。 在 Koa 里一般用 koa-views 中间件把模板引擎接进来,它会在 ctx 上挂一个 render 方法。最常见的坑是忘了 await ctx.render(),中间件函数提前结束,Koa 发现没有 body 就直接回了 404。安全上要记住两点:用户输入的内容用转义输出,模板名别拿用户参数拼。

理解「koa2加载模板引擎」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 middleware 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

Koa 接模板引擎的标准写法:

const Koa = require('koa')
const views = require('koa-views')
const path = require('path')
const app = new Koa()

// 统一错误处理:放最前面,包住后面所有中间件
app.use(async (ctx, next) => {
  try {
    await next()
  } catch (err) {
    ctx.status = err.status || 500
    ctx.body = process.env.NODE_ENV === 'production' ? '服务器内部错误' : err.stack
    ctx.app.emit('error', err, ctx)
  }
})

app.use(views(path.join(__dirname, 'view'), { extension: 'ejs' }))

app.use(async (ctx, next) => {
  ctx.state.user = { name: 'Tom' } // 所有模板都能直接用 user
  await next()
})

app.use(async (ctx) => {
  await ctx.render('index', { title: 'hello koa2' }) // 少了 await 会返回 404
})

app.listen(3000)
@前端进阶之旅: 代码已经复制到剪贴板

几个要点:模板目录用 __dirname 拼绝对路径,不依赖启动目录;错误中间件放最前面,因为 Koa 的洋葱模型里只有外层能 catch 到内层抛出的异常;模板名写死在代码里,不从请求参数里取。EJS 自带编译缓存选项 cache,开发时关掉方便改模板立即生效,生产打开避免每次请求重复编译。

面试官追问

追问 1页面一直返回 Not Found,但模板文件明明存在,最可能漏了什么?
参考回答

漏了 await。ctx.render 返回 Promise,不 await 的话中间件已经结束,Koa 看 body 为空就回 404。写成 await ctx.render('index', data)。

追问 2模板里用了 product.title,接口偶尔没返回 product,页面直接 500,怎么兜?
参考回答

模板里访问 undefined 的属性会抛错。数据层先保证结构,缺了就走 404 或降级数据;模板里也可以写 product?.title,EJS 支持现代 JS 语法。错误要被统一错误中间件接住,别把堆栈吐给用户。

追问 3多租户想按 ctx.query.theme 选模板目录,直接拼路径有什么问题?
参考回答

用户能传 ../../xxx 去加载不该加载的文件。先用白名单把参数映射成固定主题名,不在名单里就用默认主题。

追问 4全局要用的 user,每个 render 都传一遍太麻烦,有更好的写法吗?
参考回答

在前面的中间件里写 ctx.state.user = user,koa-views 会把 ctx.state 合并进模板变量,模板里直接用 user。

追问 5现在都前后端分离了,服务端模板还有用吗?
参考回答

有。内容页、营销页、后台小工具这类交互少、要首屏 HTML 和 SEO 的场景,用模板直出成本最低。交互复杂的应用才值得上 React / Vue 加 SSR 框架。

# koa2加载模板引擎

# 快速开始

30 秒速记

  • 三步:装 koa-views + ejs → app.use(views(dir, { extension: 'ejs' })) → await ctx.render('index', data)
  • 目录结构:index.js 加一个 view/ 目录放 .ejs 文件
  • render 只传模板名,不带扩展名,koa-views 按 extension 补全
  • 跑起来访问 localhost:3000,右键查看源码,能看到数据已经填进 HTML

快速开始就三步:装依赖、注册 koa-views、在中间件里 await ctx.render。 目录结构也很简单,入口 index.js 旁边放一个 view 文件夹,模板都放里面。注册时告诉 koa-views 模板目录和扩展名,之后 render('index') 就会去找 view/index.ejs。跑起来以后我会右键看一下页面源代码,确认标题这些已经是填好的文字,而不是 <%= title %> 原样输出,这说明渲染发生在服务端。

理解「快速开始」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 module 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

面试官追问

追问 1页面源码里直接显示 <%= title %>,没被替换,为什么?
参考回答

模板没走引擎,多半是被当静态文件发出去了,比如 koa-static 的目录里也有这个文件。或者 extension 和文件后缀对不上,koa-views 没找到引擎。检查请求到底命中了哪个中间件。

追问 2模板文件想用 .html 后缀,又想用 EJS 语法,怎么配?
参考回答

views(dir, { map: { html: 'ejs' } }),意思是 .html 文件交给 ejs 渲染,render('index') 就会找 index.html。

追问 3从别的目录启动服务,报找不到模板,怎么改?
参考回答

模板目录要用 path.join(__dirname, 'view'),别写 './view',相对路径是按启动目录算的。

追问 4改了模板刷新没变化,要重启才生效,为什么?
参考回答

开了模板缓存。开发环境关掉缓存,生产再打开;或者开发时用 nodemon 监听 .ejs 文件自动重启。

# 安装模块

30 秒速记

  • koa-views 是适配层,负责在 ctx 上挂 render;ejs 才是真正解析模板的引擎,两个都要装
  • npm install --save koa-views ejs,npm 5 以后 --save 是默认行为,可以省略
  • 依赖必须写进项目的 package.json,全局装的包 require 找不到
  • koa-views 后续版本迁到了 @ladjs/koa-views,新项目装之前看一眼当前维护的包名

接 EJS 要装两个包:koa-views 负责把模板渲染能力接到 Koa 上,ejs 负责真正解析模板语法。 koa-views 本身不认识任何模板语法,它根据扩展名去找对应的引擎来渲染,所以只装它会报找不到引擎。两个包都要装在项目里,写进 package.json,换台机器 npm install 就能复现。npm 5 之后 install 默认就会写进 dependencies,--save 可写可不写。

# 安装koa模板使用中间件
npm install --save koa-views

# 安装ejs模板引擎
npm install --save ejs
@前端进阶之旅: 代码已经复制到剪贴板

面试官追问

追问 1只装了 koa-views 没装 ejs,会怎样?
参考回答

渲染时报找不到 ejs 模块。koa-views 只是个调度层,具体引擎要单独装。

追问 2同事说他电脑全局装过 ejs,项目里就不用装了,对吗?
参考回答

不对。require 不会去全局目录找包,他本机能跑可能是上层目录碰巧有 node_modules。依赖一定要写进项目 package.json,CI 和别人才装得上。

追问 3--save 和 --save-dev 该用哪个?
参考回答

--save。koa-views 和 ejs 是运行时依赖,生产环境渲染页面要用,放 devDependencies 的话 npm install --production 后线上就缺包了。

追问 4CI 报 Cannot find module 'ejs',本地正常,先查什么?
参考回答

先看 package.json 和 lock 文件里有没有 ejs,大概率是本地装了但没提交依赖声明。CI 里用 npm ci 严格按 lock 文件装,能更早暴露这种问题。

# 使用模板引擎

30 秒速记

  • views(path.join(__dirname, './view'), { extension: 'ejs' }) → 模板根目录 + 默认扩展名
  • await ctx.render('index', { title }) → 渲染 view/index.ejs,title 进入模板变量
  • <%= title %> 是转义输出,< > 这类字符会被转成实体,用户内容都用它
  • ctx.state 上的数据会自动合并进模板变量,适合放 user 这类全局数据
  • 渲染结果写进 ctx.body,Content-Type 自动是 text/html

在 Koa 里用 EJS 就两步:先 app.use(views(...)) 注册,然后在中间件里 await ctx.render(模板名, 数据)。 示例把 view 目录设为模板根目录、扩展名设为 ejs,所以 render('index') 找的是 view/index.ejs,第二个参数里的 title 会变成模板里能直接用的变量。模板里三处 <%= title %> 会被替换成 hello koa2,而且是转义输出,title 里就算有 <script> 也只会显示成文字。渲染完 koa-views 会把结果写进 ctx.body,Content-Type 也帮你设好了。

demo源码

https://github.com/poetries/daily-code-practice/tree/master/node/koa/koa2-demo/blob/master/demo/ejs/

← 5.2 koa2实现session6.2 ejs模板引擎 →

fe