最近在研究koa2,感受koa-session插件用起来特别顺手,再加上本身一直对cookie、session感兴趣,索性研究起了koa-session源码,过程有点小艰辛,不过研究事后,感受仍是收货满满,很开心。现将研究成果分享给你们,但愿对你们有帮助。数据库
首先来看一个简单的例子,实现的是当浏览器访问localhost:3000,进行ctx.session.views = 2,建立session。浏览器
示例代码:cookie
const session = require('koa-session'); const Koa = require('koa'); const app = new Koa(); app.keys = ['some secret hurr']; //若CONFIG里,signed为true,则须要app.keys生成签名 const CONFIG = { key: 'koa:sess', //到源码阶段就会理解(session以cookie形式存储),这里的key至关于ctx.cookies.set(key,val)里的key,能够设置为任意值,默认为koa:sess maxAge: 86400000, overwrite: true, /** (boolean) can overwrite or not (default true) */ httpOnly: true, /** (boolean) httpOnly or not (default true) */ signed: true, /** (boolean) signed or not (default true) */ rolling: false, /** (boolean) Force a session identifier cookie to be set on every response. The expiration is reset to the original maxAge, resetting the expiration countdown. default is false **/ }; app.use(session(CONFIG, app)); app.use((ctx) => { ctx.session.views = 2; ctx.body =' views' +'miews'; }) app.listen(3000);
咱们的目标是搞清楚 ctx.session.views = 2,这一句代码,在koa-session里具体通过了哪些处理流程。session
OK,下面就要正式研究koa-session的源码了,打开源码目录,你会发现目录结构很简单,主要的4个js文件以下:app
|--index.js |-- lib |-- context.js |-- session.js |--utils.js
首先,固然是从index.js提及:koa
module.exports = function(opts, app) { }
对应示例代码里:async
const session = require('koa-session');
app.use(session(CONFIG, app));
咱们来看 session(CONFIG,app),也就是module.exports导出的模块,这里也就是koa-session源码的入口。ide
接着继续看index.js:ui
opts = formatOpts(opts); //对opts进行处理,设置默认值等,该方法里的opts.store是session存储于数据库的状况。 extendContext(app.context, opts); //index.js里很是重要的一个方法,对app.context也就是ctx,进行扩展,新增了属性session
继续index.js,这段代码是index.js的重点, await next(),表明程序的执行从index.js转移到了 咱们开头的示例代码,也就是app.use((ctx) => {})里的代码,执行完毕后,又回到了 finally里的 await sess.commit()。咱们能够看到sess.commit()应该是一个提交操做。this
return async function sessions(ctx, next) { const sess = ctx[CONTEXT_SESSION]; console.log(sess.session); if (sess.store) await sess.initFromExternal(); try { await next(); console.log(sess.session); } catch (err) { throw err; } finally { if (opts.autoCommit) { await sess.commit(); } } };
咱们接着看index.js,结合示例代码里的 ctx.session.views = 2语句。首先,ctx.session.views里的ctx.session触发了 function extendContext(context, opts) {}里,session属性的getter:
session: { get() { return this[CONTEXT_SESSION].get(); }, .... }
返回的是ctx[CONTEXT_SESSION]的get方法的返回值,其中ctx[CONTEXT_SESSION]属性,则是建立了一个新的类:
this[_CONTEXT_SESSION] = new ContextSession(this, opts); //class ContextSession 位于./lib/context.js
咱们来看class ContextSession的get方法:(this.session 也就是index.js中return async function sessions(ctx, next) {} 里的 sess.session)
get() { const session = this.session; // already retrieved if (session) return session; // unset if (session === false) return null; // create an empty session or init from cookie this.store ? this.create() : this.initFromCookie(); return this.session; }
咱们先不考虑this.store,因而程序执行了 this.initFromCookie(),并将this.session返回给ctx.session, this.initFromCookie()也就是从cookie中初始session,initFromCookie()方法中调用了create()方法,建立session,咱们来看create()方法:
create(val, externalKey) { debug('create session with val: %j externalKey: %s', val, externalKey); if (this.store) this.externalKey = externalKey || this.opts.genid && this.opts.genid(this.ctx); this.session = new Session(this, val); }
因为咱们示例代码中的 ctx.session.views是第一次执行,因此initFromCookie() {}方法里的const cookie = ctx.cookies.get(opts.key, opts); 为空,所以create里的val参数也为空,此时ContextSession 的get方法返回的this.session在这里初始化,咱们将this.session打印出来,以下:
Session {_sessCtx: ContextSession, _ctx: Object, isNew: true}
this.session 也就是ctx.session.views=2中的“ctx.session”的返回值,咱们再来看一遍ctx.session属性的get():
session: { get() { return this[CONTEXT_SESSION].get(); }, .... }
而ctx.session.views至关于给ctx.session又新增了一个views属性,并赋值2,也就等于给 this.session新增了views属性,因而this.session变成了:
Session {_sessCtx: ContextSession, _ctx: Object, views: 2,isNew: true}
而this.session也就是index.js 中的return async function sessions(ctx, next) {} 中的 sess.session,也就至关于sess.session变为了:
Session {_sessCtx: ContextSession, _ctx: Object, views: 2,isNew: true}
这里是整个koa-session源码的核心部分,须要你细品。
而后,context.js中的async commit() {}方法里,将this.session保存到了cookie中,到此也就实现了 ctx.session.views = 2 的实现流程。
文章中若是有写的不恰当的地方,欢迎你们交流指正。