likes
comments
collection
share

Typescript的应用与思考

作者站长头像
站长
· 阅读数 71

合理的实践是什么样的

在大多数coding的场景下,我们习惯于自由开发。不同的代码风格造就着无数的人在coding的时候口吐芬芳。 对于上面的情况,我分享了一些最近使用reactnative和typescript进行配合开发,在搭建基础设施的时候,所产生的思考。

前言

本文不对reactnative进行过多讲解,主要以typescript为核心给我们所带来的思考:

大多数人对typescript的理解与印象,可能是说它是一个带类型的javascript,然后就没下文了。一直在那些类型给我们带来的语法糖的区域里。个人理解tyepescript的出现给我们前端带来了那些优秀的编程思想。如面向对象,ioc容器依赖注入,aop面向切面编程。基与这些我们可以搭建出一个代码结构比较健全的项目。虽说es6的出现,这些编程理念也可灵活的运用。但没法给我们带来静态类型的好处。

一、逻辑与UI分离

逻辑是什么?逻辑是一个纯类,包含属性,方法,可以继承其他类。我们所有的逻辑都应该封装到类里,不同的逻辑,分类不同类。相同逻辑可以使用依赖注入的方法,进行共享。不需要任何的展示方法(函数)

UI是什么?UI是一个函数,以react为例,hook函数来展示UI视图,这个函数只能包括,ui展示和事件绑定这些操作。复杂的逻辑尽量写到一个类里,在hook函数里导入并赋值到一个变量里进行api处理。

每次去导入的类的时候还需要new 成一个对象来进行导入。yes?

no。当然不是,这样太过麻烦,并且导入一个类的时候,还要想办法进行组件间共享数据。相同的操作我们应该封装起来。以HOC的方式对组件进行封装。

下面我会进行详细讲解。

二、reactnative是什么?

reactnatie可以使用react来开发移动原生应用,其通过react 的虚拟dom 借助于javascript core 引擎 给原生发事件通知,原生端订阅到组件的大小,位置等信息给原生组件(android ui,ios ui),最后由原生组件来渲染,从而来提高app的性能问题。当然这种渲染方式也是有弊端的,reactnaive也有相应的解决方案。

三、面向对象的编程理念

1、面向对象的三大特征

封装   继承   多态 (相信大家对于他们了解并不少,不在这里解释了)

类里面有一个很重要的概念,没错它就是构造函数constructor,进行一些初始化操作,下文 会用到。可用于依赖注入共享一些数据

import CommonStore from '../../stores/CommonStore';  //导入通用类
import {injectable, autoInjectable, injectAll} from 'tsyringe';
@injectable()
class User{
     
     name:string='德玛西亚';   //属性
     constructor(
        public commonStore: CommonStore   //构造器依赖注入
    ) {
       super(this)
    }
      
}

上面的代码没太理解没关系,接下会讲解到。

四、ioc容器依赖注入

1、ioc是什么

Typescript的应用与思考

IOC又叫依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中 就是指通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦。

看上图,对象A和对象B,对象之间已经没有了耦合关系,彼此毫无联系,但又通过IOC把系统中的所有对象粘合在一起发挥作用。

所以对象A里可以共享对象B的数据,看起来和对象继承很像,那多继承呢,没法在一个对象里共享多个对象,IOC容器可以

2、组件之间共享对象

上文讲到,对象间可进行依赖注入共享数据。那么问题来了,如何在hook函数组间里共享这些数据呢?有人说,直接导入对象。对,那如何导入呢。前文说过我们所有的逻辑都是类,那么导入一次,就new 一次。可以是可以,不够优雅。来,优雅一点怎么搞呢。有人还记得context对象,组件间共享数据的一种。content对象提供了一个组件provider,改组件通过value属性来注入所要共享的对象,组件provider包裹这子组件。也就是是每一个被组件provider所包裹的子组件就可以拿到注入进来的对象。ok,逻辑理清了。看如何操作。

3、使用tsyringe实现IOC

[tsyringe -> 用于TypeScript/JavaScript的轻量级依赖注入容器,用于构造函数注入。

1.封装context对象 (react提供了createContext方法来创建一个context对象)

2.本地注入store(注入共享对象)

4、封装context对象

 
/**
 * @Description:  封装context对象
 * @date 2022/8/31
*/
import React, { useContext } from 'react'
import invariant from 'invariant'
import type { DependencyContainer } from 'tsyringe'
 
export const LocalStoreContext = React.createContext<any>(null)
LocalStoreContext.displayName = 'LocalStoreContext'
 
export function useLocalStore<T>(): T {
    const store: T = useContext(LocalStoreContext)
    invariant(store, 'local store is not exist!')
    return store
}
 
export const StoreContainerContext = React.createContext<DependencyContainer | null>(null)
StoreContainerContext.displayName = 'StoreContainerContext'

上面用react提供了createContext方法创建一个了普通的context对象,着重说下DependencyContainer,你可看成我们注入依赖时所需要令牌,通过createContext创建了这个实例。

5、本地注入store(注入共享对象)

/**
 * @Description:  本地注入store
 * @date 2022/8/31
*/
import React, { useContext, useMemo } from 'react'
import { container, Lifecycle,InjectionToken, } from 'tsyringe'
import hoistNonReactStatic from 'hoist-non-react-statics'
import { LocalStoreContext, StoreContainerContext } from '../../until/ContextHelpers';
 
export function withLocalStore<T extends JSX.IntrinsicAttributes, S>(
    WrappedComponent: React.ComponentType<T>,
    storeClass: { new (...args: any[]): S }
): React.ComponentType<T> {
    function WithLocalStoreHoc(props: T) {
        const superContainer = useContext(StoreContainerContext) || container
        const selfContainer = useMemo(() => {
            const componentContainer = superContainer.createChildContainer()
            if (!componentContainer.isRegistered(storeClass, false)) {
                componentContainer.register(
                    storeClass,
                    { useClass: storeClass },
                    { lifecycle: Lifecycle.Singleton }
                )
            }
            return componentContainer
        }, [superContainer])
 
        return (
            <StoreContainerContext.Provider value={selfContainer}>
                <LocalStoreContext.Provider value={selfContainer.resolve(storeClass)}>
                    <WrappedComponent {...props} />
                </LocalStoreContext.Provider>
            </StoreContainerContext.Provider>
        )
    }
 
    WithLocalStoreHoc.displayName = `WithLocalStoreHoc(${getDisplayName(
        WrappedComponent
    )})`
    hoistNonReactStatic(WithLocalStoreHoc, WrappedComponent)
    return WithLocalStoreHoc
}
 
function getDisplayName<T>(WrappedComponent: React.ComponentType<T>) {
    return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}

当我们使用tsyringe进行依赖注入时

1.创建一个DelayedConstructor的实例,其实就是token验证

2.注册一个共享数据(对象),这里还牵扯到单例模式,该对象只能创建为单例模式,只会有一个实例共享对象。new 的时候就数据就不会被重置。

3.DelayedConstructor对象提供了provider组件,通过value属性注入这个对象,解析着这个共享数据

4.DelayedConstructor对象提供了provider包裹着子组件,子组件间共享数据对象

5.在通过useContext hook 使用provider组件底层组件间的共享

五、使用IOC和Mobx进行合理的逻辑与UI分离

1、逻辑处理

import {observable, runInAction, makeObservable, action} from 'mobx'
import {injectable, autoInjectable, injectAll} from 'tsyringe';
import UserStore from "../../stores/UserStore";
import CommonStore from '../../stores/CommonStore';
import {UserInfo} from '../../stores/model/Uesr';
 
@injectable()
export default class HomeStore {
 
    @observable userInfo: UserInfo | undefined;
 
    constructor(
        public userStore: UserStore,
        public commonStore: CommonStore
    ) {
        makeObservable(this)
    }
 
 
    @action.bound
    getUserInfo() {
        this.userStore?.getInfo();
        this.userInfo = this.userStore?.userInfo;
    }
 
 
}

1.使用mobx中@observable进行数据响应时处理

2.使用tsyringe的@injectable()进行依赖注解在constructor函数初始化参数的时候,传了一个类

2.UI处理

/**
 * @Description:  主页
 * @date 2022/8/29
 */
import {View, Text, StyleSheet, ScrollView, Animated} from 'react-native';
import React, {useContext, useRef} from 'react';
import {observer} from 'mobx-react';
import HomeStore from './homeStore';
import {withLocalStore} from '../../components/withLocalStore';
import {useLocalStore} from '../../until/ContextHelpers';
import {useMount} from 'ahooks';
import {LargeList} from "react-native-largelist";
import {ChineseWithLastDateHeader} from "react-native-spring-scrollview/Customize";
 
interface IProps {}
 
const HomeScreen = (props: IProps) => {
 
    const store = useLocalStore<HomeStore>();
    useMount(() => {
        store.getUserInfo();
        console.log('userInfo', store.userInfo?.userName)
    })
 
 
    return <View style={styles.body}>
     
    </View>
}
 
export default withLocalStore(observer(HomeScreen), HomeStore);
 

1.通过封装的高阶组件withLocalStore进行组件包裹。observer是mobx提供的,可使该组件响应时监听数据的变化,视图更改。

2.通过封装useLocalStore传入一个类(依赖),得到一个可共享的对象。该对象封装所有的逻辑api处理。

总结

以上就是今天要讲的内容,本文介绍了ioc的使用及一些概念上的东西,而typescript提供了大量能使我们快速便捷开发的编程思想。对于上文有不同见解的,可以开放的进行相互学习。欢迎留言自己的不同理解。

转载自:https://juejin.cn/post/7145655653384585247
评论
请登录