[重构]

所谓重构(refactoring)是这样一个过程:代码写好之后,在不改变代码外在行为的前提下,对代码做出修改,以改进程序的内部结构, 最大限度地减小整理过程中引入错误的概率。

[什么时候重构]

重构可以一边写代码一边重构,也可以在程序写完后,拿出一段时间专门去做重构。没有说哪个方式更好,视个人情况而定。

如果你专门拿一段时间来做重构,建议你在重构一段代码后,立即进行测试。这样可以避免修改代码太多,在出错时找不到错误点。

[重构Vs性能优化]

相同的地方

是它们都在不改变程序功能的情况下修改代码;

不同的地方

  • 重构为了让代码变得更加易读、理解,
  • 性能优化则是为了让程序运行得更快。

[面向对象Solid原则]

S - 单一职责原则

Single Responsibllity Principle - 即 > SRP 一个类只能承担一个职责。通俗点儿说就是一个类只能承担一件事,并且只能有一个潜在的原因去更改这个类,否则就违反了单一职责原则。
说一说代码简洁之道 - 图1

O - 开闭原则

Open/Closed Principle - 即 > OCP 软件实体应该对 扩展 开放,对 修改 关闭。允许扩展行为而无需修改源代码。
说一说代码简洁之道 - 图2

L - 里氏替换原则

Liskov Substitution Principle - 即 > LSP 程序中的对象应该可以被其子类实例替换掉,而不会影响程序的正确性。
说一说代码简洁之道 - 图3

I - 接口隔离原则

Interface Segregation Principle - 即 > ISP 使用多个特定细分的接口比单一的总接口要好,不能强迫用户去依赖他们用不到的接口。
说一说代码简洁之道 - 图4

D - 依赖倒置原则

Dependency Inversion Principle - > DIP 程序要依赖于抽象接口,而不是具体实现。

  • 高层模块不应该依赖于低层模块,二者都应该依赖于抽象
  • 抽象不应该依赖具体实现,具体实现应该依赖抽象

说一说代码简洁之道 - 图5

[前端开发应该遵循什么原则]

首先,如果我们使用面向对象这种编程方式,我们要注意,他不只是定义一个 Class 那么简单的事情,我们知道面向对象有三大特性,**继承****封装****多态**

首先前端真的适合继承的方式吗?准确的说,UI 真的适合继承的方式吗?

在真实世界里,抽象的东西更适合定义成一个类,类本来的意思就是分类和类别,正如我们把老虎,猫,狮子这些生物统称为动物,所以我们就可以定义一个动物的类,但是真实世界并没有动物这种实体

但是页面 UI 都是真实存在可以看到的东西,我们可以把一个页面分成不同的区块,然后区块之间采用的是「组合」的方式。

因此我认为 UI 组件不适合继承,更应该组合_。如果你写过继承类的组件,你将很难去重构,甚至是重写他。

[组合方式的前端开发]

image.png

现有的前端开发框架,主流的Angular,Vue,React都是基于Mv*模式,最小粒度都是组件级别,组件Component是第一公民
可以看到React官网对React哲学的说明 - “如何确定应该将哪些部分划分到一个组件中呢?你可以将组件当作一种函数或者是对象来考虑,根据单一功能原则来判定组件的范围。也就是说,一个组件原则上只能负责一个功能。如果它需要负责更多的功能,这时候就应该考虑将它拆分成更小的组件。”

[1] 将设计稿转成UI组件

[组件]

UI = render(props,state)

[基于组件的页面视图的分离拼接组合]

说一说代码简洁之道 - 图7

[2] 基于数据流的组件划分

函数组件 Vs 类组件 [ Functional Component Vs Class Component]

说一说代码简洁之道 - 图8

(1)限制不同
  • 函数组件更简洁,代码量少,用起来比较”轻”,这种写法有重大限制,必须是纯函数,不能包含状态,也不支持生命周期方法,因此无法取代类(ps:这里说的是没有引入hook之前的函数组件)
  • 而类比较”重”,并且类组件有很多现实的缺点:
    • 大型组件很难拆分和重构,也很难测试。
    • 业务逻辑分散在组件的各个方法之中,导致重复逻辑或关联逻辑。
    • 组件类引入了复杂的编程模式,比如 render props 和高阶组件。

而且函数更符合 React 函数式的本质。

(2)编程理念不同

对于 Class Component 和 Function Component 之争由来已久。从我自身的实践来看,我觉得这是两种不同的编程思路。

Class Component 面向对象编程 继承 生命周期
Function Component 函数式编程 组合 数据驱动

(3) 代码组织形式不同

稍微复杂点的项目肯定是充斥着大量的 React 生命周期函数(注意,即使你使用了状态管理库也避免不了这个),每个生命周期里几乎都承担着某个业务逻辑的一部分,或者说某个业务逻辑是分散在各个生命周期里的,业内一直雅称为面向生命周期编程

借用vue2 option APi的动图说明一下意思,React类似的问题

说一说代码简洁之道 - 图9

[基本原则]

[1] 前端需不需要设计?

  • 入参是什么?出参是什么?
    • 无Api文档,手动在组件里面维护,使用者需要去源码找 ```javascript export default { name:’MyComponent’, props:{ age:{ required:false, default:0 }, username:{ required:true, default:’unclepis’ } }, template:{ render(){ return
      {{username}}
      } } }

  1. - 洋气一点,vuePress起一个组件的api文档
  2. ![image.png](https://cdn.nlark.com/yuque/0/2020/png/103100/1606447429266-d08d2075-2969-4f81-94a3-2a32f71453ca.png#align=left&display=inline&height=241&margin=%5Bobject%20Object%5D&name=image.png&originHeight=781&originWidth=1735&size=78423&status=done&style=none&width=535)
  3. - 面向开发,引入ts类型系统
  4. ```javascript
  5. export Interface UserInfoProps{
  6. username:string
  7. age?:number
  8. }
  9. const MyComponent: React.FC<UserInfoProps> = (props) => {
  10. //
  11. }

好处也很明显:

  • 易于理解的整个应用程序 UI 层次结构
  • 页面相同功能在设计阶段统一开发,避免重复开发;
  • Api清晰,对使用者友好
  • 组件功能职责数据``结构``层次及其数据流向清晰明了

    [结论1组件需要层次结构设计]

说一说代码简洁之道 - 图10

[2] 高内聚低耦合

组件的核心思想是它们是可复用的,为此要求它们必须具有功能性和完整性。松散耦合的实体应该能够独立运行,而不依赖于其他模块。

就前端组件而言,耦合的主要部分是组件的功能依赖于其父级及其传递的 props 的多少,以及内部使用的子组件(当然还有引用的部分,如第三方模块或用户脚本)。
如果不是要设计需要服务于特定的一次性场景的组件,那么设计组件的最终目标是让它与父组件松散耦合,呈现更好的复用性,而不是受限于特定的上下文环境。

【okr项目的痛】

image.png
image.png
image.png

【存在的问题】

业务代码耦合很高,都维护在组件内部,逻辑混乱很难重构

  1. <Okr-Panel-contaier type="xxx">
  2. if(type==='okr'){<div>okr</div>}
  3. if(type==='okrTemplate'){<div>okrTemplate</div>}
  4. if(type==='okrComment'){<div>okrComment</div>}
  5. </Okr-Panel-contaier>

[结论2组件要满足开放关闭原则] - 对扩展开放,对修改封闭

  1. // 借助插槽slot,我们把耦合的代码放到组件外面
  2. <Okr-Panel-contaier type="xxx">
  3. <slot name="okr"></slot>
  4. <slot name="okrTemplate"></slot>
  5. <slot name="okrComment"></slot>
  6. </Okr-Panel-contaier>

[3] 代码分离

因为在尝试处理组件的核心代码时,你不希望看到与技术无关的一些说明(因为会多滚动几下鼠标滚轮甚至打断思路)。在处理组件时,你希望它们尽可能通用且可重用。查看与组件当前上下文相关的特定信息可能会使得设计出来的组件不易与具体业务解耦。

一个有效的原则就是将辅助代码分离出来放在特定的地方,这样你在处理组件时就不必考虑这些。

Angularjs中

image.png
MyComponent

  • config.js
  • CONST_ACTION.js
  • service.js处理业务数据
  • controller中维护视图需要维护的状态
  • filter过滤器
  • directive指令 ```javascript app.controller(“myNoteCtrl”, function($scope,myFilter,myService,myDirective,$http,myConstant) {

});

  1. <a name="uLyou"></a>
  2. ###### Nodejs -koa2
  3. ![image.png](https://cdn.nlark.com/yuque/0/2020/png/103100/1606456512641-a5f4c05d-77bf-4e4d-b265-e72eda09fd9e.png#align=left&display=inline&height=359&margin=%5Bobject%20Object%5D&name=image.png&originHeight=718&originWidth=823&size=28591&status=done&style=none&width=412)<br />www- 快速大家一个可以处理http的服务器<br />route- 处理客户端浏览器过来的路由<br />controller - 根据route中对url的拦截处理不同的业务<br />service - 操作orm处理数据<br />db - 处理数据库和orm的映射,链接数据库,创建数据连接池等<br />![](https://www.yuque.com/api/filetransfer/images?url=https%3A%2F%2Funclepis.github.io%2F2020%2F03%2F05%2Fkoa2-9layout%2Fkoa2.png&sign=5a8e67634aeea48500eb3ea1fee2334491ed62643e77a26b308866134ee2f06b#align=left&display=inline&height=862&margin=%5Bobject%20Object%5D&originHeight=862&originWidth=1901&status=done&style=none&width=1901)
  4. <a name="cWv5B"></a>
  5. #### [结论3代码逻辑要做分层设计抽离]
  6. ```javascript
  7. <template>
  8. <div v-for="user in userList">
  9. <span>{{user.name}}</span>
  10. </div>
  11. <Select>
  12. // 1
  13. <Option value='male'></Option>
  14. <Option value='female'></Option>
  15. // 2
  16. <Option v-for="user in userList" :value="genderValue">{{genderText}}</Option>
  17. </Select>
  18. </template>
  19. import filterRetireUser from './service'
  20. import GENDER_LIST from './constant'
  21. export default {
  22. data(){
  23. return{
  24. userList:[]
  25. }
  26. },
  27. async fetchData(){
  28. const res = fetchUserList()
  29. if(res.success){
  30. // 1
  31. this.userList = res.data
  32. // 2
  33. this.userList = res.data.filter(user=>{
  34. return !user.isRetire
  35. })
  36. //3
  37. this.userList = filterRetireUser(res.data)
  38. }
  39. },
  40. created(){
  41. this.fetchData()
  42. }
  43. }

提取重复代码,封装成函数/拆分太长或功能太多的函数
  1. function register(data) {
  2. // 1. 验证用户数据是否合法
  3. /**
  4. * 验证账号
  5. * 验证密码
  6. * 验证短信验证码
  7. * 验证身份证
  8. * 验证邮箱
  9. */
  10. // 2. 如果用户上传了头像,则将用户头像转成 base64 码保存
  11. /**
  12. * 新建 FileReader 对象
  13. * 将图片转换成 base64 码
  14. */
  15. // 3. 调用注册接口
  16. // ...
  17. }
  18. function register(data) {
  19. // 1. 验证用户数据是否合法
  20. // verify()
  21. // 2. 如果用户上传了头像,则将用户头像转成 base64 码保存
  22. // tobase64()
  23. // 3. 调用注册接口
  24. // ...
  25. }