重构的艺术

重构的艺术🤪

强烈建议大家去看一下: 《重构:改善代码的既有设计》这本书,超级好看,计算机圣经。🥳

前半部分讲解为什么要进行重构什么时候进行重构以及重构的好处,超级有意思;后半部分就是纯纯工具书,不知道咋重构的时候翻一翻就行💩

里面有句话我现在都记得:傻瓜都能写出计算机能看懂的代码,而能写出人能够看懂的代码的才叫优秀的程序员

一、为什么需要重构🤡

其实在计算机早期发展阶段是没有重构的概念的,因为那个时候程序体量很小,所以推崇在开发之前就规划好一整个程序

但是随着程序体量越来越大,即使是最优秀的大牛也不能在开发之前就把所有情况都考虑进去。👾

所以只能一次又一次地给原来的程序贴补丁,然而这种面对bug贴补丁的治标不治本的方式只会使补丁越来越多,程序越来越臃肿,越来越难以维护🫨

终于,到了一点都写不下去的时候,重构的概念就诞生了,并且迅速获得了大家的青睐,并被认为是一种可以极大提高开发效率的方式。

在大家的miniproject中肯定也会遇到相似的问题:产品要求越来越多😵,一些原来根本没考虑的用法的产生,会导致代码逻辑越来越混乱,越写越自闭(去年我就在是这样的🤡🤡🤡)。

这个时候,别害怕,捋清思路,果断重构!!!

但是,重构的意义不止于此:随着技术的迅速发展,老一代技术渐渐被取代,这个时候,用新技术重构老项目就显得格外重要

就比如用react重构之前jQuery的项目🤪

因此,小小总结一下,重构就是因为:

  • 之前写的烂,改不了一点或者改的很耗时很费精力
  • 项目要与时俱进,换上新技术,提高效能

二、要怎么重构🤕

《重构》的作者称这个为发现代码里的坏味道💩

首先,肯定是要捋清逻辑混乱产生的原因,分析过后,把逻辑重写,重新组织函数、组件的关系

  • 一些逻辑混乱来源于不合理的组件拆分,不要想着让一个组件适应所有的情况,这样只会让这一个组件越来越难懂复杂

    一个组件只干一件事情😃😃😃

    就比如button组件,一开始的功能就是按下有反应。但是现在发现又需要一个可以有弹窗的button组件。

    这时候一般的想法就是做个判断,给原来的组件上加一个hasModal,有这个属性就有弹窗,也不是不行嗷🤣。

    确实是这样,稍微加一点prop会让我们的组件功能更强大,但是过多的props就挺让人头大了

    就比如再给这个button加上跳转页面、下载文件、上传文件等等的功能,这个时候浅浅看一下有多少个props呢? hasModal,navigatePath,download,upload

    这个时候我就已经相当烦了,但是情况可能更超乎意料,可能还有button在页面上存在3秒之后页面就要开始抖动这种逆天的功能呢。你永远想象不到你的老板有什么鬼马点子😏

    所以,这个时候完全可以把这些个button拆开,正常的button用一个组件,有特殊功能的,就单独再写一个,避免别人要这个组件的时候点开看到1000行的props提示发呆。👾

  • 一些逻辑问题出现在组件之间耦合度太高,改动一个小部分要修改大半个页面的代码,这种是真的痛苦🤗

    还是拿上边的button做例子,hasModal,navigatePath,download,upload这些功能,完全可以改成一个onClick回调函数😏,之后要用的时候把要执行的任务作为函数传进来,这样button就只用单独处理自身onClick的逻辑就行啦,复用性自然也就提高了

    一个好的组件写完之后用800年也不需要改一次代码

  • 还有一些出在乱七八糟的逻辑条件判断

    if嵌800行,图灵来了都看不懂,再来点循环,上帝都看不懂🦢

    好好review自己写的💩💩💩,把类似什么

    1
    const True = flag ? 1 : 0

    这种脱裤子放屁的东西都化简了(有时候ide都看不下去会给你标黄🤣);再把if这种语句尽量改成三目运算符;把switch这种语句换成多态类

    提高点可读性,对你和你的partner都好🤯🤯🤯

其次就是重新组织文件结构

  • 文件结构应该层次分明,逻辑清晰,以下是husky🐶推荐的目录结构

    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
    ├── .husky              # husky的配置文件
    ├── dist # 打包后的代码和资源文件
    ├── node_modules # 依赖库和模块
    ├── src
    │ ├── assets # 静态资源
    │ ├── components # 公共组件
    │ ├── pages # 页面
    │ ├── type # 全局类型声明
    │ ├── utils # 工具函数
    │ ├── App.tsx
    │ ├── fetch.ts # 网络请求
    │ ├── main.tsx
    │ ├── router.tsx # 路由配置信息
    │ └── vite-env.d.ts
    ├── .commitlintrc.cjs # commitlint的配置文件
    ├── .eslintignore
    ├── .eslintrc.cjs # ESLint的配置文件
    ├── .gitignore
    ├── .prettierignore
    ├── .prettierrc.cjs # Prettier的配置文件
    ├── index.html
    ├── package.json
    ├── README.md
    ├── tsconfig.json
    ├── tsconfig.node.json
    ├── vite.config.ts
    └── yarn.lock

    其中,每个pagecomponent也都可以有自己独有的component,最外层的component只放不同页面不同组件均要用到的组件

然后就是一个被很多人忽略的点:换个好的名字

  • 有时候,换一个人类能看懂的名字真的能帮上大忙😐。

    不要怕名字太长什么的,又不是你自己写,ide都没抱怨你急什么?

    就比如说(真实案例哈😕):

    你看到一个组件,叫Ritem,你能看出来它是干啥的吗?

    但是,如果改个名字,叫RescueInfoItem,是不是就清晰多了

    顺带提一句,前几天我起出了这辈子来最长的名字: RescueInfoSortedByTargetIDSlice😶‍🌫️

四、重构的步骤

光说说可能没什么感觉,接下来,我将会使用我最近重构的一段代码(js->ts)来给大家展示重构的魅力👽,用例子的方式给大家讲解一下重构的步骤(这段代码的来源保密🤪)

msgItem.jsx (这是我们要重构的组件)

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import Taro, {useReady} from '@tarojs/taro'
import { View ,Image} from '@tarojs/components'
import { useEffect, useState } from 'react'
import './index.less'
import Fetch from '../../Service/fetch'



const MsgItem=(props)=>{

const [flag,setFlag]=useState('');
const [finish,setFinish]=useState('已救援')
// const [jiean,setJiean]=useState('')
const [color,setColor]=useState(0)//0:#f57b70 1:#76A9FF 2:#f9a53e
// const [status,setStatus]=useState(0)
const [text,setText]=useState('')
const [area,setArea]=useState('')
const [level,setLevel]=useState('')
const [data,setData]=useState([])
const [ifmine,setIfmine] = useState(false)

const param=props

const status = [
'未救援',
'救援中',
'已救援'
]
useEffect(()=>{
/* console.log(param.flag) */
// setStatus(param.status)//主页item

if(param.flag=='main') //主页
{
setFlag('alarm')
if(param.ifmine)
{
setIfmine(true)
setFinish('救援中')
setColor(1)
}
else{
// 未认领的救援
setIfmine(false)
Fetch(
'/rescue/targetinfo',
{
"rescue_target_id": param.rescue_target_id,
},
'POST'
).then(
res=>{
//console.log(res)
setFinish(status[res.data.rescue_target_info.status])
setColor(res.data.rescue_target_info.status)
}
)
}}
else{
//record
setFlag('record')
setFinish(status[param.status])
setColor(param.status)
}
setText(param.text)
setArea(param.area)
setLevel(param.risk_level)
setData(param.data)
},[param])

function toR_detail()
{
console.log(flag)
if(flag=='alarm')
Taro.navigateTo({
url:`/moduleA/pages/Alarm/index?ifmine=${ifmine}&data=${JSON.stringify(data)}&finish=${finish}&color=${color}`
})
else{
console.log('todetail')
const jiean = param.status==1?'未结案':'已结案'
Taro.navigateTo({
url:`/moduleA/pages/Rescue_detail/index?finish=${jiean}&data=${JSON.stringify(data)}&teacher=${JSON.stringify(param.teacher)}&lap=${JSON.stringify(param.lap)}`
})
}
}

return(
<View className='noti-item column' onClick={toR_detail} >
<View className='inline'>
<View className='state'style={{backgroundColor:color==2?'#76A9FF':color==1?'#f9a53e':'#f57b70'}}>{finish}</View>
<View className='rescued_level'>
风险等级:{level}
</View>
</View>
<View>
<View className='rescued_info'>
<View className='row'><View className='border_l'></View>微博内容:</View>
<View className='demand'>{text}</View>
</View>
<View className='rescued_city'>
地点:{area}
</View>
</View>
{/* <View className='require'>主要诉求:<br />感情受挫,家庭不和,和亲人有隔阂</View> */}
</View>
)

}

export default MsgItem;

这是一段超级长的代码,有110行,一般我们组件超过70行就已经不是很优雅了(反正我看到超过70行的组件就头大🥺),因此,重构就应该提上日程

那问题来了,该如何下手呢?🤨

  1. 要重构一个组件,首先要清除在哪里会被用到,我这里找了它的几个用法:

    1
    2
    3
    4
    5
    6
    7
    // R_record.jsx 
    // 这里Ritem就是msgItem
    {list?list.map((item)=>{
    return (
    <Ritem {...item} data={item} teacher={teacher} status={status} lap={lap} key='rescue' flag='record' />
    )
    }):<View className='img'>
    1
    2
    3
    4
    5
    6
    // Main.jsx
    {mine?mine.map((item)=>{
    return (
    <MsgItem {...item} data={item} ifmine key='item' flag='main' />
    )
    })
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //Search.jsx 
    // 这段是之前我写的,一坨💩
    {form &&
    form.map((item) => <MsgItem
    data={item}
    {...item}
    ifmine={form.some(itm => JSON.stringify(item) === JSON.stringify(itm))}
    key='item'
    flag='main'
    ></MsgItem>)}

    在真正重构的过程中,一定要理解这些个组件有什么用,是干啥的,但是现在这个例子中,先不用管,因为这个东西有点子复杂的,我自己都看半天😁,只知道它接受啥参数就可以了。

    观察不难发现

    • 参数中有个flagkey,这俩名字一般是用来区分组件行为的,所以这个组件很可能会有两三个不同的功能

    • {...item}data={item}这俩玩意传的一摸一样,也可以删去

    • 还有一些teacher ,status等可传可不传的props,这些说明组件功能可能有较大差异,可能之后要把它分为多个组件;或者把参数取消,传回调函数,完全受控组件降低耦合度

    分析完之后,发现任务很艰巨啊,开摆得了(bushi)

  2. 之后,面对这样艰巨的任务,我一般是先挑软柿子捏,啥事软柿子嘞?

    就是名称目录结构这些改了无伤大雅但是看着会很舒服的东西🐶

    《重构》的作者在书中说: 如果有一个人在重构的过程中有一段时间代码无法运行,那他肯定不在重构

    因此,咱们重构要一步一步来。

    名称目录结构这些“皮毛”的东西开始,保证能正常运行的情况下,一点点慢慢改😝

    这里目录结构没啥问题,改个名字就行了,我给它起了个响当当的大名: RerscueInfoItem,之后都用RescueInfoItem代替MsgItem

    之后扫一眼代码,发现

    1
    const jiean = param.status==1?'未结案':'已结案'
    1
    backgroundColor:color==2?'#76A9FF':color==1?'#f9a53e':'#f57b70'
    1
    2
    3
    4
    5
    6
    const status = [
    '未救援',
    '救援中',
    '已救援'
    ]
    setFinish(status[res.data.rescue_target_info.status])

    这种东西,完全可以把它们搞成一个Map,再拿到外边的typeconfig文件里面嘛

    新开个文件咯,就叫rescueMessageItemTypes

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // rescueMessageItemTypes.ts
    export type rescueStatusType = {
    text: string
    color: string
    }
    export const rescueStatus: rescueStatusType[] = [
    {
    text: '未救援',
    color: '#f57b70',
    },
    {
    text: '救援中',
    color: '#f9a53e',
    },
    {
    text: '已救援',
    color: '#76A9FF',
    },
    ]

    结案这种一下子就搞定而且非常易懂的事情,就不单独拎出来做变量了,改完之后return里就变成这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <View className="inline">
    <View
    className="state"
    style={{
    backgroundColor:
    rescueStatus[targetInfo ? targetInfo.status : 0].color,
    }}
    >
    {rescueStatus[targetInfo ? targetInfo.status : 0].text}
    </View>
    <View className="rescued_level">风险等级:{props.risk_level}</View>
    </View>
    <View>
    <View className="rescued_info">
    <View className="row">
    <View className="border_l"></View>微博内容:
    </View>
    <View className="demand">{props.text}</View>
    </View>
    <View className="rescued_city">地点:{props.area}</View>
    </View>
  3. 还是聚焦于表面功夫,作为props传进的参数一堆,而且要state数量也不少,影响性能不说,反正是不好看

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const [flag,setFlag]=useState('');
    const [finish,setFinish]=useState('已救援')
    // const [jiean,setJiean]=useState('')
    const [color,setColor]=useState(0)//0:#f57b70 1:#76A9FF 2:#f9a53e
    // const [status,setStatus]=useState(0)
    const [text,setText]=useState('')
    const [area,setArea]=useState('')
    const [level,setLevel]=useState('')
    const [data,setData]=useState([])
    const [ifmine,setIfmine] = useState(false)

    但是这里很多state只是跟props绑定,而props不会经常改变,因此完全可以去掉这些state,直接使用props,优化性能。

    注意到跳转时传参很多,完全可以采用redux的方式,优化逻辑,把相似的变量和参数都提到外部

    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
    // rescueInfoSlice.ts

    import { createSlice, PayloadAction } from '@reduxjs/toolkit'
    import {
    SingleRescueInfo,
    RescueTargetInfoType,
    RescueProcess,
    } from '../Service/fetchTypes'
    import { rescueInfoSliceType } from './sliceTypes'

    const rescueInfoSlice = createSlice({
    name: 'rescueInfo',
    initialState: {
    rescueInfo: {} as SingleRescueInfo,
    targetInfo: {} as RescueTargetInfoType,
    process: [],
    eventID: 0,
    targetID: 0,
    } as rescueInfoSliceType,
    reducers: {
    updateRescueInfo: (
    state: rescueInfoSliceType,
    action: PayloadAction<SingleRescueInfo>,
    ) => ({
    ...state,
    rescueInfo: action.payload,
    eventID: action.payload.id,
    targetID: action.payload.rescue_target_id,
    }),
    updateTargetInfo: (
    state: rescueInfoSliceType,
    action: PayloadAction<RescueTargetInfoType>,
    ) => ({ ...state, targetInfo: action.payload }),
    updateProcess: (
    state: rescueInfoSliceType,
    action: PayloadAction<RescueProcess[]>,
    ) => ({ ...state, process: action.payload }),
    },
    })

    export default rescueInfoSlice.reducer
    export const { updateTargetInfo, updateRescueInfo, updateProcess } =
    rescueInfoSlice.actions

    这里redux不理解不要紧,主要是理解抽出state的这个思路,减少传参,提升可维护性😊

    现在,只需要一个变量:使用变量只需要从store中取出即可,也不需要另外传参给其余页面。

    同时,注意到useEffect中的调用:

    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
    useEffect(()=>{
    /* console.log(param.flag) */
    // setStatus(param.status)//主页item

    if(param.flag=='main') //主页
    {
    setFlag('alarm')
    if(param.ifmine)
    {
    setIfmine(true)
    setFinish('救援中')
    setColor(1)
    }
    else{
    // 未认领的救援
    setIfmine(false)
    Fetch(
    '/rescue/targetinfo',
    {
    "rescue_target_id": param.rescue_target_id,
    },
    'POST'
    ).then(
    res=>{
    //console.log(res)
    setFinish(status[res.data.rescue_target_info.status])
    setColor(res.data.rescue_target_info.status)
    }
    )
    }}
    else{
    //record
    setFlag('record')
    setFinish(status[param.status])
    setColor(param.status)
    }
    setText(param.text)
    setArea(param.area)
    setLevel(param.risk_level)
    setData(param.data)
    },[param])

    这些setState都可以省去,但是,很明显,这里逻辑很复杂, 需要重新组织一下逻辑

    这个时候,尘封已久的大脑就要开始转动了:

    可以看到,主要是flag这个值是逻辑复杂的开始,根据注释可以知道,写的人当时也很挣扎🐶。

    这可能是因为后端接口的问题,导致主页面上有未认领的任务,需要重新请求才能得到信息。

    然而猛然一看,看到这段,

    1
    2
    3
    4
    5
    6
    if(param.ifmine)
    {
    setIfmine(true)
    setFinish('救援中')
    setColor(1)
    }

    也没在干什么,所以就索性一视同仁,都发个请求呗

  4. 这个时候,我是喜欢把请求这些东西放到外边去的,搞个service文件夹咯

    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
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    // /service/fetchType.ts
    export type BaseResType<T> = {
    code: number
    msg: string
    data: T
    }
    export type requestType = 'GET' | 'POST' | 'PUT' | 'DELETE'

    export interface RescueTargetInfoResponse {
    rescue_target_info: RescueTargetInfoType
    }

    // 这段直接从apifox上copy就行
    export interface RescueTargetInfoType {
    create_time: string
    /**
    * 救援过程描述
    */
    description: string
    /**
    * 救援结束时间
    */
    end_time: string
    /**
    * 最终评价
    */
    evalutaion: string
    /**
    * 救援对象id
    */
    id: number
    /**
    * 救援对象昵称(按最新的救援信息数据)
    */
    nickname: string
    /**
    * 救援老师1id
    */
    rescue_teacher1_id: number
    /**
    * 救援老师1姓名
    */
    rescue_teacher1_name: string
    /**
    * 救援老师1身份
    */
    rescue_teacher1_role: number
    /**
    * 救援老师2id
    */
    rescue_teacher2_id: number
    /**
    * 救援老师2姓名
    */
    rescue_teacher2_name: string
    /**
    * 救援老师2身份
    */
    rescue_teacher2_role: number
    /**
    * 救援老师3id
    */
    rescue_teacher3_id: number
    /**
    * 救援老师3姓名
    */
    rescue_teacher3_name: string
    /**
    * 救援老师3身份
    */
    rescue_teacher3_role: number
    /**
    * 救援起始时间
    */
    start_time: string
    /**
    * 救援状态(0-待救援 1-救援中 2-已救援)
    */
    status: number
    update_time: string
    /**
    * 救援对象微博地址(唯一标识)
    */
    weibo_address: string
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // /service/rescueInfoByID.ts
    export const FetchRescueTargetInfo = async (targetID: number) => {
    const data = {
    rescue_target_id: targetID,
    }
    return Fetch<BaseResType<RescueTargetInfoResponse>>(
    '/rescue/targetinfo',
    data,
    'POST',
    )
    }

    根据它传进来的props,来请求target_info,并且变成state放起来

    这个时候,文件已经开始变得清爽了:

    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
    const MsgItem=(props)=>{
    const { navURL, ...restProps } = props
    const [targetInfo, setTargetInfo] = useState<RescueTargetInfoType>()
    useEffect(() => {
    FetchRescueTargetInfo(restProps.rescue_target_id).then((res) => {
    setTargetInfo(res.data.rescue_target_info)
    })
    }, [])
    function toR_detail()
    {
    console.log(flag)
    if(flag=='alarm')
    Taro.navigateTo({
    url:`/moduleA/pages/Alarm/index?ifmine=${ifmine}&data=${JSON.stringify(data)}&finish=${finish}&color=${color}`
    })
    else{
    console.log('todetail')
    const jiean = param.status==1?'未结案':'已结案'
    Taro.navigateTo({
    url:`/moduleA/pages/Rescue_detail/index?finish=${jiean}&data=${JSON.stringify(data)}&teacher=${JSON.stringify(param.teacher)}&lap=${JSON.stringify(param.lap)}`
    })
    }
    }

    return(
    <View className='noti-item column' onClick={toR_detail} >
    <View className="inline">
    <View
    className="state"
    style={{
    backgroundColor:
    rescueStatus[targetInfo ? targetInfo.status : 0].color,
    }}
    >
    {rescueStatus[targetInfo ? targetInfo.status : 0].text}
    </View>
    <View className="rescued_level">风险等级:{props.risk_level}</View>
    </View>
    <View>
    <View className="rescued_info">
    <View className="row">
    <View className="border_l"></View>微博内容:
    </View>
    <View className="demand">{props.text}</View>
    </View>
    <View className="rescued_city">地点:{props.area}</View>
    </View>
    </View>
    )

    }
  5. 接下来,就剩一个toR_detail函数要改咯,也是撒撒水啦,因为刚刚我们把state都放到外边去了,这里也不用通过传参来让其他页面访问咯,超级爽,略微改改,就这样咯,其中Nav是我封装的跳转函数,因为不想写那么多Taro.navigateTo({url:"......"})

    1
    2
    3
    4
    5
    6
    function Navi() {
    const navURL_real = navURL || '/moduleA/pages/Alarm/index'
    store.dispatch(updateRescueInfo(restProps))
    targetInfo && store.dispatch(updateTargetInfo(targetInfo))
    Nav(navURL_real)
    }
  6. 这下就大功告成了,吗?还记得一开始的那些组件传的props吗?也该清清啦!,像什么flag,ifmine,都可以扔掉啦,只留下基本的msg信息就行

    1
    2
    3
    // R_record.jsx 
    // 这里Ritem就是msgItem
    {list?list.map((item)=><Ritem {...item} />):<View className='img'>
    1
    2
    // Main.jsx
    {mine?mine.map((item)=><MsgItem {...item}/>)
    1
    2
    3
    //Search.jsx 
    // 这段是之前我写的,一坨💩
    {form && form.map((item) => <MsgItem {...item} />)}
  7. 现在回头看看我们重构后的组件的全貌吧:

    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
    import { View } from '@tarojs/components'
    import React, { useEffect, useState } from 'react'
    import './index.less'
    import {
    RescueMessageItemProps,
    rescueStatus,
    } from '../types/rescueMessageItemTypes'
    import { Nav } from '../../utils/taroFunctions'
    import { FetchRescueTargetInfo } from '../../Service/rescueInfoByID'
    import { RescueTargetInfoType } from '../../Service/fetchTypes'
    import {
    updateRescueInfo,
    updateTargetInfo,
    } from '../../slices/rescueInfoSlice'
    import store from '../../store/store'

    const RescueMessageItem: React.FC<RescueMessageItemProps> = (props) => {
    const { navURL, ...restProps } = props
    const [targetInfo, setTargetInfo] = useState<RescueTargetInfoType>()
    useEffect(() => {
    FetchRescueTargetInfo(restProps.rescue_target_id).then((res) => {
    setTargetInfo(res.data.rescue_target_info)
    })
    }, [])
    function Navi() {
    const navURL_real = navURL || '/moduleA/pages/Alarm/index'
    store.dispatch(updateRescueInfo(restProps))
    targetInfo && store.dispatch(updateTargetInfo(targetInfo))
    Nav(navURL_real)
    }

    return (
    <View className="noti-item column" onClick={Navi}>
    <View className="inline">
    <View
    className="state"
    style={{
    backgroundColor:
    rescueStatus[targetInfo ? targetInfo.status : 0].color,
    }}
    >
    {rescueStatus[targetInfo ? targetInfo.status : 0].text}
    </View>
    <View className="rescued_level">风险等级:{props.risk_level}</View>
    </View>
    <View>
    <View className="rescued_info">
    <View className="row">
    <View className="border_l"></View>微博内容:
    </View>
    <View className="demand">{props.text}</View>
    </View>
    <View className="rescued_city">地点:{props.area}</View>
    </View>
    </View>
    )
    }

    export default RescueMessageItem

    一共是59行,是不是特别的一目了然,看到之后,心情都变好了,不枉我重构这么久🤗

五、总结⬇️

《重构》中,作者提到了营地法则,即要保证在你来之后,代码变得更好而不是更糟糕,而保证代码变得更好的秘诀,就是重构。这次通过一个小例子,来让大家知道把时间花在重构上并不可怕,经常重构并不见得是一件坏事,相反的,经常重构代表着你在积极思考,努力让代码变得更优秀;而不重构,只能是给未来的自己和同伴添堵咯。当然,过早过频繁的重构也是不提倡的,就比如:本来这个按钮只用负责跳转,你写着写着,突然想到以后要是要让他能点一下出来一个ggbond怎么办,你库库开始重构,重构完之后,一天就过去了。然而产品老板到最后也没有点一下出来一个ggbond的需求(虽然这真的很酷😝)。那这一天就是白费的。总之,重构就是一门艺术,过多过少都不提倡,要根据项目要求,灵活重构。不多说了,这些写多了自然就有感觉啦🤗


重构的艺术
http://baidu.com/2024/03/06/refactor/
作者
KB
发布于
2024年3月6日
许可协议