mengtuifrontend / Blog

芦叶满汀洲,寒沙带浅流。二十年重过南楼。柳下系船犹未稳,能几日,又中秋。 黄鹤断矶头,故人今在否?旧江山浑是新愁。欲买桂花同载酒,终不似,少年游。
18 stars 5 forks source link

代码规范的经验借鉴总结 #33

Open songxuecc opened 4 years ago

songxuecc commented 4 years ago

思考

迎接新技术的思考

脚手架和项目模板 工具

编码规范

逻辑处理

更健壮的代码

airbnb 摘要

  1. Higher-order Component Naming

    export default function withFoo(WrappedComponent) {
    function WithFoo(props) {
    return <WrappedComponent {...props} foo />;
    };
    
    const wrappedComponentName = WrappedComponent.displayName
    || WrappedComponent.name
    || 'Component';
    
    WithFoo.displayName = `withFoo(${wrappedComponentName})`;
    return WithFoo; 
    }
  2. Props

    • Always use for prop names
    • Omit the value of the prop when it is explicitly true
    • Avoid using an array index as key prop, prefer a unique ID
    • Always define explicit defaultProps for all non-required props
  3. Ordering - class 组建书写顺序

class 的 demo: (hooks 的 demo就不写了 都差不多 )

/*
 * @Author: your name
 * @ModuleName: 2020-01-09 11:26:03
 * @Date: 2020-01-09 11:26:03
 * @Last Modified by: name
 * @Last Modified time: 2020-01-09 11:26:03
 */
import React from "react";
import PropTypes from "prop-types";

const propTypes = {
  id: PropTypes.number.isRequired,
  url: PropTypes.string.isRequired,
  text: PropTypes.string
};

const defaultProps = {
  text: "Hello World"
};
class Link extends React.PureComponent {
  static methodsAreOk() {
    return true;
  }

  componentWillMount() {}

  // 数据请求一般写在这里
  componentDidMount() {}

  // 更改状态
  toggleVisible() {}
  // 监听的events用on开头
  onChange = () => {};
  // 事件回掉类型的event用handle 开头
  handleClick = () => {};

  getFooterContent = () => {
    return this.state.content;
  };

  renderNavigation = text => {
    return <div>{text}</div>;
  };
  render() {
    return (
      <a href={this.props.url} data-id={this.props.id} show>
        {this.props.text}
      </a>
    )
  }
}
Link.propTypes = propTypes;
Link.defaultProps = defaultProps;

export default Link;

总结

编码构建设计

编码
      预先进行的工作 编码时进行的工作
      名称 注释 代码格式 等 编码约定
      错误处理 可重用代码遵循标准 性能因素思考
质量保证
      代码流程跟踪
      集成测试
      review代码
工具
      版本控制
      重构工具 编辑器 调试器 语法检查器

先对软件构建进行设计。设计就是把需求分析和编码调试联系在一起的活动。好的高层次设计可以提供一个可以稳妥容纳多个低层次设计的结构。

要考虑产品迭代的频率。对于高频率改动或觉得不合理后期会改动的点,我们要考虑逻辑抽象。在抽象的过程中要保证代码的完整性和单一性。争取一块代码只做一件事。让逻辑简单明了。

要预测产品设计后期可能会改动的点。如果不知道可以考虑问产品,只有了解产品的需求设计的目的才能为此次迭代做出更合理的解决方案。写代码的时候为这些点主动留出空位。要将这些预测加入进设计内。

如果不确定的话可以考虑写一下思维导图。在组建设计中可以考虑画uml类图。这里推荐一个chorme扩展程序 Gliffy Diagrams,命名可以使用Code Spell Checker 。在复杂判断处写注释。 代码格式 prettier

例如下面是我准备写的刮刮卡的 粗糙画图 可以看得出来简单明了

理想的设计特征

  1. 最小的复杂度,标准技术 要用标准化的,常用的方法给人熟悉的感觉,将复杂度降到最低

  2. 易于维护 把同事当作自己的听众,要写出别人听起来更能理解的代码。要对于一些逻辑进行隐藏,减少改动所带来的代码量。例如 should,is,can,have等都可以用来命名 bool类型的判断。 handle,toggle,change等都可以用来命名函数,名字一看就是回调。

  3. 松散耦合
          设计时让程序的各个组成部分之间的关联最小,即SOLID原则。通过应用类的接口中的合理抽象、封装性及信息隐藏等原则,设计出相互关联尽可能少的类。减少关联,保持子程序的短小精悍,减少思考的负担,既能写出更让人容易裂解的代码,让人更容易着手,专注在更小的问题上写出更少错误性能更好的代码。
          例如Object1 和 Object2的联系要传入Object3。那么Object2 肯定会对Object3 进行校验。 如果这个时候 Object4想要使用Object2 是不是就要传入整个Object1 传出的 Object3格式的代码呢?这个时候Object2 的代码就紧紧的和Object1耦合在一起。这个时候 可以考虑对bject2 的接口定义固定字段A。把Object1返回的Object3 放入另一块代码中进行数据处理返回Object2需要的数据格式A.这个时候 Object2就可以达到通用了。降低耦合。
          对于耦合度高,过度依赖 入参的代码,代码的健壮性和可维护性肯定的会降低的

  4. 可扩展性 增强系统功能的同时尽量不破坏底层代码。

  5. 可重用性

  6. 可移植性 代码很方便的移植到其他环境中。后期维护可能会舍弃一部分功能保留一部分功能,代码可移植性高就会更方便。

  7. 精简性 每次都要思考 代码的修改在于不能加入东西的时候可以删除什么。越多的代码需要越多的开发测试复审,可能会产生越多的bug。

  8. 层次性 对于需要重构的代码,处理旧代码的兼容和新代码的书写,两个层次的剥离 Hoc实际上就是一种层次的剥离

怎样写出来的代码利于维护

  1. 写代码之前思考下我可以隐藏什么?
          对于多次引用和操作的具名常量,我们可以用字面两代替。例如整数 20 要在代码里引用。这个引用要使用100次。那我们以后万一要改这个引用值是不是就要改100个地方呢?我们可以在刚开始的时候用具名常量 MAX_LENGTH 代替字面量,就只用改变一次了。
          但是如果后期我们要对 MAX_LENGTH 进行 ++ 的操作,同时还想使用非连续的 MAX_LENGTH 或保留旧的 MAX_LENGTH 进行操作,我们要命名一个 PREVIOS_MAX_LENGTH 吗?随着需求的迭代MAX_LENGTH操作越来越多,旧的迭代代码被保留,代码会越来越多,以后改bug根本不知道哪些操作是有用的。后期对 MAX_LENGTH 的 操作越多维护起来就越复杂。
          如果我们刚开始在 MAX_LENGTH 要进行操作更改的时候,意识到 把它放在子程序中 return MAX_LENGTH 。后续的维护就会变得很容易。

  2. 找出容易变化的点,把容易变化点抽离,把可能变化的点隔离

    • 不要使用布尔变量来作为状态变量,使用枚举类型 Enum 。使用枚举可以知道每个状态变量的具体含义。增加新的状态变量都很方便。
    • 使用访问器子程序取代状态变量的直接检查。 大家观看源码的时候会经常发现一个get函数返回的是另一个package的api接口。这样的隔离好处是以后这个包如果迭代,接口不存在,这里就会有预留空间来解决。这种第三方包就属于可能变化的点

简易版本 Enum

 const Color = {
    RED: 0,
    GREEN: 1,
    BLUE: 2
}

Object.freeze(Color);

Color.RED === Color.RED     // true
Color.RED = 4;              // error 不能修改枚举常量

简易实现 Enum

 const createEnum = enumObj => {
  const forEach = (target) =>{
    console.log('forEach')
  }
  const handler = {
    get: function(target, key) {
      switch(key){
        // 增加foreach 方法
        case 'forEach':
          return () => forEach(target)
        // 获取 详细描述
        case 'descs':
            return 'descs'
        // entries
        // valuas
        // keys
          default:
            break;
      }

      if (target[key]) {
        if (
          typeof target[key] === "object" &&
          target[key].hasOwnProperty("value")
        ) {
          return target[key].value;
        } else if (
          typeof target[key] === "object" &&
          !target[key].hasOwnProperty("value")
        ) {
          return key;
        }
        return target[key];
      }
      throw new Error(`No such enumerator: ${key}`);
    }
  };

  return new Proxy(enumObj, handler)
};

const httpMethods = createEnum({
  DELETE: {
    value: "DELETE",
    desc: "删除"
  },
  GET: {
    value: "GET",
    desc: "获取"
  },
  OPTIONS: {
    desc: "获取"
  },
  PATCH: "PATCH",
  POST: "POST",
  PUT: "PUT"
});
console.log(httpMethods.GET); // GET
console.log(httpMethods.OPTIONS); // OPTIONS
console.log(httpMethods.PATCH); // PATCH
httpMethods.forEach() // console.log('forEach') 

使用访问器子程序取代状态变量的直接检查

 class GetState {

  constructor(originState){
    this.state = originState;
  }

  // 使用子程序访问 变量
  get State () {
    return this.state
  }

  setState(result){
    this.state = result
  }

  increase(){
    this.setState(this.state ++)
  }
}
  1. 如何找出容易变化的区域
    以不能变的最小单位为基础。它是这个对象的最小单元。然后在此基础上扩充。在扩充的过程当中考虑功能的改变和质量的变化。把这些附加功能提取出来并改进隐藏。例如一个人有一定有头,身体,四肢。这构成了一个人的最小基本单位。但是人的性别,身高,体重这些就属于可以变的。在此基础上,会衍生出各种职位,优点缺点等附加功能。按照这个思路循序渐进。

如何使代码更健壮

  1. 对入参的判断 。参数的临界值 ,参数的不可能存在的范围 排除
    
    function demo(handle){
    if( !handle || (handle && typeof handle !== 'function')){
        return false
    }
    handle()
    }

// 参数的不可能存在的范围 function demo(number){ if(number < 3 || number > 10){ return false } console.log(number) }

2. 对入参的数据格式化
```javascript
function demo(status){
    status = status.toUpperCase()
    if(status === 'LOADING'){
        return 
    }
}
  1. 异常的捕获处理
    async function (){
    try {
        const data = await PromiseDemo();
    } catch (e) {
      utils.info(e.message || e)
    }
    }
  2. 给定默认值 有的时候,后段返回的数据或者传入的props是非必须的,这个值就存在undefied情况。这个时候需要给定默认值
    function demo(props){
    return (<div>
    {
        ( props.list || []).map(item=>(
            <div key={item.id}>{item.text}</div>
        )
    }
    </div>)
    }
ajccom commented 4 years ago

泻药,人在公司,还在上班。

工具已经 star。

文章总体结构上要再调整下,整体下面看着没有明确的叙述意图。

建议以总分总这种最可靠的方式进行写作,最后的总结,不宜过长,点题、归纳为主。

如果是经验总结的话,可以写下相关经验的实践情况、比较适合应用该经验的场景、不太适合的场景等。