www.2527.com_澳门新葡8455手机版_新京葡娱乐场网址_
做最好的网站

【Web前端】询问Koa和成立路由实例,Nunjucks模板引

2020-04-23 01:01 来源:未知

时间: 2019-12-22阅读: 61标签: KoaKoa 介绍

一、历史

广大的Web框架、ORM空间、模板引擎、测验框架和创设筑工程具

  • 常见的Web框架:Express,Sails.js,koa,Meteor,DerbyJS,Total.js,restify
  • ORM框架:Sequelize,ORM2,Bookshelf.js,Objection.js
  • 模版引擎:Jade,EJS,Swig,Nunjucks,doT.js
  • 测量试验框架:Mocha,Expresso,Unit.js,Karma
  • 塑造筑工程具:Grunt,Gulp,Webpack

koa是由Express原班人马构建的,三个根据Node.js平台的 Web 开荒框架

1、koa是express的晚辈,是Express的团体基于ES6语法中的generator写的web框架

koa

官网:http://koajs.com/。

koa是Express的子弟基于Node.js的web框架,如今有1.x和2.0七个本子。

Express是基于 ES5 的语法,随着新版 Node.js 从前扶植 ES6 ,该团伙再次编写诞生了koa 1.0,而koa2.0则是尤为提前的基于 ES7

2、express 的劣势:它是基于ES5的语法,如若异步嵌套档期的顺序过多,代码写起来就极度难看

历史

Express是率先代最风靡的web框架,它对Node.js的http举办了包装:

'use strict';

var express = require('express');

var app = express();

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

app.listen(8899, function () {
    console.log('A express app listening at port 8899');
});

即使Express的API超级粗略,然则它是基于ES5的语法,要得以完毕异步代码,独有三个主意:回调。固然异步嵌套档案的次序过多,代码写起来就那三个难看。

'use strict';

var express = require('express');
var fs = require('fs');

var app = express();

app.get('/test', function (req, res) {
    fs.readFile('/file1', function (err, data) {
        if (err) {
            res.status(500).send('read file1 error');
        }

        fs.readFile('/file2', function (err, data) {
            if (err) {
                res.status(500).send('read file2 error');
            }

            res.type('text/plain');
            res.send(data);
        });
    });
});

app.listen(8899, function () {
    console.log('A express app listening at port 8899');
});

虽说可以用async那样的库来协会异步代码,但是用回调写异步实乃太难受了!

坐飞机新版Node.js开头帮衬ES6,Express的公司又基于ES6的generator再度编写了下一代web框架koa。和Express相比,koa 1.0使用generator兑现异步,代码看起来像贰头的。

'use strict';

var koa = require('koa');

var app = koa();

app.use('/test', function* () {
    yield doReadFile1();
    var data = yield doReadFile2();
    this.body = data;
});

app.listen(8899);

用generator完成异步比回调轻易了成百上千,但是generator的本心实际不是异步Promise才是为异步设计的,不过Promise的写法复杂。为了简化异步代码,ES7(最近是草案,还平素不公布)引进了新的重大字asyncawait,能够轻巧地把叁个function变为异步情势。上边是鹏程JS的正式异步代码,越发从简了。

async function () {
    var data = await fs.read('/file1');
}

koa团队提前地基于ES7费用了koa2,koa2全然使用Promise并同盟async来落实异步。出于宽容性思虑,近日koa 2仍帮助generator的写法,但下贰个本子将会去掉。

app.use(async (ctx, next) => {
    await next();
    var data = await doReadFile();
    ctx.response.type = 'text/plain';
    ctx.response.body = data;
});

上边接收koa2。

安装 koa2

(1)这是用express的代码:

Hello World

创制项目文件夹hello-koa。

导入koa包的二种方法:

  • 动用npm命令直接设置。命令行切换来当前项目目录,试行命令npm install koa@2.0.0。npm会把koa2以致koa2依赖的具备包全数安装到当前目录的node_modules目录下。
  • 种类目录下开创package.json,该公文叙述项目标重视包。(推荐)
{
    "name": "hello-koa2",
    "version": "1.0.0",
    "description": "hello-koa web app",
    "main": "app.js",
    "scripts": {
        "start": "node app.js"
    },
    "keywords": [
        "koa",
        "async"
    ],
    "author": "Tim",
    "license": "Apache-2.0",
    "repository": {
        "type": "git",
        "url": "https://github.com/A1546488968/hello-koa.git"
    },
    "dependencies": {
        "koa": "2.0.0"
    }
}

dependencies描述了大家的工程重视的包以至版本号。别的字段均用来陈说项目音信,可放肆填写。

进而,在档案的次序目录下进行npm install即可。

留意,任曾几何时候都足以平昔删除全部node_modules目录,因为用npm install一声令下能够全体地再一次下载全数信任。并且,本条款录不该被归入版本调整中

上边编写app.js。

// 导入koa,和koa 1.x不同,在koa2中,我们导入的是一个class,因此用大写的Koa表示:
const Koa = require('koa');

// 创建Koa对象表示当前Web app
const app = new Koa();

// 异步处理请求
app.use(async (ctx, next) => {
    await next();
    ctx.response.type = 'text/html';
    ctx.response.body = '<h1>Hello World!</h1>';
});

app.listen(8899);
console.log('app start at port 8899...');

参数ctx是由koa传入的卷入了request和response的变量,大家能够透过它访谈request和response,next是koa传入的就要管理的下二个异步函数

大家率先用await next(卡塔尔;管理下一个异步函数,然后,设置response的Content-Type和内容。

由async标识的函数称为异步函数,在异步函数中,能够用await调用另叁个异步函数,那多少个器重字就要ES7中引进

伊始koa项目方可在等级次序目录,使用node app.js或者npm start。这里的npm start对应于项目目录下边package.json里面的start键。

"scripts": {
    "start": "node app.js"
}
npm i koa

app.get('/test', function (req, res) {

koa中间件(middleware)

调用await next()的原因:koa把过多async函数组成三个管理链,种种async函数都能够做一些和好的事体,然后用await next(卡塔尔来调用下一个async函数。大家把每种async函数称为middleware(中间件),那个middleware能够整合起来,完毕比相当多实用的职能。

middleware的逐个很入眼,也正是调用app.use(卡塔尔国的顺序决定了middleware的顺序

上边的代码结构了多个middleware,组成管理链,依次打字与印刷日志,记录管理时间,输出HTML。

const Koa = require('koa');

const app = new Koa();

app.use(async (ctx, next) => {
    console.log(`${ctx.request.method} ${ctx.request.url}`);
    await next();
});

app.use(async (ctx, next) => {
    const start = new Date().getTime(); // 当前时间
    await next();
    const ms = new Date().getTime() - start;    // 耗时
    console.log(`Time: ${ms}ms`);
});

app.use(async (ctx, next) => {
    await next();
    ctx.response.type = 'text/html';
    ctx.response.body = '<h1>Hello world!</h1>';
    console.log('Hello world!');
});

app.listen(8899);
console.log('app start at port 8899...');

打字与印刷结果:

GET /
Hello world!
Time: 51ms
GET /favicon.ico
Hello world!
Time: 2ms

假设一个middleware未有调用await next(卡塔尔国,后续的middleware将不再实行了。这种景观很普及,比如,三个检验客户权限的middleware可以决定是不是继续管理伏乞,依然直接再次来到403荒唐(禁绝访问)。

app.use(async (ctx, next) => {
    if (await checkUserPerssion(ctx)) {
        await next();
    } else {
        ctx.response.status = 403;  // 禁止访问
    }
});

上学koa的基本点是清楚middleware!

注意,ctx.url等价于ctx.request.urlctx.type等价于ctx.response.type

创建 koa2 工程

    fs.readFile('/file1', function (err, data) {

处理URL

我们须要对不一样的UTiggoL调用不相同的管理函数。上面包车型地铁一种烦琐的写法。

const Koa = require('koa');

const app = new Koa();

app.use(async (ctx, next) => {
    if (ctx.request.path === '/') {
        ctx.response.body = 'index page';
    } else {
        await next();   // 别忘了传递!
    }
});

app.use(async (ctx, next) => {
    if (ctx.request.path === '/test') {
        ctx.response.body = 'test page';
    } else {
        await next();
    }
});

app.use(async (ctx, next) => {
    if (ctx.request.path === '/error') {
        ctx.response.body = 'error page';
    } else {
        await next();
    }
});

app.listen(8899);
console.log('app start at port 8899...');

koa提供了极度管理UMuranoL映射的模块koa-router

首先须要在package.json累计凭仗项"koa-router": "7.0.0",然后推行命令npm install

// 导入koa,和koa 1.x不同,在koa2中,我们导入的是一个class,因此用大写的Koa表示:const Koa = require('koa');// 创建一个Koa对象表示web app本身:const app = new Koa();app.use( ... )// 在端口3000监听:app.listen(3000);

        if (err) {

处理get请求

const Koa = require('koa');

// 注意require返回的是函数
const router = require('koa-router')();

const app = new Koa();

app.use(async (ctx, next) => {
    console.log(`Process ${ctx.request.method} ${ctx.request.url}`);
    await next();
});

router.get('/hello/:name', async (ctx, next) => {
    var name = ctx.params.name;
    ctx.response.body = `<h1>Hello, ${name}</h1>`;
});

router.get('/', async (ctx, next) => {
    ctx.response.body = `<h1>Index</h1>`;
});

app.use(router.routes());

app.listen(8899);
console.log('app start at port 8899...');

注意,require('koa-router')回来的是函数,要求加上(State of Qatar来调用函数。

留意访谈格式,/hello/:name的变量name指的是http://127.0.0.1:8899/hello/Tim里面的Tim

app.use([path,] function [, function…])

            res.status(500).send('read file1 error');

处理post请求

使用router.post('/path', async fn)

看似的,put、delete、head央求也能够由router管理。

是因为发送post表单时,表单作为request的body体发送,不过node.js和koa的request对象都不提供分析request的body的效果!

这里运用新的middlewarekoa-bodyparser来深入分析原始request央求,然后,把解析后的参数,绑定到ctx.request.body中。

丰硕注重项:"koa-bodyparser": "3.2.0"

middleware的逐个很关键,这一个koa-bodyparser必需在router早先被注册到app对象上

const Koa = require('koa');

const router = require('koa-router')();
const bodyParser = require('koa-bodyparser');

const app = new Koa();

router.get('/', async (ctx, next) => {
    ctx.response.body = `<h1>Index</h1>
                        <form action="/signin" method="post">
                            <p>name:<input name="name" type="text" value="koa"></p>
                            <p>password:<input name="password" type="password"></p>
                            <p><input type="submit" value="submit"></p>
                        </form>`;
});

router.post('/signin', async (ctx, next) => {
    var name = ctx.request.body.name || '';
    var password = ctx.request.body.password || '';
    console.log(`signin with name: ${name}, password: ${password}`);

    // 直接使用===判等!
    if (name === 'koa' && password === '123456') {
        ctx.response.body = `<h1>Welcome, ${name}</h1>`;
    } else {
        ctx.response.body = `<h1>Login failed</h1>
                                <p><a href="/">Try again</p>`;
    }
});


// koa-bodyparser必须在router之前被注册到app对象上
app.use(bodyParser());
app.use(router.routes());

app.listen(8899);
console.log('app start at port 8899...');

只顾表单默许值的写法:var name = ctx.request.body.name || '';

在 path 上设置中间件,每当央求的门道的基路线和该path相配时,都会引致该中间件函数被实践,借使path 未有被设定,那么私下认可为/

        }

重构与Controller Middleware

具备的ULX570L管理函数都置于app.js里彰显很乱,并且,每加八个UTiguanL,就须求修正app.js。随着U本田UR-VL越来越多,app.js就能更加的长。

假若能把U普拉多L处理函数集中到某些js文件,大概某多少个js文件中就好了,然后让app.js自动导入全部拍卖U奥迪Q5L的函数。那样,代码一分开,逻辑就显得清楚了。

在类型目录下新建controllers文件夹。在controllers文件夹下新建立模型块命名称叫index.js,管理登记逻辑。

var fn_index = async (ctx, next) => {
    ctx.response.body = `<h1>Index</h1>
                        <form action="/signin" method="post">
                            <p>name:<input name="name" type="text" value="koa"></p>
                            <p>password:<input name="password" type="password"></p>
                            <p><input type="submit" value="submit"></p>
                        </form>`;
}

var fn_signin = async (ctx, next) => {
    var name = ctx.request.body.name || '';
    var password = ctx.request.body.password || '';
    console.log(`signin with name: ${name}, password: ${password}`);

    if (name === 'koa' && password === '123456') {
        ctx.response.body = `<h1>Welcome, ${name}</h1>`;
    } else {
        ctx.response.body = `<h1>Login failed</h1>
                                <p><a href="/">Try again</p>`;
    }
}

module.exports = {
    'GET /': fn_index,
    'POST /signin': fn_signin
};

相符的,在controllers文件夹下新建hello.js,管理登记成功的页面包车型客车逻辑。

var fn_hello = async (ctx, next) => {
    var name = ctx.params.name;
    ctx.response.body = `<h1>Hello, ${name}!</h1>`;
}

module.exports = {
    'GET /hello/:name': fn_hello
};

改正app.js,让它自动扫描controllers目录,找到全数js文件,导入,然后注册每一个UTucsonL。

下面的__dirname是日前施行文书在文件系统的总体目录名。

const Koa = require('koa');
const router = require('koa-router')();
const bodyParser = require('koa-bodyparser');
const fs = require('fs');

const app = new Koa();

function addControllers(router) {
    // 用同步的readdirSync列出文件
    // 启动时只运行一次,无性能问题
    var files = fs.readdirSync(_dirname   '/controllers');

    var js_files = files.filter((f) => {
        return return f.endsWith('.js');
    });

    for (var f of js_files) {
        console.log(`process controller: ${f}`);
        let mapping = require(_dirname   '/controllers'   f);
        addMapping(router, mapping);
    }
}

function addMapping(router, mapping) {
    for (var url in mapping) {
        if (url.startsWith('GET ')) {
            var path = url.substring(4);
            router.get(path, mapping[url]);
            console.log(`register URL mapping: ${url}`);
        } else if (url.startsWith('POST ')) {
            var path = url.substring(5);
            router.post(router, mapping[url]);
            console.log(`register URL mapping: ${url}`);
        } else {
            console.log('invalid URL: ${url}');
        }
    }
}

addControllers(router);

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

app.listen(8899);
console.log('app start at port 8899...');

地方逻辑代码过多,能够把围观代码收取来。在项目目录下新建controller.js。

const fs = require('fs');

function addControllers(router, dir) {
    // 用同步的readdirSync列出文件
    // 启动时只运行一次,无性能问题
    // __dirname是当前执行文件在文件系统的完整目录名
    var files = fs.readdirSync(__dirname   '/'   dir);

    var js_files = files.filter((f) => {
        return f.endsWith('.js');
    });

    for (var f of js_files) {
        console.log(`process controller: ${f}`);
        let mapping = require(__dirname   '/'   dir   '/'   f);
        addMapping(router, mapping);
    }
}

function addMapping(router, mapping) {
    for (var url in mapping) {
        if (url.startsWith('GET ')) {
            var path = url.substring(4);
            router.get(path, mapping[url]);
            console.log(`register URL mapping: ${url}`);
        } else if (url.startsWith('POST ')) {
            var path = url.substring(5);
            router.post(path, mapping[url]);
            console.log(`register URL mapping: ${url}`);
        } else {
            console.log('invalid URL: ${url}');
        }
    }
}


module.exports = function (dir) {
    let controllers_dir = dir || 'controllers';
    let router = require('koa-router')();
    addControllers(router, controllers_dir);

    return router.routes();
}

最后是代码比比较少的app.js。

const Koa = require('koa');
const bodyParser = require('koa-bodyparser');

const app = new Koa();

const controller = require('./controller');

app.use(bodyParser());

app.use(controller());

app.listen(8899);
console.log('app start at port 8899...');

重构后的门类拥有蛮好的模块化,全体拍卖UTiguanL的函数按作用组寄放在controllers目录,以后大家也只供给不停往那几个目录下加东西就足以了,app.js保持不改变。

app.use('/admin', function(req, res) { console.log(req.originalUrl); // '/admin/new'console.log(req.baseUrl); // '/admin'console.log(req.path); // '/new'});

app.use(async (ctx, next) = { await next(); // 处理下一个异步函数 ctx.response.type = 'text/html' // 设置 response 的 Content-Type ctx.response.body = 'h1Hello, koa2!/h1' // 设置 response 的内容})

        fs.readFile('/file2', function (err, data) {

Nunjucks模板引擎

官网:https://mozilla.github.io/nunjucks/cn/templating.html。

模板引擎正是依据模板协作数据构造出字符串输出的二个组件。

模板引擎最广泛的输出正是出口网页,也正是HTML文本。当然,也能够输出放肆格式的文件,比方Text,XML,Markdown等。

ES6起,JS支持模板字符串了,用反引号括起来。不过,JS的模板字符串必得写在JS代码中。

因此大家需求第三方的模板引擎。原因还会有:

  • 转义。对特殊字符要转义,防止碰着XSS攻击。比方,若是变量name的值不是小明,而是小明<script>...</script>,模板引擎输出的HTML到了浏览器,就能够电动施行恶意JavaScript代码
  • 格式化。对区别档案的次序的变量要格式化,比如,货币供给变成12,345.00这么的格式,日期须求形成2015-01-01如此的格式。
  • 简短逻辑。模板还必要能推行一些简短逻辑。比如标准输出。

NunjucksMozilla付出的四个纯JavaScript编写的模版引擎,不仅能够用在Node碰着下,又能够运营在浏览器端。不过,重要依旧运作在Node情状下,因为浏览器端有越来越好的模版设计方案,举个例子MVVM框架

Nunjucks与python的多个模板引擎jinjia2语法完全相近。实际上是Mozilla用JS重写了jinjia2代码。

动用二个模板引擎拾壹分简答,本质上只供给组织这样贰个函数。

function render(view, model) {

}

其中,view是模板的名号(又称作视图),因为也许存在四个模板,供给选取之中四个。model就是数据,在JavaScript中,它便是四个轻便易行的Object。render函数再次回到三个字符串,就是模板的输出。

在项目目录下创办理文件件夹views,存放模板文件。

增加正视库:"nunjucks": "2.4.2"。该模块不依附koa。

编写app.js。

const nunjucks = require('nunjucks');

function createEnv(path, opts) {
    var autoescape = opts.autoescape === undefined ? true : opts.autoescape;
    var noCache = opts.noCache || false;
    var watch = opts.watch || false;
    var throwOnUndefined = opts.throwOnUndefined || false;
    var env = new nunjucks.Environment(
                    new nunjucks.FileSystemLoader('views', {
                        noCache: noCache,
                        watch: watch
                    }), {
                        autoescape: autoescape,
                        throwOnUndefined: throwOnUndefined
                    });

    if (opts.filters) {
        for (var f in opts.filters) {
            env.addFilter(f, opts.filters[f]);
        }
    }

    return env;
}

var env = createEnv('views', {
    watch: true,
    filters: {
        hex: function (n) {
            return '0x'   n.toString(16);
        }
    }
});

变量env就象征Nunjucks模板引擎对象,它有一个render(view, model卡塔尔国方法,正好传入view和model四个参数,并回到字符串。

这里创办env使用new nunjucks.FileSystemLoader('views')开创多个文件系统加载器,从views目录读取模板。

现在,在views文件夹下边新建叁个模板文件hello.html:

<h1>
    Hello {{ name }}
</h1>

末尾,渲染该模板。

const nunjucks = require('nunjucks');

function createEnv(path, opts) {
    var autoescape = opts.autoescape === undefined ? true : opts.autoescape;
    var noCache = opts.noCache || false;
    var watch = opts.watch || false;
    var throwOnUndefined = opts.throwOnUndefined || false;
    var env = new nunjucks.Environment(
                    new nunjucks.FileSystemLoader(path || 'views', {
                        noCache: noCache,
                        watch: watch
                    }), {
                        autoescape: autoescape,
                        throwOnUndefined: throwOnUndefined
                    });

    if (opts.filters) {
        for (var f in opts.filters) {
            env.addFilter(f, opts.filters[f]);
        }
    }

    return env;
}

var env = createEnv('views', {
    watch: true,
    filters: {
        hex: function (n) {
            return '0x'   n.toString(16);
        }
    }
});

var s = env.render('hello.html', {name: 'Tim'});
console.log(s);

打字与印刷结果:

<h1>
    Hello Tim
</h1>

插入一段恶意JS代码后,

var s = env.render('hello.html', {name: '<script>alert("Hello");</script>'});
console.log(s);

打字与印刷结果是:

<h1>
    Hello <script>alert("Hello");</script>
</h1>

故而幸免了隐衷的恶意脚本。

仍可以够运用Nunjucks提供的成效强盛的tag,编写条件判别、循环等职能。

hello.html:

<body>
    <h3>Fruits List</h3>
    {% for f in fruits %}
    <p>{{ f }}</p>
    {% endfor %}
</body>

app.js部分代码:

var s = env.render('hello.html', {fruits: ['apple', 'pear', 'orange']});
console.log(s);

打字与印刷结果:

<body>
    <h3>Fruits List</h3>

    <p>apple</p>

    <p>pear</p>

    <p>orange</p>

</body>

Nunjucks模板引擎最有力的效果与利益在于模板的后续

新建立模型板base.html:

<html>
    <body>
        {% block header %} <h3>Unnamed</h3> {% endblock %}
        {% block body %} <div>No body</div> {% endblock %}
        {% block footer %} <div>copyright</div> {% endblock %}
    </body>
</html>

概念了四个可编写制定的块,分别命名称为header、body和footer。子模板能够有选用地对块进行再一次定义。

新建立模型板文件index.html,重新定义header和body:

{% extends 'base.html' %}

{% block header %} <h1>{{ header }}</h1> {% endblock %}

{% block body %} <p>{{ body }}</p> {% endblock %}

继之渲染(这里还要在网页上海展览中心示):

const nunjucks = require('nunjucks');
const Koa = require('koa');
const router = require('koa-router')();

const app = new Koa();

function createEnv(path, opts) {
    var autoescape = opts.autoescape === undefined ? true : opts.autoescape;
    var noCache = opts.noCache || false;
    var watch = opts.watch || false;
    var throwOnUndefined = opts.throwOnUndefined || false;
    var env = new nunjucks.Environment(
                    new nunjucks.FileSystemLoader(path || 'views', {
                        noCache: noCache,
                        watch: watch
                    }), {
                        autoescape: autoescape,
                        throwOnUndefined: throwOnUndefined
                    });

    if (opts.filters) {
        for (var f in opts.filters) {
            env.addFilter(f, opts.filters[f]);
        }
    }

    return env;
}

var env = createEnv('views', {
    watch: true,
    filters: {
        hex: function (n) {
            return '0x'   n.toString(16);
        }
    }
});

var s = env.render('index.html', {header: 'Hello', body: '我爱Node.js!'});
console.log(s);

router.get('/index.html', async (ctx, next) => {
    ctx.response.type = 'text/html';
    ctx.response.body = s;
});

app.use(router.routes());

app.listen(8899);
console.log('app start at port 8899...');

打印结果:

<html>
    <body>
         <h1>Hello</h1>
         <p>我爱Node.js!</p>
         <div>copyright</div>
    </body>
</html>

上面包车型地铁footer未有重定义,所以仍利用父模板的原委。

由async标识的函数称为异步函数,在异步函数中,能够用await调用另二个异步函数,那七个关键字将在ES7 中引进(用法看上去好像 Promise)

            if (err) {

性能

对此模板渲染自己来讲,速度是至极丰硕快的,即拼字符串,纯CPU操作。

品质难点重要性出以后从文件读取模板内容这一步。那是一个IO操作,在Node.js境况中,我们了然,单线程的JavaScript最不可能经得住的哪怕一块IO,但Nunjucks默许就使用同步IO读取模板文件。

好消息是Nunjucks会缓存已读取的文件内容,也便是说,模板文件最多读取二回,就能放在内部存款和储蓄器中,前边的号召是不会重新读取文件的,只要大家钦赐了noCache: false这一个参数。

在开拓情况下,可以关闭cache,那样每一遍重复加载模板,便于实时改革模板。在分娩情状下,必须要开垦cache,那样就不会有总体性难点

Nunjucks也提供了异步读取的不二法门,但是写起来很麻烦,有大概的写法我们就不会思量复杂的写法。保持代码简单是可维护性的首要性。

上述代码中,参数ctx是由 koa 传入的卷入了request和response的变量,大家能够通过它访谈request和response,next是 koa 传入的就要管理的下贰个异步函数

                res.status(500).send('read file2 error');

使用MVC

用koa管理不相同的U君越L,用Nunjucks能够渲染模板。现在能够把双方结合起来了。

当客商通过浏览器诉求一个U奇骏L时,koa将调用有个别异步函数管理该UOdysseyL。在这里个异步函数内部,调用相近于ctx.render('home.html', { name: 'Tim' });,通过Nunjucks把数量用内定的沙盘模拟经营渲染成HTML,然后输出给浏览器。

Web前端 1

MVC示意图.png

这就是MVCModel-View-Controller,中文名模型-视图-控制器

  • M:用来传给View的,这样View在轮番变量的时候,就能够从Model中收取相应的数额。举例地点的JavaScript对象{ name: 'Tim' }
  • V:包蕴变量{{ name }}的模版,View肩负展现逻辑,通过轻易地更替部分变量,View最后输出的正是客商观望的HTML。
  • C:异步函数,Controller肩负业务逻辑,举例检查顾客名是还是不是存在,收取顾客消息等。

在品种目录下树立新文件夹static。

增加产能2个依赖项mimemz,全数依赖如下:

"dependencies": {
    "koa": "2.0.0",
    "koa-router": "7.0.0",
    "koa-bodyparser": "3.2.0",
    "nunjucks": "2.4.2",
    "mime": "1.3.4",
    "mz": "2.4.0"
}

对于每贰个 http 央浼, koa 将调用通过app.use(State of Qatar注册的async函数,并传播ctx和next参数

            }

编写controller

编纂三个controller。

首先是拍卖首页的GET /

var fn_index = async (ctx, next) => {
    ctx.render('index.html', {
        title: 'Welcome'
    });
}

小心,koa并不曾经在ctx对象上提供render方法,这里假如应该如此使用,那样,我们在编写Controller的时候,最终一步调用ctx.render(view, model卡塔尔就产生了页面输出。

接下去是拍卖登陆的POST /signin

var fn_signin = async (ctx, next) => {
    var email = ctx.request.body.email || '';
    var password = ctx.request.body.password || '';

    if (email === 'admin@tim.com' && password === '123456') {
        ctx.render('signin-ok.html', {
            title: 'Sign In OK',
            name: 'Admin'
        });
    } else {
        ctx.render('signin-failed.html', {
            title: 'Sign In Failed'
        });
    }
}

完整的index.js:

var fn_index = async (ctx, next) => {
    ctx.render('index.html', {
        title: 'Welcome'
    });
}

var fn_signin = async (ctx, next) => {
    var email = ctx.request.body.email || '';
    var password = ctx.request.body.password || '';

    if (email === 'admin@tim.com' && password === '123456') {
        ctx.render('signin-ok.html', {
            title: 'Sign In OK',
            name: 'Admin'
        });
    } else {
        ctx.render('signin-failed.html', {
            title: 'Sign In Failed'
        });
    }
}


module.exports = {
    'GET /': fn_index,
    'POST /signin': fn_signin
};

完整的controller.js

const fs = require('fs');

function addControllers(router, dir) {
    // 用同步的readdirSync列出文件
    // 启动时只运行一次,无性能问题
    // __dirname是当前执行文件在文件系统的完整目录名
    var files = fs.readdirSync(__dirname   '/'   dir);

    var js_files = files.filter((f) => {
        return f.endsWith('.js');
    });

    for (var f of js_files) {
        console.log(`process controller: ${f}`);
        let mapping = require(__dirname   '/'   dir   '/'   f);
        addMapping(router, mapping);
    }
}

function addMapping(router, mapping) {
    for (var url in mapping) {
        if (url.startsWith('GET ')) {
            var path = url.substring(4);
            router.get(path, mapping[url]);
            console.log(`register URL mapping: ${url}`);
        } else if (url.startsWith('POST ')) {
            var path = url.substring(5);
            router.post(path, mapping[url]);
            console.log(`register URL mapping: ${url}`);
        } else {
            console.log('invalid URL: ${url}');
        }
    }
}


module.exports = function (dir) {
    let controllers_dir = dir || 'controllers';
    let router = require('koa-router')();
    addControllers(router, controllers_dir);

    return router.routes();
}

实例:

            res.type('text/plain');

编写view

需要3个view:index.html、signin-ok.html和signin-failed.html。

编辑静态HTML使用现有的前端框架十三分必要。这里运用Bootstrap。下载后解压拷贝到项目目录的static目录上面。

const Koa=require('koa');const app=new Koa();app.use(async (ctx, next) = { console.log('第一1'); await next(); // 调用下一个middleware console.log('第一2');});app.use(async (ctx, next) = { console.log('第二1'); await next(); // 调用下一个middleware console.log('第二2');});app.use(async (ctx, next) = { console.log('第三1'); await next(); console.log('第三2');});app.listen(3000);// 打印结果如下:// 第一1// 第二1// 第三1// 第三2// 第二2// 第一2

            res.send(data);

编纂middleware处理静态财富文件

也能够去npm搜索能用来koa2的管理静态文件的包并直接行使

由于静态文件在子文件夹static下,因而内需编写制定middleware来管理/static/开头的URL

编写static-files.js:

const path = require('path');
const mime = require('mime');
const fs = require('mz/fs');

// url: 类似 '/static/'
// dir: 类似 __dirname   '/static'
function staticFiles(url, dir) {
    return async (ctx, next) => {
        let rpath = ctx.request.path;
        if (rpath.startsWith(url)) {
            // 获取文件完整路径,substring后的结果字符串开头没有/,join后会加上的
            let fp = path.join(dir, rpath.substring(url.length));
            if (await fs.exists(fp)) {
                ctx.response.type = mime.lookup(rpath);
                ctx.response.body = await fs.readFile(fp);
            } else {
                ctx.response.status = 404;
            }
        } else {
            await next();        
        }
    };
}


module.exports = staticFiles;

staticFiles是多少个不足为道函数,它选用七个参数:UCRUISERL前缀和四个目录,然后再次回到一个async函数。这一个async函数会剖断当前的UENCOREL是不是以钦赐前缀带头,如若是,就把U讴歌MDXL的门路正是文件,并发送文书内容。如果不是,这几个async函数就不做其余业务,而是简单的说地调用await next()让下叁个middleware去管理供给

上边用到了mz模块。mz提供的API和Node.js的fs模块完全相符,但fs模块使用回调,而mz封装了fs对应的函数,并改为Promise。那样,大家就足以特别轻便的用await调用mz的函数,而无需此外回调。上边mz的调用加上了await

koa-router 和 koa-bodyparser

        });

集成Nunjucks

集成Nunjucks实际上也是编写制定叁个middleware,那个middleware的效果是给ctx对象绑定一个render(view, model卡塔尔的主意,那样,前面包车型客车controller就足以调用那一个方式来渲染模板了。

新建templating.js:

const nunjucks = require('nunjucks');

function createEnv(path, opts) {
    var autoescape = opts.autoescape === undefined ? true : opts.autoescape;
    var noCache = opts.noCache || false;
    var watch = opts.watch || false;
    var throwOnUndefined = opts.throwOnUndefined || false;
    var env = new nunjucks.Environment(
                    new nunjucks.FileSystemLoader(path || 'views', {
                        noCache: noCache,
                        watch: watch
                    }), {
                        autoescape: autoescape,
                        throwOnUndefined: throwOnUndefined
                    });

    if (opts.filters) {
        for (var f in opts.filters) {
            env.addFilter(f, opts.filters[f]);
        }
    }

    return env;
}

function templating(path, opts) {
    var env = createEnv(path, opts);

    return async (ctx, next) => {
        // 给ctx绑定render函数
        ctx.render = function (view, model) {
            ctx.response.body = env.render(view, Object.assign({}, ctx.state || {}, model || {}));
            ctx.response.type = 'text/html';
        };

        // 继续处理请求
        await next();
    };
}


module.exports = templating;

上边包车型客车代码给ctx“安装”了二个render(State of Qatar函数,安装完后必需一而再三回九转调用下三个middleware。

注意,Object.assign(target, ...sources)方式用于将全部可枚举的品质的值从三个或几个源对象复制到目的对象。它将回来指标对象target。

上边的代码中,ctx.render内部渲染模板时,Model对象并非流传的model变量,而是Object.assign。这是为着扩展

首先,model || {}作保了就算传入undefinedmodel也会化为暗中认可值{}Object.assign()会把除第一个参数外的别的参数的全体属性复制到第三个参数中。第2个参数是ctx.state || {},这几个目标是为着能把一些共用的变量放入ctx.state并传给View

诸如,有些middleware担负检查客商权限,它能够把当前顾客放入ctx.state中:

app.use(async (ctx, next) => {
    var user = tryGetUserFromCookie(ctx.request);
    if (user) {
        ctx.state.user = user;
        await next();
    } else {
        ctx.response.status = 403;
    }
});

这么就不曾要求在各样Controller的async函数中都把user变量归入model中。

在行使时,app.js加上:

const isProduction = process.env.NODE_ENV === 'production';

app.use(templating('views', {
    noCache: !isProduction,
    watch: !isProduction
}));

判别当前条件是不是是production情状。要是是,就动用缓存(noCache: false),若是否,就关门缓存(noCache: true)。在支付景况下,关闭缓存后,我们改良View,可以一向刷新浏览器见到成效,不然,老是修改都不得不重启Node程序,会大幅地下落开辟功用。

watch参数:暗中同意值: false,当模板变化时再也加载。使用前请确定保障已安装可选依赖chokidar。watch参数临蓐条件设置成false,开辟遇到设置成true。

Node.js在全局变量process中定义了三个情况变量env.NODE_ENV,在支付时,蒙受变量应该安装为'development',而安顿到服务器时,情况变量应该设置为'production'。

注意:临盆条件上必需安插情状变量NODE_ENV = 'production',而支付境况无需配备,实际上NODE_ENV可能是undefined,所以认清的时候,不要用NODE_ENV === 'development'

长久以来的,在行使方面编写的拍卖静态文件的middleware时,也得以依赖境遇变量决断。

if (!isProduction) {
    let staticFiles = require('./static-files');
    app.use(staticFiles('static', __dirname   'static/'));
}

因为在分娩条件下,静态文件是由布置在最前边的反向代理服务器(如Nginx)处理的,Node程序无需处理静态文件!而在付出意况下,大家期望koa能顺带管理静态文件,不然,就亟须手动配置一个反向代理服务器,那样会引致支出条件非常复杂。

下边就足以初始编写制定view了。首先编写一个模板骨架:base.html,让此外模板继承它,实现代码重用。参谋Bootstrap的官方网站不难编写了base.html。

base.html

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <meta name="description" content="Hello Koa">
    <title>{{ title }}</title>
    <link rel="stylesheet" href="/static/css/bootstrap.css">
    <script src="/static/js/jquery-2.2.4.js"></script>
    <script src="/static/js/bootstrap.js"></script>
</head>

<body>
    <header class="navbar navbar-static-top">
        <div class="container">
            <div class="navbar-header">
                <a href="/" class="navbar-brand">Hello Koa</a>
            </div>
            <nav class="collapse navbar-collapse" id="bs-navbar">
                <ul class="nav navbar-nav">
                    <li><a target="_blank" href="http://www.liaoxuefeng.com/">Get Courses</a></li>
                    <li><a target="_blank" href="https://github.com/michaelliao/learn-javascript">Source Code</a></li>
                    <li><a target="_blank" href="http://getbootstrap.com/">Resource</a></li>
                </ul>
            </nav>
        </div>
    </header>
    <div id="important" style="color:#cdbfe3; background-color:#6f5499; padding:30px 0; margin:-20px 0 20px 0;">
        <div class="container">
            <h1 style="color:#fff; font-size:60px">Hello Koa</h1>
            <p style="font-size:24px; line-height:48px">Hello World!</p>
        </div>
    </div>
    {% block main %} {% endblock %}
    <footer style="background-color:#ddd; padding: 20px 0;">
        <div class="container">
            <p>
                <a target="_blank" href="http://www.liaoxuefeng.com">Website</a> - 
                <a target="_blank" href="https://github.com/michaelliao/learn-javascript">GitHub</a> - 
                <a target="_blank" href="http://weibo.com/liaoxuefeng">Weibo</a>
            </p>
            <p>This JavaScript course is created by <a target="_blank" href="http://weibo.com/liaoxuefeng">@廖雪峰</a>.</p>
            <p>Code licensed <a target="_blank" href="https://github.com/michaelliao/learn-javascript/blob/master/LICENSE">Apache</a>.</p>
        </div>
    </footer>
</body>

</html>

index.html

{% extends "base.html" %} {% block main %}
<div class="container">
    <div class="row">
        <div class="col-md-6">
            <div class="panel panel-default">
                <div class="panel-heading">
                    <h3 class="panel-title"> Please sign in</h3>
                </div>
                <div class="panel-body">
                    <form action="/signin" method="post">
                        <div class="form-group">
                            <label>Email address</label>
                            <input type="email" name="email" class="form-control" placeholder="Email">
                            <p class="help-block">Use email: admin@example.com</p>
                        </div>
                        <div class="form-group">
                            <label>Password</label>
                            <input type="password" name="password" class="form-control" placeholder="Password">
                            <p class="help-block">Use password: 123456</p>
                        </div>
                        <button type="submit" class="btn btn-primary">Sign In</button>
                    </form>
                </div>
            </div>
        </div>
        <div class="col-md-6">
            <div class="panel panel-default">
                <div class="panel-heading">
                    <h3 class="panel-title"> Video training</h3>
                </div>
                <div class="panel-body">
                    <video width="100%" controls="controls">
                        <source src="https://github.com/michaelliao/learn-javascript/raw/master/video/vscode-nodejs.mp4">
                    </video>
                </div>
            </div>
        </div>
    </div>
    <div class="row">
        <div class="col-md-12">
            <h1>Get more courses...</h1>
        </div>
    </div>
    <div class="row">
        <div class="col-md-4">
            <div class="panel panel-default">
                <div class="panel-heading">
                    <h3 class="panel-title">JavaScript</h3>
                </div>
                <div class="panel-body">
                    <p>full-stack JavaScript course</p>
                    <p><a target="_blank" href="http://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000">Read more</a></p>
                </div>
            </div>
        </div>
        <div class="col-md-4">
            <div class="panel panel-default">
                <div class="panel-heading">
                    <h3 class="panel-title">Python</h3>
                </div>
                <div class="panel-body">
                    <p>the latest Python course</p>
                    <p><a target="_blank" href="http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000">Read more</a></p>
                </div>
            </div>
        </div>
        <div class="col-md-4">
            <div class="panel panel-default">
                <div class="panel-heading">
                    <h3 class="panel-title">git</h3>
                </div>
                <div class="panel-body">
                    <p>A course about git version control</p>
                    <p><a target="_blank" href="http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000">Read more</a></p>
                </div>
            </div>
        </div>
    </div>
</div>
{% endblock %}

signin-ok.html

{% extends "base.html" %} {% block main %}
<div class="container">
    <div class="row">
        <div class="col-md-12">
            <h1>Sign in successfully!</h1>
            <div class="alert alert-success"> <strong>Well done!</strong> You successfully signed in as {{ name }}!
            </div>
            <p><a href="/">Back to home</a></p>
        </div>
    </div>
</div>
{% endblock %}

signin-failed.html

{% extends "base.html" %} {% block main %}
<div class="container">
    <div class="row">
        <div class="col-md-12">
            <h1>Sign in failed!</h1>
            <div class="alert alert-danger"> <strong>Sorry!</strong> Your email or password does not match! Please try again.
            </div>
        </div>
    </div>

    <div class="row">
        <div class="col-md-6">
            <div class="panel panel-default">
                <div class="panel-heading">
                    <h3 class="panel-title"> Please sign in</h3>
                </div>
                <div class="panel-body">
                    <form action="/signin" method="post">
                        <div class="form-group">
                            <label>Email address</label>
                            <input type="email" name="email" class="form-control" placeholder="Email">
                            <p class="help-block">Use email: admin@example.com</p>
                        </div>
                        <div class="form-group">
                            <label>Password</label>
                            <input type="password" name="password" class="form-control" placeholder="Password">
                            <p class="help-block">Use password: 123456</p>
                        </div>
                        <button type="submit" class="btn btn-primary">Sign In</button>
                    </form>
                </div>
            </div>
        </div>
        <div class="col-md-6">
            <div class="panel panel-default">
                <div class="panel-heading">
                    <h3 class="panel-title"> Video training</h3>
                </div>
                <div class="panel-body">
                    <video width="100%" controls="controls">
                        <source src="https://github.com/michaelliao/learn-javascript/raw/master/video/vscode-nodejs.mp4">
                    </video>
                </div>
            </div>
        </div>
    </div>
</div>
{% endblock %}

koa-router担任管理 U奥迪Q5L 映射,通过它能够更省心的对 U君越L 实行操作

    });

编写app.js

尤其引人瞩目middleware的顺序。app.js分别富含了上边多少个通用的middleware:

  • 第一个middleware:记录U大切诺基L以至页面实行事件
  • 第二个middleware:拍卖静态文件
  • 第三个middleware:解析POST请求
  • 第四个middleware:给ctx加上render(卡塔尔来使用模板引擎Nunjucks
  • 第五个middleware:处理URL路由

完整的app.js

const Koa = require('koa');
const templating = require('./templating');
const bodyParser = require('koa-bodyparser');
const controller = require('./controller');

const app = new Koa();

const isProduction = process.env.NODE_ENV === 'production';


// 第一个middleware:记录URL以及页面执行事件:
app.use(async (ctx, next) => {
    console.log(`Process ${ctx.request.method} ${ctx.request.url}...`);

    var start = new Date().getTime();
    var execTime;
    await next();
    execTime = new Date().getTime() - start;
    ctx.response.set('X-Response-Time', `${execTime}ms`);
});


// 第二个middleware:处理静态文件
if (!isProduction) {
    let staticFiles = require('./static-files');
    app.use(staticFiles('/static/', __dirname   '/static/'));
}

// 第三个middleware:解析POST请求
app.use(bodyParser());


// 第四个middleware:给ctx加上render()来使用模板引擎Nunjucks
app.use(templating('views', {
    noCache: !isProduction,
    watch: !isProduction
}));


// 第五个middleware:处理URL路由
app.use(controller());



app.listen(8899);
console.log('app start at port 8899...');

前些天得以尝试了:

koa-router 使用

});

const router = require('koa-router')(); // 引入 koa-router……// add router middleware:app.use(router.routes());

(2)使用koa:将读取文件的不二法门移出去

留意导入koa-router的言辞最终的(卡塔尔(قطر‎是对函数调用

app.use('/test', function *() {

因此选拔 koa-router ,大家能够拍卖部分更复杂的 U奥迪Q5L 操作,比方 GET 诉求

    yield doReadFile1();

// log request URL:app.use(async (ctx, next) = { console.log(`h1Hello, ${name}!/h1`); await next();});// add url-route:router.get('/hello/:name', async (ctx, next) = { var name = ctx.params.name; // 通过 ctx.params.name 可以获取 URL 带来的变量 ctx.response.body = `h1Hello, ${name}!/h1`;});

    var data = yield doReadFile2();

处理 get 请求

    this.body = data;

用router.get('/path', async fn卡塔尔(قطر‎管理的是 get 央浼

});

处理 post 请求

(3)为了简化异步代码,ES7(近些日子是草案,尚未曾颁发)引进了新的最首要字async和await,能够轻易地把二个function变为异步格局。koa团队相当超前地基于ES7支付了koa2,它完全接受Promise并协作async来落实异步:

用router.post('/path', async fn)处理 post 请求

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

亟待当心,post 央浼的数码,无法正确为顾客端深入解析成正确的 request 的 body ,那个时候须求选拔koa-bodyparser组件完结解析

    await next();

koa-bodyparser 使用

    var data = await doReadFile();

const router = require('koa-router')(); // 引入 koa-routerconst bodyParser = require('koa-bodyparser');app.use(bodyParser()); // 在 router.routes 之前注入 bodyParser……router.post('/path', async fn)……// add router middleware:app.use(router.routes());app.listen(3000);

    ctx.response.type = 'text/plain';

注意koa-bodyparser必须在router以前被注册到app对象上

    ctx.response.body = data;

路由实例提取路由

});

为了使目录构造进一层精粹,能够将具有的 USportageL 管理函数都领到出来

由于包容性思忖,如今koa2仍扶持generator的写法,但下一个版本将会去掉。

// index.jsvar fn_index = async (ctx, next) = { …… };var fn_signin = async (ctx, next) = { …… };module.exports = { 'GET /': fn_index, 'POST /signin': fn_signin};

二、入门应用

app.js页面在赢得上,廖雪峰的实例代码略显复杂,如下

1、源码:

var fs = require('fs') // 引入 Node.js 核心模块 fs// 通过 fs.readdirSync 同步读取 /controllers 目录下的文件// 该源实例 index.js 存放于 /controllers 目录下var files = fs.readdirSync(__dirname   '/controllers');// 源实例注解:这里可以用sync是因为启动时只运行一次,不存在性能问题// 过滤该目录下所有 .js 后缀的文件var js_files = files.filter((f)={ return f.endsWith('.js');});// 循环所有 .js 后缀的文件for (var f of js_files) { // 动态引入每一个 js 文件,此处并非真实引入 let mapping = require(__dirname   '/controllers/'   f); for (var url in mapping) { // 此时的 mapping 为每一个 .js 文件的 exports 对象,循环该对象 if (url.startsWith('GET ')) { // 如果对象键名 url 已 'GET ' 开头 var path = url.substring(4); // 提取键名中的路径部分,此处是 '/' router.get(path, mapping[url]); } else if (url.startsWith('POST ')) { // 如果对象键名 url 已 'POST ' 开头 var path = url.substring(5); // 提取键名中的路径部分,此处是 '/signin' router.post(path, mapping[url]); } else { // 无效的URL: console.log(`invalid URL: ${url}`); } }}

// 导入koa,和koa 1.x例外,在koa第22中学,我们导入的是三个class,由此用小写的Koa表示:

fs.readdirSync(path[, options])一道版本的 fs.readdir(),参数如下pathString | Buffer | U陆风X8LoptionsString | ObjectencodingString默认值'utf8'withFileTypesBoolean默认值false返回 string[] | Buffer[]| fs.Dirent[]

const Koa = require('koa');

可选的options参数可以是钦点编码的字符串,也足以是负有encoding属性的靶子,该属性内定用于传给回调的文本名的字符编码。 要是encoding设置为'buffer',则赶回的公文名是Buffer对象。

// 创制一个Koa对象表示web app自身:

__dirname

const app = new Koa();

在各种 Node.js 模块中,除 require 、exports 等模块之外还都包括七个新鲜成员

// 对于其它诉求,app将调用该异步函数管理诉求:

__dirname动态获取当前文件模块所属目录的相对路线__filename动态获取当前文件的相对路线

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

str.startsWith(searchString [, position])

    await next();

startsWith(卡塔尔方法用来推断当前字符串是或不是是以别的几个加以的子字符串初阶的,依据推断结果再次回到true 或 false

    ctx.response.type = 'text/html';

searchString要物色的子字符串position在 str 中寻觅 searchString 的起来地方,私下认可值为 0,也正是确实的字符串初始处

    ctx.response.body = '

endsWith()

Hello, koa2!

';

});

// 在端口3000监听:

app.listen(3000);

console.log('app started at port 3000...');

(1)ctx:封装了request和response的变量

(2)await调用另二个异步函数

2、导入koa2:编辑好package.json中的正视项,然后施行npm install

3、运维格局:根据喜好接纳

(1)使用极限:node app.js

(2)VSCode:编辑好launch.json,然后使用VSCode的调护医疗工具

(3)npm:编辑package.json中的scripts,此中start对应的正是运转命令

4、middleware:koa中的关键概念

(1)上述代码中,每收到贰个http央求,koa就能够调用通过app.use(卡塔尔注册的async函数,并传到ctx和next参数

(2)koa可以把众多async函数组成二个管理链,每一种async函数都得以做一些协调的事情,然后用await next(卡塔尔来调用下八个async函数。大家把各类async函数称为middleware,那一个middleware可以结合起来,实现比比较多平价的效果。比方,能够用以下3个middleware组成管理链,依次打字与印刷日志,记录管理时间,输出HTML:

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

    console.log(`${ctx.request.method} ${ctx.request.url}`); // 打印URL

    await next(State of Qatar; // 调用下三个middleware

});

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

    const start = new Date(卡塔尔(قطر‎.getTime(State of Qatar; // 当前时光

    await next(卡塔尔; // 调用下一个middleware

    const ms = new Date(卡塔尔(قطر‎.getTime() - start; // 耗时

    console.log(`Time: ${ms}ms`卡塔尔国; // 打印耗时

});

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

    await next();

    ctx.response.type = 'text/html';

    ctx.response.body = '

措施用于测量检验字符串是还是不是以钦命的后缀截止。如若参数表示的字符体系是此指标表示的字符体系的后缀,则赶回 true,不然重回 false

Hello, koa2!

';

});

(3)调用app.use(State of Qatar的逐个决定了middleware的一一,即便叁个middleware未有调用await next(卡塔尔(قطر‎,后续的middleware将不再施行了。通过如此的机制,大家得以操纵程序推行的走向。如:

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

    if (await checkUserPermission(ctx)) {

        await next();

    } else {

        ctx.response.status = 403;

    }

});

三、处理URL

1、引进koa-router那些middleware,让它肩负U安德拉L映射的逐个管理情势。

(1)先用npm导入koa-router

(2)使用koa-router之前:

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

    if (ctx.request.path === '/test') {

        ctx.response.body = 'TEST page';

    } else {

        await next();

    }

});

(3)使用koa-router之后:

router.get('/hello/:name', async (ctx, next) => {

    var name = ctx.params.name;

    ctx.response.body = `

stringObject.substring(start,stop)

Hello, ${name}!

`;

});

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

    ctx.response.body = '

substring(卡塔尔方法用于提取字符串中介于三个钦定下标之间的字符

Index

';

});

// add router middleware:

app.use(router.routes());

一定于把UOdysseyL映射交给了koa-router,最后把koa-router交给app,并简短了await语句,特别简单。

2、koa-bodyparser用于拆解深入分析原始request伏乞,然后,把深入分析后的参数,绑定到ctx.request.body中,那样大家也能处理post央求了。

const bodyParser = require('koa-bodyparser');

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

    var

        name = ctx.request.body.name || '',

        password = ctx.request.body.password || '';

    console.log(`signin with name: ${name}, password: ${password}`);

    if (name === 'koa' && password === '12345') {

        ctx.response.body = `

start必需。贰个非负的卡尺头,规定要提取的子串的首先个字符在 stringObject 中之处

Welcome, ${name}!

`;

    } else {

        ctx.response.body = `

stop可选。二个非负的卡尺头,比要提取的子串的结尾三个字符在 stringObject 中的地点多 1。

Login failed!

       

Try again

`;

    }

});

3、有了拍卖UHavalL的方式,大家能够把品种构造设计的更客观:

url2-koa/

|

- .vscode/

|  |

|  - launch.json <-- VSCode 配置文件

|

- controllers/

|  |

|  - login.js <-- 处理login相关URL

|  |

|  - users.js <-- 管理客户管理相关U奥迪Q5L

|

- app.js <-- 使用koa的js

|

- package.json <-- 项目描述文件

|

- node_modules/ <-- npm安装的保有注重包

(1)把分化ULacrosseL映射职分分别协会到分裂的moudles中,把她们投身controllers目录上边

var fn_hello = async (ctx, next) => {

    var name = ctx.params.name;

    ctx.response.body = `

只要省略该参数,那么重临的子串会一向到字符串的末梢

Hello, ${name}!

`;

};

module.exports = {

    'GET /hello/:name': fn_hello

};

(2)增加controller.js模块,让它自动扫描controllers目录,找到全体js文件,导入,然后注册每种UQX56L

function addMapping(router, mapping卡塔尔国 {//依据映射模块施行映射职责

    for (var url in mapping) {

        if (url.startsWith('GET ')) {

            var path = url.substring(4);

            router.get(path, mapping[url]);

            console.log(`register URL mapping: GET ${path}`);

        } else if (url.startsWith('POST ')) {

            var path = url.substring(5);

            router.post(path, mapping[url]);

            console.log(`register URL mapping: POST ${path}`);

        } else {

            console.log(`invalid URL: ${url}`);

        }

    }

}

function addControllers(routerState of Qatar { //导入映射模块

    var files = fs.readdirSync(__dirname '/controllers');

    var js_files = files.filter((f) => {

        return f.endsWith('.js');

    });

    for (var f of js_files) {

        console.log(`process controller: ${f}...`);

        let mapping = require(__dirname '/controllers/' f);

        addMapping(router, mapping);

    }

}

module.exports = function (dir) {

    let

        controllers_dir = dir || 'controllers', // 即便不传参数,扫描目录默以为'controllers'

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

    addControllers(router, controllers_dir);

    return router.routes();

};

(3)简化app.js,现在app.js就不会与‘U凯雷德L映射任务’耦合了

// 导入controller middleware:

const controller = require('./controller');

// 使用middleware:

app.use(controller());

四、Nunjucks:三个模板引擎,说白了正是拼字符串,http://mozilla.github.io/nunjucks/,那部分不深远学习

1、一般的接收,正是拼接三个html,重返给客户端。

2、结合面向对象、UEscortL处理,大家比较轻巧想到MVC方式。

简洁明了入口文件(app.js)

将以上代码复制一份,命名称叫controller.js,优化代码,如下

var fs = require('fs') // 引入 Node.js 核心模块 fsconst router = require('koa-router')(); // 引入 koa-router// 函数 addMapping 包含两个参数// router koa-router 实例// mapping 所有路由 urlfunction addMapping(router, mapping) { for (var url in mapping) { // 此时的 mapping 为每一个 .js 文件的 exports 对象,循环该对象 if (url.startsWith('GET ')) { // 如果对象键名 url 已 'GET ' 开头 var path = url.substring(4); // 提取键名中的路径部分,此处是 '/' router.get(path, mapping[url]); } else if (url.startsWith('POST ')) { // 如果对象键名 url 已 'POST ' 开头 var path = url.substring(5); // 提取键名中的路径部分,此处是 '/signin' router.post(path, mapping[url]); } else { console.log(`invalid URL: ${url}`); } }}function addControllers(router, dir) { // 通过 fs.readdirSync 同步读取 /controllers 目录下的文件// 该源实例 index.js 存放于 /controllers 目录下 var files = fs.readdirSync(__dirname   '/'   dir); // 过滤该目录下所有 .js 后缀的文件 var js_files = files.filter((f) = { return f.endsWith('.js'); }); // 循环所有 .js 后缀的文件 for (var f of js_files) { // 动态引入每一个 js 文件,此处并非真实引入 let mapping = require(__dirname   '/'   dir   '/'   f); // 调用 addMapping 函数,并将 addMapping(router, mapping); }}module.exports = function (dir) { let controllers_dir = dir || 'controllers', // 如果不传参数,扫描目录默认为'controllers' router = require('koa-router')(); // 动态引入 koa-router 组件 addControllers(router, controllers_dir); // 实参 router 为实例化 router // 实参 controllers_dir 为路径 return router.routes(); // add router middleware};

那样一来,大家在app.js的代码又简化了

// 导入 controller middleware:const controller = require('./controller');// 使用 middleware:app.use(controller());

文章已同步作者的村办博客:《Node学习笔记 初识Koa》

TAG标签:
版权声明:本文由澳门新葡8455手机版发布于Web前端,转载请注明出处:【Web前端】询问Koa和成立路由实例,Nunjucks模板引