Typescript的应用与思考
合理的实践是什么样的
在大多数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是什么
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