Node.js Express Helloworld

参考《Node.js开发指南 ByVoid》 Page83

一、Express简介

Node.js 由于不需要另外的 HTTP 服务器,因此减少了一层抽象,给性能带来不少提升,但同时也因此而提高了开发难度。举例来说,我们要实现一个 POST 数据的表单,例如:

这个表单包含两个字段: title 和 text ,提交时以 POST 的方式将请求发送给http://localhost:3000/。假设我们要实现的功能是将这两个字段的东西原封不动地返回给用户,PHP 只需写两行代码,储存为 index.php 放在网站根目录下即可:

echo $_POST['title'];
echo $_POST['text'];

在 3.5.1 节中使用了类似下面的方法(用 http 模块):

var http = require('http');
var querystring = require('querystring');
var server = http.createServer(function(req, res) {
    var post = '';

    req.on('data', function(chunk) {
        post += chunk;
    });

    req.on('end', function() {
        post = querystring.parse(post);
        res.write(post.title);
        res.write(post.text);
        res.end();
    });
}).listen(3000);

这种差别可能会让你大吃一惊,PHP 的实现要比Node.js容易得多。Node.js 完成这样一个简单任务竟然如此复杂:你需要先创建一个 http 的实例,在其请求处理函数中手动编写req 对象的事件监听器。当客户端数据到达时,将 POST数据暂存在闭包的变量中,直到 end事件触发,解析 POST 请求,处理后返回客户端。

其实这个比较是不公平的,PHP 之所以显得简单并不是因为它没有做这些事,而是因为PHP 已经将这些工作完全封装好了,只提供了一个高层的接口,而 Node.js 的 http 模块提供的是底层的接口,尽管使用起来复杂,却可以让我们对 HTTP 协议的理解更加清晰。但是等等,我们并不是为了理解 HTTP 协议才来使用 Node.js 的,作为 Web 应用开发者,我们不需要知道实现的细节,更不想与这些细节纠缠从而降低开发效率。难道 Node.js 的抽象如此之差,把不该有的细节都暴露给了开发者吗?

实际上,Node.js 虽然提供了 http 模块,却不是让你直接用这个模块进行 Web 开发的。http 模块仅仅是一个 HTTP 服务器内核的封装,你可以用它做任何 HTTP 服务器能做的事情,不仅仅是做一个网站,甚至实现一个 HTTP代理服务器都行。你如果想用它直接开发网站,那么就必须手动实现所有的东西了,小到一个 POST 请求,大到 Cookie、会话的管理。当你用这种方式建成一个网站的时候,你就几乎已经做好了一个完整的框架了。

npm 提供了大量的第三方模块,其中不乏许多 Web 框架,我们没有必要重复发明轮子,因而选择使用 Express 作为开发框架,因为它是目前最稳定、使用最广泛,而且 Node.js 官方推荐的唯一一个 Web 开发框架。Express 除了为 http 模块提供了更高层的接口外,还实现了许多功能,其中包括:

  • 路由控制;
  • 模板解析支持;
  • 动态视图;
  • 用户会话;
  • CSRF 保护;
  • 静态文件服务;
  • 错误控制器;
  • 访问日志;
  • 缓存;
  • 插件支持。

需要指出的是,Express 不是一个无所不包的全能框架,像 Rails 或 Django 那样实现了模板引擎甚至 ORM (Object Relation Model,对象关系模型)。它只是一个轻量级的 Web 框架,多数功能只是对 HTTP协议中常用操作的封装,更多的功能需要插件或者整合其他模块来完成。下面用 Express 重新实现前面的例子:

var express = require('express');
var app = express.createServer();
app.use(express.bodyParser());
app.all('/', function(req, res) {
    res.send(req.body.title + req.body.text);
});
app.listen(3000);

可以看到,我们不需要手动编写 req 的事件监听器了,只需加载 express.bodyParser()就能直接通过 req.body 获取 POST 的数据了。

二、快速开始

1.安装Express
首先我们要安装 Express。如果一个包是某个工程依赖,那么我们需要在工程的目录下使用本地模式安装这个包,如果要通过命令行调用这个包中的命令,则需要用全局模式安装(关于本地模式和全局模式,参见 3.3.4节),因此按理说我们使用本地模式安装 Express 即可。但是Express 像很多框架一样都提供了 Quick Start(快速开始)工具,这个工具的功能通常是建立一个网站最小的基础框架,在此基础上完成开发。当然你可以完全自己动手,但我还是推荐使用这个工具更快速地建立网站。为了使用这个工具,我们需要用全局模式安装Express,因为只有这样我们才能在命令行中使用它。运行以下命令:$ npm install -g express

Node.js Express Helloworld_第1张图片
image.png

但是发现命令行里用不了express指令,参考 解决windows下npm安装的模块执行报错:无法将“cnpm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称,用 npm list --depth=0 -global查看已经全局安装的模块,也看到express的版本是4.16.3了。然后把C:\Users\ 用户名\AppData\Roaming\npm目录加入环境变量Path重启,发现还是不行啊。
又参考 windows全局安装express,无法命令行执行。,有提示说,4版本需要安装express-generatorc才能使用express命令。

这是express 4.X 版本的更新 导致的。参见 https://github.com/visionmedia/express/wiki/New-features-in-4.x。使用 $ npm install -g express-generator后就解决了。

2.建立工程
Express 在初始化一个项目的时候需要指定模板引擎,默认支持Jade和ejs,为了降低学习难度我们推荐使用 ejs,同时暂时不添加 CSS 引擎和会话支持。

注:ejs (Embedded JavaScript) 是一个标签替换引擎,其语法与 ASP、PHP 相似,易于学习,目前被广泛应用。Express默认提供的引擎是 jade,它颠覆了传统的模板引擎,制定了一套完整的语法用来生成 HTML 的每个标签结构,功能强大但不易学习。可参考知乎 关于nodejs的模板引擎,如何选择 EJS 和 Jade?,简单来说EJS看起来更像HTML,可以直接拿原生的HTML页面改成模板。

通过命令express --view=ejs microblog在当前目录中创建子目录microblog(因版本问题,原书是express -t ejs microblog,最新的使用方式根据npm库上面的说明来操作),然后执行npm install,所有package.json中记录的插件都会安装到node_modules文件夹内。

//package.json:
{
  "name": "microblog",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "cookie-parser": "~1.4.3",
    "debug": "~2.6.9",
    "ejs": "~2.5.7",
    "express": "~4.16.0",
    "http-errors": "~1.6.2",
    "morgan": "~1.9.0"
  }
}

然后在命令行执行npm start,这个命令会查找package.json中的scripts。查看bin目录下的www文件:

var app = require('../app');
var debug = require('debug')('microblog:server');
var http = require('http');

/**
 * Get port from environment and store in Express.
 */

var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);

/**
 * Create HTTP server.
 */

var server = http.createServer(app);

/**
 * Listen on provided port, on all network interfaces.
 */

server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

此时访问localhost:3000即可访问示例页面。
3.在www中看到引用了app.js

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use('/users', usersRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

这里把public,routes,views三个文件夹都引入进来了,下面逐个分析一下
4.访问localhost:3000的流程

var indexRouter = require('./routes/index');
app.use('/', indexRouter);
...
//index.js
var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

module.exports = router;

将“ / ”路径映射到 exports.index函数下。然后调用模板解析引擎,翻译名为 index 的模板,并传入一个对象作为参数,这个对象只有一个属性,即 title: 'Express'。看一下index.ejs



  
    <%= title %>
    
  
  
    

<%= title %>

Welcome to <%= title %>

替换内容后,浏览器发现要获取 /stylesheets/style.css,因此会再次向服务器发起请求。app.js 中并没有一个路由规则指派到 /stylesheets/style.css,但 app 通过app.use(express.static(__dirname + '/public')) 配置了静态文件服务器,因此/stylesheets/style.css 会定向到 app.js 所在目录的子目录中的文件public/stylesheets/style.css
同样道理,可以看看http://localhost:3000/users是如何路由的:在users.js中并没有使用模板,而是直接返回一个字符串

var express = require('express');
var router = express.Router();

/* GET users listing. */
router.get('/', function(req, res, next) {
  res.send('respond with a resource');
});

module.exports = router;

你可能感兴趣的:(Node.js Express Helloworld)