NodeJS入门从这篇文章开始

NodeJs简介

V8引擎本身就是用于chrome浏览器的JS部分,但是Ryan Dahe 这哥们,鬼才般的,把这个V8搬到了服务器上用于服务器的软件。是一个专注实现高性能Web 服务器优化的专家,几经探索,几经挫折后,遇到V8而诞生的项目。

Node.js是一个让JavaScript运行在服务器端的开发平台,它让所有JavaScript的触角延伸到了服务器,可以与PHPPythonRuby 平起平坐。

Node 似乎有些不同:

  • Node.js不是一种独立的语言,与 PHP Python Ruby的“基是语言也是平台”不同, Node.js的使用是JavaScript进行编程,运行在JavaScript引擎上(V8)。

  • PHP JSP等相比, Node.js跳过了Apache Nginx IISHTTP 服务器,他自己不用建设在任何服务器软件之上。Node.js的许多设计理念与经典架构(LAMP)有着很大的不同,可以提供强大的伸缩能力。

Node.js自身哲学,是花最小的硬件成本,最求更高的并发,更高的处理性能。

NodeJS官网 Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

Node.js的特点

单线程

Java PHP或者.Net等服务器语言中,会为每一个客户端链接创建一个新的线程。而每个线程需要消耗大约2MB内存。也就是说,理论上,一个8GB内存的服务器可以同时连接的最大用户数为4000个左右。要让Web应用程序支持更多的用户,就需要增加服务器的数量,而Web应用程序的硬件成本当然就上升了。

Node.js不为每个客户链接创建一个新的线程,而仅仅使用一个线程。当有用户连接了,就出发一个内部事件,通过非阻塞 I/O 、事件驱动机制,让Node.js程序宏观上也是并行的。使用Node.js,一个8GB内存的服务器可以同时处理超过4万用户的连接。

另外,单线程带来的好处,还有操作系统不再有线程创建、销毁的事件开销。

坏处,一个用户造成了线程的崩溃,整个服务都崩溃了。


非阻塞I/O

例如,当在访问数据库取得数据的时候,需要一段时间。在传统的单线程处理机制中,在执行了访问数据库代码之后,整个线程豆浆暂停下来,等待数据库返回结构,才能执行后面的代码。也就是说,<font color="red">I/O 阻塞了代码的执行,极大地降低了程序执行速率。</font>

由于Node.js中采用了非阻塞性 I/O 机制,因此在执行了访问数据库的代码之后,将立即转而执行其后面的代码,把数据库返回结果的处理代码放在回调函数中,从而提高了程序的执行效率。

当某个 I/O 执行完毕时,将以事件的形式通知 I/O 操作的线程,线程执行这个事件的回调函数。为了处理异步 I/O,线程必须有事件循环,不断检查有没有未处理的事件,依次予以处理。

事件驱动

Node中,客户端请求建立连接,提交数据等行为,会触发相应的事件。在Node中,在一个时刻,只能执行一个事件回调函数,但是在执行一个事件回调函数的中途,可以转而处理其他事件(比如,又有新用户连接了),然后返回执行原事件的回调函数,这种事件机制,称为“事件环”事件。


说是三个特点,实际上时一个特点,离开谁都不行,都玩不转了。
Node.js很像抠门的餐厅老板,只聘请一个服务员,服务很多人。结果比很多服务员效率还高。
Node.js中所有 I/O 都是异步的,回调函数套回调函数。

适合开发什么?

Node.js 适合用来开发什么样的应用程序呢?

善于 I/O, 不善于计算。因为Node.js最擅长就是任务调度,如果你的任务有很多的 CPU 计算,实际上是这个计算阻塞了这个单线程,就不适合 Node 开发。

当应用程序需要处理大量并发的 I/O, 而在向客户端发出响应之前,应用程序内部并不需要进行非常复杂的处理的时候,Node.js非常合适。Node.js也非常适合与 web socket配合,开发长连接的实时交互应用程序。

比如

  • 用户表单收集

  • 考试系统

  • 聊天室

  • 图文直播

  • 提供 JSONAPI

Node.js 不是银弹

Node.js 无法挑战老牌的后台语言

Node.js 本身就是极客追求性能极致的产物,缺少了很多服务器的健壮考量。所以 Node 不可能应用在银行、证券、电信等需要极高可靠性的业务当中。

一线企业中,Node是怎么用的?拿来做什么?怎么做?

哪些公司在用nodejs?

Node不是银弹,只是你工具箱中的一个小工具

NodeJs安装及配置

Node.js安装及环境配置 For Windows

Node.js安装及环境配置 For Mac

NodeJs 没有Web容器的概念

NodeJs顶层路由设计,Url和物理文件是没有关系的

NodeJs没有 Web 容器的概念所以无法搜寻静态资源

NodeJs模块

NodeJs中,将很多功能划分成了一个个 mudule,类似于与Java中包的概念,程序中会用到一些模块,有一些不用,所以为了效率,你用什么,就 require 什么。

Http 模块

http文档

该模块中创建 server 时,回调参数中会接受 resreq 两个参数。

req 中最关键的就是 req.url 属性,表示用户请求的 URL 地址。所有的路由设计,都是通过 req.url 来实现的。

我们比较关心的不是拿到 URL,而是识别这个 URL

识别 URL,用到两个新模块,第一个就是 url 模块,第二个就是 querystring 模块。

url模块

url文档

url.parse() //解析 URL (http://nodejs.cn/api/http.html#http_message_url)

querystring 模块

querystring 文档

querystring.stringify(obj[, sep[, eq[, options]]])

CommonJS 和 NodeJs模块、自定义模块

什么是CommonJS

JavaScript是一个强大的面相对象的语言。他有很多快速高效的解释器。然而,JavaScript标准定义的 API 是为了构建基于浏览器的应用程序。并没有制定一个用于更广泛的应用程序的标准。CommonJS 规范的提出,主要是为了弥补当前 JavaScript没有标准库的缺陷。它的终极目标就是:提供一个类似 Python,RubyJava语言的标准库,而不只是让JavaScipt停留在小脚本程序的阶段。用 CommonJS API编写出的应用,不仅可以利用JavaScript开发客户端应用,而且还可以编写以下应用。

  • 服务端 JavaScript 应用程序 (Nodejs)

  • 命令行工具

  • 桌面图像界面应用程序

CommonJS 就是模块化的标准, nodejs就是 CommonJS(模块化)的实现

CommonJS简介

Nodejs中模块化

Node应用由模块组成,采用 CommonJS 模块规范

Node中,模块分为两类:一类是 核心模块;另一类是用户编写的模块,称为 文件模块

  • 核心模块部分在 Node 源码的编译过程中,编译进了二进制执行文件。在 Node进程启动时,部分核心模块就被直接加载进内存中,所以这部分核心模块引入时,文件定位和编译执行这两个步骤可以省略掉,并且在路径分析中优先判断,所以它的加载速度时最快的。 如:HTTP模块、URL模块、FS模块都是 node 内置的核心模块,可以直接引入使用
  • 文件模块则是在运行时动态加载,需要完成的路径分析,文件定位,编译执行过程,速度相比核心模块稍微慢一些,但是用的非常多。这些模块需要我们自己定义。

CommonJS(Nodejs)中自定义模块的规定

  1. 我们可以把公共的抽离成为一个单独的 js 文件作为一个模块,默认情况下面这个模块里面的方法或者属性,外面是没法访问的。如果要让外部可以访问模块里面的方法或者属性,就必须在模块里面通过 exports 或者 module.exports 暴露属性或者方法。
  2. 在需要使用这些模块的文件中,通过 require 的方式引入这个模块。这个时候可以使用模块里面暴露的属性和方法。

Nodejs 中包、npm、第三方模块、package.json的使用

Nodejs中处理它自己提供的核心模块外,我们可以 自己定义模块,也可以使用 第三方的模块Nodejs中第三方模块由包组成,可以通过 来对一组具有相互依赖关系的模块进行统一管理。

完全符合CommnonJS规范的的<font color=red>包目录</font>一般包含如下文件

  • package.json 包描述文件
  • bin: 用于存放可执行二进制的目录
  • Lib: 用于存放 JavaScript 代码的目录
  • Doc: 用于存放文档的目录

NPM概述及使用简介

Yarn使用简介

NodeJS Fs模块

文件系统API

/*
* 1. fs.stat  检测是文件还是目录
* 2. fs.mkdir 创建目录
* 3. fs.writeFile 创建写入文件
* 4. fs.appendFile 追加文件
* 5. fs.readFile 读取文件
* 6. fs.rename 重命名文件
* 7. fs.readdir 读取目录
* 8. fs.rmdir 删除目录
* 9. fs.unlink 删除文件
* 10. fs.createReadStream 一步读取文件
* 11. fs.createWriteStream 一步写入文件
* 管道形式写入
* */

Path 模块

Path 模块API

Nodejs 的非阻塞 I/O,异步,事件驱动

Nodejs中大部分方法都是异步的, Nodejs有很多内置事件,我们可以通过引入 events 模块,并通过实例化 EventEmmitter 类来绑定和监听事件。

可以在异步事件中添加回调函数来获取异步数据。

EJS 模板渲染 (一种保持前后端分离的后端数据渲染)

EJS模板API文档

ejs.renderFile(filename,data,option,function(err,str){});

布局相关(Layouts)

Express 路由模块的封装

const url = require('url');

/* 封装改变res, 绑定res.send */
function changeRes(res){
    res.send = function (data) {
        res.writeHead(200, {'Content-Type':'text/html;charset="utf8"'});
        res.end(data);
    }
}

/* 暴露的模块 */
const Serve = function () {
    let G = this;
    /* 处理get和post请求 */
    this._get = {};
    this._post = {};

    let app = function (req,res) {
        changeRes(res);
        /* 获取路由 */
        let pathname = url.parse(req.url).pathname;
        if(!pathname.endsWith('/')){
            pathname = pathname + '/';
        }

        /* 获取 get post 请求 */
        let method = req.method.toLowerCase();
        if(G['_'+method][pathname]){
            if(method === 'post'){
                let strPost = '';
                req.on('data',function (chunk) {
                    strPost += chunk;
                });
                req.on('end',function (err,chunk) {
                    req.body = strPost;
                    G['_'+method][pathname](req,res);
                })
            }else{
                G['_' + method][pathname](req,res);
            }
        }else{
            res.end('no router');
        }
    };

    app.get = function (string, callback) {
        if(!string.endsWidth('/')){
            string = string + '/';
        }
        if(!string.startsWith('/')){
            string = '/' + string;
        }
        G._get[string] = callback;
    };
    app.post = function (string,callback) {
        if(!string.endsWidth('/')){
            string = string + '/';
        }
        if(!string.startsWith('/')){
            string = '/' + string;
        }
        G._post[string] = callback;
    };

    return app;
};

module.exports = Serve();

MongoDB 数据库介绍、安装、使用

NoSql介绍

由于互联网的迅速发,云计算与 Web2.0。这样大量的交互数据给数据库提出了更高的性能要求,传统数据库,即关系型数据库虽然具备良好的事物管理,但在处理大量数据的应用时很难在性能上满足设计要求。NoSQL 就是主要为了解决当下大量高并发要求的数据应用需求,关系数据库具有严格的参照性,一致性,可用性,原子性,隔离性等特点,因此会产生一些例如表链接等操作,这样会大大降低系统性能。而在当前很多应用场景下对性能的要求远远强于传统数据库关注的点。 NoSQL 就是为了解决大规模数据与多样数据种类等问题,尤其是其中大数据的相关问题。

NoSQL(not only sql) 它指的是非关系型数据库,是以 key-value 形式存储 和传统的关系型数据库不一样,不一定遵循传统数据库的一些基本要求,比如说遵循 SQL标准、ACID属性、表结构 等等。NoSQL 最早提出是在20世纪80年代,在当时更多是强调的是与关系型数据库区别对待,最近这些年被提及的更多的是强调协助解决大数据等相关问题。NoSQL 在大数据时代有自己的意义。

NoSQL 应用情况介绍

国内互联网蓬勃发展,不仅涌现出 BAT 之类的巨头,也带动了整个互联网行业的发展,大量的创业共识如春笋般涌出。在国家层面也提出了 互联网+万众创业 的口号。更多传统的行业也开始拥抱互联网。但是无论是所谓的生态平台还是传统业务的转型,涉及到的业务是多种多样的。这个时候企业架构师对于应用系统的核心——数据库管理,不仅有传统的 SQL 选项也有了 NoSQL 这种适合特定场景需求的选项。

NoSQL 数据库在以下的几种情况比较适用:

  • 数据模型比较简单
  • 需要灵活性更强的 IT 系统
  • 对数据库性能要求较高
  • 不需要高度的数据一致性
  • 对于给定 key, 比较容易映射复杂值的环境

NoSQL 发展现状

国外:Google 的 BigTable 和 Amazon 的 Dynamo 使用的就是 NoSQL 型数据库。

国内:百度、阿里、腾讯、新浪微博、视觉中国、优酷运营数据分析、豆瓣社区等..

什么时候建议使用 NoSQL

  • 对数据库高并发读写的需求
  • 对海量数据的高效存储和访问的需求
  • 对数据库的高可扩展性和高可用性的需求

NoSQL和传统数据库简单对比

非结构型数据库。没有行、列的概念。用 JSON 来存储数据。

集合就相当于“表”,文档就相当于“行”。

NoSQL 种类

MongoDB介绍

MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像

关系数据库的。他支持的数据结构非常松散,是类似 json 的 bson 格式,因此可以存储比较复杂的数据类

型。Mongo 最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以

实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。它的特点是高性能易部署

易使用存储数据非常方便

Mongodb安装

Mongodb 基本操作

创建、使用数据库

use sms

如果真想把这个数据库创建成功,那么必须插入一个数据

数据库中不能直接插入数据,只能往集合(collection)中插入数据。不需要专门创建集合,只需要写点语法插入数据就会创建集合。

db.student.insert({name:'zhangsan'})

db.student系统发现student是一个陌生的集合名字,所以就会自动创建了集合。

显示当前的数据集合:

show collections

删除数据库,删除当前所在的数据库

db.dropDatabase();

删除集合,删除指定的集合(删除表)

db.student.drop();

插入(增加)数据

插入数据,随着数据的茶服,数据库常见成功了,集合也创建成功了

db.表名.insert({name:'zhangsan'})

查找数据

1 查询所有记录

db.userInfo.find()  === select * from userInfo

2 查询去重后某列的数据

db.userInfo.distinct('name') === select distict name from userInfo;

3 查询 age = 18 的记录

db.userInfo.find({age:22}) === select * from userInfo where age=18;

4 查询 age > 18 的记录

db.userInfo.find({age:{$gt:22}}) === select * from userInfo where age > 22;

5 查询 age >= 18 的记录

db.userInfo.find({age:{$gte:18}}) === select * from userInfo where age>=18;

6 查询 age <= 18 的记录

db.userInfo.find({age:{$lte:18}}) === select * from userInfo where age<=18;

7 查询 age >=18 并且 age <= 20 的记录

db.userInfo.find({age:{$gte:18,$lte:20}});

9 查询 name 中包含 mongo 的数据

db.userInfo.find({name:/mongo/}) === select * from userInfo where name like '%mongo%';

10 查询 name 中以 mongo 开头的

db.userInfo.find({name:/^mongo/}) === select * from userInfo where name like 'mongo%';

11 查询指定列 name age 的数据

db.userInfo.find({},{name:1,age:1}) === select name,age from userInof;

当然 name 也可以用 true 或 false, 当 true 的情况和 name:1 效果一样, 如果用 false 就是排除 name,显示 name 以外的列信息。

12 查询指定列name、age 数据,age > 25

db.userInfo.find({age:{$gt:25}},{name:1,age:1})

13 按照年龄排序 1 升序 -1 降序

db.userInfo.find().sort({age:1}) // 升序
db.userInfo.find().sort({age:-1}) //降序

14 查询 name=zhangsan, age=22的数据

db.userInfo.find({name:'zhangsan',age:22}) === select * from userInfo where name='zhangsan' and age=22;

15 查询前 5 条数据

db.userInfo.find().limit(5) === select top 5 * from userInfo;

16 查询 10 条以后的数据

db.userInfo.find().skip(10) === select * from userInfo where id not in ( selct top 10 * from userInfo);

17 查询在 5-10 之间的数据

db.userInfo.find().limit(10).skip(5);

可用于分页,limit 是 pageSize,skip是第几页 * pageSize

18 or 与 查询

db.userInfo.find({$or:[{age:22},{age:25}]}) === select * from userInfo where age=22 or age=25;

19 findOne 查询第一条数据

db.userInfo.findOne() === db.userInfo.find().limt(1)

20 查询某个结果集的记录条数, 统计数量

db.userInfo.find({age:{$gte:25}}).count() === select count(*) from userInfo where age >= 20;

如果要返回限制之后的记录数量,要使用 count(true) 或者 count(非0)

db.user.find().skip(10).limit(5).count(true);

修改数据

修改里面还有查询条件。语法格式如下:

db.collection.update(
   <query>,
   <update>,
   {
     upsert: <boolean>,
     multi: <boolean>,
     writeConcern: <document>
   }
)

参数说明:

  query : update的查询条件,类似sql update查询内where后面的。
  update : update的对象和一些更新的操作符(如$,$inc...)等,也可以理解为sql update查询内set后面的
  upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。
  multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。
  writeConcern :可选,抛出异常的级别。

1 查找名字叫做小明,把年龄更改为16岁:

db.student.update({name:'小明'},{$set:{age:16}});

2 查询数学成绩是70,把年龄更改为33岁(只匹配修改一个)

db.student.update({"score.shuxue":70},{$set:{age:18}});

3 更改所有匹配项目:

db.student.update({sex:'男'},{$set:{age:18,}},{multi:true});

4 完整替换,不出现 $set 关键字: 注意

db.student.update({name:'小明'},{name:'大明',age:16});

db.users.update({name:'Lisi'},{$inc:{age:50}},false,true);
update users set age=age+50 where name = 'Lisi';

db.users.update({name:'Lisi'},{$inc:{age:50}, $set:{name:'hoho'}},false,true);
update users set age=age+50,name='hoho' where name='Liis';

删除数据

db.collectionsNames.remove({'borough':'Manhattan'})

db.users.remove({age:12}) // 会删除全部匹配项目
db.users.remove({age:12},{justOne:true}) // 删除一个

MongoDB索引和explain的使用

索引基础

索引是对数据库表中一列或多列的值进行排序的一种结构,可以让我们查询数据库变得更快。MongoDB的索引几乎与传统的关系型数据库一样,其中也包括一些基本的查询优化技巧

数据库创建索引的缺点,和什么时候不该创建索引

1 下面是创建索引的命令

db.user.ensureIndex({username:1});

2 获取当前集合的索引

db.user.getIndexes();

3 删除索引的命令是

db.user.dropIndex({username:1});

4 在 Mongodb 中,我们同样可以创建复合索引,如:

db.user.ensureIndex({username:1,age:-1})

数字1 表示 username 键的索引按升序排序存储,-1表示 age 键的索引按照降序方式存储

该索引被创建后,基于username和age的查询将会用到该索引,或者是基于username的查询也会用到该索引,但是只是基于age的查询将不会用到该复合索引。因此可以说,如果想用到复合索引,必须在查询条件中包含复合索引中的前 N 个序列。然而如果查询条件中的键值顺序和复合索引中的创建顺序不一致的话,MongoDB可以智能的帮助我们调节该顺序,以便使复合索引可以为查询所用。如:

db.user.find({age:30,username:'zhangsan'});

对于上面示例中的查询条件。mongodb在检索之前将会动态的调整查询文档的顺序,以使该查询可以用到刚刚创建的复合索引。

对于上面的创建的索引,MongoDB都会根据索引的keyname和索引方向为新创建的索引自动分配一个索引名,下面命令可以在创建索引时为其指定索引名,如:

db.user.ensureIndex({username:1},{name:'userIndex'});

随着集合的增长,需要针对查询中大量的排序做索引。如果没有对索引的键调用sort,mongoDB需要将所有数据提取到内存并排序。因此在做无索引排序时,如果数据过大以至无法在内存中进行排序,此时mongoDB将会报错

mongodb中索引对range查询和sort操作的影响

唯一索引

在缺省状况下创建的索引均不是唯一索引。下面示例将创建唯一索引,如:

db.user.ensureIndex({userid:1},{unique:true});

如果再次插入 userid 重复的文档时,MongoDB将报错,以提示插入重复键,如:

db.user.insert({userid:5})
db.user.insert({userid:5})
// E11000 duplicate key error index: user.user.$userid_1 dup key: { : 5.0  }

如果在创建唯一索引时存在了重复项,我们可以通过以下的命令帮助我们在创建唯一索引时消除重复文档,仅保留发现的第一个文档,如:

先删除刚创建的唯一索引

db.user.dropIndex({userid}:1)

插入测试数据,以保证集合中有重复键存在

db.user.remove()
db.user.insert({userid:5})
db.user.insert({userid:6})

重新创建唯一索引

db.user.ensureIndex({userid:1},{dropDups:true})

我们同样可以通过创建复合唯一索引,即保证复合键值唯一即可。如:

db.user.ensureIndex({userid:1,age:1},{unique:true})

索引的一些参数

使用explain

explain 是非常有用的工具,会帮助你获得查询方面诸多有用的信息。只要对游标调用该方法,就可以查询细节。explain会返回一个文档,而不是游标本身。如:

explain 会返回查询使用的索引情况,耗时和扫描文档数的统计信息。

explain executionStats 查询具体的执行时间

db.tablename.find().explain('executionStats')
关注输出的如下数值:explain.executionStats.executionTimeMills

NodeJS 操作 MongoDB 数据库

在 NodeJS 中使用MongoDB

npm install mongodb --save-dev

MongoDB 教程

MongDB 官方文档

Koa 框架介绍

Node.js 是一个一步的世界,官方API支持的都是 callback 形式的异步编程,这会带来很多问题,例如:1 callback 嵌套问题; 2、异步函数中可能存在同步调用callback返回来的数据,带来不一致。为了解决以上问题 Koa 出现了。

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

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

>阿里是业界最早的一批使用 Node.js 来做线上大流量应用的公司,早在 2011 年的就已经开始在生产环境中使用。 
>
>众所周知,在阿里的技术栈中, Java 是最最核心的,那 Node.js 扮演怎么样的一个角色呢? 
>
>1、基础设施大部分采用 Java 实现,变化较少,有事务要求的 Business Services 通常使用 
>
>Java。 
>
>2、而 Node.js 则替代过去 PHP/Java Web 的场景,用在需要快速迭代,需求变化非常快的 
>
>用户侧。
>
>3、很多内部的工程化支撑系统也逐渐基于 Node.js 了。 
>
>据不完全统计,目前阿里 Node.js 的开发者几百号人,线上的应用也非常之多,仅次于 Java 应 
>
>用,光对外服务的进程数就超过 1w+。 
>
>阿里内部就在使用 Koa 框架,并在 Koa 基础上面做了一些扩展和封装。并且基于 koa 开发了一个 
>
>开源框架 egg。 

Koa 文档

npm install koa --save-dev

Koa 路由

Koa路由与express中有所不同,在Express中直接引入Express说就可以配置路由,但是在Koa中我们需要封装对应的 koa-router路由模块来实现

npm install koa-router --save

一个简单使用的例子

/*
*  https://www.npmjs.com/package/koa-router
*  1 安装模块
*  2 看文档使用
* */

/* 引入 Koa 模块 */
const Koa = require('koa');
const router = require('koa-router')();

const app = new Koa();

router.get('/', async (ctx)=>{
    ctx.body = '这是首页数据';
});

router.get('/news', async (ctx)=>{
    ctx.body = '这是新闻页面数据';
});

router.get('/news_content', async (ctx)=>{
    /*
    * 在 Koa 中 GET 传值通过 request 接收,但是接收方法有两种 query 和 querystring.
    * query: 返回格式化好的参数对象
    * querystring: 返回的是请求字符串
    *  */
    /* 从 ctx 里面 获取 get 的传值 */
    console.log(ctx.query);
    console.log(ctx.querystring);
    console.log(ctx.url);
    ctx.body = '12343354';
});

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

app.listen(3000);

路由分层

对于一个完整的项目来说如果所有的路由配置都写在一个文件中,这个文件无疑会变得很大,而且及其不容易维护,所以我们需要对路由进行模块化的配置,这样指责更加明确,维护也变得更加简单。

朋们如果你们用过 react-router 或者 vue-router 那么 koa-router 对你们来说就会非常简单,他们模块化的思想是一样的,前端的路由划分模块的形式是什么样子的,koa-router 也一样划分就可以了,只是语法上有点不同而已。举个例子:

一个项目中假如有 后台管理模块(admin) 前台模块(default) API模块 (api)



后台管理模块又分为 user、focus、newscast 三个模块,我们在进行路由划分是可以按照如下目录结构



即当我们设定视图文件和我们的路由路径是一样的,这样达成了一个统一,我们书写起来会很方便,查看该项目时也会很清晰。
/* app.js */

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

/* 路由模块 start */
const index = require('./routes/index');
const api = require('./routes/api');
const admin = require('./routes/admin');
/* 路由模块 end */

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


router.use('/', index); 
router.use('/api', api);
router.use('/admin', admin); // 在 /admin 根路由下使用 admin 路由

/* 最后处理无匹配页面 */
router.get('*', async (ctx)=>{
    ctx.body = '<h2>404</h2>'
});

app.use(router.routes());
app.use(router.allowedMethods());
app.listen(3000);
/* admin.js */

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

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

router.use('/user', user);  // 在此路由下使用 user 的路由
router.use('/newscate', newscate);
router.use('/focus', focus);

module.exports = router.routes();

/* admin/user.js */
const router = require('koa-router')();

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

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

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

router.get('/delete', async (ctx)=>{
    ctx.body = '删除这是删除页面'
});

module.exports = router.routes();

Koa中间件

1 什么是中间件

通俗的讲:中间件就是在匹配路由之前或者匹配路由完成的一系列操作,我们就可以把它叫做中间件。

在 express 中间件是一个函数,他可以访问请求对象 req 和 res,和 Web 应用中处理请求-响应循环流程的中间件,一般命名为 next 的变量。在 Koa 中中间件和 express 有点类似。

中间件的功能包括

 * 执行任何代码
 * 修改请求和响应对象
 * 终结请求-响应循环

如果我的 get、post回调函数中,没有 next 参数,那么就匹配上第一个路由,就不会往下匹配了。如果想往下匹配的话,那么就必须要写 next();

2 Koa 应用可以使用如下几种中间件

 * 应用级中间件

 * 路由级中间件

 * 错误处理中间件

 * 第三方中间件

应用级中间件


路由中间件

错误处理中间件

第三方中间件

3 Koa中间件执行顺序

Koa 的中间件和 Express 不同,Koa 选择了洋葱圈模型。

中间件洋葱图:

Koa 静态资源

通过使用 koa-staitc 静态资源中间件可以统一管理静态资源,无须自己配置静态文件的读写

1 安装 koa-static

npm install koa-static --save

2 引入 koa-static 配置中间件

const static = require('koa-static');

3 配置中间件

const path = require('path');
const Koa = require('koa');
const statics = require('koa-static');
app.use(statics(path.resolve(__dirname, 'static'))) // 参数是文件路径

Koa 中使用 ejs模板

1 安装 koa-views 和 ejs

npm install --save koa-views
npm install ejs --save

2 引入 koa-views 配置中间件

const views = require('koa-views');
app.use(views,{map:{html:'ejs'}});

3 koa中使用 ejs:

router.get('/add',async (ctx)=>{
    let title = 'hello world';
    await ctx.render('index',{title})
})

Ejs 文档
koa-views 文档

Koa post 提交数据 body-parser 中间件使用

koa-bodypase 文档

安装 koa-bodyparser

npm install koa-bodypaeser --save

安装 引入配置中间件

var Koa = require('koa'); 
var bodyParser = require('koa-bodyparser'); 
var app = new Koa(); 
app.use(bodyParser()); 

使用

ctx.request.body 获取 post 提交的数据

koa art-template 模板引擎

常见模版引擎的性能对比

适用于 koa 的模板引擎的选择非常多,比如 jade、ejs、nunchucks、art-template 等。

Art-template 是一个简约、超快的模板引擎。

它采用作用域生命的技术来优化模板渲染速度,从而获得仅仅javascript极限的运行性能,并且同时支持NodeJS和浏览器。

Art-template 支持ejs的语法,可以用自己类似 angular 数据绑定的语法。

官网:http://aui.github.io/art-template/

中文文档: http://aui.github.io/art-template/zh-cn/docs/



在 Koa 中使用 art-template 模板引擎

npm install art-template --save
npm install koa-art-template --save

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

const app = new Koa();
render({
    root: path.join(__dirname, 'view'),
    extname:'.art', //后缀名
    debugger:process.env.NODE_ENV != 'production'
});

app.use(async function(ctx){
    await ctx.render('user');
});

app.listen(3000);

art-template m模板引擎语法
参考:http://aui.github.io/art-template/zh-cn/docs/syntax.html

Koa 中 Cookie 的使用

Cookie 简介

cookie 是存储于访问者计算机中的变量。可以让我们用同一个浏览器访问同一个域名的时候共享数据。

http是无状态协议。简单的说,当你浏览了一个页面,然后转到同一个网站的另一个页面,服务器无法认识到这是同一个浏览器在访问同一个网站,每一次的访问,都是没有任何关系的。

Koa Cookie 的使用

1 Koa中设置 Cookie 的值

ctx.cookies.set(name, value, [options])

通过 options 设置,cookie name 的 value


2 Koa 中获取 Cookie 的值

ctx.cookies.get('name');

3 Koa 中设置中文 Cookie

console.log(new Buffer('hello, world!').toString('base64'));// 转换成 base64 字符 串:aGVsbG8sIHdvcmxkIQ== 

console.log(new Buffer('aGVsbG8sIHdvcmxkIQ==', 'base64').toString());// 还原 base 64 字符串:hello, world!

Koa session 的使用

session 简单介绍
session 是另一种记录客户状的机制,不同的是cookie保存在客户端浏览器中,而session保存在服务器上。

session的工作流程
当浏览器访问服务器并发送一次请求是,服务器端会创建一个session对象,生成类似一个key value的键值对,然后将key(cookie)返回到浏览器端,浏览器下次再访问是,携带key,找到对应的session。客户的信息都保存在session中。

Koa-session 的使用 文档
1 安装 express-session

npm install koa-session --save

2 引入 express-session

const session = require('koa-session')

3 设置官方文档提供的中间件

app.keys = ['somej secret hurr'];
const config = {
  key:'koa:sess', // cookie key (default is koa:sess)
  maxAge:8640000, // cookie 的过期时间 maxAge in ms (default is 1 days)
  overwrite: true, //是否可以 overwrite (默认 default true)
  httpOnly:true, //cookie 是否只有服务器端可以访问 httpOnly or not (default true)
  signed: true, //签名默认 true
  rolling: false, //在每次请求时强行设置 cookie,这将重置 cookie 过期时间(默认:false) 
  renew: false, //(boolean) renew session when session is nearly expired,
}

app.use(session(config,app));

4 使用

ctx.session.username = '张三';//设置
ctx.session.username //获取

文章源码地址:https://gitee.com/hanway/NodeJS

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345