Express

Express

第一章 Express简介

npm提供了大量的第三方模块,其中不乏许多Web框架,我们没有必要重复发明轮子,因而选择使用 Express作为开发框架,因为它是目前最稳定、使用最广泛,而且Node.js官方推荐的唯一一个 Web开发框架。
Express是一个轻量级的Web框架,多数功能只是对HTTP协议中常用操作的封装,更多的功能需要插件或者整合其他模块来完成.

Express中文官网 http://www.expressjs.com.cn/

第二章 初始化一个Express应用

  1. 我们想要安装Express,首先需要安装Epxress生成器,在全局通过命令安装
npm install express-generator -g
  1. 等待安装完毕,查看帮助信息:
express --help

可以看到,

Usage: express [options] [dir]

我们可以用express 在当前目录创建dir,并且依据配置选项
我们可以通过option设置express使用的模板,这里我们用ejs,也可以设置css的引擎,比如less和sass,这里我们先使用原生的css
所以只需要输入

express myapp --view ejs

express会给我们快速的搭建一个app框架,然后根据提示进入myapp文件夹,运行

npm install

npm会自动帮助我们安装当前应用所需要的模块,等待安装完毕之后,启动:

npm start

浏览器打开:“localhost:3000”,可以看到express框架给我返回的信息。

在调试过程中,如果我们不想反复的重启服务端,可以npm安装nodemon,使用nodemon启动express项目

第三章 目录分析

回到我们安装express之前,看一下express快速搭建的目录结构:

 create : taobao
   create : taobao/package.json //依赖关系文件
   create : taobao/app.js // 入口文件
   create : taobao/public //公共资源文件夹
   create : taobao/public/images
   create : taobao/public/stylesheets
   create : taobao/public/stylesheets/style.css
   create : taobao/public/javascripts
   create : taobao/views //视图文件夹
   create : taobao/views/index.ejs
   create : taobao/views/error.ejs
   create : taobao/routes //路由文件夹
   create : taobao/routes/index.js
   create : taobao/routes/users.js
   create : taobao/bin
   create : taobao/bin/www //配置文件

由此可见,express是一个标准的MVC框架,通过路由分配视图,并且可以在路由中写上我们的控制结构.
修改 routes下的index.js

router.get('/', function(req, res, next) {
  res.render('index', { title: '你好express' });
});

发现我们的页面出现了改变,这个路由跟我们自己写的路由作用是差不多的,具体用法我们后边再谈.

第四章 模板的使用方式

我们在安装express的时候,使用的模板是ejs,具体什么是模板呢?ejs又怎么用呢?
简单来讲,模板就是帮助我们在view层进行输出的工具.我们打开view下的index.ejs,可以看到,这个模板其实就是在ejs下写了我们常见的html代码,只是多了几个不一样的标签:

<!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 %>可以帮助我们将路由中的{ title: '你好express' }输出到模板当中,其中的render就是模板渲染的方法,帮助我们渲染第一个参数index所对应的模板.类似angular,我们也可以在标签内部写自己的表达式

<h1><%= title.toUpperCase() %></h1>

4.1 模板引擎的用法

ejs有三种常用的标签

  1. <% code %>:运行 JavaScript 代码,不输出
  2. <%= code %>:显示转义后的 HTML内容
  3. <%- code %>:显示原始 HTML 内容

注意:<%= code %><%- code %>都可以是JavaScript表达式生成的字符串,当变量code为普通字符串时,两者没有区别。当 code 比如为 <h1>hello</h1> 这种字符串时,<%= code %> 会原样输出 <h1>hello</h1>,而 <%- code %> 则会显示H1大的hello字符串

下面的例子可以帮我们解释<% code %>的用法:

index.ejs

<!DOCTYPE html>
<html>
  <head>
    <title><%= title %></title>
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  <body>
    <h1><%= title %></h1>
    <ul>
    <% for(var i=0; i<contents.length; i++) { %>
        <li><%= contents[i] %></li>
    <% } %>
    </ul>
  </body>
</html>

index.js

router.get('/', function(req, res, next) {
    res.render('index',{ 
        title: 'express',
        contents:['AngularJS','Node.js','Express']
    });
});

查看渲染之后的结果.
同样,如果你想要动态的调整js代码,可以将js代码在<script>标签中按照原样渲染.

4.3 渲染模板的方法

我们刚刚看到,routes下的index.js使用render可以将后边的参数渲染到模板之中,但是,我们在views下无法看到users.ejs,这是因为在user.js下使用了send方法,可以将后边的信息直接渲染而不通过模板.

4.4 includes

我们使用模板引擎通常不是一个页面对应一个模板,这样就失去了模板的优势,而是把模板拆成可复用的模板片段组合使用,如在 views 下新建 header.ejs 和 footer.ejs,并修改users.ejs

views/header.ejs

<!DOCTYPE html>
<html>
  <head>
    <style type="text/css">
      body {padding: 50px;font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;}
    </style>
  </head>
  <body>

views/footer.ejs

  </body>
</html>

views/users.ejs

<%- include('header') %>
  <h1><%= name.toUpperCase() %></h1>
  <p>hello, <%= name %></p>
<%- include('footer') %>

routes/users.js

router.get('/', function(req, res, next) {
  // res.send('respond with a resource');
  res.render('users',{ 
        name: 'express'
    });
});

访问’localhost:3000/users’可以看到渲染后的结果.
我们将users.ejs拆成出了header.ejs和footer.ejs,并在users.ejs通过 ejs 内置的 include方法引入,从而实现了跟以前一个模板文件相同的功能.

注意:要用 <%- include('header') %> 而不是 <%= include('header') %>

第五章 路由

我们在路由中将数据渲染并且分配到模板当中,那么我们如何写一个路由呢?

5.1 入口文件

首先,我们先要了解入口文件的组成,打开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 index = require('./routes/index');
var users = require('./routes/users');

路由都是模块写法,而入口文件将这些路由引入,我们就可以直接使用了.

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

这则使用了我们的路由

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

这两句话设置了我们的模板引擎,如果我们想要使用sass或者less等css引擎,同样可以向下追加.

// catch 404 and forward to error handler
...
// error handler
...

剩下的部分则是规定了404和500错误下的页面渲染方法.
由此,我们可以在routes下添加的路由文件,并且在入口文件引入

5.2 新增路由

在routes下添加新的路由:
routers/tests.js

var express = require('express');
var router = express.Router();
router.get('/', function(req, res, next) {
   res.send('这是新的路由');
});
module.exports = router;

同样,在入口文件添加
app.js

var tests = require('./routes/tests');
app.use('/tests', tests);

访问’localhost:3000/tests’则可以获取我们新增加的路由信息.

5.3 路由中的参数

其中 req,res是我们熟悉的请求和响应对象,当我们的地址栏访问模板的时候,可以获取参数

ruoter.get('/:name', function(req, res, next) {
  res.send('hello, ' + req.params.name);
});

我们在地址栏请求’localhost:3000/tests/Y18’,我们可以看到响应信息

hello, Y18

不难看出:req 包含了请求来的相关信息,res 则用来返回该请求的响应。下面介绍几个常用的

req 的属性:

  • req.query: 解析后的 url 中的 querystring,如 ?name=haha,req.query 的值为 {name: 'haha'}
  • req.params: 解析 url 中的占位符,如 /:name,访问 /haha,req.params 的值为 {name: 'haha'}
  • req.body: 解析后请求体,需使用相关的模块,如 body-parser,请求体为 {"name": "haha"},则 req.body 为 {name: 'haha'},我们可以用接受post请求为例.
  • 注意,无论是send或者render,我们只能获取第一次符合标准的渲染页面

5.4 next和中间件

前面我们讲解了express中路由和模板引擎ejs的用法,但express的精髓并不在此,在于中间件的设计理念。

我们可以看到,在路由的回调函数中,有一个参数next,这就是我们使用中间件最重要的部分。
express中的中间件(middleware)是用来处理请求的,当一个中间件处理完,可以通过调用 next() 传递给下一个中间件,如果没有调用 next(),则请求不会往下传递,如内置的 res.render 其实就是渲染完 html 直接返回给客户端,如果调用了next(),那么请求会继续向下传递,访问路由组中的下一个路由。

next中间件分为应用级中间件和路由级中间件

应用级中间件

我们回头再来看一下入口文件:

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('/', index);
app.use('/users', users);
app.use('/tests', tests);

其实,这些app.use()也是给我们加载中间件,只是这些中间件是在应用中使用的,应用级中间件绑定到 app 对象 使用 app.use() 和 app.METHOD(), 其中, METHOD 是需要处理的 HTTP 请求的方法,例如 GET, PUT, POST 等等,全部小写。我们自定义一下全局中间件:

// 挂载到全局的中间件,任何请求都会执行它
app.use(function (req, res, next) {
  console.log('Time:', Date.now());
  next();
});
// 挂载至 /hello/:name 的中间件,任何指向 /hello/:name 的请求都会执行它
app.use('/hello/:name', function (req, res, next) {
  console.log('Request Type:', req.method);
  next();
});
// 路由和句柄函数(中间件系统),处理指向 /hello/:name 的 GET 请求
app.get('/hello/:name', function (req, res, next) {
  res.send('hello '+ req.params.name);
});

如果我们不指定路径,则默认匹配所有路径,我们在最后一个路由中并没有使用next,所以请求没有向下传递.
我们在上方引入的其余全局中间件,即第三方中间件,其实也在回调函数的末尾使用了next方法,使得请求能够继续向下传递.

在我们访问’localhost:3000/hello/Y18’,可以在控制台和页面都能看到数据返回.

注意:

作为中间件系统的路由句柄,使得为路径定义多个路由成为可能。

// 一个中间件栈,处理指向 /user/:id 的 GET 请求
app.get('/hello/:id', function (req, res, next) {
  console.log('ID:', req.params.id);
  next();
}, function (req, res, next) {
  res.send('hello Info');
});

// 处理 /user/:id, 打印出用户 id
app.get('/hello/:id', function (req, res, next) {
  res.end(req.params.id);
});

但是由于第一个路由已经终止了请求-响应循环,第二个路由虽然不会带来任何问题,但却永远不会被调用.
如果需要在中间件栈中跳过剩余中间件,调用 next(‘route’) 方法将控制权交给下一个路由

// 一个中间件栈,处理指向 /user/:id 的 GET 请求
app.get('/hello/:id', function (req, res, next) {
  console.log('ID:', req.params.id);
  if(req.params.id == 4) next('route');
  else next();
}, function (req, res, next) {
  res.send('hello Info');
});

// 处理 /user/:id, 打印出用户 id
app.get('/hello/:id', function (req, res, next) {
  res.end(req.params.id);
});

路由级中间件

路由级中间件和应用级中间件一样,只是它绑定的对象为 express.Router()

修改index.js

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

/* GET home page. */
router.get('/:name', function(req, res, next) {
    // 如果 user id 为 0, 跳到下一个路由
    if (req.params.name != 'Y18') next('route');
    // 否则将控制权交给栈中下一个中间件
    else next(); 
}, function (req, res, next) {
    // 渲染常规页面
    res.send('你别来了,Y18');
});
// 处理name为Y18的路由, 渲染一个特殊页面
router.get('/:name', function (req, res, next) {
  res.send('欢迎你~ '+ req.params.name);
});

module.exports = router;

错误处理中间件

当我们访问一个不存在的路由时,虽然我们最后一个路由没有写next中间件,但是错误路由依旧执行,查看源码:

https://github.com/expressjs/express/blob/master/lib/router/index.js

function next(err) {
    ... //此处源码省略
    // find next matching layer
    var layer;
    var match;
    var route;

    while (match !== true && idx < stack.length) {
      layer = stack[idx++];
      match = matchLayer(layer, path);
      route = layer.route;

      if (typeof match !== 'boolean') {
        // hold on to layerError
        layerError = layerError || match;
      }

      if (match !== true) {
        continue;
      }
      ... //此处源码省略
    }
  ... //此处源码省略
    // this should be done for the layer
    if (err) {
        layer.handle_error(err, req, res, next);
    } else {
      layer.handle_request(req, res, next);
    }
  }

next函数内部有个while循环,每次循环都会从stack中拿出一个layer,这个layer中包含了路由和中间件信息,然后就会用layer和请求的path就行匹配,如果匹配成功就会执行layer.handle_request,调用中间件函数。但如果匹配失败,就会循环下一个layer(即中间件)。

express有成百上千的第三方中间件,在开发过程中我们首先应该去npm上寻找是否有类似实现的中间件,尽量避免造轮子,节省开发时间。下面给出几个常用的搜索 npm 模块的网站:

  1. http://npmjs.com(npm 官网)
  2. http://node-modules.com
  3. https://npms.io
  4. https://nodejsmodules.org

第六章 在express中使用less或sass

如果我们想要使用sass或者less,首先要对其进行学习;

Express支持sass,less等css引擎,我们现在使用sass编译模板的样式:
重新安装一个express应用:express app --view ejs -c sass
我们可以看到在入口文件中看到变化:

app.use(require('node-sass-middleware')({
  src: path.join(__dirname, 'public'),
  dest: path.join(__dirname, 'public'),
  indentedSyntax: true,
  sourceMap: true
}));

这其实就是引入了node的sass中间件。
同时,在public/stylesheets中可以看到style.sass这个sass文件。
启动express,访问’localhost:8000/'我们可以看到在这个文件夹中生成了两个新的文件:
‘style.css’
‘style.css.map’
这就是我们熟悉的sass经过编译后的文件。
同样,我们可以写自己的sass文件,决定样式:
修改style.sass

$fontStack:    Helvetica, sans-serif;
$primaryColor: #8A2BE2;

body
    font-family: $fontStack;
    padding: 50px;
p
  color: $primaryColor;

ul li
     color: #BDB76B;

a
  text-decoration: none;

修改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>
    <ul>
        <li>AngularJS</li>
        <li>Node.js</li>
        <li>Express</li>
    </ul>
    <a id="test" href="www.zixue.it">自学it网</a>
  </body>
</html>

访问’localhost:3000’,可以看到我们修改之后的样式.

我们可以修改indentedSyntax改为false,实现scss方式的书写

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值