忙里偷闲,打开平时关注的前端相关的网站,浏览最近最新的前端动态。佼佼者,平凡的我做不到,但还是要争取不做落后者。

前端中的IoC理念,看到这个标题就被吸引了。IoC 理念,不认识呢,点击去一看,果然没让我失望,原文结合案例把概念诠释的很清楚。原来 Ioc控制反转依赖倒置

控制反转依赖倒置依赖注入 这三个名词,我倒是很耳熟了,毕竟在大学学 java web 课程的时候接触过,记得当时还很认真的学了并做了笔记。时间真是遗忘的罪魁祸首,现在就只记得名词,而全然忘了概念。

什么是 IoC ?

IoC 的全称是Inversion of Control,翻译为 控制反转依赖倒置,主要包含了三个准则:

  1. 高层次的模块不应该依赖于底层次的模块,它们都应该依赖于抽象
  2. 抽象不应该依赖于具体实现,具体实现应该依赖于抽象
  3. 面向接口编程,而不要面向实现编程

举例

假设需要构建一款应用叫 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() 的模块,就必须满足两个约定

  1. 模块必须包含 init 属性
  2. 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()的更多相关文章

  1. 前端中的 IoC 理念

    背景 前端应用在不断壮大的过程中,内部模块间的依赖可能也会随之越来越复杂,模块间的 低复用性 导致应用 难以维护,不过我们可以借助计算机领域的一些优秀的编程理念来一定程度上解决这些问题,接下来要讲述的 ...

  2. 深入理解koa中的co源码

    阅读目录 一:理解Generator 二:理解js函数柯里化 三:理解Thunk函数 四:理解CO源码 回到顶部 一:理解Generator 在看co源码之前,我们先来理解下Generator函数.G ...

  3. 图说js中的this——深入理解javascript中this指针

    没搞错吧!js写了那么多年,this还是会搞错!没搞错,javascript就是回搞错! ………… 文章来源自——周陆军的个人网站:http://zhoulujun.cn/zhoulujun/html ...

  4. JQuery选择器大全 前端面试送命题:面试题篇 对IOC和DI的通俗理解 c#中关于协变性和逆变性(又叫抗变)帮助理解

    JQuery选择器大全   jQuery 的选择器可谓之强大无比,这里简单地总结一下常用的元素查找方法 $("#myELement")    选择id值等于myElement的元素 ...

  5. 前端 IoC 理念入门

    背景 近几年,前端应用(WebApp)正朝着大规模方向发展,在这个过程中我们会对项目拆解成多个模块/组件来组合使用,以此提高我们代码的复用性,最终提高研发效率. 在编写一个复杂组件的时候,总会依赖其他 ...

  6. 浅析对spring中IOC的理解

    学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家 ...

  7. Spring中IOC的理解

    Spring中IOC的理解 1.什么是IOC? (1)控制反转.把对象创建和对象间的调用过程交给Spring进行管理. (2)使用IOC的目的:为了耦合度降低. 2.IOC底层原理? (1)xml解析 ...

  8. 理解Spring中的IoC和DI

    什么是IoC和DI IoC(Inversion of Control 控制反转):是一种面向对象编程中的一种设计原则,用来减低计算机代码之间的耦合度.其基本思想是:借助于"第三方" ...

  9. 前端知识体系:JavaScript基础-原型和原型链-理解 es6 中class构造以及继承的底层实现原理

    理解 es6 中class构造以及继承的底层实现原理 原文链接:https://blog.csdn.net/qq_34149805/article/details/86105123 1.ES6 cla ...

随机推荐

  1. ServletConfig、ServletContext 的应用

    一.ServletConfig对象及其应用(用的不多) 1. Context和ContextPath:一个web工程,若名为JavaWeb,访问的路径为:http://localhost:8080/J ...

  2. Linux学习之编译安装apache

    疯狂吐槽腾某云..编译安装apache折腾了一下午..还是我不太熟练. 首先要先准备好安装apache的三个依赖包以及apache包.(一定要准备好!!折腾了一下午的小白握拳!!) -rw-r--r- ...

  3. 进程哪一个cpu

    问题:我有个 Linux 进程运行在多核处理器系统上.怎样才能找出哪个 CPU 内核正在运行该进程? 当你在 多核 NUMA 处理器上运行需要较高性能的 HPC(高性能计算)程序或非常消耗网络资源的程 ...

  4. 杨其菊/常惠琢《面向对象程序设计(java)》第十一周学习总结

    <面向对象程序设计>第十一周学习总结 第一部分:理论知识 JAVA的集合框架 JAVA的集合框架实现对各种数据结构的封装,以降低对数据管理与处理的难度. 所谓框架就是一个类库的集合,框 ...

  5. rds

    数据库:提供数据的高可用保证,至少要用双节点(一主已备,经典高可用架构:采用基于binlog的数据复制技术维护数据库的可用性和数据一致性.同时,高可用版的性能也可以满足业务生产环境的需求,配置上采用物 ...

  6. python3 第三十二章 - 标准库概览

    1. 操作系统接口 os 模块提供很多函数与操作系统进行交互︰ >>> import os >>> os.getcwd() # 返回当前的工作目录 'C:\\Pyt ...

  7. 关于微信小程序切换获取不到元素的问题

    1.由于公司要实现微信小程序的自动化,所以开始学习python + appium 实现微信小程序自动化.在学习过程中遇到在切换webview后获取不到页面元素的问题,导致无法继续.今天在网上看到一篇关 ...

  8. Android中 实现队列方式处理优先级信息

    需求:当界面在处理消息A时,突然接收到消息B,需要立马显示B的信息,然后再继续显示消息A,或者接收到消息C,再显示完消息A后再显示消息C: 原理很简单 在一个轮询中,查询消息列表中的元素,先处理优先级 ...

  9. 关于Visual Studio调试C/C++,JS,PHP,JAVA,Python等语言的方法

    我在开始接触vs code后,确实对它的高颜值和小巧灵活而着迷,但是有一个非常现实的问题,相对于vs来说,vscode是一个代码编辑器,而不是一个IDE,在代码编译运行上存在着极大的问题,尤其是开始编 ...

  10. Liang-Barsky直线段裁剪算法

    Liang-Barsky直线段裁剪算法 梁友栋与Barsky提出的裁剪算法以直线的参数方程为基础,把判断直线段与窗口边界求交的 二维裁剪问题转化为求解一组不等式,确定直线段参数的一维裁剪问题.设起点为 ...