背景
过去一年偶尔在Gitlab上给团队成员做Code Review并发现了一些问题,并陆续记录下来。我把这些问题进行总结归类,整理成文档。
React基础问题
useEffect和useMemo
假设场景:组件中的color值需要经过initColor计算而来,有三种实现
第一种:直接写
// ...
// 不推荐:任何组件状态更新都会重新计算颜色
const color = calcColor(initColor);
return <div> 当前颜色:{ color } </div>;
// ...
第二种:useEffect
// ...
// 只有initColor变化才计算
const [color, setColor] = useState();
useEffect(() => {
setColor(calcColor(initColor));
}, [initColor]);
return <div> 当前颜色:{ color } </div>;
// ...
第三种:useMemo
// ...
// 只有initColor变化才计算
const color = useMemo(() => calcColor(initColor), [initColor]);
return <div> 当前颜色:{ color } </div>;
// ...
第二种和第三种的区别:useMemo在渲染时就执行。useEffect在渲染之后再执行然后更新color。执行时机上第一种和第三种相同,第三种是第一种的基础上做了优化减少重复执行。
编码规范
if else 问题
多层嵌套if else 互斥的操作用if else 而不是多个if
指令式和声明式
// 指令式
let arr = [1, 2, 3, 4, 5];
let squares = [];
for(let i = 0; i < arr.length; i++) {
squares.push(arr[i] * arr[i]);
}
// 声明式,更简洁
let arr = [1, 2, 3, 4, 5];
let squares = arr.map(num => num * num);
消除重复!
写代码的时候总是会有意无意的出现重复,下面这两个例子是逻辑判断的重复逻辑,整合之后可以让代码更简洁:
// 不推荐:
isActive ? 'opacity(1)' : isSelected ? 'opacity(1)' : 'opacity(0.5)'
// 推荐:
isActive || isSelected ? 'opacity(1)' : 'opacity(0.5)'
// 不推荐
if (value) {
this.setState({ title: value, createDisabled: false });
} else {
this.setState({ title: value, createDisabled: true });
}
// 推荐
this.setState({ title: value, createDisabled: !value });
真是无处不在的重复,再比如
函数传参:
// 传了item就不要传key了
onClick={() => onClick({ item, key: item.key })}
样式设置:
<div
className={{
[styles.UnSelected]: !isSelected,
}}
{/* 重复出现isSelected。这里className和style应该只留一种写法 */}
style={{
color: isSelected ? '#FFFFFF' : '#666666'
}}
/>
还是要对重复保持敏感
命名问题
通用组件/方法的命名需要抽象,使其与业务无关。应该脱离特殊场景从普遍性的角度去考虑
例子:
// 改变color的透明度
// 虽然业务中是把透明度变浅。但该函数可以加深透明度呀
const getLightColor = (color: string, opacity: number) => {
// ...
// 可以改成
const changeColorOpacity = (color: string, opacity: number) => {
typescript中 " | " 的使用例子
不推荐:
// 危险!
interface IActionsProps {
selectedBranchId: string | bigint;
onSwitchBranch: (branchId: string | bigint) => void;
}
业务说明:IActionsProps是一个组件的props类型定义。selectedBranchId和onSwitchBranch函数的branchId参数都有可能是string 或 bigint类型,但它们需要是相同类型
推荐:
type IActionsProps =
| {
selectedBranchId: string;
onSwitchBranch: (branchId: string) => void;
}
| {
selectedBranchId: bigint;
onSwitchBranch: (branchId: bigint) => void;
};
类型约束后,这两者一定是相同类型
下面开始是按 review 的时间分类
2022/05/20
- 状态抽象,不同的视图可以由同一个状态决定,那应该只维护一个状态,用同个动作去控制。例如以下场景:根据页面路由来改变Tab组件状态和页面内容
那么数据流应该是:
而不是:
- 用更符合强类型语言/函数式的规范解决ts类型问题
不推荐:
commitList.forEach((commit: InterfaceCommit) => {
// 在dfs中修改commit从而也修改commitList,此时commit类型错误
dfs(commit);
});
推荐:
const commitListForSort = commitList.map((commit: InterfaceCommit) => {
// dfs不再直接修改commit而是返回新的commit
return dfs(commit);
});
2022/04/29
- 如果某个元素,在点击时添加了某个click事件,但同时又触发了该事件,可以考虑添加一个stopPropergation方法阻止冒泡。
- 组件接受一个字符串参数时,可以采用以下示例的方式。
//不推荐
interface ITipPopProps {
direction?: Direction | string;
}
enum Direction {
TOP = 'Top',
RIGHT = 'Right',
BOTTOM = 'Bottom',
LEFT = 'Left',
}
//推荐,对使用者更友好 ✅
interface ITipPopProps {
direction?: Direction;
}
type Direction = 'Top'|'Right'|'Bottom'|'Left'
2022/04/02
- Css 类覆盖问题,尽量不要使用!important
- 复杂组件要尽量细分成小组件,函数也一样
2022/02/18
- useEffect return问题
// 错误
useEffect(() => {
if (condition) {
// ...
}
return () => {
// ...
}
}, [condition]);
// 正确
useEffect(() => {
if (condition) {
// ...
return () => {
// ...
}
}
}, [condition]);
- 用lodash flow 方法处理连续调用的问题,更优雅
2022/02/11
- 微前端对于当前项目是否必要
微前端解决的问题: 子项目技术栈无关(研发效率)、子项目预加载(用户体验)
目前不同子项目独立开发部署,通过nginx代理集合不同子项目,实际已经解决了第一个问题。第二个问题当前痛点不明显。
微前端解决方案:single-spa 、garfish
微前端资料:swearer23.github.io/micro-front…
- drive.js 实现镂空效果原理:
加一层遮罩,给需要高亮的元素设置 position: relative
以及一个较高z-index。用js递归重置高亮元素的所有父级的z-index为auto,
获取元素计算后css属性值: Window.getComputedStyle()
2021/12/31
代数效应
- react suspense 与 传统方式对比
react资料: react.iamkasong.com/#导学视频
2021/12/24
- 不使用匿名函数,增加代码可读性, 案例对比:
// 不推荐写法
useEffect(() => {
if (!value) {
setResultList([]);
return;
}
const resultList = searchResult.filter((search) => {
return path.basename(search.name).indexOf(value) != -1;
});
setResultList(resultList);
}, [value]);
// 推荐写法
useEffect(() => {
const search = (value) => {
if (!value) {
return [];
}
const resultList = searchResult.filter((search) => {
return path.basename(search.name).indexOf(value) != -1;
});
return resultList;
};
setResultList(search(value));
}, [value]);
- 使用纯函数,减少副作用,函数返回值只和输入值有关
// 不推荐
const toHelpSearchPage = (baseName) => {
// 其中value为react组件中定义的state
router.push(/help/search?search=${baseName ? baseName : value})
}
// 推荐
const toHelpSearchPage = (baseName, value) => {
// 其中value为react组件中定义的state
router.push(/help/search?search=${baseName ? baseName : value})
}