React 设计模式和最佳实践
- 在 React 中,界面完全由数据驱动;
- 在 React 中,一切都是组件;
- props 是 React 组件之间通讯的基本方式。
生命周期图: https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
设计 React 组件的原则
保持接口小,props 数量要少;
根据数据边界来划分组件,充分利用组合(composition);
把 state 往上层组件提取,让下层组件只需要实现为纯函数。
create-react-app
webpack 配置: sass: cra 已内置 sass-loader,只需安装 node-sass/sass(dart-sass)
修 改其他配置,不 eject 的方式:
-
需要安装:react-app-rewired customize-cra
config-overrides.jsconst {
override,
addBabelPlugin,
addBabelPreset,
addWebpackAlias,
adjustStyleLoaders,
} = require('customize-cra');
const path = require('path');
module.exports = override(
//写样式的方式
addBabelPlugin('styled-jsx/babel'),
//别名
addWebpackAlias({
'@': path.resolve(__dirname, '.', 'src'),
}),
//sass-resources-loader共享公共样式文件
adjustStyleLoaders((rule) => {
if (rule.test.toString().includes('scss')) {
rule.use.push({
loader: require.resolve('sass-resources-loader'),
options: {
resources: './src/styles/shared.scss', //地址
},
});
}
}),
); -
通过 craco 配置,antd4 推荐。 无 eject 重写 CRA 配置 — Craco 详解 - 掘金
React 中的性能优化
React 工作流
reconciliation 调和阶段:
- 将目标 state 计算出虚拟 DOM 结构。
- DOM diff ,寻找到目标虚拟 DOM 的最优更新方案。
commit 阶段:
- 对于 reconciliation 调和阶段比较完成后,将获取到的变化部分应用到真实的 DOM 树上。
- 调用暴露给用户的钩子方法。 比如 ComponentDidUpdate/useLayoutEffect 等。
主要的性能优化点在计算虚拟 DOM 阶段:跳过不必要的组件更新。
重新渲染 reconciliation
渲染触发时间
-
组件挂载。React 组件构建并将 DOM 元素插入页面的过程称为挂载。当组件首次渲染的时候会调用 render,这个过程不可避免。
-
执行 setState 会触发 render。
但是这里有个点值得关注,执行 setState 的时候一定会重新渲染吗?答案是不一定。当 setState 传入 null 的时候,并不会触发 render 。
- 父组件更新触发子组件重新渲染。父组件重新渲染了,即使传入子组件的 props 未发生变化,那么子组件也会重新渲染,进而触发 render。
js 为单线程执行,显然,不必要的子组件的 render 会浪费 js 线程资源,复杂任务还会长时间占用线程导致假死状态,也就是页面卡顿,react 底层有 Fiber 来优化任务队列,但无法优化业务代码上的问题。
一般子组件可以通过确认 props 是否发生变化来控制自身是否进行 render,比如 react-mobx 中的 observer 高阶方法或者 React.PureComponet 就是用来做浅层比较进行控制处理。