从前端中的IOC理念理解koa中的app.use()
忙里偷闲,打开平时关注的前端相关的网站,浏览最近最新的前端动态。佼佼者,平凡的我做不到,但还是要争取不做落后者。
前端中的IoC理念,看到这个标题就被吸引了。IoC 理念,不认识呢,点击去一看,果然没让我失望,原文结合案例把概念诠释的很清楚。原来 Ioc 是 控制反转 或 依赖倒置。
控制反转、依赖倒置、依赖注入 这三个名词,我倒是很耳熟了,毕竟在大学学 java web 课程的时候接触过,记得当时还很认真的学了并做了笔记。时间真是遗忘的罪魁祸首,现在就只记得名词,而全然忘了概念。
什么是 IoC ?
IoC 的全称是Inversion of Control,翻译为 控制反转 或 依赖倒置,主要包含了三个准则:
- 高层次的模块不应该依赖于底层次的模块,它们都应该依赖于抽象
- 抽象不应该依赖于具体实现,具体实现应该依赖于抽象
- 面向接口编程,而不要面向实现编程
举例
假设需要构建一款应用叫 App,它包含一个路由模块 Router 和一个页面监控模块 Track,一开始可能会这么实现:
// app.js
import Router from './modules/Router';
import Track from './modules/Track';
class App {
constructor(options) {
this.options = options;
this.router = new Router();
this.track = new Track();
this.init();
}
init() {
window.addEventListener('DOMContentLoaded', () => {
this.router.to('home');
this.track.tracking();
this.options.onReady();
});
}
}
// index.js
import App from 'path/to/App';
new App({
onReady() {
// do something here...
},
});
看起来没什么问题,但是实际应用中需求是非常多变的,可能需要给路由新增功能(比如实现 history 模式)或者更新配置(启用 history, new Router({ mode: 'history' }))。这就不得不在 App 内部去修改这两个模块,而对于之前测试通过了的 App 来说,也必须重新测试。
很明显,这不是一个好的应用结构,高层次的模块 App 依赖了两个低层次的模块 Router 和 Track,对低层次模块的修改都会影响高层次的模块 App。那么如何解决这个问题呢,解决方案就是接下来要讲述的 依赖注入(Dependency Injection)。
什么是依赖注入?
所谓的依赖注入,简单来说就是把高层模块所依赖的模块通过传参的方式把依赖「注入」到模块内部,上面的代码可以通过依赖注入的方式最终改造成如下方式:
class App {
static modules = []
constructor(options) {
this.options = options;
this.init();
}
init() {
window.addEventListener('DOMContentLoaded', () => {
this.initModules();
this.options.onReady(this);
});
}
static use(module) {
Array.isArray(module) ? module.map(item => App.use(item)) : App.modules.push(module);
}
initModules() {
App.modules.map(module => module.init && typeof module.init == 'function' && module.init(this));
}
}
经过改造后 App 内已经没有「具体实现」了,看不到任何业务代码了,那么如何使用 App 来管理我们的依赖呢:
// modules/Router.js
import Router from 'path/to/Router';
export default {
init(app) {
app.router = new Router(app.options.router);
app.router.to('home');
}
};
// modules/Track.js
import Track from 'path/to/Track';
export default {
init(app) {
app.track = new Track(app.options.track);
app.track.tracking();
}
};
// index.js
import App from 'path/to/App';
import Router from './modules/Router';
import Track from './modules/Track';
App.use([Router, Track]);
new App({
router: {
mode: 'history',
},
track: {
// ...
},
onReady(app) {
// app.options ...
},
});
可以发现 App 模块在使用上也非常的方便,通过 App.use() 方法来「注入」依赖,在 ./modules/some-module.js 中按照一定的「约定」去初始化相关配置即可。
要实现一个可以被 App.use() 的模块,就必须满足两个约定:
- 模块必须包含 init 属性
- init 必须是一个函数
这其实就是 IoC 思想中对面向接口编程 而不要面向实现编程这一准则的很好的体现。App 不关心模块具体实现了什么,只要满足对 接口init 的约定就可以了。
在理解了本文的后,再回首看 koa 中的 app.use(),理解起来就没那么难了。
koa 中的 app.use()
koa 的源码文件中关于 constructor 的代码如下:
constructor() {
super();
this.proxy = false;
this.middleware = [];
this.subdomainOffset = 2;
this.env = process.env.NODE_ENV || 'development';
this.context = Object.create(context);
this.request = Object.create(request);
this.response = Object.create(response);
if (util.inspect.custom) {
this[util.inspect.custom] = this.inspect;
}
}
koa 的源码文件中关于 use 的代码如下:
use(fn) {
if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');
if (isGeneratorFunction(fn)) {
deprecate('Support for generators will be removed in v3. ' +
'See the documentation for examples of how to convert old middleware ' +
'https://github.com/koajs/koa/blob/master/docs/migration.md');
fn = convert(fn);
}
debug('use %s', fn._name || fn.name || '-');
this.middleware.push(fn);
return this;
}
可以看到,可上面的案例很相似,只不过再没有init方法,而是直接调用方法。
而实际使用的时候,是这样的:
const Koa = require('koa')
const app = new Koa()
const m1 = require('./middleware/koa-m1') // 模拟的中间件
app.use(m1())
// 中间件
function m1 (ctx) {
global.console.log('m1', ctx.path)
}
module.exports = function () {
return async function (ctx, next) {
global.console.log('m1 start')
m1(ctx)
await next()
global.console.log('m1 end')
}
}
总结:IoC 的理念,很值得编程借鉴。刚接触前端时,只知道html、css、JavaScript。而现在发现难的地方,很多都是借鉴了后端的编程思想。比如设计模式,SSR(其实本质应该就是JSP)。IoC 的理念,我想最初的实现应该也是后端。
最后深入了解:
从前端中的IOC理念理解koa中的app.use()的更多相关文章
- 前端中的 IoC 理念
背景 前端应用在不断壮大的过程中,内部模块间的依赖可能也会随之越来越复杂,模块间的 低复用性 导致应用 难以维护,不过我们可以借助计算机领域的一些优秀的编程理念来一定程度上解决这些问题,接下来要讲述的 ...
- 深入理解koa中的co源码
阅读目录 一:理解Generator 二:理解js函数柯里化 三:理解Thunk函数 四:理解CO源码 回到顶部 一:理解Generator 在看co源码之前,我们先来理解下Generator函数.G ...
- 图说js中的this——深入理解javascript中this指针
没搞错吧!js写了那么多年,this还是会搞错!没搞错,javascript就是回搞错! ………… 文章来源自——周陆军的个人网站:http://zhoulujun.cn/zhoulujun/html ...
- JQuery选择器大全 前端面试送命题:面试题篇 对IOC和DI的通俗理解 c#中关于协变性和逆变性(又叫抗变)帮助理解
JQuery选择器大全 jQuery 的选择器可谓之强大无比,这里简单地总结一下常用的元素查找方法 $("#myELement") 选择id值等于myElement的元素 ...
- 前端 IoC 理念入门
背景 近几年,前端应用(WebApp)正朝着大规模方向发展,在这个过程中我们会对项目拆解成多个模块/组件来组合使用,以此提高我们代码的复用性,最终提高研发效率. 在编写一个复杂组件的时候,总会依赖其他 ...
- 浅析对spring中IOC的理解
学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家 ...
- Spring中IOC的理解
Spring中IOC的理解 1.什么是IOC? (1)控制反转.把对象创建和对象间的调用过程交给Spring进行管理. (2)使用IOC的目的:为了耦合度降低. 2.IOC底层原理? (1)xml解析 ...
- 理解Spring中的IoC和DI
什么是IoC和DI IoC(Inversion of Control 控制反转):是一种面向对象编程中的一种设计原则,用来减低计算机代码之间的耦合度.其基本思想是:借助于"第三方" ...
- 前端知识体系:JavaScript基础-原型和原型链-理解 es6 中class构造以及继承的底层实现原理
理解 es6 中class构造以及继承的底层实现原理 原文链接:https://blog.csdn.net/qq_34149805/article/details/86105123 1.ES6 cla ...
随机推荐
- Oracle 12c 创建新的数据库实例、用户
前提:安装好了Oracle 12c数据库,已有一个数据库实例xe,登录用户/密码:system/oralce 我用的是docker安装的Oracle 12c的实例: docker run --name ...
- 512MB内存VPS服务器安装宝塔WEB客户端建站 - 环境部署篇
原本以为我们很多网友用VPS搭建网站不会用WEB面板,而采用一键包或者自己部署编译环境,但是最后发现其实目前我们使用WEB面板的还是挺多的,无论是免费还是付费的都有不少人使用.比如当初一直免费的AMH ...
- 第二阶段第五次spring会议
昨天我对软件加上了写便签时自动加上时间的功能. 今天我将对初始页面进行加工和修改. 我用两个小动物作为按钮分别进入动物便签界面和植物便签界面,可以让用户自由选择. 明天我将尝试对软件进行添加搜索引擎的 ...
- spring boot 注解
一级注解:(写在类名前面的)@RestController: 等价于在函数前面写@ResponseBody ,会直接返回要显示的内容 @ControllerString返回的是模板文件的名称. 二级注 ...
- A Senior Interview
1.How many time did they spent to plan the final project? 答:两天左右,两次开会. 4.How many time did they spen ...
- spring实现一个简单的事务管理
前两天给公司的数据库操作加了事务管理,今天博客就更一下这个吧. 先说明:本文只是简单得实现一下事务,事务的具体内容,比如事务的等级,事务的具体实现原理等等... 菜鸟水平有限,暂时还更不了这个,以后的 ...
- Win7 VS2017编译Blender2.79
去年在VS2013环境编译过一次,重装系统后换了VS2017,正好刚编译完Godot3.0.2,顺手把Blender也编译了吧. 官方Windows下编译指南 https://wiki.blender ...
- 线程中的队列(queue)
队列的类型和常用方法 队列是一种数据结构,它类似于列表.但列表是线程不安全的,而队列是线程安全的. python的queue(python3,python2为Queue)提供了3种队列: Queue: ...
- [小结]InnoDB体系结构及工作原理
参阅:<innodb存储引擎内幕>原创文章,会不定时更新,转发请标明出处:http://www.cnblogs.com/janehoo/p/7717041.html 一.概述: innod ...
- mpvue 初体验之改写车标速查小程序
前文 说到我开发了一个简单的小程序叫做 车标速查(代码以及二维码详见 这里),本文简单讲讲如何将这个小程序转为 mpvue 开发(最终 成果 ) mpvue 官网的 文档 真的是非常简单,不,应该说是 ...