React最佳实践

React

事件机制,合成事件

是什么

React 基于浏览器的事件机制⾃身实现了⼀套事件机制,包括事件注册、事件的合成、事件冒泡、事件派发等。
在 React 中这套事件机制被称之为合成事件

工作过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import React from 'react';
class App extends React.Component{
constructor(props) {
super(props);
this.parentRef = React.createRef();
this.childRef = React.createRef();
}

componentDidMount() {
console.log("React componentDidMount!");
this.parentRef.current?.addEventListener("click", () => {
console.log("原⽣事件:⽗元素 DOM 事件监听!");
});
this.childRef.current?.addEventListener("click", () => {
console.log("原⽣事件:⼦元素 DOM 事件监听!");
});

document.addEventListener("click", (e) => {
console.log("原⽣事件:document DOM 事件监听!");
});
}

parentClickFun = () => {
console.log("React 事件:⽗元素事件监听!");
};

childClickFun = () => {
console.log("React 事件:⼦元素事件监听!");
};

render() {
return (
<div ref={this.parentRef} onClick={this.parentClickFun}>
<div ref={this.childRef} onClick={this.childClickFun}>
分析事件执⾏顺序
</div>
</div>
);
}
}

export default App;

最终输出

1
2
3
4
5
原⽣事件:⼦元素 DOM 事件监听!
原⽣事件:⽗元素 DOM 事件监听!
React 事件:⼦元素事件监听!
React 事件:⽗元素事件监听!
原⽣事件:document DOM 事件监听!

总结

  • React 上注册的事件最终会绑定在document这个 DOM 上,⽽不是 React 组件对应的 DOM(减少内存开销就是因为所有的事件都绑定在 document 上,其他节点没有绑定事件)
  • React 通过队列的形式,从触发的组件向⽗组件回溯,然后调⽤他们 JSX 中定义的 callback
  • React 有⼀套⾃⼰的合成事件 SyntheticEvent

CSS引入

在组件内直接使⽤

内联style引入

组件中引⼊ .css ⽂件

全局生效,样式之间会互相影响

组件中引⼊ .module.css ⽂件

将 css ⽂件作为⼀个模块引⼊,这个模块中的所有 css ,只作⽤于当前组件。不会影响当前组件的后代组件
这种⽅式是 webpack 特⼯的⽅案,只需要配置 webpack 配置⽂件中 modules:true 即可

CSS in JS

CSS-in-JS, 是指⼀种模式,其中 CSS 由 JavaScript ⽣成⽽不是在外部⽂件中定义
此功能并不是 React 的⼀部分,⽽是由第三⽅库提供,例如:

  • styled-components
  • emotion
  • glamorous

本质是通过函数的调⽤,最终创建出⼀个组件:

  • 这个组件会被⾃动添加上⼀个不重复的class
  • styled-components会给该class添加相关的样式

组件通信

高阶组件

本质上是一个函数,接收一个或多个组件作为参数,并返回一个组件

错误边界

错误边界是一种 React 组件,这种组件可以捕获发生在其子组件树任何位置的 JavaScript 错误,并打印这些错误,同时展示降级 UI,而并不会渲染那些发生崩溃的子组件树。错误边界可以捕获发生在整个子组件树的渲染期间、生命周期方法以及构造函数中的错误。

形成错误边界组件的两个条件:

  • 使⽤了 static getDerivedStateFromError()
  • 使⽤了 componentDidCatch()
    抛出错误后,使⽤ static getDerivedStateFromError() 渲染备⽤ UI ,使⽤ componentDidCatch() 打印错误信息,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}

static getDerivedStateFromError(error) {
// 更新 state 使下一次渲染能够显示降级后的 UI
return { hasError: true };
}

componentDidCatch(error, errorInfo) {
// 你同样可以将错误日志上报给服务器
logErrorToMyService(error, errorInfo);
}

render() {
if (this.state.hasError) {
// 你可以自定义降级后的 UI 并渲染
return <h1>Something went wrong.</h1>;
}

return this.props.children;
}
}

下⾯这些情况⽆法捕获到异常:

  • 事件处理
  • 异步代码(例如 setTimeout 或 requestAnimationFrame 回调函数)
  • 服务端渲染
  • 它自身抛出来的错误(仅能捕获子组件抛出的错误)

setState更新,批量更新

render原理(jsx经过babel编译)、render触发时机

Diff算法

React Portal

Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案。
ReactDOM.createPortal(child, container)
第一个参数(child)是任何可渲染的 React 子元素,例如一个元素,字符串或 fragment。第二个参数(container)是一个 DOM 元素。

React router

是什么

react-router 等前端路由的原理⼤致相同,可以实现⽆刷新的条件下切换显示不同的⻚⾯
路由的本质就是⻚⾯的 URL 发⽣改变时,⻚⾯的显示结果可以根据 URL 的变化⽽变化,但是⻚⾯不会刷新因此,可以通过前端路由可以实现单⻚(SPA)应⽤

模式

HashRouter(Hash模式)
  • 采用监听 window 上的 hashchange 事件实现;
  • path的表现形式是 localhost:3000/#/demo/test
  • 服务器无须做额外配置(因为对于服务器而言,http://example.com/#/homehttp://example.com这两个请求在服务器看来是完全一样的,因为服务器只处理http://example.com这个部分,而#/home这个哈希部分的变化不会触发新的服务器请求)
BrowserRouter(History模式)
  • BrowserRouter使用的是H5的history API来实现;
  • path的表现形式是 localhost:3000/demo/test
  • 服务器需要进行额外的配置(当path变化时,浏览器会向服务器发送请求。服务器需要能够识别这些请求并返回正确的页面或者资源。例如,在服务器重定向配置中,需要设置一个通配符路由(*)来处理所有可能的路由请求,将请求重定向到应用的入口文件(通常是index.html),这样客户端的 JavaScript 代码才能根据 URL 中的路径来正确地渲染页面)

lazyload

render props、children

Hooks相关

React fiber

React性能优化

React状态管理

Redux

react如何实现ssr