(三)node学习——koa

目录

安装:

1、一个简单例子

2、koa-router

3、中间件(有点类似Express的中间件)

4、ejs模板

5、获取post传值

6、静态文件托管

7、高效的模板引擎art-template

8、Cookie

9、Session

10、路由模块化

 

Node.js 是一个异步的世界,官方 API 支持的都是 callback 形式的异步编程模型,这会带来许多问题,例如:

1、callback 嵌套问题

2、异步函数中可能同步调用 callback 返回数据,带来不一致性。

为了解决以上问题 Koa 出现了。

Koa -- 基于 Node.js 平台的下一代 web 开发框架。

Koa 是由 Express 原班人马打造的,致力于成为一个更小、更富有表现力、更健壮的Web 框架。 使用 koa 编写 web 应用,可以免除重复繁琐的回调函数嵌套, 并极大地提升错误处理的效率。koa 不在内核方法中绑定任何中间件, 它仅仅提供了一个轻量优雅的函数库,使得编写 Web 应用变得得心应手。开发思路和 express 差不多,最大的特点就是可以避免异步嵌套。

 

安装:

cnpm install koa --save

 

准备知识——es6

 

1、一个简单例子

var Koa= require('koa');

var app = new koa();

app.use(async (ctx) => {

    ctx.body = '这是一个简单的koa例子';

});

app.listen(8001);

 

2、koa-router

(1)、一个简单的例子

var Koa = require('koa');
var app = new Koa();

/**
 * 1、安装
 *      cnpm install koa-router --save
 *
 * 2、引用和实例化
 *      var Router = require('koa-router');
 *      var router = new Router();
 *
 * 3、使用router.get('/', async (ctx) => {});
 *
 * 4、开启路由
 *      app.use(router.routes());
 *      app.use(router.allowedMethods());
 */

var Router = require('koa-router');
var router = new Router();

router.get('/', async (ctx) => {

    ctx.body = '首页'; // 类似原生的  res.writeHead(), res.end('首页');

}).get('/news', async (ctx) => {

    ctx.body = '这是一个新闻页面';

});

// 作用:开启路由
app.use(router.routes());
// 作用:这是官方文档的推荐用法,我们可以
// 看到 router.allowedMethods()用在了路由匹配 router.routes()之后,所以在当所有
// 路由中间件最后调用.此时根据 ctx.status 设置 response 响应头
app.use(router.allowedMethods());

app.listen(8001);
// 路由引用的默认方式
var Router = require('koa-router');
var router = new Router();



// 简单的写法
var router = require('koa-router')();

(2)、获取get传值

var Koa = require('koa');
var app = new Koa();

var router = require('koa-router')();

// get传值以及获取get传值
// http://127.0.0.1:8001//news?cid=123&aid=456
router.get('/news', async (ctx) => {

    console.log(ctx.query); //  { cid: '123', aid: '456' } // ******推荐使用这种
    console.log(ctx.request.query); //  { cid: '123', aid: '456' }

    console.log(ctx.querystring); // cid=123&aid=456
    console.log(ctx.request.querystring); // cid=123&aid=456

    console.log(ctx.url); // /news?cid=123&aid=456
    console.log(ctx.request.url); // /news?cid=123&aid=456

    ctx.body = '这是一个新闻页面';

});

// 开启路由
app.use(router.routes());
app.use(router.allowedMethods());

app.listen(8001);

(3)、动态传值

var Koa = require('koa');
var app = new Koa();

var router = require('koa-router')();

// 动态路由:
// http://127.0.0.1:8001/newscontent/123
router.get('/newscontent/:aid', async (ctx) => {

    console.log(ctx.params); // { aid: '123' }

    ctx.body = '这是一个新闻页面';

});

// http://127.0.0.1:8001/paper/123/456
router.get('/paper/:aid/:cid', async (ctx) => {

    console.log(ctx.params); // { aid: '123', cid: '456' }

    ctx.body = '这是一个新闻页面';

});

// 开启路由
app.use(router.routes());
app.use(router.allowedMethods());

app.listen(8001);

 

3、中间件(有点类似Express的中间件)

(1)、应用级中间件

var Koa = require('koa');
var app = new Koa();

var Router = require('koa-router');
var router = new Router();

/**
 * 应用级中间件,不管是放在 哪个位置,都会在访问路由之前被访问到,
 * 一匹配到这个中间件,就会终止后面的程序,所以需要通过next()使得路由往下匹配
 */
app.use(async (ctx, next) => {
    console.log(new Date());

    next();

});

router.get('/', async (ctx) => {

    ctx.body = '首页'; // 类似原生的  res.writeHead(), res.end('首页');

});

router.get('/news', async (ctx) => {

    ctx.body = '这是一个新闻页面';

});

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(8001);

(2)、路由中间件

var Koa = require('koa');
var app = new Koa();


var Router = require('koa-router');
var router = new Router();


router.get('/', async (ctx) => {

    ctx.body = '首页'; // 类似原生的  res.writeHead(), res.end('首页');

})

/**
 * 路由中间件,在匹配路由前做判断用
 */
router.get('/news', async (ctx, next) => {

    console.log('这是一个路由中间件');

    next();

});

router.get('/news', async (ctx) => {

    ctx.body = '这是一个新闻页面';

});

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(8001);

(3)、错误中间件

var Koa = require('koa');
var app = new Koa();


var Router = require('koa-router');
var router = new Router();


/**
 * 错误处理中间件:比如在匹配路由的时候,如果匹配不到,则返回404,可以通过应用级中间件的next() 后来实现
 */

app.use(async (ctx, next) => {

    console.log(ctx.url);

    next();

    // 当所有的路由匹配完之后,会进到这里来
    if (ctx.status == 404) {
        ctx.status = 404;
        ctx.body = '这是一个404页面';
    }

});

router.get('/', async (ctx) => {

    ctx.body = '首页'; // 类似原生的  res.writeHead(), res.end('首页');

});

router.get('/news', async (ctx) => {

    ctx.body = '这是一个新闻页面';

});

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(8001);

(4)、中间件的执行流程——洋葱圈(从外到里,在从里到外)

var Koa = require('koa');
var app = new Koa();


var Router = require('koa-router');
var router = new Router();


/**
 * 中年件执行顺序:洋葱圈图:从外到里,再从里到外(类似堆栈,先进后出)
 * 匹配路由的过程中,先匹配next()之前的信息,然后next(),再匹配路由信息,最后再匹配next()后面的信息
 *  例如此例子打印顺序:
 *           01 这是最先打印的信息
             02 这是第二个打印的信息
             03 这是第三个打印的信息
             04 这是第四个打印的信息
             05 这是最后打印的信息
 */

app.use(async (ctx, next) => {

    console.log('01 这是最先打印的信息');

    next();

    console.log('05 这是最后打印的信息');

});

app.use(async (ctx, next) => {

    console.log('02 这是第二个打印的信息');

    next();

    console.log('04 这是第四个打印的信息');

});

router.get('/', async (ctx) => {

    ctx.body = '首页'; // 类似原生的  res.writeHead(), res.end('首页');

});

router.get('/news', async (ctx) => {

    console.log('03 这是第三个打印的信息');

    ctx.body = '这是一个新闻页面';

});

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(8001);

 

4、ejs模板

var Koa = require('koa');
var app = new Koa();

var router = require('koa-router')();


/**
 * 1、安装koa-views和ejs
 *      cnpm install koa-views --save
 *      cnpm install ejs --save
 *
 * 2、引用,这里不需要引用ejs,会自动使用的
 *      var views  = require('koa-views');
 *
 * 3、配置中间件,这里“__dirname”是指模板引用存放的目录
 *      方式一:app.use(views(__dirname, { map: {html: 'ejs' }}))
 *      方式二:app.use(views(__dirname, { extension: 'ejs' }))
 *
 * 4、使用:
 *      await ctx.render('index');     注意,这种方式配置的模板文件扩展名是“html”
 *      await ctx.render('index.ejs'); 注意,这种方式配置的模板文件扩展名是“ejs”
 */
var views = require('koa-views');

// 配置第三方中间件
// app.use(views('05 koa_ejs模板引擎/views', { map: {html: 'ejs' }}));
app.use(views('05 koa_ejs模板引擎/views', { extensions: 'ejs' }));

// 全局的共享信息设置
// 给ctx.state设置全局的属性,这样每个页面都可以共拿到
app.use(async (ctx, next) => {

    ctx.state = {
        username: '张三',
        sex: '男'
    };

    await next();

});

router.get('/', async (ctx) => {

    // 异步改同步操作,注意*****
    await ctx.render('index.ejs', {
        msg: '这是一个ejs信息'
    });

});

router.get('/news', async (ctx) => {

    let list = ['1111','2222','3333'];
    let hcode = '<h2>我是一段html代码</h2>'
    await ctx.render('news.ejs', {
        list: list,
        hcode: hcode
    });

});

app.use(router.routes());
app.use(router.allowedMethods());


app.listen(8001);
// index.ejs

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

这里引用的方式和原生ejs相比,可以省略-
<%- include public/header.ejs %>
<% include public/header.ejs %>

这是一个ejs模板引擎——ejs

<h2><%=msg%></h2>
</body>
</html>
// new.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

这里引用的方式和原生ejs相比,可以省略-
<%- include public/header.ejs %>
<% include public/header.ejs %>

<p>
    <div>这是通过全局设置(ctx.state)获取的属性</div>
    username:<%= username %><br>
    sex:<%= sex %><br>
</p>


这是一个新闻页面

<ul>
    <% for (var i = 0; i < list.length; i++) { %>
        <li><%= list[i] %></li>
    <% } %>
</ul>

通过&lt;%=&nbsp;hcode&nbsp;%&gt;这种方式是这样效果:<br>
<%= hcode %><br><br>
通过&lt;%-&nbsp;hcode&nbsp;%&gt;这种方式是这样效果:<br>
<%- hcode %>

</body>
</html>

 

5、获取post传值

(1)、原生

var Koa = require('koa'),
    app = new Koa(),
    router = require('koa-router')(),
    views = require('koa-views'),
    common = require('./modules/common');

app.use(views('06 koa_psot_koa-bodyparser/views', { extensions: true }));

router.get('/', async (ctx) => {

    // 异步改同步操作,注意*****
    await ctx.render('index.ejs');

});

router.post('/doPost', async (ctx) => {

    // ********注意
    // 由于原生的写法是一个异步操作,而这里需要将异步变同步,所以可以通过封装成一个方法来实现
    // await ctx.render('index.ejs');

    let data = await common.getPostData(ctx);
    ctx.res.writeHead(200, {'Content-Type': 'text/html;charset=utf-8'});
    ctx.body = data; // username=admin&password=123456

});

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(8001);
// common.js

exports.getPostData = function(ctx) {

    return new Promise( (resolve, reject) => {

        try {

            var str = '';
            ctx.req.on('data', (chunk) => {
                str += chunk;
            });

            ctx.req.on('end', (chunk) => {
                resolve(str);
            });

        } catch (err) {
            reject(err);
        }

    } );

}

(2)、koa-bodyparser

/**
 *
 * 1、安装
 *      cnpm install koa-bodyparser --save
 *
 * 2、引用
 *      var bodyParser = require('koa-bodyparser');
 *
 * 3、配置第三方中间件
 *      app.use(bodyParser());
 *
 * 4、获取post数据
 *      ctx.request.body;
 *      // {"username":"admin","password":"123456"}
 *
 */

var Koa = require('koa'),
    app = new Koa(),
    router = require('koa-router')(),
    views = require('koa-views'),
    bodyParser = require('koa-bodyparser');

app.use(views('06 koa_psot_koa-bodyparser/views', { extensions: true }));

// 引用koa-bodyparser中间件
app.use(bodyParser());

router.get('/', async (ctx) => {

    // 异步改同步操作,注意*****
    await ctx.render('index.ejs');

});

router.post('/doPost', async (ctx) => {

    ctx.body = ctx.request.body; // {"username":"admin","password":"123456"}

});

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(8001);

 

6、静态文件托管

/**
 * 1、安装
 *      cnpm install koa-static --save
 *
 * 2、引用
 *      var static = require('koa-static');
 *
 * 3、配置中间件
 *      app.use(static('07 koa_koa-static/statics'));
 */

var Koa = require('koa'),
    app = new Koa(),
    router = require('koa-router')(),
    views = require('koa-views'),
    static = require('koa-static');

app.use(views('07 koa_koa-static/views', { extensions: true }));

// 引用koa-static中间件,可以配置多个,通过next()向下寻找
// http://127.0.0.1:8001/css/basic.css
// <img src="images/iphone8.jpg">    http://127.0.0.1:8001/images/iphone8.jpg
app.use(static('07 koa_koa-static/statics'));
// <img src="images/李伊利雅.jpg">   http://127.0.0.1:8001/images/李伊利雅.jpg
app.use(static('07 koa_koa-static/public'));

router.get('/', async (ctx) => {

    // 异步改同步操作,注意*****
    await ctx.render('index.ejs');

});

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(8001);
// index.ejs

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="css/basic.css">
</head>
<body>

<h2 class="title">这是一个h2</h2>

<form action="/doPost" method="post">
    用户名:<input type="text" name="username">
    <br>
    <br>
    密 码:<input type="password" name="password">
    <br>
    <br>
    <input type="submit" value="提交">
</form>

<img src="images/iphone8.jpg">
<img src="images/李伊利雅.jpg">

</body>
</html>

目录结构:

 

7、高效的模板引擎art-template

/**
 *
 * 高性能模板引擎art-template,由腾讯开发,速度快,且直接ejs和angular的写法
 *
 * 1、安装
 *      cnpm install art-template --save
 *      cnpm install koa-art-template --save
 *
 * 2、引用
 *      var render = require('koa-art-template');
 *      var path = require('path');
 *
 * 3、配置
 *      render(app, {
          root: path.join(__dirname, 'views'), // 配置模板引擎的位置,这里通过path拼接成绝对路径
          extname: '.html', // 后缀名
          debug: process.env.NODE_ENV !== 'production' // 是否开始调试模式
        });
 *
 * 4、使用
 * `   ctx.render('index');
 */

var Koa = require('koa'),
    app = new Koa(),
    router = require('koa-router')(),
    render = require('koa-art-template'),
    path = require('path');

render(app, {
    root: path.join(__dirname, 'views'),
    extname: '.html',
    debug: process.env.NODE_ENV !== 'production'
});


// ejs写法
router.get('/', async (ctx) => {

    var data = {
        name: '这是ejs的写法',
        h: '<h2>这是一个好</h2>',
        num : 12,
        list: [1111,2222,33333]
    }
    await ctx.render('index', {
        data
    });

});

// angular写法
router.get('/news', async (ctx) => {

    var data = {
        name: '这是angular的写法',
        h: '<h2>这是一个好</h2>',
        num : 12,
        list: [1111,2222,33333]
    }
    await ctx.render('news', {
        data
    });

});

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(8001);
// index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="css/basic.css">
</head>
<br>

<h2 class="title">这是一个koa-art-template</h2>

<hr>
<br>

<%= data.name %>

<hr>
<br>

<%= 1 + 2 %>

<hr>
<br>

<%= data.h %>
<%- data.h %>

<hr>
<br>

<% if (data.num > 15) { %>
    大于15
<% } else { %>
    小于15
<% } %>

<hr>
<br>

<ul>
    <% for (var i = 0; i < data.list.length; i++) { %>

        <li><%= i %>-----<%= data.list[i] %></li>

    <% } %>
</ul>

<hr>
<br>

    这里引入外部文件,注意和ejs的区别,这里是带括号的
    <%- include('./public/footer')  %>

<hr>
<br>

</body>
</html>
// news.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="css/basic.css">
</head>
<br>

<h2 class="title">这是一个koa-art-template</h2>

<hr>
<br>

{{data.name}}

<hr>
<br>

{{1 + 2}}

<hr>
<br>

{{data.h}}
{{@ data.h}}

<hr>
<br>

{{ if data.num > 15 }}
    大于15
{{ else }}
    小于15
{{/if}}

<hr>
<br>

    <ul>
        {{each data.list}}
            <li>{{$index}}----{{$value}}</li>
        {{/each}}
    </ul>

<hr>
<br>

这里引入外部文件,注意和ejs的区别,这里是带括号的
<%- include('./public/footer')  %>

<hr>
<br>

</body>
</html>

 

8、Cookie

/**
 *
 * 不需要安装
 *
 * 1、设置cookie:ctx.cookie.set(name, value, options); // 这里options和Express的一样,只不过多了一个overwrite,但用处不大
 *
 * 2、获取cookie:ctx.cookie.get(name);
 *
 */

var Koa = require('koa'),
    app = new Koa(),
    router = require('koa-router')(),
    render = require('koa-art-template'),
    path = require('path');

render(app, {
    root: path.join(__dirname, 'views'),
    extname: '.html',
    debug: process.env.NODE_ENV !== 'production'
});


router.get('/', async (ctx) => {

    ctx.cookies.set('userinfo', 'zhangsan', {
        maxAge: 60 * 1000 * 60
    });
    await ctx.render('index');

});

router.get('/news', async (ctx) => {


    var userinfo = ctx.cookies.get('userinfo');

    await ctx.render('news', {
        userinfo
    });

});

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(8001);

由于koa的cookies无法设置成中文,所以这里可以通过Buffer对象,可以将中文转成base64;在获取的时候,可以再通过Buffer对象转成中文


router.get('/', async (ctx) => {

    // 默认下koa是不允许设置中文的,可以通过将中文设置成base64,获取的时候再转成中文即可
    let userinfo = new Buffer('张三').toString('base64');
    ctx.cookies.set('userinfo', userinfo, {
        maxAge: 60 * 1000 * 60
    });
    await ctx.render('index');

});

router.get('/news', async (ctx) => {


    let data = ctx.cookies.get('userinfo');
    let userinfo = new Buffer(data, 'base64').toString();

    await ctx.render('news', {
        userinfo
    });

});

 

9、Session

/**
 *
 * 1、安装
 *      cnpm install koa-session --save
 *
 * 2、引用
 *      const session = require('koa-session');
 *
 * 3、配置Session
 *
 *      app.keys = ['some secret hurr'];
 *
 *      const CONFIG = {
            key: 'koa:sess',    // 默认
            maxAge: 86400000,   // 配置过期时间,【需要修改】
            overwrite: true,    // 无效果,默认即可
            httpOnly: true,     // true表示只能在服务端访问,默认
            signed: true,       // 加密,默认
            rolling: false,     // 是否每次访问的时候都强制刷新,【需要修改】
            renew: false,       // 在快要过期的时候访问时,刷新session,【需要修改】
        };
 *
 * 4、设置中间件
 *      app.use(session(CONFIG, app));
 *
 * 5、设置 session
 *     ctx.session.userinfo = '张三';
 *
 * 6、获取 session
 *      ctx.session.userinfo
 */

var Koa = require('koa'),
    app = new Koa(),
    router = require('koa-router')(),
    render = require('koa-art-template'),
    path = require('path'),
    session = require('koa-session');

render(app, {
    root: path.join(__dirname, 'views'),
    extname: '.html',
    debug: process.env.NODE_ENV !== 'production'
});

// 这里要注意这是keys
app.keys = ['some secret hurr'];
const CONFIG = {
    key: 'koa:sess',    // 默认
    maxAge: 5000,       // 配置过期时间,【需要修改】
    overwrite: true,    // 无效果,默认即可
    httpOnly: true,     // true表示只能在服务端访问,默认
    signed: true,       // 加密,默认
    rolling: true,     // 是否每次访问的时候都强制刷新,【需要修改】
    renew: true        // 在快要过期的时候访问时,刷新session,【需要修改】
};
app.use(session(CONFIG, app));


router.get('/login', async (ctx) => {

    ctx.session.userinfo = '张三';
    ctx.body = '登录成功';

});

router.get('/', async (ctx) => {

    console.log(ctx.session.userinfo);
    await ctx.render('index');

});

router.get('/news', async (ctx) => {

    var userinfo = ctx.session.userinfo;

    await ctx.render('news', {
        userinfo
    });

});

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(8001);

 

10、路由模块化

通过引入外部的路由子模块文件(层级路由):“router.use('/', index)”的方式,来模块化路由,例如下面的这个例子:

// app.js


const Koa = require('koa'),
      router = require('koa-router')(),
      render = require('koa-art-template'),
      path = require('path');

// 引入模块化的后台管理模块
var admin = require('./routers/admin');
// 引入模块化的api模块
var api = require('./routers/api');
// 引入模块化的前台模块
var index = require('./routers/default');

var app = new Koa();

render(app, {
    root: path.join(__dirname, 'views'),
    extname: '.html',
    debug: process.env.NODE_ENV !== 'production'
});

/**
 * 前台配置方式一:
 *      这种配置方式,要求在模块中,不能写“/”:get('about')
 * /
 * /about
 * /product
 */
// router.use('/', index);

/**
 * 前台配置方式二:
 *      这种配置方式,要求在模块中,要写“/”:get('/about')
 */
router.use(index);

/**
 * /admin
 * /admin/user
 * ...
 */
router.use('/admin', admin.routes());

/**
 * /api
 * /api/newslist
 * ...
 */
router.use('/api', api); // 在api模块中暴露,同时开始路由


app.use(router.routes()).use(router.allowedMethods());


app.listen(8001);
// default.js

// 这里要引入koa-router模块
var router = require('koa-router')();

// /
router.get('/', async ctx => {
    await ctx.render('default/index');
});

/**
 * 前台配置方式一:router.use('/', index);
 */
// // 注意,前台的配置和后台的配置不一样,页面通过
// // /about访问,不能写成'/about'
// router.get('about', async ctx => {
//     await ctx.render('default/about');
// });
// // /product
// router.get('product', async ctx => {
//     await ctx.render('default/index');
// });


/**
 * 前台配置方式二:router.use(index);
 */
// 注意,这里和方式一不一样在于,需要写“/”
// /about
router.get('/about', async ctx => {
    await ctx.render('default/about');
});
// /product
router.get('/product', async ctx => {
    await ctx.render('default/index');
});

// 这类要在暴露这个路由模块的同时,开启路由
module.exports = router.routes();
// admin.js

// 路由模块化,子路由模块、层级路由
var router = require('koa-router')();

var user = require('./admin/user'),
    focus = require('./admin/focus'),
    newscate = require('./admin/newscate');

// /admin
router.get('/', async (ctx) => {
    await ctx.render('admin/index');
});

// /admin/user
router.use('/user', user);
// /admin/focus
router.use('/focus', focus);
// /admin/newscate
router.use('/newscate', newscate);



// 暴露当前路由模块
module.exports = router;

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

rising_chain

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值