Koa基础

2019-04-21  本文已影响0人  hellomyshadow

Koa2.x

1. Koa是基于Node.js的下一代web开发框架,也是由Express团队开发的;
    1. Node.js是一个异步的世界,官方API支持的都是callback形式的异步编程模型;
    2. Koa的开发思路与Express差不多,最大的特点就是,可以避免异步嵌套(回调地狱);
    3. Koa不在内核方法中绑定任何中间件,仅仅提供一个轻量优雅的函数库,所以体积更小,更健壮
    4. 阿里是业界最早一批使用Node.js做线上大流量应用的公司,并基于koa开发了egg.js
2. 环境搭建:koa2.0开始才算是稳定版,npm install koa --save
    1. koa2.x要求Node.js的版本大于v7.6,因为node7.6开始完全支持async/await
    2. 简单使用:const Koa = require('koa');  const app = new Koa();
    app.use( async (ctx)=>{ -----> 配置中间件
        ctx.body = 'hello koa2';
    })
    app.listen(3000, 'Ip') ---> 默认Ip为127.0.0.1
3. koa的工作内容
    1. 接收请求(request) --> 处理数据,生成数据(middleware) --> 发送数据(response)
    2. koa已经处理request和response,开发者只需要专心处理数据即可;
    3. 通过 use() 注册中间件,处理数据、生成数据。
4. 热重载工具:npm i supervisor -g,启动:supervisor app.js

四大对象

1. koa的四大对象:Application -> Context -> Request、Response
2. Application:当前应用程序对象,由 new Koa() 创建的实例对象,Context是其子类;
    let app = new Koa();
    1. app.listen():启用监听;
    2. app.use(callback):启用中间件的方法,callback(ctx, next)
    3. ctx 就是 Context对象,next 是迭代器,实现手动控制执行过程;
    4. 异步中间件:use(async(ctx, next) => { ... });
    5. 监听错误:app.on('error', (err, ctx) => { ...//统计错误处理 });
    app.use((ctx, next) => {
        throw new Error(); ---> 把错误抛给error事件去处理
    })
3. Context:每次请求都会包装成一个Context对象,它进一步封装了node的request和response
    1. koa把Context传入到中间件函数的第一个参数ctx中;
    2. ctx.req、ctx.res:分别是Node的request对象和response对象(太原始,并不推荐使用)
    3. ctx.request、ctx.response:分别是Koa的request对象和response对象,包含更多信息
    4. ctx.state:用户数据存储空间,ctx.username会污染Context对象,所以用ctx.state
    app.use((ctx, next) => {
        ctx.state.username = 'abc';
        next();
    }); 
    app.use((ctx, next) => { --> 同一个请求的Context对象也都是同一个
        console.log(ctx.state.username);
    });
    5. ctx.app:当前应用程序的Application对象
    6. ctx.throw():抛出错误时不建议使用 throw new Error(); 它属于JS,而throw()带有
    http的错误信息,如抛出404错误:ctx.throw(404, '错误信息', {附带参数});
4. Request、Response
    1. ctx.request、ctx.response分别获取本次请求的Request对象和Response对象;
    2. Request和Response可以获取/设置本次请求和响应的各种信息;
    3. Request和Response的很多方法会被映射到 Context 上,通过 ctx 直接调用;
    4. 重定向:ctx.redirect('/login'); -->ctx.response.redirect('/login');
    5. 设置下载文件头:ctx.attachment(filename); -->ctx.response.attachment();
    6. ctx.query 同 ctx.request.query,ctx.url 同 ctx.request.url ...

路由

1. 安装路由模块:cnpm install koa-router --save
2. 使用方式:let Router = require('koa-router');
    let router = new Router();  ---> 等效于:let router = Router();
    router.get('/', async(ctx, next) => {
        ctx.body = 'Index'; ---------> 相当于 res.writeHead();  res.end();
    })
    router.get('/news', async(ctx, next) => {
        ctx.body = 'News';
    }) -----------------------> 可以使用链式调用:router.get().get().get();
    app.use(router.routes());
    app.use(router.allowedMethods());  ---->可以用链式调用,app.use().use();
    1. app.use(router.routes()):启用路由;
    2. app.use(router.allowedMethods()):不是必须,但官方推荐,写在所有路由后面;
    3. allowedMethods() 的作用是会根据 ctx.status 设置response的响应头。
3. router.redict():路由重定向
    1. router.redict('/admin', '/user', 301);
    2. 访问 /admin 时,会被重定向到 /user
4. URL生成器:Router.url();
    1. Router.url('/list', {page: 1}, {query: {order:'desc'}});
    2. 生成路由:/list/1?order=desc

参数传递

1. get传值
    router.get('/news', async(ctx, next) => {
        ctx.body = 'News';
    }) --> http://localhost:3000/news?aid=123&uname=Jack
    1. 接收get传值的方式有两种:query(格式化的参数对象)和querystring(请求字符串)
    2. ctx.query:获取的是参数对象,{ aid:'123', uname:'Jack' }
    3. ctx.querystring:请求字符串的形式,aid=123&uname=Jack
    4. ctx.url:请求的路由地址,/news?aid=123&uname=Jack
2. 动态路由
    router.get('/news/:aid', async(ctx, next) => {
        ctx.body = 'News';
    }) --> http://localhost:3000/news/123
    1. ctx.params:获取动态路由的参数对象,{ aid: '123' }
    2. /news/:aid/:uname:多个占位符,http://localhost:3000/news/123/Mack

中间件

1. 在express中,中间件必须写在所有路由之前,而在koa中不管写在哪个位置,都会优先匹配;
2. 应用级中间件
    app.use(async (ctx)=>{
        ctx.body = '应用级中间件'
    })
    1. 用于匹配所有路由,一旦匹配成功,将不再自动向下匹配;
    2. 手动让路由继续向下匹配
    app.use(async (ctx, next)=>{
        ...
        await next();  ---> 继续向下匹配
    })
3. 路由中间件
    router.get('/news', async(ctx, next) => {
        console.log('首次匹配news')
        await next(); ---> 继续向下匹配
    })
    router.get('/news', async(ctx, next) => {
        ctx.body = 'News';
    })
4. 错误处理中间件
    app.use(async (ctx, next)=>{
        console.log('1', '中间件')
        await next(); ----------------->去匹配路由,不会再继续向下执行,等待匹配结果
        console.log('2', ctx.url)
        if(ctx.status == 404) { ---> 路由不存在,匹配失败,进行错误处理
            console.log('3', '404')
            ctx.body = '404页面'
        } else { ---------------------> 匹配成功
            console.log('4', ctx.url)
        }
    })
    router.get('/news', async(ctx, next) => {
        console.log('5', 'news')
        ctx.body = 'News'
    })
    1. 访问http://localhost:3000/xxx --> 1 --> 不存在此路由,匹配失败 --> 2 --> 3
    2. 访问http://localhost:3000/news --> 1 --> 5 --> 2 --> 4
5. koa的洋葱模型
    1. koa的匹配过程(request-->response)类似于一个洋葱
koa2.png
    2. 由外而内,再由内而外:
    中间件-1 --> 中间件2 --> 匹配路由,返回匹配结果 --> 中间件2 --> 中间件1
    app.use(async(ctx, next)=>{ ----------> 中间件-1
        console.log('1-1')
        await next();
        console.log('1-2')
    })
    app.use(async(ctx, next)=>{ ----------> 中间件-2
        console.log('2-1')
        await next();
        console.log('2-2')
    })
    router.get('/news', async(ctx, next) => {
        console.log('news')
        ctx.body = 'News'
    })
    3. 匹配过程:1-1 ==> 2-1 ==> news ==> 2-2 ==> 1-2

POST

1. 原生NodeJs获取POST提交的数据:<input type="text" name="uname" />
    function parserPost(ctx) {
        return new Promise((resolve, reject) => {
            try{
                let postData = "";
                ctx.req.on('data', (chunk)=>{
                    postData += chunk.toString();
                });
                ctx.req.on('end', ()=>{ ---> 数据传输完成
                    resolve(postData);
                });
            } catch(error) {
                reject(error);
            }
        });
    }
    router.post('/dologin', async(ctx) => {
        let data = await parserPost(ctx);
        ctx.body = '登录成功!'
    })
2. 中间件处理POST提交的数据:npm i koa-bodyparser --save
    1. 配置中间件:let parser = require('koa-bodyparser');
    app.use(parser());
    2. 获取post提交的数据
    router.post('/dologin', async (ctx)=>{
        let postData = ctx.request.body;
    });
3. 在url中,? 后的内容称为querystring
    1. querystring是url的一部分,与提交方式(无论是post还是get)没有任何关系;
    2. url的长度有限,所以在数据量较大时,不推荐使用querystring的方式传输,跟get没关系;
    3. post请求也可以在url上使用querystring,在后台也可以获取此参数;
    4. 不同的是,get方式不能操作正文,而post可以把参数写入正文(请求体)

静态服务

1. 静态资源中间件:npm install koa-static --save
2. 配置中间件:const static = require('koa-static');
    1. app.use(static('static')); ----> 托管 根目录/static 下的资源文件
    2. 在模板中访问static/css/base.css:
    <link rel="stylesheet" href="css/base.css" /> ---> href="/css/base.css"
3. koa静态资源的目录也可以配置多个:
    app.use(static('static')); ---> 根目录/static
    app.use(static(path(__dirname, 'public'))); ---> 根目录/public
    1. 在匹配资源时,会先在static目录下查找,如果查找失败,再去public目录下查找;
    2. app.use(static('.')); -->表示去根目录下去查找
    <img src="public/img/a.png" /> --> 根目录/public/img/a.png
4. app.use(static('.'))是不安全的,它能访问根目录下的所有资源,如app.js、package.json

模板引擎

适用于koa的模板引擎有很多,如ejs、jade、nunjucks、art-template...

koa与ejs

1. 安装ejs之后,安装koa-views:npm install koa-views --save
2. 多种方式配置koa-views中间件,const views = require('koa-views');
    1. app.use(views('views', { map:{ html:'ejs' } })); -->模板的后缀名为html
    2. app.use(views('views', { extension:'ejs' })); -->模板的后缀名为ejs
    3. views()的第一个参数表示ejs模板存放的目录,'views'表示在根目录/views
3. 在根目录/views中创建index.ejs
    router.get('/', async(ctx) => {
        await ctx.render('index', {key: value}) --->渲染index.ejs
    })
4. 公共数据(全局变量)
    1. 在koa中,通过 ctx.state 管理公共数据,在所有模板中都能访问;
    2. 在中间件中设置公共数据
    app.use(async(ctx, next) => {
        ctx.state = { uname: 'Mack', age: 20 } ----> 设置公共数据uname和age
        await next();
    })
    3. 在模板中使用:<h2><%= uname %></h2>

art-template

1. art-template是一个简约、超快的模板引擎,它采用作用域预声明的技术来优化模板渲染速度;
    1. art-template的模板渲染速度接近JavaScript极限地运行性能,且同时支持NodeJs和浏览器
    2. art-template支持ejs语法,也可以用类似angular数据绑定地语法
    3. 由腾讯开发的,同时支持Express、Koa
2. 渲染速度:art-template(15-25ms) > jade(51ms) > ejs(141ms)
3. 安装:npm install art-template --save,npm install koa-art-template --save
    const render = require('koa-art-template');
    render(app, {
        root: path.join(__dirname, 'views'), ---> 配置模板的位置:根目录/view
        extname: '.art', ---> 模板的扩展名,也可以设置为'.html'
        debug: process.env.NODE_ENV !== 'production' --> 开发环境下开启调试模式
    })
    app.use(async (ctx)=>{
        await ctx.render('index', {key: value}); --> 根目录/views/index.art
    })
4. art-template支持类似ejs的语法(原始语法),也支持类似angular的语法(标准语法)
    1. 原始语法:<%= value %>,<%= a?b:c %> ,<%= a+b %>,<%- value %> ...
    2. 引入子模板的方式与ejs不同:<% include('./public/header.art') %>
    <% include('./public/header.art', data) %>
5. 标准语法:{{value}},{{a?b:c}},{{a+b}},{{@value}},{{if a}} ... {{/if}}
    1. {{@value}}:表示原样输出value,如value="<p>123</p>",原样输出的结果就是<p>标签
    2. 循环数据:{{each a}}  {{$index}} {{$value}}  {{/each}}
    3. 引入子模板:{{include './header.art'}},{{include './header.art', data}}
    4. 引入不在同一目录下的子模板时,不能使用相对路径,而是直接相对于模板的根目录;
    {{include 'admin/public/header.html'}}  --->views/admin/public/header.html
6. 在标签内直接使用模板语言
    <input type="checkbox" {{if list.check==1}} checked {{/if}} />

Cookie

1. Koa可以直接操作cookie:ctx.cookies.set(name, value, [options])
    router.get('/', async (ctx)=>{
        ctx.cookies.set('uname', 'Jack', {
            maxAge: 60*1000*60 ---> 60s后过期
        })
    })
    1. 获取cookie:ctx.cookies.get('uname')
2. options的可选参数:maxAge、expires、path、domain、secure、httpOnly、overwrite
    1. httpOnly:是否只允许服务器访问cookie,默认为true,不允许浏览器端的JS访问cookie
    2. secure:安全的cookie,默认为false,设置为true表示只允许https可以访问;
    3. overwrite:是否覆盖以前设置的同名cookie,默认为false
    ctx.cookies.set('uname', 'Jack', {
        path: '/news',  ---> 只允许 /news 路由页面可以访问此cookie
        domain: '.baidu.com'  -->设置允许访问cookie的域名,默认当前域名下的所有路由页面
    }) -------------------------> 都可以访问,所以正常情况下不会设置
3. 在koa中无法直接设置中文的cookie,如ctx.cookies.set('uname', '李蕾');
    1. 可以把中文转为base64编码:new Buffer('李蕾').toString('base64');
    2. 解码:new Buffer('base64编码数据', 'base64').toString();

Session

1. 安装中间件:npm i koa-session --save
2. 配置session中间件
    1. const session = require('koa-session');
    2. app.keys = ['some secret']; ---> cookie的签名/加密,可以是任意字符串
    2. app.use(session(CONFIG, app));
3. CONFIG表示session的配置
    const CONFIG = {
        key: 'koa:sess',  ---> 也类似于cookie的key,使用默认配置即可
        maxAge: 86400000, ---> cookie的过期时间
        overwrite: true,
        httpOnly: false,  ---> 默认为true,表示只允许服务器端访问cookie
        signed: true, ----> 启用签名
        rolling: false,
        renew: false
    }
    1. rolling:true --> 浏览器每次访问都强制设置cookie,每次都会重置过期时间
    2. renew:true --> 每次访问时会检查cookie的过期时间,在将要过期时才会重置过期时间
4. 设置/获取:ctx.session.uname='Mack';  let uname=ctx.session.uname;
上一篇下一篇

猜你喜欢

热点阅读