美文网首页
纯TypeScript开发Web前端(五)依赖注入

纯TypeScript开发Web前端(五)依赖注入

作者: ChenReal | 来源:发表于2019-01-30 23:09 被阅读0次

    前言

            这一篇,我们谈谈TypeScript的设计模式,也是对我自己Demo的一个总结。如果说前面几篇所讲的基本都是新手入门,那么这里开始算是进阶的内容。

    依赖注入(Dependency Injection)

            依赖注入,简称DI。近年非常火,相信如果有订阅一些技术文章推送的朋友,隔三差五会收到一些文章介绍DI的原理然后附带各种IoC框架的推介。不少朋友尽管早已耳熟能详,甚至对其原理和用途随口都能讲出来,可是,一旦动手实践起来总会觉得比较以难入手。最后,只能将探索行动抛在一边去,直接堆框架,开启无脑调用模式。
            其实,我想说,DI的实现并没有那么玄乎。也许,在你自己的项目你也曾经不经意地用过,但是没有意识到这就是DI而已。实现DI有很多种方法,我并不打算面面俱到,从概念到原理到编码这样讲。相信这些文章随便一搜就一大把。我主要分享一下我自己在TypeScript项目中,所实践出来的DI套路。

    1、模块

            要实现依赖注入,首先,得有模块。模块可以作为注入的零件,也快是被注入的操控者。先看看我的模块定义:

        src/
        ├── app.ts
        ├── libs/
        │   └── router.ts
        ├── modules/
        │   ├── product/
        │   │   ├── list.ts
        │   │   └── detail.ts
        │   └── user/
        │       ├── login.ts
        │       └── info.ts
        └── utils/
            ├── http.ts
            ├── template.ts
            └── cache.ts
    

    这个其实在之前也有分享过的。我的模块主要分两大类

    • 工具类(Utils)

    例如:router(路由)、http、template(模板)、cache(缓存)

    • 业务类(Modules)

    例如:user/login(用户登录)、user/info(用户中心)、product/list(产品列表)、product/detail(产品详情)

            很容易你就可以判断出来,在我的应用场景下,基本上都是业务类的模块依赖于工具类的模块,来实现相关的业务功能的。拿产品列表来举个栗子:

    • GetProductList - 请求服务端产品数据,需要用到http
    • RenderData - 将产品列表数据在页面渲染出来,需要用到template
    • ShowProductDetail - 打开产品详情页,需要先将产品数据放到cache里,然后product/detail从cache获取数据,无需再多走一次http请求;而模块页面的跳转,需要用到路由router的方法

    那么按照,那么传统的做法。就要这么写:

    import { Router } from "../../libs/router";
    import { Http } from "../../utils/http";
    import { DataCache } from "../../utils/cache";
    import { Template } from "../../utils/template";
    
    export class ProductList {
      GetProductList(){
        var http = new Http();
        http.Post(...);
      }
      RenderData(){
        var tpl = new Template();
        tpl.Parse(...);
      }
      ShowProductDetail(){
        var cache = new DataCache();
        cache.Set(...);
        var router = new Router();
        router.Go(...);
      }
    }
    

            这样的代码看着,似乎还好吧?只能说没有对比就没有伤害:)
            想象一下,如果有几十个业务模块都需要类似这样引用,单单复制粘贴就要几十次。万一要改一下构建函数加个参数呢?都是纯体力活呐~
    厌恶了这样的体力活之后,你会首先想到的是DI。

    2、容器

            有了模块之后,最好有一个容器(Container)把所有工具模组装起来,然后作为一个对象参数注入到业务模块当中。个人认为这是最省时最省力的方法!当然,容器并不是必选项,你可以选择单个单个的注入。那么,同样面临着如果需要扩增注入模块要大面积的改代码的问题。

            在TS里面DI容器可以简单定义成一个接口,然后把里面的对象实都例化,然后就可以用于注入了。

    interface DIContainer {
      http? : Http;
      router? : Router;
      cache? : DataCache;;
      tpl? : Template;
    }
    
    const container : DIContainer = { 
      http: new Http() ;
      router: new Router() ;
      cache: new DataCache() ;
      tpl: new Template() ;
    }
    

            其实,也可以弄的稍微复杂一些,可以让容器中的模块先不初始化,而是建立一个工厂模型,需要用到的时候再去初始化。那么容器的弹性会强一些,至少不会一开始占用那么多资源。

    interface DIContainer {
        http?: Http;
        router?: Router;
        cache?: DataCache;
        tpl?: Template;
        Build(key: string): void;
    }
    
    class Container implements DIContainer {
        http: Http;
        router: Router;
        cache: DataCache;
        tpl: Template;
    
        Build(key: string) {
            switch (key) {
                case "http":
                    if (!this.http) this.http = new Http();
                    break;
                case "router":
                    if (!this.router) this.router = new Router();
                    break;
                case "cache":
                    if (!this.cache) this.cache = new DataCache();
                    break;
                case "tpl":
                    if (!this.tpl) this.tpl = new Template();
                    break;
            }
        }
    }
    var container :Container();
    container.Build("http");
    container.Build("tpl");
    container.Build("cache");
    container.Build("router");
    
    3、注入

            好了,说了半天,总算“万事俱备只欠注入”了!直接上代码:

    export class ProductList {
      constructor(private di : DIContainer) {}
      GetProductList(){
        this.di.http.Post(...);
      }
      RenderData(){
        this.di.tpl.Parse(...);
      }
      ShowProductDetail(){
        this.di.cache.Set(...);
        this.di.router.Go(...);
      }
    }
    var proList = new Product(container); //把上面的容器作为构建参数注入
    

            就是这样,把容器作为模块构建参数注入。这样容器里面的模块就能随便用了~哈哈,看懂了之后,是不是应该拍手称快呢?

    4、控制

            反转控制(Inversion of Control),这算是属于纯概念的东西。真要讲的话可以扯上一千多字,我就懒得再去绕了。
            反正,依赖注入已经搞定,然后注入的容器中的对象也都用起来了,耦合度大大降低了,代码越看越帅了,心情舒爽不少,胃口也好了。那么,就已经达到我的目的了。至于到底谁控制了谁,自己体会吧。跟我半毛钱关系都没有~~

    相关文章

      网友评论

          本文标题:纯TypeScript开发Web前端(五)依赖注入

          本文链接:https://www.haomeiwen.com/subject/cdbcsqtx.html