官网
koa是express原班人马打造的轻量、健壮、富有表现力的nodejs框架。目前koa有koa1和koa2两个版本;koa2依赖Node.js 7.6.0或者更高版本;koa不在内核方法中绑定任何中间件,它仅仅是一个轻量级的函数库,几乎所有功能都必须通过第三方插件来实现。
主要核心功能:
- 处理了请求和响应的基本逻辑
- 提供一个接口进行扩展 use 进行中间件注册(插件)
安装
npm i koa
koa简单使用
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
ctx.body = 'Hello World';
});
app.listen(3000);
Application对象
- application是koa的实例 简写app
- app.use 将给定的中间件方法添加到此应用程序,分为同步和异步,异步:通过es7中的async和await来处理
- app.listen设置服务器端口;
- app.on 错误处理;
Koa 中间件概念
app.use(ctx,next),Koa 利用中间件 控制"上游",调用"下游“;
app.use(ctx,next) 这里是koa底层中间件的基础模型。一个程序执行下来,它中间经历很多模块函数。这个Application对象 处理一个中间件的时候,会执行里面你定义好的函数就是ctx,当这个ctx执行性完成后,就要调用next。然后继续往下执行下一个中间件。直到最后。
//这里的middleWare函数就是一个中间件
let middleWare = async (ctx,next)=>{
console.log("first middleWare");
ctx.body = "hello world";
next();
}
app.use(middleWare);
中间件的执行流程
上下文(Context)
Koa Context 将 node 的 request 和 response 对象封装到单个对象中,为编写 Web 应用程序和 API 提供了许多有用的方法。 这些操作在 HTTP 服务器开发中频繁使用,它们被添加到此级别而不是更高级别的框架,这将强制中间件重新实现此通用功能。
简单来说就是Context里面封装好了node 的 request 和 response。
提供了node原生的res 和 req ,以及它封装过的response 和request
-
ctx 对象下的属性方法
- ctx.app:应用程序实例引用,等同于app;
- ctx.req:Node 的
request对象. - ctx.res:Node 的
response对象. - ctx.request:koa中的Request对象;
- ctx.response:koa中的response对象;
- ctx.state:对象命名空间,通过中间件传递信息;
- ctx.throw:抛出错误;
-
request及response别名
-
koa会把ctx.requset上的属性直接挂载到ctx上如:
ctx.header//头信息;ctx.headersctx.methodctx.method=ctx.urlctx.url=
…...
-
同样也会把ctx.response上的属性直接挂载到ctx上如:
ctx.bodyctx.body=ctx.statusctx.status=
….
-
ctx.status 获取响应状态。默认情况下,
response.status设置为404而不是像 node 的res.statusCode那样默认为200。 -
ctx.set 设置响应头 具体可以看官网的api
-
http状态码:1xx(消息)、2xx(成功)、3xx(重定向)、4xx(请求错误)、5xx和6xx(服务器错误)
-
常见http状态码 (302 location 跳转)
HTTP状态码 描述 100 继续。继续响应剩余部分,进行提交请求 200 成功 301 永久移动。请求资源永久移动到新位置 302 临时移动。请求资源零时移动到新位置 304 未修改。请求资源对比上次未被修改,响应中不包含资源内容 401 未授权,需要身份验证 403 禁止。请求被拒绝 404 未找到,服务器未找到需要资源 500 服务器内部错误。服务器遇到错误,无法完成请求 503 服务器不可用。零时服务过载,无法处理请求
-
koa生态环境之常用中间件
koa-router (处理动态资源)
路由是引导匹配之意,是匹配url到相应处理程序的活动。也就是说,路由是把 一个 url 与 一个函数进行管理。
我们创建这个koa-router的对象后。调用里面的routes()方法,他会返回一个中间件函数。然后用koa的use方法执行这个中间件函数,这个koa-router 就可以起作用了
-
koa-router安装
npm i koa-router -S
-
koa-router使用
router 提供的 get,post,put……这些方法来注册url函数,等同于我们前面使用 switch 对 url 的判断。这个函数也会被作为中间件去执行。说明这些get,post,put 也有ctx函数 (上下文(Context))
const koaRouters = new koaRouter(); let content = '' koaRouters.get('/',ctx=>{ // console.log(ctx.req.url); // ctx.body = 'hello koa' content = fs.readFileSync('./static/index.html'); ctx.body = content.toString(); }) koaRouters.get('/login',ctx=>{ // console.log(ctx.req.url); // ctx.body = 'hello koa' content = fs.readFileSync('./static/login.html'); ctx.body = content.toString(); }) koaRouters.get('/register',ctx=>{ // console.log(ctx.req.url); // ctx.body = 'hello koa' content = fs.readFileSync('./static/register.html'); ctx.body = content.toString(); }) server.use(koaRouters.routes()); -
koa-router + 模板引擎
这两个一起使用可以动态传递参数
安装模板引擎
npm i -d nunjucks
koa-static-cache(处理静态资源)
koa的静态资源代理,前面的node基础中,我们花费了很多switch case 和导入导出了mime文件类型来处理各种静态资源。其实koa生态中这个中间件就很好帮我们处理中间件。
安装
npm i -d koa-static-cache
使用
const koaStc = require('koa-static-cache');
//./static 硬盘的相对目录
server.use(koaStc('./static',{
//url 请求的开头 相当于之前url.startWith('./static')
prefix:'/static',
//启动压缩
gzip: true,
//新资源放入内存
dynamic: true
}))
然后就可以通过地址访问静态非常方便,他还给我们提供了很多配置
staticCache(dir [, options] [, files])
dir(str) - the directory you wish to serve, priority thanoptions.dir.options.dir(str) - the directory you wish to serve, default toprocess.cwd.options.maxAge(int) - cache control max age for the files,0by default.options.cacheControl(str) - optional cache control header. Overridesoptions.maxAge.options.buffer(bool) - store the files in memory instead of streaming from the filesystem on each request.options.gzip(bool) - when request's accept-encoding include gzip, files will compressed by gzip.options.usePrecompiledGzip(bool) - try use gzip files, loaded from disk, like nginx gzip_staticoptions.alias(obj) - object map of aliases. See below.options.prefix(str) - the url prefix you wish to add, default to''.options.dynamic(bool) - dynamic load file which not cached on initialization.options.filter(function | array) - filter files at init dir, for example - skip non build (source) files. If array set - allow only listed filesoptions.preload(bool) - caches the assets on initialization or not, default totrue. always work together withoptions.dynamic.options.files(obj) - optional files object. See below.files(obj) - optional files object. See below.
Json 解析
JSON具有以下这些形式:
对象是一个无序的“‘名称/值’对”集合。一个对象以 {左括号 开始, }右括号 结束。每个“名称”后跟一个 :冒号 ;“‘名称/值’ 对”之间使用 ,逗号 分隔。
在node中使用require加载一个json文件数据的话,node会自动转成对象
新闻列表案例
-
使用的模块
- koa
- koa-router
- nunjucks
-
引入必要的模块创建koa服务
const server = new koa(); const koaRouters = new koaRouter(); let content = '' server.use(koaRouters.routes()); server.listen(3001,function () { console.log('服务启动, http://localhost:3001'); }) -
利用中间件封装nunjucks 中的render 方法
-
基本思路,把数据传入模板中,渲染出最终页面
koaRouters.get('/',(ctx)=>{ //把数据传入模板中,渲染出最终页面 ctx.render ('index.html',{ datas }); }) -
注册一个自己写的中间件
const tpls = require('./middleware/tpl'); server.use(tpls; -
中间件就是一个返回一个函数,这个函数里面要调用next()符合koa对中间件的定义。导出的函数注意是使用commonJs的规范。
// ./middleware/tpl const nunjucks =require('nunjucks'); //配置模板环境 let tpl = new nunjucks.Environment( //自动加载views new nunjucks.FileSystemLoader('views') ) module.exports = function (ctx, next) { //和外面的接口相对应,传入页面名称和数据 ctx.render = function (filename,data) { //这里调用nunjucks render ctx.body = tpl.render(filename, data); } next(); }但是这里的views是写死要是我们模板有多个环境,那不是要很多个这样的相同逻辑的中间件,所以我们可以改写一下。不返回render 函数,返回一个中间件函数
// app.js server.use(tplS('views')); // ./middleware/tpl module.exports = function (dir) { return function (ctx ,next) { let tpl = new nunjucks.Environment( //自动加载views new nunjucks.FileSystemLoader(dir,{ //实时更新数据 watch:true }) ) ctx.render = function (filename,data) { //这里调用nunjucks render ctx.body = tpl.render(filename, data); } next(); } }
-
-
在设置好的模板引擎中修改自己想要的视图
<ul class="news-list"> {%for item in data%} <li class="news"> <a href="javascript:;"> <!-- 配置好静态资源 koa-static-cache--> <img src="./static/img/3.jpeg" alt=""> </a> <div> <h3> <a href="javascript:;">{{item.title}}</a> </h3> <div class="info"> <span class="tips"><span>{{item.from}}</span></span> <!-- <span class="line"></span> --> <span class="time">| {{item.newTime}}</span> </div> </div> </li> {%endfor%} </ul> -
详情页配置
我们要增加一个路由 /detial。 然后再首页的新闻标题添加路由配置的地址。然后我们有问题,每个详情页需要处理不同的数据。
//详情页 koaRouters.get('/detail',(ctx)=>{ //把数据传入模板中,渲染出最终页面 ctx.render ('detail.html',{ //data }); })这个数据要怎么处理,其实前后端交互中传递数据的方法有很多
- 请求头
- url querystring hash
- 请求正文
- ....
这里我们使用url 的那种方式
动态路由:
可以使用/detail?id ,或者 /detail/:id。:id这里表示的是一个变量
地址中输入 http://localhost:3001/detail/123 这里自己定义的123会以变量的形式传给我们后端 。这个id后面还能跟正则 '/detail/:id(\d+) 只允许是数字
//详情页 koaRouters.get('/detail/:id',(ctx)=>{ //把数据传入模板中,渲染出最终页面 console.log('pra',ctx.params) //console.log(ctx.req); ctx.render ('detail.html',{ }); }) //request pra { id: '123' }拿到id之后转成数字类型,然后在data中找到对应的数据,在首页中地址栏后面要带上数据的id做一个数据的对应
//首页 <h3> <a href="/detail/{{item.id}}">{{item.title}}</a> </h3> //详情页 koaRouters.get('/detail/:id(\\d+)',(ctx)=>{ //把数据传入模板中,渲染出最终页面 let id = Number(ctx.params.id) console.log(id); //通过id寻找数据 let dataItem = data.find(item => item.id === id) //没有这条数据的处理 if(!dataItem){ ctx.write('404'); ctx.end(); } //传递数据 ctx.render ('detail.html',{ dataItem }); }) -
分页处理
传递页码的时候如果再用动态路由就有问题,当一个url上的可选动态数据多的时候,用动态路由比较麻烦,就像一个函数如果参数多了,用一个一个的形参比较麻烦,这个情况下用 options 对象传参更方便。
-
url,动态数据少可以使用动态路由
-
url,动态数据多使用 queryString
queryString 就不要配:id 直接可以在url ?page=1...就可以。然后后台使用ctx.query 可以获得这个?后面的为对象内容的对象。
http://localhost:3001/?page=1 console.log(ctx.query); [Object: null prototype] { page: '1' }新闻首页代码
//新闻首页 koaRouters.get('/',(ctx)=>{ //把数据传入模板中,渲染出最终页面 //分页处理 //每页数量 let pageNum = 5; //起始页 let page = ctx.query.page ||1 ; page = Number(page) //起始点,因为slice 是包头不包尾, //page = 1 start = 0 ; page = 2 start = 5 .. let start = (page - 1) * pageNum; //结束点 // start = 1 end = 5 , start = 2 end =10 let end = start + pageNum //所以截取的点就出来了 let dataGroup = data.slice(start,end); //页码处理 let pageNumber = Math.ceil((data.length/pageNum)); ctx.render ('index.html',{ data: dataGroup, pageNumber, page }); })分页列表
<div class="pagination"> <a href="javascript:;" class="prev">⌜</a> {% for i in range(1, pageNumber+1) %} {%if i==page %} <a href="/?page={{i}}" class="current">{{i}}</a> {%else%} <a href="/?page={{i}}">{{i}}</a> {%endif%} {% endfor %} <a href="javascript:;" class="next">⌝</a> </div> -
数据持久化保存mysql2 插件
上面的数据都是从json读取,我们可以利用mysql2 操作数据库。这里不细说Mysql。只是简单了解这个库是怎么运作的。数据库反面的知识,可以看书了解一下
-
安装
npm install --save mysql2 -
简单的例子
// get the client const mysql = require('mysql2'); // create the connection to database const connection = mysql.createConnection({ host: 'localhost', user: 'root', database: 'test' }); // simple query connection.query( 'SELECT * FROM `table` WHERE `name` = "Page" AND `age` > 45', function(err, results, fields) { console.log(results); // results contains rows returned by server console.log(fields); // fields contains extra meta data about results, if available } ); // with placeholder connection.query( 'SELECT * FROM `table` WHERE `name` = ? AND `age` > ?', ['Page', 45], function(err, results) { console.log(results); } ); -
基本的数据库操作
数据库中的数据操作;
-
一、增
`INSERT INTO 表名 (字段一,字段二,字段三) VALUES ("值一","值二","值三");` -
二、删
DELETE FROM 表名 WHERE 条件; -
三、改
UPDATE 表名 SET 设置的内容 WHERE 条件语句; -
四、查
SELECT 字段 FROM 表名 WHERE 条件语句; -
五、条件语句;
1.ADN 2 OR 3. ORDER BY (DESC/ASC) 4.LIMIT 5.LIKE 6.JOIN ON 7.AS
-