2年前端沉淀的web最佳实践

最近3个月忙的有点儿不知道干什么了,以至于没有产出一篇博客。但就在这忙到不知道干什么的时候,似乎从匆忙的迭代中悟出了些许关于代码可维护性相关的道理。以下就是工作2年来的些许沉淀,可能有点儿杂,如果你也有这样的经历,欢迎评论一起探讨。

减少直接访问函数作用域外部的变量

bad

const a = 1;
const b = 1;
const addNum = () => {
  return a + b;
};

good

const addNum = (a: number, b: number) => {
  return a + b;
};

why

  • bad示例延长了函数addNum外部变量a和b的作用域链,不利于垃圾回收机制尽快回收a和b;

  • 延长了代码阅读的链路;

  • 不利于复用addNum函数;

  • 不够纯,有副作用;

  • 不够内聚;

减少三元运算符实现的条件渲染

bad

{isShow ? <div>显示内容</div> : null}

good

.displayed {
  display: block;
}
.hidden {
  display: none;
}

why

  • CSS 中的 display 条件渲染对性能影响较小,因为只需要在样式表中设置一个属性即可,不需要执行额外的代码来控制元素的显示或隐藏。

充分利用尾调用

bad

const onClick = (a: number) => {
  if (number < 10) {
    return number;
  }
  return number + 1;
};
const onClick = (a: number) => (number < 10 ? number : number + 1);

good

const onClick = (a: number) => (number < 10 ? number : number + 1);
  • 尾调用指的是一个函数在最后一步调用另一个函数,并将其结果返回。

    然后,调用函数没有必要保留任何本地状态,因为返回值就是它自己的返回值。

  • 通过这种方式,尾调用可以优化递归算法,避免出现栈溢出的情况。

不要下放与子组件接收名不一致的属性或方法

bad

const addNum = ()=>{...};
<Component onClick={addNum} />

good

const addNum = ()=>{...}
<Component addNum={addNum} />

why

  • bad的写法会出现道具下钻陷阱,使代码难以阅读和理解,如果在子组件里面接收该方法,可能子组件的方法名与props传递下来的方法名不一致,代码阅读的链路和复杂性增加了

  • 一旦porps的属性被传递2层以上,就得考虑设计的是否不够合理

不要在渲染层处理业务数据

bad

const Header = ()=>{
  const data = [1,2,3,4];
  return <>
  {data.filter((d)=>d>2).map((d)=><span key={d}>{d}</span>)}
  </>
}

good

const Header = ()=>{
  const data = [1,2,3,4].filter((d)=>d>2);
  return <>
  {data.map((d)=><span key={d}>{d}</span>)}
  </>
}
why
  • UI渲染应该与数据逻辑处理分离,减少耦合

不要维护与父组件渲染无关的状态

bad

import React, { useState } from "react";
interface ModalProps {
  open: boolean;
  setOpen: (v: boolean) => void;
  otherProps: any;
}


const Modal = (props: ModalProps) => {
  return <>根据父组件传递下来的open和setOpen决定是否要打开和关闭Modal</>;
};


const FatherComponent = () => {
  const [open, setOpen] = useState(false);
  const [otherProp, setOtherProp] = useState("");
  return (
    <>
      <span
        onClick={() => {
          // 可能存在这样的业务,触发某个事件需要将最新的otherProps修改,并传给Modal
          setOtherProp("");
        }}
      >
        父组件其他的业务逻辑代码
      </span>
      <Modal open={open} setOpen={setOpen} otherProps={otherProp} />
    </>
  );
};
export default FatherComponent;

good

import React, { forwardRef, useImperativeHandle, useRef, useState } from "react";
interface ModalProps {
  // 由于对外暴露了组件自己的实例,所以基本上不需要接收props,父组件与子组件的耦合度降低
}


interface ModalRef {
  show: (otherProps: any) => void;
  close: () => void;
}


export const useModalRef = () => useRef<ModalRef>(null);


const Modal = forwardRef<ModalRef, ModalProps>((props, ref) => {
  const [otherProps, setOtherProp] = useState("");
  const [open, setOpen] = useState(false);
  useImperativeHandle(ref, () => ({
    show: (v: any) => {
      setOtherProp(v);
      setOpen(true);
    },
    close: () => {
      setOpen(false);
    },
  }));
  return (
    <>
      打不打开Modal{open}、怎么储存{otherProps}的能力被自己回收,父组件只能调用自己的实例触发
    </>
  );
});


const FatherComponent = () => {
  const modalRef = useModalRef();
  return (
    <>
      <span
        onClick={() => {
          // 父组件再也不用维护与自身业务无关的,本该属于子组件管理的状态——otherProps
          modalRef.current?.show("otherProps");
        }}
      >
        父组件其他的业务逻辑代码
      </span>
      <Modal ref={modalRef} />
    </>
  );
};
export default FatherComponent;
why
  • bad示例如果有更多的其他业务modal处理,并且也需要根据父组件的某些业务更新子组件的状态,父组件将变得越来越臃肿,有两个modal就得存两份open、存两份otherProps,这对于通用业务组件的封装和抽离将是灾难

  • good示例利用对外暴露实例的方式解耦了父组件与子组件状态的维护和管理逻辑,将状态通过调用组件实例的方式传给了子组件,如果有新的业务扩展,子组件完全有能力继续内聚继续扩展实例方法和属性。

    但是bad示例将会在父组件维护数不清的本该属于子组件的状态和方法。

写到这里突然不知道怎么写了,其实关于最佳实践太多了,比如正确使用useState和useRef,比如不要无缘无故用useMemo、usecallback等,无非就是一句话“高内聚、低耦合”。在写代码前最好可以给自己一段思考的时间,比如我这样写会不会与什么什么耦合度很高,这样写是不是不够内聚,别人来看我代码的链路是不是变长了。

关于上述的最佳实践你是否有更好的思路去实现,欢迎交流~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值