资讯专栏INFORMATION COLUMN

React 源码深度解读(六):依赖注入

glumes / 2774人阅读

摘要:依赖注入和控制反转,这两个词经常一起出现。一句话表述他们之间的关系依赖注入是控制反转的一种实现方式。而两者有大量的代码都是可以共享的,这就是依赖注入的使用场景了。下一步就是创建具体的依赖内容,然后注入到需要的地方这里的等于这个对象。

前言

React 是一个十分庞大的库,由于要同时考虑 ReactDom 和 ReactNative ,还有服务器渲染等,导致其代码抽象化程度很高,嵌套层级非常深,阅读其源码是一个非常艰辛的过程。在学习 React 源码的过程中,给我帮助最大的就是这个系列文章,于是决定基于这个系列文章谈一下自己的理解。本文会大量用到原文中的例子,想体会原汁原味的感觉,推荐阅读原文。

本系列文章基于 React 15.4.2 ,以下是本系列其它文章的传送门:
React 源码深度解读(一):首次 DOM 元素渲染 - Part 1
React 源码深度解读(二):首次 DOM 元素渲染 - Part 2
React 源码深度解读(三):首次 DOM 元素渲染 - Part 3
React 源码深度解读(四):首次自定义组件渲染 - Part 1
React 源码深度解读(五):首次自定义组件渲染 - Part 2
React 源码深度解读(六):依赖注入
React 源码深度解读(七):事务 - Part 1
React 源码深度解读(八):事务 - Part 2
React 源码深度解读(九):单个元素更新
React 源码深度解读(十):Diff 算法详解

正文

依赖注入(Dependency Injection)这个概念的兴起已经有很长时间了,把这个概念融入到框架中达到出神入化境地的,非Spring莫属。然而在前端领域,似乎很少会提到这个概念,难道前端的代码就不需要解耦吗?前端的代码就没有依赖了?本文将以 React 的源码为例子,看看它是如何使用依赖注入这一设计模式的。

二、依赖注入的基本概念

在看代码之前,有必要先简单介绍一下依赖注入的基本概念。依赖注入和控制反转(Inversion of Control),这两个词经常一起出现。一句话表述他们之间的关系:依赖注入是控制反转的一种实现方式。另一种方式叫依赖查找(Dependency Lookup)。

在控制不反转的情况下,某个类如果依赖另一个类,它会自己来创建依赖:

class Person {
    eat() {
        const dinner = new Dinner("法国菜");
        console.log("开饭啦!,今晚自己做:", dinner.name);
    }
}

class Dinner {
    constructor(name) {
        this.name = name;
    }
}

假设一个人要吃饭,如果控制不反转,就需要自己来做,像上面的代码一样要自己new Dinner

如果使用控制反转,吃什么就不用自己费脑子了,别人给我做好放到我面前,我直接吃就好!

class Person {
    eat(dinner) {
        console.log("开饭啦!,今晚有大厨给我做:", dinner.name);
    }
}

也就是说,不需要自己来创建依赖的对象了,由外部传入,这就是依赖注入!

三、React 中的依赖注入

众所周知,React 除了可以在浏览器运行外(ReactDOM),也可以制作 App 在手机端运行(ReactNative)。而两者有大量的代码都是可以共享的,这就是依赖注入的使用场景了。

我们来看下具体是如何注入的:

// ReactDOM.js
var ReactDefaultInjection = require("ReactDefaultInjection");
ReactDefaultInjection.inject();

// ReactNative.js
var ReactNativeDefaultInjection = require("ReactNativeDefaultInjection");
ReactNativeDefaultInjection.inject();

注入的位置都在框架代码最开始加载的位置。下面以 ReactDOM 为例子,详细讲解注入的逻辑。

先来看看需要注入的对象都有哪些,定义在 ReactInjection.js 这个文件当中:

var DOMProperty = require("DOMProperty");
var EventPluginHub = require("EventPluginHub");
var EventPluginUtils = require("EventPluginUtils");
var ReactComponentEnvironment = require("ReactComponentEnvironment");
var ReactEmptyComponent = require("ReactEmptyComponent");
var ReactBrowserEventEmitter = require("ReactBrowserEventEmitter");
var ReactHostComponent = require("ReactHostComponent");
var ReactUpdates = require("ReactUpdates");

var ReactInjection = {
  Component: ReactComponentEnvironment.injection,
  DOMProperty: DOMProperty.injection,
  EmptyComponent: ReactEmptyComponent.injection,
  EventPluginHub: EventPluginHub.injection,
  EventPluginUtils: EventPluginUtils.injection,
  EventEmitter: ReactBrowserEventEmitter.injection,
  HostComponent: ReactHostComponent.injection,
  Updates: ReactUpdates.injection,
};

module.exports = ReactInjection;

这里面每一个 injection 都是一个对象,对象内定义了一个或多个 inject 的方法来注入对应的内容。以ReactUpdates.injection为例子:

// ReactUpdates.js
var ReactUpdatesInjection = {
    injectReconcileTransaction: function (ReconcileTransaction) {
        ...
        ReactUpdates.ReactReconcileTransaction = ReconcileTransaction;
    },

    injectBatchingStrategy: function (_batchingStrategy) {
        ...
        batchingStrategy = _batchingStrategy;
    },
};

var ReactUpdates = {
    ...
    injection: ReactUpdatesInjection,
};

可以看到 ReactUpdates 依赖的ReactReconcileTransactionbatchingStrategy就是通过这 2 个方法注入进去的。

有了上面的内容,相当于定义好需要依赖的内容了。下一步就是创建具体的依赖内容,然后注入到需要的地方:

// ReactDefaultInjection.js
var ReactInjection = require("ReactInjection");
var ReactReconcileTransaction = require("ReactReconcileTransaction");
var ReactDefaultBatchingStrategy = require("ReactDefaultBatchingStrategy");

...

function inject() {
    ...

    ReactInjection.Updates.injectReconcileTransaction(
        ReactReconcileTransaction
    );
    ReactInjection.Updates.injectBatchingStrategy(
        ReactDefaultBatchingStrategy
    );
}

这里的 ReactInjection.Updates 等于 ReactUpdates.injection 这个对象。而 inject 方法,就是在前文的 ReactDOM.js 中调用的方法ReactDefaultInjection.inject()

上述各个文件整体的调用关系如下:

四、总结

本文介绍了依赖注入的基本概念,并结合 React 的源码讲解具体的使用场景。这样做的主要目的是解耦,可以根据实际的上下文传入不同的依赖对象,优雅的实现了代码的抽象与复用。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/99044.html

相关文章

  • React 源码深度解读(九):单个元素更新

    摘要:作为声明式的框架,接管了所有页面更新相关的操作。是用于内部操作的实例,这里将它的初始化为空数组并插入一个新的。连续次后,期望的结果应该是。原因很简单,因为次的时候,取到的都是在完后不会同步更新。 前言 React 是一个十分庞大的库,由于要同时考虑 ReactDom 和 ReactNative ,还有服务器渲染等,导致其代码抽象化程度很高,嵌套层级非常深,阅读其源码是一个非常艰辛的过程...

    kid143 评论0 收藏0
  • React 源码深度解读(八):事务 - Part 2

    摘要:前言是一个十分庞大的库,由于要同时考虑和,还有服务器渲染等,导致其代码抽象化程度很高,嵌套层级非常深,阅读其源码是一个非常艰辛的过程。在学习源码的过程中,给我帮助最大的就是这个系列文章,于是决定基于这个系列文章谈一下自己的理解。 前言 React 是一个十分庞大的库,由于要同时考虑 ReactDom 和 ReactNative ,还有服务器渲染等,导致其代码抽象化程度很高,嵌套层级非常...

    airborne007 评论0 收藏0
  • React 源码深度解读(三):首次 DOM 元素渲染 - Part 3

    摘要:在学习源码的过程中,给我帮助最大的就是这个系列文章,于是决定基于这个系列文章谈一下自己的理解。到此为止,首次渲染就完成啦总结从启动到元素渲染到页面,并不像看起来这么简单,中间经历了复杂的层级调用。 前言 React 是一个十分庞大的库,由于要同时考虑 ReactDom 和 ReactNative ,还有服务器渲染等,导致其代码抽象化程度很高,嵌套层级非常深,阅读其源码是一个非常艰辛的过...

    U2FsdGVkX1x 评论0 收藏0
  • React 源码深度解读(七):事务 - Part 1

    摘要:在学习源码的过程中,给我帮助最大的就是这个系列文章,于是决定基于这个系列文章谈一下自己的理解。说明就算抛出了错误,部分的代码也要继续执行,随后再将错误往上层代码抛。和都能保证其中一个成员抛出错误的时候,余下的能继续执行。 前言 React 是一个十分庞大的库,由于要同时考虑 ReactDom 和 ReactNative ,还有服务器渲染等,导致其代码抽象化程度很高,嵌套层级非常深,阅读...

    KavenFan 评论0 收藏0
  • React 源码深度解读(二):首次 DOM 元素渲染 - Part 2

    摘要:本文将要讲解的调用栈是下面这个样子的平台无关相关如果看源码,我们会留意到很多相关的代码,我们暂时先将其忽略,会在后续的文章中进行讲解。现在我们来看一下各实例间的关系目前为止的调用栈平台无关相关下一篇讲解三总结本文讲解了转化为的过程。 欢迎关注我的公众号睿Talk,获取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言 R...

    wean 评论0 收藏0

发表评论

0条评论

glumes

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<