# koa

cnpm i --save koa

# 初步使用

 const koa = require('koa')
 const app = new koa() 
 
 app.use(async (ctx,next)=>{
 	await next()
 	ctx.response.type='text/html'
 	ctx.response.body='<p>2019!!!</p>'
 })
 /*它的作用是:每收到一个 http 请求,Koa 都会调用通过 app.use() 注册的 async 函数,
 同时为该函数传入 ctx 和 next 两个参数。而这个 async 函数就是我们所说的中间件*/
 
 app.listen(3000,()=>{
 	console.log("ok")
 })

# Context对象

上下文,用于确定一次请求的环境。

koa将nodejs中请求( request )和相应( response )对象封装到Context对象中,所以可以把Context对象称为一次对话的上下文,通过加工Context对象,就可以返回给用户的内容。

 app.use(async ctx=>{
    ctx,
	//this,//ctx,this,都是指向ctx
})

# 常用属性和方法

app.use(async (ctx,next)=>{
	ctx.response.body={
		url:ctx.request.url,
		query:ctx.request.query,
		querystring:ctx.request.querystring
		
	}
})

浏览器地址栏:http://localhost:3000/demo?id=098&name=wei,显示界面结果如下

{
 "url":"/demo?id=098&name=wei",
 "query":{"id":"098","name":"wei"},
 "querystring":"id=098&name=wei"
} 

# ctx.request

koa没有封装获取post参数的方法,需要解析Context中原生node的req,调用 ctx.req

 app.use(async (ctx,next)=>{
 	let postData="";
 	ctx.req.on('data',(data)=>{
 		postData+=data
 	})
 	ctx.req.on('end',()=>{
 		console.log(postData)
 	})
 
 })
//用gitbash模拟请求
//-d表示post请求
//curl -d 'name=lili&age=20' http://localhost:3000
//在启动的cmd窗口打印出 name=lili&age=20

# ctx.request.method

判断请求方式

 app.use(async (ctx,next)=>{
	console.log(ctx.method===ctx.request.method)//true
	console.log(ctx.url===ctx.request.url)//true
	console.log(ctx.type===ctx.response.type)//true
	console.log(ctx.body===ctx.response.body)//true
 	if(ctx.request.method==='POST'){
 		ctx.response.body="post"
 	}else if(ctx.request.method==='GET'){
 		ctx.response.body="get"
 	}else{
 		ctx.response.body="helloworld"
 	}
 
 })

# ctx.request.path

请求路径

# ctx.response

response 作用
ctx.response.body 用于设置返回给用户响应主体
ctx.response.status 设置请求状态(200/404/500)
ctx.response.type 可以设置响应Content-Type
ctx.response.redirect 重定向ctx.response.redirect(url,[alt])

ctx.response.type:

  • 如果返回内容是html,ctx.response.type='html';
  • png图片ctx.response.type='image/png';
  • 默认ctx.response.type='text/plain';
  • ctx.response.type='txt',txt文件
 app.use(async (ctx,next)=>{
 	ctx.response.status=200;
 	if(ctx.request.accepts('json')){
 		console.log('json')
 		ctx.response.type='json';
 		ctx.response.body={data:'hello world'}	
 	}else if(ctx.request.accepts('html')){
 		ctx.response.type='html';
 		ctx.response.body='<h1>hello</h1>'
 	}else{
 		ctx.response.type='txt';
 		ctx.response.body='hello'	
 	}
 
 })

ctx.response.accepts()用来判断期望接受类型, ctx.response.is(types...) 和其类似,不过它是用来检查响应类型是否是提供的其中之一

# ctx.state

通过 state 可以存储一些数据,比如用户数据,版本信息等。

ctx.state.user = yield  User.find(id) ;
//把user属性放到ctx.state对象中,以便能被另一个中间件读取

# ctx.cookies

用于获取和设置cookie

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

options配置

key value
maxAge 一个以毫秒为单位的数字,表示cookie过去时间
signed cookie签名值
expires cookie的过期的Date
path cookie路径,默认/
domain cookie域名
secure 安全cookie,只能使用https访问
httpOnly 如果为true,cookie无法被js获取
overwrite 一个布尔值,表示是否覆盖之前同名的cookie,默认false

# ctx.throw

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

抛出错误,给用户返回信息

# koa中间件

 const met=async (ctx,next)=>{
 	if(ctx.method==='POST'){
 		ctx.response.body="post"
 	}else if(ctx.method==='GET'){
 		ctx.response.body="get1"
 	}else{
 		ctx.response.body="helloworld"
 	}
 }
 app.use(met)

met就是一个中间件,app.use是加载中间件

中间件函数有两个内置参数ctx、next

next 参数的作用是将处理的 控制权 转交给下一个中间件,用await是因为返回的是个promise,而 next() 后面的代码,将会在下一个中间件及后面的中间件(如果有的话)执行结束后再执行。

注意: 中间件的顺序很重要!

 // 记录执行的时间

 app.use(async (ctx, next) => {
   console.log('中间件1 doSomething')
   await next();
   console.log('中间件1 end')
 })
 
 app.use(async (ctx, next) => {
   console.log('中间件2 doSomething')
   await next();
   console.log('中间件2 end')
 })
 
 app.use(async (ctx, next) => {
   console.log('中间件3 doSomething')
   await next();
   console.log('中间件3 end')
 })
//中间件1 doSomething
//中间件2 doSomething
//中间件3 doSomething
//中间件3 end
//中间件2 end
//中间件1 end
app.use(async (ctx, next) => {
  console.log('中间件1 doSomething')
  await next();
  console.log('中间件1 end')
})

app.use(async (ctx, next) => {
  console.log('中间件2 doSomething')
  console.log('中间件2 end')
})

app.use(async (ctx, next) => {
  console.log('中间件3 doSomething')
  await next();
  console.log('中间件3 end')
})
//中间件2缺next,所以后面的中间件3就不执行了
//中间件1 doSomething
//中间件2 doSomething
//中间件2 end
//中间件1 end

# koa-compose

多个中间件组合成单一中间件,便于重用和导出

async function k1(ctx, next)  {
  console.log('中间件1 doSomething')
  await next();
  console.log('中间件1 end')
}

async function k2(ctx, next)  {
  console.log('中间件2 doSomething')
  console.log('中间件2 end')
}

async function k3(ctx, next) {
  console.log('中间件3 doSomething')
  await next();
  console.log('中间件3 end')
}

const all =compose([k1,k2,k3])
app.use(all)
//中间件1 doSomething
//中间件2 doSomething
//中间件2 end
//中间件1 end

# koa常用中间件

# koa-router

通过ctx.method方法判断路由相对而言还是麻烦,所以引用koa-router

cnpm  install  --save koa-router
 const koa = require('koa')
 const compose=require('koa-compose')
 const bodyParser=require('koa-bodyparser')
 const Router=require('koa-router')
 const app = new koa() 
 const router= new Router()
 
 router.get('/',(ctx,next)=>{
 	ctx.type='html';
 	let html=`
 	<h1>登录</h1>
 	<form method='POST' action='/'>
 		<p>用户名</p>
 		<input name='username'/><br>
 		<p>密码</p>
 		<input name='password'/><br>
 		<button type='submit'>提交</button>
 	</form>
 	`;
 	ctx.body=html
 })
 router.post('/',(ctx,next)=>{
 	let postData=ctx.request.body;
 	ctx.body=postData
 })
 
 app
 .use(bodyParser())
 .use(router.routes())
 .use(router.allowedMethods())
 
 app.listen(3000,()=>{
 	console.log("ok")
 })
  //显示界面,get方式,然后输入了数据提交后变成了json

注意

  • const Router=require('koa-router')
  • Router=require('koa-router')
  • app .use(bodyParser()) .use(router.routes()) .use(router.allowedMethods())

router.routes()

# koa-static 与 koa-views

  • koa-static:专门用于加载静态资源的中间件
  • koa-views:加载html模板文件
cnpm i --save koa-static koa-views
 暂缺

# koa电商项目

# vue-cli 2.0版本

//全局安装 vue-cli
cnpm install --global vue-cli
//创建一个基于 webpack 模板的新项目
vue init webpack my-project

# 安装stylus

cnpm install stylus stylus-loader --save-dev

# 验证

<style scoped lang="stylus">
#app
  background green
</style>

# 配置ui框架

参考 koa学习之前端ui框架vant

# 移动端适配

  • 固定高度宽度百分比
  • 媒体查询
  • flex布局+rem(font size of the root element:相对根元素的font-size的计算值倍数)
(function(){
    var screenW = document.documentElement.offsetWidth||document.body.offsetWidth;
    var oHtml = document.getElementsByTagName("html")[0];
    oHtml.style.fontSize = 100*(screenW/750)+"px";
})();
(function(){
    var screenW = document.documentElement.offsetWidth||document.body.offsetWidth;
    var oHtml = document.getElementsByTagName("html")[0];
    oHtml.style.fontSize = (screenW/20)+"px";
})();
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">

# 快捷插件

vue vscode snippets

# 模拟数据

easymock 可以,但没必要!如果有个完整的json格式文件,也可以用用。

# 重置css

body,html,#app{
  width:100%;
  height:100%;
  font-size:0.32rem;
  background:#F2F2F2;

}
a, blockquote, body, code, dd, div, dl, dt, em, fieldset, form, h1, h2, h3, h4, h5, h6, iframe, img, input, label, li, object, ol, p, q, small, span, strong, table, tbody, td, th, tr, ul {
    margin: 0;
    padding: 0;
    border: 0;
}
i,b{
  font-style:normal;
  font-weight:normal
}
[v-cloak] {
  display: none;
}

# 滑动区域

npm install vue-awesome-swiper --save
  • 全局引入 //mainjs
import Vue from 'vue'
import VueAwesomeSwiper from 'vue-awesome-swiper'

// require styles
import 'swiper/dist/css/swiper.css'

Vue.use(VueAwesomeSwiper, /* { default global options } */)
  • 局部引入
import 'swiper/dist/css/swiper.css'

import { swiper, swiperSlide } from 'vue-awesome-swiper'

export default {
  components: {
    swiper,
    swiperSlide
  }
}

# 安装mongodb

安装mongodb后,再配上可视化操作界面(激活)

# Koa用Mongoose连接数据库

Mongoose 是一个开源的封装好的实现Node和MongoDB数据通讯的 数据建模库 ,帮助我们完成数据库的操作和通讯。

npm install mongoose --save
npm install glob --save

# Schema

建模,也就是定义Schema,他相当于MongoDB数据库的一个映射。Schema是一种以文件形式存储的数据库模型骨架,无法直接通往数据库端,也就是说它不具备对数据库的操作能力。Schema是以key-value形式Json格式的数据。

# Schema中的数据类型

  1. String :字符串类型
  2. Number :数字类型
  3. Date : 日期类型
  4. Boolean: 布尔类型
  5. Buffer : NodeJS buffer 类型
  6. ObjectID : 主键,一种特殊而且非常重要的类型
  7. Mixed :混合类型
  8. Array :集合类型
  • schema :用来定义表的模版,实现和MongoDB数据库的映射。用来实现每个字段的类型,长度,映射的字段,不具备表的操作能力。
  • model :具备某张表操作能力的一个集合,是mongoose的核心能力。我们说的模型就是这个Mondel。
  • entity :类似记录,由Model创建的实体,也具有影响数据库的操作能力。
# 数据库配置
//init.js
const mongoose = require('mongoose')
const db = "mongodb://localhost/smile-db"
const glob = require('glob');
const {resolve} = require('path');
exports.initSchemas = () =>{
    glob.sync(resolve(__dirname,'./schema/','**/*.js')).forEach(require)
}
exports.connect = ()=>{
    //连接数据库
    mongoose.connect(db)

    let maxConnectTimes = 0 

    return  new Promise((resolve,reject)=>{
    //把所有连接放到这里
        
        //增加数据库监听事件
        mongoose.connection.on('disconnected',()=>{
            console.log('***********数据库断开***********')
            if(maxConnectTimes<3){
                maxConnectTimes++;
                mongoose.connect(db)    
            }else{
                reject()
                throw new Error('数据库出现问题,程序无法搞定,请人为修理......')
            }
            
        })

        mongoose.connection.on('error',err=>{
            console.log('***********数据库错误***********')
            if(maxConnectTimes<3){
                maxConnectTimes++
                mongoose.connect(db)   
            }else{
                reject(err)
                throw new Error('数据库出现问题,程序无法搞定,请人为修理......')
            }

        })
        //链接打开的时
        mongoose.connection.once('open',()=>{
            console.log('MongoDB connected successfully') 
            resolve()   
        })

    })

}
  • 新版本增加{useNewUrlParser:true},兼容以后的版本
 mongoose.connect(db,{useNewUrlParser:true},()=>{
	 console.log("连接成功")
 })
# 创建一个schema
//User.js
const mongoose = require('mongoose')    //引入Mongoose
const Schema = mongoose.Schema          //声明Schema
let ObjectId = Schema.Types.ObjectId    //声明Object类型

//创建我们的用户Schema
const userSchema = new Schema({
    UserId:ObjectId,
    userName:{unique:true,type:String},
    password:String,
    createAt:{type:Date,default:Date.now()},
    lastLoginAt:{type:Date,default:Date.now(),
	//gender: { type: String, enum: ['male', 'female'], default: 'male', required: true },
	//ref表示引用,引用了Tpoic中的ObjectId
	//business: { type: Schema.Types.ObjectId, ref: 'Topic', select: false },
	//employments: {
	//  type: [{
	//    company: { type: Schema.Types.ObjectId, ref: 'Topic' },
	//    job: { type: Schema.Types.ObjectId, ref: 'Topic' },
	//  }],
	//  select: false,
	//},
	
	//ceshi:{type:String,select:false}
}

})

//发布模型
mongoose.model('User',userSchema)

String等字段相当于Schema中的关键字,不需要再加引号 如果某个字段不想要直接返回给客户端,可以添加字段select:false,如上ceshi,也可以在mongoose语法请求数据库时设置如下 可枚举性和数组模式的写法,如上注释部分

const User = mongoose.model('User')
//展示password字段
User.find().select("+password")
//隐藏password字段
User.find().select("-password")
//根据前台传过来的参数具体展示某些信息
const selectFields = fields.split(';').filter(f => f).map(f => ' +' + f).join('');
//populate,如果字段是用ref引用的,可以使用populate处理信息
const user = await User.findById(ctx.params.id).select(selectFields).populate(populateStr);
# 路由使用到schema
//index.js
const Router = require('koa-router')
const mongoose = require('mongoose')

let router = new Router()
router.get('/',async(ctx)=>{
    ctx.body="这是用户操作首页"
})

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

	
    const User = mongoose.model('User')
    let newUser = new User(ctx.request.body)

    await newUser.save().then(()=>{
        ctx.body={
            code:200,
            message:'注册成功'
        }
    }).catch(error=>{
        ctx.body={
            code:500,
            message:error
        }
    })
})

router.post('/login',async(ctx)=>{
    let loginUser = ctx.request.body
    console.log(loginUser)
    let userName = loginUser.userName
    let password = loginUser.password

    //引入User的model
    const User = mongoose.model('User')

    await User.findOne({userName:userName}).exec().then(async(result)=>{
        console.log(result)
        if(result){
            let newUser = new User()
            await newUser.comparePassword(password,result.password)
            .then(isMatch=>{
                ctx.body={code:200,message:isMatch}
            })
            .catch(error=>{
                console.log(error)
                ctx.body={code:500,message:error}
            })
        }else{
            ctx.body={code:200,message:'用户名不存在'}
        }
    }).catch(error=>{
        console.log(error)
        ctx.body={code:500,message:error}
    })

})

module.exports =router

# mongoose分页 模糊搜索

 async find(ctx) {
    const { per_page = 10 } = ctx.query;
    const page = Math.max(ctx.query.page * 1, 1) - 1;
    const perPage = Math.max(per_page * 1, 1);
	//借助正则就可以实现 模糊搜索
    ctx.body = await Topic
      .find({ name: new RegExp(ctx.query.q) })
      .limit(perPage).skip(page * perPage);
  }
//模糊搜索多个字段
  async find(ctx) {
    const { per_page = 10 } = ctx.query;
    const page = Math.max(ctx.query.page * 1, 1) - 1;
    const perPage = Math.max(per_page * 1, 1);
    const q = new RegExp(ctx.query.q);
    ctx.body = await Question
      .find({ $or: [{ title: q }, { description: q }] })
      .limit(perPage).skip(page * perPage);
  }
# index
const Koa = require('koa')
const app = new Koa()
const { connect , initSchemas } = require('./database/init.js')
const mongoose = require('mongoose')
const bodyParser = require('koa-bodyparser')
const cors = require('koa2-cors')
const Router = require('koa-router')

app.use(bodyParser())
app.use(cors())

let user = require('./appApi/user.js')
let home = require('./appApi/home.js')
let goods = require('./appApi/goods.js')

//装载所有子路由
let router = new Router()
router.use('/user',user.routes())
router.use('/home',home.routes())
router.use('/goods',goods.routes())
//egg.js

//加载路由中间件

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


;(async ()=>{
    await connect()
    initSchemas()
})()


app.use(async(ctx)=>{
    ctx.body='<h1>Hello Koa2</h1>'
})

app.listen(3000,()=>{
    console.log('[Server] starting at port 3000')
})

# 数据库加密

cnpm install --save bcrypt 

# koa2-cors

npm install --save koa2-cors
最后更新: 12/28/2020, 12:17:34 PM