文档章节

使用Express初体验

AllenOR灵感
 AllenOR灵感
发布于 2017/09/10 01:20
字数 2927
阅读 2
收藏 0

十一小长假,学习了一下比较流行的网站开发技术:NodeJS+Express+MongoDB,记录一下初入门感觉。


我安装的软件版本是:

Node : v6.3.1
Express: 4.13.4
MongoDB: 3.2.4

接下来,我们新建一个工程,在命令行中输入:

$ express -e blog
$ cd blog && npm install

初始化一个 express 项目并安装所需模块,如下图所示:


然后运行:

DEBUG=blog:* npm start

但一般,我个人习惯我们会把运行程序的命令写到一个shell里面,如下:

#!/usr/bin/env bash

DEBUG=blog:* npm start

启动项目,此时命令行中会显示 blog Express server listening on port 3000 +0ms ,在浏览器里访问 localhost:3000,会显示如下图所示结果:


至此,我们用 express 初始化了一个项目工程,并指定使用 ejs 作为模板引擎(命令行中的 -e 参数),下面具体讲解工程的内部结构。

我们打开刚刚创建的 blog 文件夹,里面如图所示:


接下来,具体解释每一个文件在工程中的作用:

app.js: 启动文件,或者说入口文件。

package.json: 存储着工程的信息及模块依赖,当在 dependencies 中添加依赖的模块时, 运行 npm install ,npm 会检查当前目录下的 package.json,并自动安装所有指定的模块。

node_modules: 存放 package.json 中安装的模块,当你在 package.json 添加依赖的模块并安装后,存放在这个文件夹下。

public: 存放 image、css、js 等文件。

routes: 存放路由文件。

views: 存放视图文件或者说模板文件。

bin: 存放可执行文件。

现在,我们打开 app.js 文件,来看看里面具体有哪些代码:

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

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

var app = express();

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

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes);
app.use('/users', users);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
  app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: {}
  });
});


module.exports = app;

这里我们通过 require() 加载了 express、path 等模块,以及 routes 文件夹下的 index.js 和 user.js 路由文件。下面解释每一行代码的含义:

(1) var app = express()

生成一个 express 实例 app。

(2) app.set('view', path.join(dirname, 'views'))

设置 views 文件夹为存放视图文件的目录,即存放模板文件的地方,dirname 为全局变量,存储当前正在执行的脚本所在的目录。

(3) app.set('view engine', 'ejs')

设置视图模板引擎为 ejs 。

(4) app.use(favicon(dirname + '/public/favicon.ico'))

设置 /public/favicon.ico 为 favicon 图标。

(5) app.use(logger('dev'))

加载日志中间件

(6) app.use(bodyParser.json())

加载解析 json 的中间件。

(7) app.use(bodyParser.urlencoded({ extended: false }))

加载解析 urlencoded 请求体的中间件。

(8) app.use(cookieParser())

加载解析 cookie 的中间件。

(9) app.use(express.static(path.join(dirname, 'public')))

设置 public 文件夹为存放静态文件的目录。

(10) app.use('/', routes)和app.use('/users', users)

路由控制。

(11)

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

捕获404错误,并转发到错误处理器。

(12)

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
  app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

开发环境下的错误处理器,将错误信息渲染 error 模板并显示到浏览器中。

(13)

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: {}
  });
});

生产环境下的错误处理器,将错误信息渲染 error 模板并显示到浏览器中。

(14) module.exports = app

导出 app 实例供其他模板调用。

接下来我们再看看 bin/www 文件:

#!/usr/bin/env node

/**
 * Module dependencies.
 */

var app = require('../app');
var debug = require('debug')('blog: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);

/**
 * Normalize a port into a number, string, or false.
 */

function normalizePort(val) {
  var port = parseInt(val, 10);

  if (isNaN(port)) {
    // named pipe
    return val;
  }

  if (port >= 0) {
    // port number
    return port;
  }

  return false;
}

/**
 * Event listener for HTTP server "error" event.
 */

function onError(error) {
  if (error.syscall !== 'listen') {
    throw error;
  }

  var bind = typeof port === 'string'
    ? 'Pipe ' + port
    : 'Port ' + port;

  // handle specific listen errors with friendly messages
  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:
      throw error;
  }
}

/**
 * Event listener for HTTP server "listening" event.
 */

function onListening() {
  var addr = server.address();
  var bind = typeof addr === 'string'
    ? 'pipe ' + addr
    : 'port ' + addr.port;
  debug('Listening on ' + bind);
}

接下来我们解释一下文件中的每一行代码:

(1) #!/usr/bin/env node

表明这是一个 node 可执行文件。

(2) var debug = require('debug')('blog:server')

引入 debug 模块,打印调试日志。

(3) var app = require('../app')

引入我们上面导出的 app 实例。

(4)

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

设置端口号。

(5) var server = http.createServer(app)

创建 HTTP 服务。

(6)

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

启动工程并监听 3000 端口。

接下来我们学习 routes/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;

这个代码的意思是,先导入 express 模块,然后生成一个路由实例用来捕获访问主页的 GET 请求,导出这个路由并在 app.js 中通过 app.use('/', routes) 加载。这样,当访问主页时,就会调用 res.render('index', {title: 'Express'}) 渲染 views/index.ejs 模板并显示到浏览器中。

接下来,我们可以看看 views/index.ejs 文件:

<!DOCTYPE html>
<html>
  <head>
    <title><%= title %></title>
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  <body>
    <h1><%= title %></h1>
    <p>Welcome to <%= title %></p>
  </body>
</html>

在渲染模板时,我们传入了一个变量 title 值为 express 字符串,模板引擎将所有 <%= title %> 替换为 express,然后将渲染后生成的html显示到浏览器中,如上图网页显示。

至此,我们大致了解了如何创建一个工程并且一些基础,接下来我们学习 express 的基本使用及路由控制。

路由控制

工作原理

先来查看一下 routes/index.js 文件中的主要路由分配代码:

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

这段代码的意思是当访问主页时,调用 ejs 模板引擎,来渲染 index.ejs 模板文件(即将 title 变量全部替换为字符串 Express),生成静态页面并显示在浏览器中。

接下来,我们学习一下路由添加规则,当我们访问 localhost:3000 时,会显示:


但是,当我们访问 localhost:3000/asdf 这种不存在的页面时,就会显示:


为什么会显示这个呢?因为我们在路由规则中,不存在 /asdf 的路由规则,而且它也不是一个 public 目录下的文件,所有 express 返回了 404 Not Found 的错误。下面我们来添加这条路由规则,使得当访问 localhost:3000/asdf 时,页面显示 Hello World! 。

注意:以下路由修改仅用于测试,看到效果后把代码还原回来。

修改 index.js,在路由规则中添加一条路由规则:

router.get('/asdf', function(req, res, next) {
  res.send('Hello World!');
});

重启之后,重新访问 localhost:3000/asdf 页面显示如下:


有没有感觉添加路由规则很简单啊,自己动手试试吧。

模板引擎

先来解释一下,什么是模板引擎:模板引擎(Template Engine)是一个将页面模板和要显示的数据结合起来生成HTML页面的工具。如果说上面讲到的 express 中的路由控制方法相当于 MVC 中的控制器的话,那模板引擎就相当于 MVC 中的视图。

模板引擎的功能是将页面模板和要显示的数据结合起来生成 HTML 页面。它既可以运 行在服务器端又可以运行在客户端,大多数时候它都在服务器端直接被解析为 HTML,解析完成后再传输给客户端,因此客户端甚至无法判断页面是否是模板引擎生成的。有时候模板引擎也可以运行在客户端,即浏览器中,典型的代表就是 XSLT,它以 XML 为输入,在客户端生成 HTML 页面。但是由于浏览器兼容性问题,XSLT 并不是很流行。目前的主流还是由服务器运行模板引擎。 在 MVC 架构中,模板引擎包含在服务器端。控制器得到用户请求后,从模型获取数据,调用模板引擎。模板引擎以数据和页面模板为输入,生成 HTML 页面,然后返回给控制器,由控制器交回客户端。
——《Node.js开发指南》

那么,什么是 ejs 呢?

ejs 是模板引擎的一种,也是我们这个教程中使用的模板引擎,因为它使用起来十分简单,而且与 express 集成良好。

使用模板引擎

前面我们通过以下两行代码设置了模板文件的存储位置和使用的模板引擎:

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

注意:我们通过 express -e blog 只是初始化了一个使用 ejs 模板引擎的工程而已,比如 node_modules 下添加了 ejs 模板,views 文件夹下有 index.ejs 。并不是说强制该工程只能使用 ejs 不能使用其他的模板引擎,比如 jade ,真正指定使用哪个模板引擎的是 app.set('view engine', 'ejs') 。

在 routes/index.js 中通过调用 res.render() 渲染模板,并将其产生的页面直接返回给客户端。它接受两个参数,第一个是模板的名称,即 views 目录下的模板文件名,扩展名 .ejs 可选。第二个参数是传递给模板的数据对象,用于模板翻译。

接下来,我们看看 views/index.ejs 中的代码:

<!DOCTYPE html>
<html>
  <head>
    <title><%= title %></title>
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  <body>
    <h1><%= title %></h1>
    <p>Welcome to <%= title %></p>
  </body>
</html>

当我们 res.render('index', {title: 'Express'}); 时,模板引擎会把 <%= title %> 替换成 Express,然后把替换成的页面显示给用户。

渲染后生成的页面代码为:

<!DOCTYPE html>
<html>
  <head>
    <title>Express</title>
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  <body>
    <h1>Express</h1>
    <p>Welcome to Express</p>
  </body>
</html>

注意:我们通过 app.use(express.static(path.join(__dirname, 'public'))) 设置了静态文件目录为 public 文件夹,所以上面代码中的 href = '/stylesheets/style.css' 就相当于 href = 'public/stylesheets/style.css' 。

ejs 的标签系统非常简单,它只有以下三种标签:

  • <% code %>: JavaScript 代码。
  • <%= code %>: 显示替换过 HTML 特殊字符的内容。
  • <%- code %>: 显示原始 HTML 内容。

注意:<%= code %> 和 <%- code %> 的区别,当变量 code 为普通字符串时,两者没有区别。当 code 比如为 <h1> hello </h1> 这种字符串时,<%= code %> 会原样输出 <h1> hello </h1> ,而 <%- code %> 则会直接显示 H1 大小的 hello 字符串。

我们还可以在 <% %> 内使用 JavaScript 代码。下面是 ejs 的官网实例:

The Data
supplies: ['mop', 'broom', 'duster']
The Template
<ul>
<% for(var i=0; i<supplies.length; i++) {%>
   <li><%= supplies[i] %></li>
<% } %>
</ul>
The Result
<ul>
  <li>mop</li>
  <li>broom</li>
  <li>duster</li>
</ul>

我们可以用上述三种标签实现页面模板系统能实现的任何内容。

页面布局

这里我们不使用 layout 进行页面布局,而是使用更为简单灵活的 include 。include 的简单使用如下:

index.ejs

<%- include a %>
hello,world!
<%- include b %>

a.ejs

this is a.ejs

b.ejs

this is b.ejs

最终 index.ejs 会显示:

this is a.ejs
hello, world!
this is b.ejs

至此,我们学习完了模板引擎的相关知识。

接下来,我们利用Express实现一个简单的博客功能


Reference:

Express入门教程:一个简单的博客

本文转载自:http://www.jianshu.com/p/2c0ed2dcb768

共有 人打赏支持
AllenOR灵感
粉丝 10
博文 2634
码字总数 82983
作品 0
程序员
API网关express-gateway初体验

昨天朋友发来一个消息,问我express源码中的一个问题,我去看了一番源码以后,发现自己并不是很懂,只能看懂一些字面意思... 不过在聊天过程中,了解到他们公司在用express做api网关,什么是...

趁你还年轻233
04/03
0
0
微软数据库服务器--SQL Server

SQL Server Express Edition 是 MSDE 的下一个版本,它是免费、易于使用、可嵌入的 SQL Server 2005 轻型版本。可免费下载、重新发布、嵌入,便于新的开发人员立即使用。SQL Server Express ...

匿名
2009/03/23
11.6K
3
Nodejs架构之Express初体验

参考:http://www.cnblogs.com/Darrencode/p/express4.html 本文是基于 node 0.12.7 express 4.13.1 1)安装 npm install -g expressnpm install -g express-generator 2)创建项目 express ......

cs_sharp
2015/09/07
212
0
SQL SERVER FOR LINUX初体验

今天得空,就在Ubuntu17.04上安装了SQL SERVER 2017体验下,总体来说还是不错的。 SQL SERVER 2017 在Ubuntu上安装SQL SERVER 2017还是比较方便的,只需几条命令即可: 目前,在安装SQL SER...

雪飞鸿
2017/08/20
0
0
koahubjs 1.0.0 发布,基于 Koa.js 快速开发框架

koahubjs 发布 1.0.0 正式版,基于 Koa.js 的快速开发框架。 KoaHub.js -- 基于 Koa.js 平台的 Node.js web 快速开发框架。可以直接在项目里使用 ES6/7(Generator Function, Class, Async ...

einsqing
2017/01/07
1K
6

没有更多内容

加载失败,请刷新页面

加载更多

Qt编写自定义控件属性设计器

以前做.NET开发中,.NET直接就集成了属性设计器,VS不愧是宇宙第一IDE,你能够想到的都给你封装好了,用起来不要太爽!因为项目需要自从全面转Qt开发已经6年有余,在工业控制领域,有一些应用...

飞扬青云
32分钟前
1
0
我为什么用GO语言来做区块链?

Go语言现在常常被用来做去中心化系统(decentralised system)。其他类型的公司也都把Go用在产品的核心模块中,并且它在网站开发中也占据了一席之地。 我们在决定做Karachain的时候,考量(b...

HiBlock
38分钟前
1
0
大数据学习脑图以及入门教程!

近些年,大数据的火热可谓是技术人都知道啊,很多人呢,也想学习大数据相关,所以,这里分享几个大数据脑图,希望可以让你清楚明白从哪里入门大数据,知道该学习以及掌握哪些知识点; 大数据...

董黎明
今天
1
0
聊聊redis的监控工具

序 本文主要研究一下redis的监控工具 redis-stat redis-stat是一个比较有名的redis指标可视化的监控工具,采用ruby开发,基于redis的info命令来统计,不影响redis性能。 docker运行 docker r...

go4it
今天
2
0
TypeScript基础入门之高级类型的索引类型(Index types)

转发 TypeScript基础入门之高级类型的索引类型(Index types) 高级类型 索引类型(Index types) 使用索引类型,编译器就能够检查使用了动态属性名的代码。 例如,一个常见的JavaScript模式是从...

durban
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部