1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        如何利用AOP+IOC思想解構(gòu)前端項(xiàng)目開(kāi)發(fā)

        共 12379字,需瀏覽 25分鐘

         ·

        2020-11-12 19:43

        作者:evio

        來(lái)源:SegmentFault 思否社區(qū)




        本文將通過(guò) TypeClient 架構(gòu)來(lái)闡述如何利用AOP+IOC思想來(lái)解構(gòu)前端項(xiàng)目的開(kāi)發(fā)。


        首先聲明,AOP+IOC思想的理解需要有一定的編程架構(gòu)基礎(chǔ)。目前,這兩大思想使用的場(chǎng)景,基本都在nodejs端,在前端的實(shí)踐非常少。我本著提供一種新的項(xiàng)目解構(gòu)思路的想法,而非推翻社區(qū)龐大的全家桶。大家看看就好,如果能給你提供更好的靈感,那么再好不過(guò)了,非常歡迎交流。


        以下我們將以 TypeClient 的 React 渲染引擎為例。




        AOP


        一種面向切面編程的思想。它在前端的表現(xiàn)是前端的裝飾器,我們可以通過(guò)裝飾器來(lái)攔截函數(shù)執(zhí)行前與執(zhí)行后的自定義行為。


        • AOP的主要作用是把一些跟核心業(yè)務(wù)邏輯模塊無(wú)關(guān)的功能抽離出來(lái),這些跟業(yè)務(wù)邏輯無(wú)關(guān)的功能通常包括日志統(tǒng)計(jì)、安全控制、異常處理等。把這些功能抽離出來(lái)之后, 再通過(guò)“動(dòng)態(tài)織入”的方式摻入業(yè)務(wù)邏輯模塊中。AOP的好處首先是可以保持業(yè)務(wù)邏輯模塊的純凈和高內(nèi)聚性,其次是可以很方便地復(fù)用日志統(tǒng)計(jì)等功能模塊。


        以上是網(wǎng)絡(luò)上對(duì)AOP的簡(jiǎn)單解釋。那么實(shí)際代碼也許是這樣的


        @Controller()
        class?Demo?{
        ??@Route()?Page()?{}
        }
        復(fù)制代碼


        但很多時(shí)候,我們僅僅是將某個(gè)class下的函數(shù)當(dāng)作一個(gè)儲(chǔ)存數(shù)據(jù)的對(duì)象而已,而在確定運(yùn)行這個(gè)函數(shù)時(shí)候拿出數(shù)據(jù)做自定義處理??梢酝ㄟ^(guò) reflect-metadata 來(lái)了解更多裝飾器的作用。




        IOC


        Angular難以被國(guó)內(nèi)接受很大一部分原因是它的理念太龐大,而其中的DI(dependency inject)在使用時(shí)候則更加讓人迷糊。其實(shí)除了DI還有一種依賴注入的思想叫 IOC。它的代表庫(kù)為 inversify。它在github上擁有6.7K的star數(shù),在依賴注入的社區(qū)里,口碑非常好。我們可以先通過(guò)這個(gè)庫(kù)來(lái)了解下它對(duì)項(xiàng)目解構(gòu)的好處。


        例子如下:


        @injectable()
        class?Demo?{
        ??@inject(Service)?private?readonly?service:?Service;
        ??getCount()?{
        ????return?1?+?this.service.sum(2,?3);
        ??}
        }
        復(fù)制代碼


        當(dāng)然,Service已經(jīng)優(yōu)先被注入到inversify的container內(nèi)了,才可以通過(guò) TypeClient 這樣調(diào)用。




        重新梳理前端項(xiàng)目運(yùn)行時(shí)


        一般地,前端項(xiàng)目會(huì)經(jīng)過(guò)這樣的運(yùn)行過(guò)程。


        1. 通過(guò)監(jiān)聽(tīng)hashchange或者popstate事件攔截瀏覽器行為。
        2. 設(shè)定當(dāng)前獲得的window.location 數(shù)據(jù)如何對(duì)應(yīng)到一個(gè)組件。
        3. 組件如何渲染到頁(yè)面。
        4. 當(dāng)瀏覽器URL再次變化的時(shí)候,我們?nèi)绾螌?duì)應(yīng)到一個(gè)組件并且渲染。

        這是社區(qū)的通用解決方案。當(dāng)然,我們不會(huì)再講解如何設(shè)計(jì)這個(gè)模式。我們將采用全新的設(shè)計(jì)模式來(lái)解構(gòu)這個(gè)過(guò)程。

        重新審視服務(wù)端路由體系


        我們聊的是前端的架構(gòu),為什么會(huì)聊到服務(wù)端的架構(gòu)體系?

        那是因?yàn)?,其?shí)設(shè)計(jì)模式并不局限在后端或者前端,它應(yīng)該是一種比較通用的方式來(lái)解決特定的問(wèn)題。

        那么也許有人會(huì)問(wèn),服務(wù)端的路由體系與前端并不一致,有何意義?

        我們以nodejs的http模塊為例,其實(shí)它與前端有點(diǎn)類似的。http模塊運(yùn)行在一個(gè)進(jìn)程中,通過(guò)http.createServer的參數(shù)回調(diào)函數(shù)來(lái)響應(yīng)數(shù)據(jù)。我們可以認(rèn)為,前端的頁(yè)面相當(dāng)于一個(gè)進(jìn)程,我們通過(guò)監(jiān)聽(tīng)相應(yīng)模式下的事件來(lái)響應(yīng)得到組件渲染到頁(yè)面。

        服務(wù)端多個(gè)Client發(fā)送請(qǐng)求到一個(gè)server端端口處理,為什么不能類比到前端用戶操作瀏覽器地址欄通過(guò)事件來(lái)得到響應(yīng)入口呢?

        答案是可以的。我們稱這種方式為 virtual server 即基于頁(yè)面級(jí)的虛擬服務(wù)。
        既然可以抽象稱一種服務(wù)架構(gòu),那當(dāng)然,我們可以完全像nodejs的服務(wù)化方案靠攏,我們可以將前端的路由處理的如nodejs端常見(jiàn)的方式,更加符合我們的意圖和抽象。



        改造路由設(shè)計(jì)


        如果是以上的書(shū)寫方式,那么也可以解決基本的問(wèn)題,但是不符合我們AOP+IOC的設(shè)計(jì),書(shū)寫的時(shí)候還是比較繁瑣的,同時(shí)也沒(méi)有解構(gòu)掉響應(yīng)的邏輯。

        我們需要解決以下問(wèn)題:

        1. 如何解析路由字符串規(guī)則?
        2. 如何利用這個(gè)規(guī)則快速匹配到對(duì)應(yīng)的回調(diào)函數(shù)?

        在服務(wù)端有很多解析路由規(guī)則的庫(kù),比較代表的是 path-to-regexp,它被使用在KOA等著名架構(gòu)中。它的原理也就是將字符串正則化,使用當(dāng)前傳入的path來(lái)匹配相應(yīng)的規(guī)則從而得到對(duì)應(yīng)的回調(diào)函數(shù)來(lái)處理。但是這種做法有一些瑕疵,那就是正則匹配速度較慢,當(dāng)處理隊(duì)列最后一個(gè)規(guī)則被匹配的時(shí)候,所有規(guī)則都將被執(zhí)行過(guò),當(dāng)路由過(guò)多時(shí)候性能較差,這一點(diǎn)可以參看我之前寫的 koa-rapid-router超越koa-router性能的100多倍。還有一點(diǎn)瑕疵是,它的匹配方式是按照你編寫順序匹配的,所以它具有一定的順序性,開(kāi)發(fā)者要非常注意。比如:

        http.get('/:id(d+)',?()?=>?console.log(1));
        http.get('/1234',?()?=>?console.log(2));
        復(fù)制代碼

        如果我們?cè)L問(wèn)/1234,那么它將打印出1,而非2。

        為了解決性能以及優(yōu)化匹配過(guò)程的智能性,我們可以參考 find-my-way 的路由設(shè)計(jì)體系。具體請(qǐng)看官自己看了,我不解析。總之,它是一種字符串索引式算法,能夠快速而智能地匹配到我們需要的路由。著名的 fastify 就是采用這個(gè)架構(gòu)來(lái)達(dá)到高性能的。



        TypeClient 的路由設(shè)計(jì)


        我們可以通過(guò)一些簡(jiǎn)單的裝飾器就能快速定義我們的路由,本質(zhì)還是采用find-my-way的路由設(shè)計(jì)原則。

        import?React?from?'react';
        import?{?Controller,?Route,?Context?}?from?'@typeclient/core';
        import?{?useReactiveState?}?from?'@typeclient/react';
        @Controller('/api')
        export?class?DemoController?{
        ??@Route('/test')
        ??TestPage(props:?Reat.PropsWithoutRef)?{
        ????const?status?=?useReactiveState(()?=>?props.status.value);
        ????return?
        Hello?world!?{status}
        ;
        ??}
        }
        //?--------------------------
        //?在index.ts中只要
        app.setController(DemoController);
        //?它就自動(dòng)綁定了路由,同時(shí)頁(yè)面進(jìn)入路由?`/api/test`?的時(shí)候
        //?就會(huì)顯示文本?`Hello world! 200`。
        復(fù)制代碼

        可見(jiàn),TypeClient 通過(guò) AOP 理念定義路由非常簡(jiǎn)單。



        路由生命周期


        當(dāng)從一個(gè)頁(yè)面跳轉(zhuǎn)到另一個(gè)頁(yè)面的時(shí)候,前一個(gè)頁(yè)面的生命周期也隨即結(jié)束,所以,路由是具有生命周期的。再此,我們將整個(gè)頁(yè)面周期拆解如下:

        1. beforeCreate 頁(yè)面開(kāi)始加載
        2. created 頁(yè)面加載完成
        3. beforeDestroy 頁(yè)面即將銷毀
        4. destroyed 頁(yè)面已經(jīng)銷毀

        為了表示這4個(gè)生命周期,我們根據(jù)React的hooks特制了一個(gè)函數(shù)useContextEffect來(lái)處理路由生命周期的副作用。比如:

        import?React?from?'react';
        import?{?Controller,?Route,?Context?}?from?'@typeclient/core';
        import?{?useReactiveState?}?from?'@typeclient/react';
        @Controller('/api')
        export?class?DemoController?{
        ??@Route('/test')
        ??TestPage(props:?Reat.PropsWithoutRef)?{
        ????const?status?=?useReactiveState(()?=>?props.status.value);
        ????useContextEffect(()?=>?{
        ??????console.log('路由加載完成了');
        ??????return?()?=>?console.log('路由被銷毀了');
        ????})
        ????return?
        Hello?world!?{status}
        ;
        ??}
        }
        復(fù)制代碼

        其實(shí)它與useEffect或者useLayoutEffect有些類似。只不過(guò)我們關(guān)注的是路由的生命周期,而react則關(guān)注組件的生命周期。

        其實(shí)通過(guò)上面的props.status.value我們可以猜測(cè)出,路由是有狀態(tài)記錄的,分別是100和200還有500等等。我們可以通過(guò)這樣的數(shù)據(jù)來(lái)判斷當(dāng)前路由處于什么生命周期內(nèi),也可以通過(guò)骨架屏來(lái)渲染不同的效果。



        中間件設(shè)計(jì)


        為了控制路由生命周期的運(yùn)行,我們?cè)O(shè)計(jì)了中間件模式,用來(lái)處理路由前置的行為,比如請(qǐng)求數(shù)據(jù)等等。中間件原則上采用與KOA一致的模式,這樣可以大大兼容社區(qū)生態(tài)。

        const?middleware?=?async?(ctx,?next)?=>?{
        ??//?ctx.....
        ??await?next();
        }
        復(fù)制代碼

        通過(guò)AOP 我們可以輕松引用這個(gè)中間件,達(dá)到頁(yè)面加載完畢狀態(tài)前的數(shù)據(jù)處理。

        import?React?from?'react';
        import?{?Controller,?Route,?Context,?useMiddleware?}?from?'@typeclient/core';
        import?{?useReactiveState?}?from?'@typeclient/react';
        @Controller('/api')
        export?class?DemoController?{
        ??@Route('/test')
        ??@useMiddleware(middleware)
        ??TestPage(props:?Reat.PropsWithoutRef)?{
        ????const?status?=?useReactiveState(()?=>?props.status.value);
        ????useContextEffect(()?=>?{
        ??????console.log('路由加載完成了');
        ??????return?()?=>?console.log('路由被銷毀了');
        ????})
        ????return?
        Hello?world!?{status}
        ;
        ??}
        }
        復(fù)制代碼

        設(shè)計(jì)周期狀態(tài)管理 - ContextStore


        不得不說(shuō)這個(gè)是一個(gè)亮點(diǎn)。為什么要設(shè)計(jì)這樣一個(gè)模式呢?主要是為了解決在中間件過(guò)程中對(duì)數(shù)據(jù)的操作能夠及時(shí)響應(yīng)到頁(yè)面。因?yàn)橹虚g件執(zhí)行與react頁(yè)面渲染是同步的,所以我們?cè)O(shè)計(jì)這樣的模式有利于數(shù)據(jù)的周期化。

        我們采用了非常黑科技的方案解決這個(gè)問(wèn)題:@vue/reactity

        對(duì),就是它。

        我們?cè)趓eact中嵌入了VUE3最新的響應(yīng)式系統(tǒng),讓我們開(kāi)發(fā)快速更新數(shù)據(jù),而放棄掉dispatch過(guò)程。當(dāng)然,這對(duì)中間件更新數(shù)據(jù)是及其有力的。

        這里 我非常感謝 sl1673495 給到的黑科技思路讓我們的設(shè)計(jì)能夠完美兼容react。

        我們通過(guò)@State(callback)來(lái)定義ContextStore的初始化數(shù)據(jù),通過(guò)useContextState或者useReactiveState跟蹤數(shù)據(jù)變化并且響應(yīng)到React頁(yè)面中。

        來(lái)看一個(gè)例子:

        import?React?from?'react';
        import?{?Controller,?Route,?Context,?useMiddleware,?State?}?from?'@typeclient/core';
        import?{?useReactiveState?}?from?'@typeclient/react';
        @Controller('/api')
        export?class?DemoController?{
        ??@Route('/test')
        ??@useMiddleware(middleware)
        ??@State(createState)
        ??TestPage(props:?Reat.PropsWithoutRef)?{
        ????const?status?=?useReactiveState(()?=>?props.status.value);
        ????const?count?=?useReactiveState(()?=>?props.state.count);
        ????const?click?=?useCallback(()?=>?ctx.state.count++,?[ctx.state.count]);
        ????useContextEffect(()?=>?{
        ??????console.log('路由加載完成了');
        ??????return?()?=>?console.log('路由被銷毀了');
        ????})
        ????return?Hello?world!?{status}?-?{count}
        ;
        ??}
        }

        function?createState()?{
        ??return?{
        ????count:?0,
        ??}
        }
        復(fù)制代碼

        你可以看到不斷點(diǎn)擊,數(shù)據(jù)不斷變化。這種操作方式極大簡(jiǎn)化了我們數(shù)據(jù)的變化寫法,同時(shí)也可以與vue3響應(yīng)式能力看齊,彌補(bǔ)react數(shù)據(jù)操作復(fù)雜度的短板。

        除了在周期中使用這個(gè)黑科技,其實(shí)它也是可以獨(dú)立使用的,比如在任意位置定義:

        //?test.ts
        import?{?reactive?}?from?'@vue/reactity';

        export?const?data?=?reactive({
        ??count:?0,
        })
        復(fù)制代碼

        我們可以在任意組件中使用

        import?React,?{?useCallback?}?from?'react';
        import?{?useReactiveState?}?from?'@typeclient/react-effect';
        import?{?data?}?from?'./test';

        function?TestComponent()?{
        ??const?count?=?useReactiveState(()?=>?data.count);
        ??const?onClick?=?useCallback(()?=>?data.count++,?[data.count]);
        ??return?{count}

        }
        復(fù)制代碼

        利用IOC思想解構(gòu)項(xiàng)目


        以上的講解都沒(méi)有設(shè)計(jì)IOC方面,那么下面將講解IOC的使用。

        Controller 服務(wù)解構(gòu)


        我們先編寫一個(gè)Service文件

        import?{?Service?}?from?'@typeclient/core';

        @Service()
        export?class?MathService?{
        ??sum(a:?number,?b:?number)?{
        ????return?a?+?b;
        ??}
        }
        復(fù)制代碼

        然后我們可以在之前的Controller中直接調(diào)用:

        import?React?from?'react';
        import?{?Controller,?Route,?Context,?useMiddleware,?State?}?from?'@typeclient/core';
        import?{?useReactiveState?}?from?'@typeclient/react';
        import?{?MathService?}?from?'./service.ts';
        @Controller('/api')
        export?class?DemoController?{
        ??@inject(MathService)?private?readonly?MathService:?MathService;

        ??@Route('/test')
        ??@useMiddleware(middleware)
        ??@State(createState)
        ??TestPage(props:?Reat.PropsWithoutRef)?{
        ????const?status?=?useReactiveState(()?=>?props.status.value);
        ????const?count?=?useReactiveState(()?=>?props.state.count);
        ????const?click?=?useCallback(()?=>?ctx.state.count++,?[ctx.state.count]);
        ????const?value?=?this.MathService.sum(count,?status);
        ????useContextEffect(()?=>?{
        ??????console.log('路由加載完成了');
        ??????return?()?=>?console.log('路由被銷毀了');
        ????})
        ????return?Hello?world!?{status}?+?{count}?=?{value}
        ;
        ??}
        }

        function?createState()?{
        ??return?{
        ????count:?0,
        ??}
        }
        復(fù)制代碼

        你可以看到數(shù)據(jù)的不斷變化。

        Component 解構(gòu)


        我們?yōu)閞eact的組件創(chuàng)造了一種新的組件模式,稱IOCComponent。它是一種具備IOC能力的組件,我們通過(guò)useComponent的hooks來(lái)調(diào)用。

        import?React?from?'react';
        import?{?Component,?ComponentTransform?}?from?'@typeclient/react';
        import?{?MathService?}?from?'./service.ts';

        @Component()
        export?class?DemoComponent?implements?ComponentTransform?{
        ??@inject(MathService)?private?readonly?MathService:?MathService;

        ??render(props:?React.PropsWithoutRef<{?a:?number,?b:?number?}>)?{
        ????const?value?=?this.MathService.sum(props.a,?props.b);
        ????return?
        {value}

        ??}
        }
        復(fù)制代碼

        然后在任意組件中調(diào)用

        import?React?from?'react';
        import?{?Controller,?Route,?Context,?useMiddleware,?State?}?from?'@typeclient/core';
        import?{?useReactiveState?}?from?'@typeclient/react';
        import?{?MathService?}?from?'./service.ts';
        import?{?DemoComponent?}?from?'./component';
        @Controller('/api')
        export?class?DemoController?{
        ??@inject(MathService)?private?readonly?MathService:?MathService;
        ??@inject(DemoComponent)?private?readonly?DemoComponent:?DemoComponent;

        ??@Route('/test')
        ??@useMiddleware(middleware)
        ??@State(createState)
        ??TestPage(props:?Reat.PropsWithoutRef)?{
        ????const?status?=?useReactiveState(()?=>?props.status.value);
        ????const?count?=?useReactiveState(()?=>?props.state.count);
        ????const?click?=?useCallback(()?=>?ctx.state.count++,?[ctx.state.count]);
        ????const?value?=?this.MathService.sum(count,?status);
        ????const?Demo?=?useComponent(this.DemoComponent);
        ????useContextEffect(()?=>?{
        ??????console.log('路由加載完成了');
        ??????return?()?=>?console.log('路由被銷毀了');
        ????})
        ????return?
        ??????Hello?world!?{status}?+?{count}?=?{value}?
        ??????
        ????
        ;
        ??}
        }

        function?createState()?{
        ??return?{
        ????count:?0,
        ??}
        }
        復(fù)制代碼

        Middleware 解構(gòu)


        我們完全可以拋棄掉傳統(tǒng)的中間件寫法,而采用能加解構(gòu)化的中間件寫法:

        import?{?Context?}?from?'@typeclient/core';
        import?{?Middleware,?MiddlewareTransform?}?from?'@typeclient/react';
        import?{?MathService?}?from?'./service';

        @Middleware()
        export?class?DemoMiddleware?implements?MiddlewareTransform?{
        ??@inject(MathService)?private?readonly?MathService:?MathService;

        ??async?use(ctx:?Context,?next:?Function)?{
        ????ctx.a?=?this.MathService.sum(1,?2);
        ????await?next();
        ??}
        }
        復(fù)制代碼

        為react新增Slot插槽概念


        它支持Slot插槽模式,我們可以通過(guò)useSlot獲得Provider與Consumer。它是一種通過(guò)消息傳送節(jié)點(diǎn)片段的模式。

        const?{?Provider,?Consumer?}?=?useSlot(ctx.app);
        "foo">provider?data
        "foo">placeholder
        復(fù)制代碼

        然后編寫一個(gè)IOCComponent或者傳統(tǒng)組件。

        //?template.tsx
        import?{?useSlot?}?from?'@typeclient/react';
        @Component()
        class?uxx?implements?ComponentTransform?{
        ??render(props:?any)?{
        ????const?{?Consumer?}?=?useSlot(props.ctx);
        ????return?

        ??????

        title


        ??????"foo"?/>
        ??????{props.children}
        ????

        ??}
        }
        復(fù)制代碼

        最后在Controller上調(diào)用

        import?{?inject?}?from?'inversify';
        import?{?Route,?Controller?}?from?'@typeclient/core';
        import?{?useSlot?}?from?'@typeclient/react';
        import?{?uxx?}?from?'./template.tsx';
        @Controller()
        @Template(uxx)
        class?router?{
        ??@inject(ttt)?private?readonly?ttt:?ttt;
        ??@Route('/test')
        ??test()?{
        ????const?{?Provider?}?=?useSlot(props.ctx);
        ????return?

        ??????child?...
        ??????"foo">
        ????????this?is?foo?slot
        ??????
        ????

        ??}
        }
        復(fù)制代碼

        你能看到的結(jié)構(gòu)如下:


        ??

        title


        ??this?is?foo?slot
        ??
        child?...


        復(fù)制代碼

        解構(gòu)項(xiàng)目的原則


        我們可以通過(guò)對(duì)IOC服務(wù)與Middleware還有組件進(jìn)行不同緯度的解構(gòu),封裝成統(tǒng)一的npm包上傳到私有倉(cāng)庫(kù)中供公司內(nèi)部開(kāi)發(fā)使用。

        類型

        1. IOCComponent + IOCService
        2. IOCMiddleware + IOCService
        3. IOCMiddlewware
        4. IOCService

        原則

        1. 通用化
        2. 內(nèi)聚合
        3. 易擴(kuò)展

        遵循這種原則的化可以使公司的業(yè)務(wù)代碼或者組件具有高度的復(fù)用性,而且通過(guò)AOP能夠很清楚直觀的表現(xiàn)代碼即文檔的魅力。

        通用化


        即保證所封裝的邏輯、代碼或者組件具體高度的通用化特性,對(duì)于不太通用的沒(méi)必要封裝。比如說(shuō),公司內(nèi)部統(tǒng)一的導(dǎo)航頭,導(dǎo)航頭有可能被用到任意項(xiàng)目中做統(tǒng)一化,那么就非常適合封裝為組件型模塊。

        內(nèi)聚性


        通用的組件需要得到統(tǒng)一的數(shù)據(jù),那么可以通過(guò)IOCComponent + IOCService + IOCMiddleware的形式將其包裝,在使用的適合只需要關(guān)注導(dǎo)入這個(gè)組件即可。還是舉例通用導(dǎo)航頭。比如導(dǎo)航頭需要下拉一個(gè)團(tuán)隊(duì)列表,那么,我們可以這樣定義這個(gè)組件:

        一個(gè)service文件:

        //?service.ts
        import?{?Service?}?from?'@typeclient/core';
        @Service()
        export?class?NavService?{
        ??getTeams()?{
        ????//?...?這里可以是ajax請(qǐng)求的結(jié)果
        ????return?[
        ??????{
        ????????name:?'Team?1',
        ????????id:?1,
        ??????},
        ??????{
        ????????name:?'Team?2',
        ????????id:?1,
        ??????}
        ????]
        ??}

        ??goTeam(id:?number)?{
        ????//?...
        ????console.log(id);
        ??}
        }
        復(fù)制代碼

        組件:

        //?component.ts
        import?React,?{?useEffect,?setState?}?from?'react';
        import?{?Component,?ComponentTransform?}?from?'@typeclient/react';
        import?{?NavService?}?from?'./service';

        @Component()
        export?class?NavBar?implements?ComponentTransform?{
        ??@inject(NavService)?private?readonly?NavService:?NavService;
        ??render()?{
        ????const?[teams,?setTeams]?=?setState'getTeams']>>([]);
        ????useEffect(()?=>?this.NavService.getTeams().then(data?=>?setTeams(data)),?[]);
        ????return?

          ??????{
          ????????teams.map(team?=>??this.NavService.goTeam(team.id)}>{team.name})
          ??????}
          ????

        ??}
        }
        復(fù)制代碼

        我們將這個(gè)模塊定義為@fe/navbar,同時(shí)導(dǎo)出這個(gè)個(gè)對(duì)象:

        //?@fe/navbar/index.ts
        export?*?from?'./component';
        復(fù)制代碼

        在任意的IOC組件中就可以這樣調(diào)用

        import?React?from?'react';
        import?{?Component,?ComponentTransform,?useComponent?}?from?'@typeclient/react';
        import?{?NavBar?}?from?'@fe/navbar';

        @Component()
        export?class?DEMO?implements?ComponentTransform?{
        ??@inject(NavBar)?private?readonly?NavBar:?NavBar;
        ??render()?{
        ????const?NavBar?=?useComponent(this.NavBar);
        ????return?
        ??}
        }
        復(fù)制代碼

        你可以發(fā)現(xiàn)只要加載這個(gè)組件,相當(dāng)于請(qǐng)求數(shù)據(jù)都自動(dòng)被載入了,這就非常有區(qū)別與普通的組件模式,它可以是一種業(yè)務(wù)型的組件解構(gòu)方案。非常實(shí)用。

        易擴(kuò)展


        主要是讓我們對(duì)于設(shè)計(jì)這個(gè)通用型的代碼或者組件時(shí)候保持搞擴(kuò)展性,比如說(shuō),巧用SLOT插槽原理,我們可以預(yù)留一些空間給插槽,方便這個(gè)組件被使用不同位置的代碼所傳送并且替換掉原位置內(nèi)容,這個(gè)的好處需要開(kāi)發(fā)者自行體會(huì)。

        演示


        我們提供了一個(gè)demo來(lái)表現(xiàn)它的能力,而且可以從代碼中看到如何解構(gòu)整個(gè)項(xiàng)目。我們的每個(gè)Controller都可以獨(dú)立存在,使得項(xiàng)目?jī)?nèi)容遷移變得非常容易。

        • 框架:?github.com/flowxjs/Typ…
        • 項(xiàng)目模板:?github.com/flowxjs/Typ…
        • 簡(jiǎn)單的最佳實(shí)踐:?github.com/flowxjs/Typ…

        大家可以通過(guò)以上的兩個(gè)例子來(lái)了解開(kāi)發(fā)模式。



        總結(jié)


        新的開(kāi)發(fā)理念并不是讓你摒棄掉傳統(tǒng)的開(kāi)發(fā)方式和社區(qū),而且提供更好的思路。當(dāng)然,這種思路的好與壞,各有各的理解。但是我還是想聲明下,我今天僅僅是提供一種新的思路,大家看看就好,喜歡的給個(gè)star。非常感謝!



        點(diǎn)擊左下角閱讀原文,到?SegmentFault 思否社區(qū)?和文章作者展開(kāi)更多互動(dòng)和交流。

        -?END -

        瀏覽 44
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产精品v | 播五月婷婷 | 明星裸体性做爰 | 天天色操 | 性少妇bbwbbw实拍 | 国产性生大片免费观看性 | 日韩 中文字幕 无码 | 色色色色色色色网站 | 欧美后门菊门交3p视频 | 免费看91片|