前端代码复用

唉,新公司的代码不忍卒读,忍不住记录下个人对于前端代码复用,分离的理解….

  1. 不要在代码中直接使用请求函数

强烈谴责在每次发起请求的时候写类似这样的代码

1
2
3
componentDidMount(){
Request({method:'post',url:'/info',data:{id:'123'}}).then(res=>{})
}

所有的请求应该单独写在service文件夹下,提供一个语义化的方法如下所示

1
2
3
4
5
6
7
8
export const getUserInfo = (id:string):Promise<{name:string,age:number}>=>{
return Request({method:'post',url:'/info',data:{id:'123'}})
}
// 用async也行。
export async function getUserInfo(id:string):{
const result = await Request({method:'post',url:'/info',data:{id:'123'}})
return result
}

好处如下:

  • 给接口语义化,在没有注释的情况下不至于抓瞎,特别是如果接口本身不够rest(比如现在公司所有接口都是/123/123这样的数字组成,完全不能根据接口地址来判断这个接口有个啥子用)。
  • 复用性强,现在在项目中全局搜索某个接口名三四个地方用到该接口,如果放到service,找到相关调用地点,直接跑到函数定义出find refrence,他不香吗?还可以少写几遍接口名称,或者是相关参数。
  • 还是复用强,所有的接口管理得当,可以直接作为一个单独的项目发布,给所有的业务项目提供底层依赖,而service项目可以编写相关单测脚本,定时去跑测试代码,监控接口的状态。
    1. React 代码分层

 当一个功能人家说做过了可复用,结果我发现这个功能杂糅在一个七八百行的jsx内要复用就得自己拆出来的时候,我的内心是崩溃的。

 写一个功能多的展示活动页(活动页为什么没有活动页面的素材组件啊!!!!),个人建议将组件用api影响范围分为多个container。
 比如首页的banner,必定是访问一个获取banner数据的api然后将api返回的内容放到banner。这个时候建议将这个api分离出一个banner container,container包含了获取api数据,和render banner的功能。
 如果这个banner可能其它地方用到呢,就将里面的视图相关的内容再拆出来拆成一个component。在其它地方就可以访问api然后直接return <Banner {…data}/>就好了。
 那么当其它页面也要用到,并且连接口都是相同的,只是参数换了下嘞。建议将接口相关内容做成高阶函数,通过props注入的方式去组合高阶函数和Component(高阶函数可以复用setState相关的逻辑,不只是props注入,各种逻辑复用都可以考虑用高阶,当然现在有了hook,如果用hook逻辑复用第一考虑的一定是自定义hook啦)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import React from 'react'
import { getCurrentPosition, Postition } from './util'
import Utils from 'utils'

const defaultLocationInfo: LocationInfo = {
code: '1',
name: 'hangzhou',
longitude: '',
latitude: ''
}

export type LocationInfo = {
code: string
longitude: string
name: string
latitude: string
}
type IWithLocationState = {} & LocationInfo

function WithLocation<T extends LocationInfo>(WrapComponent: React.ComponentType<T>) {
return class extends React.Component<{}, IWithLocationState> {
constructor(props: {} | Readonly<{}>) {
super(props)
this.state = {
...defaultLocationInfo
}
}
async componentDidMount() {
const position = await getPosition()
this.setState({
longitude: position.lng,
latitude: position.lat,
code: position.code,
name: position.name
})
}
render() {
// 可以有一些其它的操作逻辑
const inject = { ...this.state }
return <WrapComponent {...(inject as T)} />
}
}
}

export default WithLocation


import React, { Component } from 'react'
import WithLocation, { LocationInfo } from '../../HOC/WithLocation/WithLocation'

type DevTestProp = {} & LocationInfo

export class DevTest extends Component<DevTestProp> {
componentDidUpdate(){
console.log(this.props,'props')
}
render() {
return <div>{this.props.cityName}</div>
}
}

export default WithLocation<DevTestProp>(DevTest)
  1. 使用状态管理

 要做到更好的复用,但是hoc和分组件当然是不够用啊,首页要用某个api的数据,用户主页也需要用某个api的数据,最好的设计当然是,只需要请求一次接口,然后将数据保存在本地,其它页面就那本地存储即可。
 用什么呢,localStorage sessionStorage,indexDB,web SQL?不行,单纯只是为了本地存储无法做出有效的管理,阅读代码的时候永远不知道这个storage从哪里来,为了什么,什么时候该清除(我是谁,我从哪里来,我要去哪里既视感)。
 状态管理就是为了解决这些个问题。维护一颗状态树,所有需要复用的数据存在状态树中,随用随取,用的数据不同时,搞一个子树作为存储,提供相应的更新的函数即可。妈的简直不要太好用好吗。
 当然有同事说,之前碰到过状态树过大,不好维护,这里建议树太大的时候拆模块呢,直接将各种子树拆成子模块,在加载的时候提供获取基础数据的方式(可以是api,可以是本地存储,办法总比困难多)
 公司所有项目有Dva的依赖,但是真的用的地方少的可怜….真的强烈建议还是用一下。