一、框架简介
在建立第一个Demo后,我们引入一个开发框架实现前面的功能;引入开发框架有以下几个主要原因:
提高开发效率:开发框架提供了一套约定俗成的代码结构和功能模块,可以帮助开发者快速搭建应用程序的基本架构,减少重复编码工作。
确保最佳实践:成熟的框架通常遵循最佳开发实践,例如MVC(模型-视图-控制器)模式,这有助于保持代码的清晰性和可维护性。
提供中间件支持:许多框架内置了中间件支持,中间件是一种特殊类型的软件,可以拦截请求和响应,并进行处理。这对于处理诸如身份验证、会话管理、日志记录等功能非常有用。
增强安全性:框架通常会提供一些安全特性,如防SQL注入、XSS攻击等,帮助开发者构建更安全的应用程序。
促进社区协作:流行的框架通常拥有活跃的社区,开发者可以从中获得支持、插件和最佳实践分享,这有助于解决开发过程中的问题。
便于团队协作:框架提供的标准化结构有助于团队成员之间的协作,新加入的成员可以更快地熟悉项目结构。
促进项目维护:框架的使用使得项目更易于维护和升级,因为框架的更新通常会考虑向后兼容性。
支持模块化和可重用性:框架鼓励开发者编写模块化的代码,这有助于代码的重用和测试。
优化性能:一些框架还提供了性能优化,如内存泄漏防护、缓存机制等。
适应不同需求:现代框架通常设计灵活,能够适应不同的开发需求,无论是构建RESTful API、单页面应用(SPA)还是传统的网站。
在Node.js生态中,流行的框架包括Express、Koa、NestJS等,它们各有特点,适用于不同的开发场景和需求。选择合适的框架可以大大提升开发效率和项目质量。
Express.js:Express是一个流行的Node.js框架,提供了许多用于构建Web应用程序的功能。它具有简单性和灵活性,适用于构建各种规模的应用程序。Express具有丰富的插件生态系统,可以轻松地扩展和定制应用程序。
Koa:Koa是一个基于Node.js的下一代Web应用程序框架。它使用ES6的语法,具有简洁、可扩展和易于理解的特点。Koa提供了一些中间件,可以快速构建具有强大功能的Web应用程序。
Nest.js:Nest.js是一个用于构建高效、可扩展的Node.js服务端应用程序的框架。它采用TypeScript编写,具有模块化、可测试和易于扩展的特点。Nest.js提供了许多内置功能,如身份验证、授权、数据访问等。
Fastify:Fastify是一个快速、低开销的Web框架,适用于构建实时、高并发的应用程序。它具有简单、易于使用和高效的API,可以快速地构建应用程序并提高开发效率。
这次使用Express生成器(Express Generator),Express生成器是一个官方提供的命令行工具,可以快速搭建一个Express项目的骨架。首先,你需要全局安装Express生成器:
npm install -g express-generator
然后,你可以使用express命令创建一个新的项目:
express Demo4Express //在当前目录下创建Demo4Express目录,同时在该目录下创建Demo4Express项目及相应的子文件夹
cd Demo4Express npm install //
express Demo4Express //这将在当前目录下创建一个名为Demo4Express的新目录,并在其中生成项目的骨架文件;子文件夹结构如下:
├──app.js`app.js`:这是主应用程序文件,它启动了Express服务器,并设置了一些基本的中间件和路由。
├──bin/
│└──www`bin/www`:这个文件是一个启动脚本,它启动了`app.js`中的服务器。你可以通过运行`node bin/www`来启动你的应用程序。
├──node_modules/`node_modules/`:这个文件夹包含了所有通过npm安装的依赖包。
├──package.json`package.json`:这个文件列出了项目的依赖包,以及一些其他的元数据,如项目名称、版本、描述等。
├──public/`public/`:这个文件夹包含了所有的静态资源,如图片、样式表和JavaScript文件。
│├──images/
│├──javascripts/
│└──stylesheets/
│└──style.css
├──routes/`routes/`:这个文件夹包含了路由文件,这些文件定义了应用程序的不同URL路径如何响应HTTP请求。
│├──index.js
│└──users.js
└──views/`views/`:这个文件夹包含了应用程序的视图模板。默认情况下,`express-generator`使用Pug模板引擎,但你可以使用其他模板引擎,如EJS或Handlebars。
├──error.pug
├──index.pug
└──layout.pug
完成后,你可以启动项目:
npm start
在浏览器中输入“http://127.0.0.1:3000/”浏览默认实现;
1. package.json,这个文件列出了项目的依赖包,以及一些其他的元数据,如项目名称、版本、描述等。
你可以使用 `npm install` 命令根据这个文件来安装所有必要的依赖包。其内容如下:
{
"name":"demo4express",//项目名称
"version":"0.0.0",//项目版本号
"private":true,//是否为私有
"scripts": {//脚本文件
"start":"node ./bin/www"//指定当执行npm start命令时执行的命令
},
"dependencies": {//所有依赖包
"cookie-parser":"~1.4.4",
"debug":"~2.6.9",
"express":"~4.16.1",
"http-errors":"~1.6.3",
"jade":"~1.11.0",
"morgan":"~1.9.1"
}
}
2. App.js,它是使用 Express.js 框架的基本 Node.js 应用程序的入口点。下面是对每行代码的逐行注释:
//导入内置的http-errors模块,用于创建各种HTTP错误。
varcreateError =require('http-errors');
//导入express模块,这是Express应用程序的核心。
varexpress =require('express');
//导入path模块,用于处理文件路径。
varpath =require('path');
//导入cookie-parser中间件,用于解析Cookie头部信息。
varcookieParser =require('cookie-parser');
//导入morgan中间件,用于日志记录HTTP请求。
varlogger =require('morgan');
//导入index路由器,它处理根URL (/)的请求。
varindexRouter =require('./routes/index');
//导入users路由器,它处理与用户相关的URL (/users)的请求。
varusersRouter =require('./routes/users');
//创建一个Express应用实例。
varapp =express();
//设置视图模板的存放目录,__dirname是当前文件所在的目录。
app.set('views', path.join(__dirname,'views'));
//设置视图模板引擎为Jade(现在称为Pug)。
app.set('view engine','jade');
//使用morgan中间件来记录每个请求的信息到控制台,'dev'是一种预定义的格式。
app.use(logger('dev'));
//解析JSON格式的请求体。
app.use(express.json());
//解析URL编码的请求体。
app.use(express.urlencoded({extended:false}));
//使用cookieParser中间件来解析请求中的Cookie。
app.use(cookieParser());
//设置静态文件服务的目录为public文件夹。
app.use(express.static(path.join(__dirname,'public')));
//将根URL (/)的请求路由到indexRouter。
app.use('/', indexRouter);
//将与用户相关的URL (/users)的请求路由到usersRouter。
app.use('/users', usersRouter);
//如果前面的路由都没有处理请求,则创建一个404错误并传递给错误处理器。
app.use(function(req, res, next) {
next(createError(404));
});
//错误处理器,用于处理所有路由和中间件中抛出的错误。
app.use(function(err, req, res, next) {
//设置本地变量,只在开发环境下提供错误详情。
res.locals.message= err.message;
res.locals.error= req.app.get('env') ==='development'? err : {};
//渲染错误页面。
res.status(err.status||500);
res.render('error');
});
//导出app实例,以便在其他文件中(如启动脚本)使用。
module.exports= app;
这段代码设置了一个基本的 Express 应用程序,包括中间件、路由和错误处理。它还定义了视图引擎和静态文件服务的目录。最后,它导出了应用程序实例,以便可以将其传递给 HTTP 服务器并在端口上监听。
#!/usr/bin/env node
/**
*这行代码是Shebang,它告诉系统使用env来查找node解释器,并使用它来执行这个脚本。
*/
/**
* Module dependencies.
*/
varapp =require('../app');
//导入位于父目录中的app.js文件,它导出了Express应用实例。
vardebug =require('debug')('demo4express:server');
//导入debug模块,并创建一个名为'demo4express:server'的调试器实例。
varhttp =require('http');
//导入Node.js内置的http模块,用于创建HTTP服务器。
/**
* Get port from environment and store in Express.
*/
varport =normalizePort(process.env.PORT||'3000');
//获取环境变量中的端口,如果没有设置,则使用默认端口3000,并规范化端口。
app.set('port', port);
//将端口存储在Express应用实例中,以便可以在应用中使用。
/**
* Create HTTP server.
*/
varserver = http.createServer(app);
//使用Express应用实例创建一个HTTP服务器。
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
//服务器开始监听指定端口。
server.on('error', onError);
//为服务器注册一个错误事件监听器。
server.on('listening', onListening);
//为服务器注册一个监听事件监听器。
/**
* Normalize a port into a number, string, or false.
*/
functionnormalizePort(val) {
varport =parseInt(val,10);
//尝试将端口转换为整数。
if(isNaN(port)) {
//如果转换失败(不是数字),则假设它是一个命名管道。
returnval;
}
if(port >=0) {
//如果转换成功且是有效端口,则返回端口数字。
returnport;
}
returnfalse;
//如果端口无效,则返回false。
}
/**
* Event listener for HTTP server "error" event.
*/
functiononError(error) {
if(error.syscall!=='listen') {
throwerror;
}
//如果错误不是监听错误,则抛出错误。
varbind =typeofport ==='string'
?'Pipe '+ port
:'Port '+ port;
//根据端口类型创建一个友好的错误消息。
//处理特定的监听错误,并显示友好的错误消息。
switch(error.code) {
case'EACCES':
console.error(bind +' requires elevated privileges');
process.exit(1);
break;
case'EADDRINUSE':
console.error(bind +' is already in use');
process.exit(1);
break;
default:
throwerror;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
functiononListening() {
varaddr = server.address();
//获取服务器正在监听的地址。
varbind =typeofaddr ==='string'
?'pipe '+ addr
:'port '+ addr.port;
//根据地址类型创建一个友好的监听消息。
debug('Listening on '+ bind);
//使用debug调试器输出服务器正在监听的消息。
}
这个脚本首先导入了必要的模块,然后从环境变量中获取端口号,创建一个 HTTP 服务器,并为该服务器添加了错误和监听事件的处理函数。如果服务器启动失败,它会输出错误信息并退出进程。如果服务器成功启动,它会输出监听的信息。
4. Index.js 路由器模块,它处理对主页的 GET 请求。
varexpress =require('express');
//导入Express框架,用于创建和管理HTTP服务器和路由。
varrouter = express.Router();
//创建一个Express路由器实例,它是一个中间件和路由的集合,可以用于路由特定的请求。
/* GET home page. */
//这是一条注释,说明下面的路由处理的是对主页的GET请求。
router.get('/',function(req, res, next) {
//为路由器添加一个处理GET请求的路由。当用户访问根URL (/)时,这个函数会被调用。
res.render('index', {title:'Express'});
//使用res.render方法渲染名为'index'的视图模板,并传递一个包含标题的对象作为模板变量。
//假设Express已经配置了视图引擎(如Jade/Pug、EJS等),这里会将模板渲染成HTML并发送给客户端。
});
module.exports= router;
//导出这个路由器模块,以便在其他文件中(如应用程序的主文件)可以require它并使用它来处理请求。
这个路由器模块定义了一个路由,当用户访问主页时,它会渲染一个名为 index 的视图,并将 title 变量设置为 'Express'。这个模块可以被包含在一个更大的 Express 应用程序中,作为处理主页请求的一部分。
在 Express 应用程序中使用 Jade(或 Pug)作为模板引擎时,layout.jade 文件和 index.jade 文件通常用于定义应用程序的布局和主页内容。
layout.jade
layout.jade 文件是一个布局模板,它定义了应用程序的公共部分,比如页头、导航栏、页脚等。这个文件通常包含一个或多个块(blocks),这些块可以在继承它的其他模板中填充具体内容。
例如,layout.jade 文件的内容可能如下所示:
html
head
title My Express App
link(rel='stylesheet', href='/stylesheets/style.css')
body
header
h1 My Express App
nav
//导航栏代码
section.container
block content
footer
p Copyright©2023
在这个例子中,block content 是一个占位符,它表示子模板可以插入自己的内容。其他模板通过 extends 关键字继承 layout.jade 并在 block content 中填充自己的内容。
index.jade
index.jade 文件是主页的模板文件,它继承了 layout.jade 文件,并在 block content 中定义了主页特有的内容。这样,当用户访问主页时,他们会看到 layout.jade 中定义的公共布局,以及 index.jade 中定义的主页特定内容。
例如,index.jade 文件的内容可能如下所示:
extends layout block content h1 Home Page p Welcome to the home page of My Express App. // 其他主页特定的 HTML 和数据
在这个例子中,extends layout 表示 index.jade 继承自 layout.jade。block content 中的内容会替换 layout.jade 中的 block content,从而在主页上显示特定的标题和段落。
通过这种方式,layout.jade 和 index.jade 文件共同工作,提供了一个结构化的方式来创建和维护网页的布局和内容。这种模式可以减少代码重复,使得更新和维护变得更加容易。
app.js 和 index.js的关系
在使用 Express 框架时,app.js 和 index.js 文件通常都存在于项目的根目录中,但它们的职责有所不同。这些文件的角色可能会根据项目的规模和结构而有所变化,但以下是一些常见的职责分配:
app.js 文件通常作为应用程序的入口点,它负责设置 Express 应用程序的基本配置和中间件,以及启动服务器。在这个文件中,你会看到以下的操作:
初始化 Express 应用实例。
设置视图引擎。
加载中间件(如 body-parser,morgan,cookie-parser 等)。
定义静态文件服务的目录。
引入路由器模块并使用它们来处理不同的端点。
设置错误处理中间件。
启动 HTTP 服务器。
var express = require('express'); var app = express(); // 设置视图引擎等中间件 app.set('view engine', 'ejs'); // 静态文件服务等 app.use(express.static('public')); // 路由 app.use('/', require('./routes/index')); app.use('/users', require('./routes/users')); // 错误处理 app.use(function(err, req, res, next) { // ... }); // 启动服务器 var port = process.env.PORT || 3000; app.listen(port, function() { console.log('Express server listening on port ' + port); });
index.js
index.js 文件在不同的上下文中可能有不同的用途。在一些项目中,index.js 可能是整个应用程序的入口点,而在其他项目中,它可能是一个路由模块的入口点。以下是两种常见的使用方式:
作为主入口点(替代 app.js):在这种情况下,index.js 执行与 app.js 类似的任务,初始化 Express 应用并启动服务器。
作为路由模块:在这种情况下,index.js 是一个路由器文件,它导出一个或多个路由处理函数,这些函数通常被 app.js 或其他路由聚合文件导入和使用。
var express = require('express'); var router = express.Router(); // 定义主页路由 router.get('/', function(req, res, next) { res.render('index', { title: 'Express' }); }); module.exports = router;
在实际项目中,app.js 和 index.js 的职责可能会根据项目的具体需求和开发者的个人偏好而有所不同。重要的是要保持一致性并确保项目的其他开发者能够理解文件的结构和职责。
2.路由代码
在 Express 应用程序中,app.js 文件通常负责创建和配置 Express 应用实例,以及将路由器连接到应用的路由系统中。index.js 文件通常是一个路由器模块,它定义了特定的路由和处理函数。
app.js 文件中的这两行代码:
var indexRouter = require('./routes/index'); app.use('/', indexRouter);
index.js 文件中也有类似的代码:
var express = require('express'); var router = express.Router(); router.get('/', function(req, res, next) { res.render('index', { title: 'Express' }); }); module.exports = router;
下面解释它们之间的关系:
在App.js中 var indexRouter = require('./routes/index');这行代码使用 require 函数导入 index.js 文件中导出的路由器模块。index.js 文件中有一个名为 router 的 Express 路由器实例,它定义了一个或多个路由。
app.use('/', indexRouter);这行代码将导入的路由器模块 indexRouter 绑定到根路径 /。这意味着任何对根 URL (/) 的 HTTP GET 请求都将由 index.js 文件中定义的路由处理。
这里,index.js 文件创建了一个路由器实例 router,并为根路径定义了一个 GET 请求的处理函数。当用户访问根 URL (/) 时,这个处理函数会被调用,并且使用 res.render 方法来渲染一个名为 index 的视图,同时传递一个包含 title 属性的对象作为视图渲染时的数据。
总结来说,app.js 文件通过 require 导入 index.js 文件中定义的路由器,并通过 app.use 将其绑定到应用的路由系统中。这样,当用户发起特定的 HTTP 请求时,请求会被路由到正确的处理函数。这是 Express 中常见的路由和组织代码的方式,有助于保持代码的模块化和可维护性。