一、基本语法
1.JSX语法
(1)setState是异步还是同步? 两者都有可能,
- setTimeout和原生事件(事件监听)中,可以立即拿到更新结果。也就是同步
- 在合成事件和生命周期中,不能立即拿到更新结果。也就是所谓的“异步”
- 在合成事件和生命周期中,如果对同一个值进行多次setState,setState的批量更新策略会对其进行覆盖,取最后一次的执行
constructor () {
super();
this.state = {
counter: 0
};
}
componentDidMount() {
// 生命周期中调用
console.log(this.state.counter); // 0
this.setState({ counter: this.state.counter + 1 });
this.setState({ counter: this.state.counter + 1 });
console.log(this.state.counter); // 0
setTimeout(() => {
// setTimeout中调用
console.log(this.state.counter); // 1
this.setState({ counter: this.state.counter + 1 });
console.log(this.state.counter); // 2
}, 0);
}
2.列表渲染
map(同级元素添加唯一key值, 避免用index或者random);
key值作用:用来判断VDOM元素的唯一依据,帮助 react 识别元素被更改、添加或者删除,加快diff算法比对的速度(diff算法通过tag和key来判断,是否为相同的节点),减少渲染次数,提升性能。
3.事件
onClick,绑定事件的几种方法:
1.constructor()里指定函数this指向
class Binding extends React.Component {
constructor() {
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
alert("Button Clicked")
}
render() {
return (
<input type="button" value="Click" onClick={this.handleClick} />
)
}
}
2.静态方法,不需要constructor()里绑定
class Binding extends React.Component {
handleClick = () => {
alert("Button Clicked")
}
render() {
return (
<input type="button" value="Click" onClick={this.handleClick} />
)
}
}
4.组件通信
单项数据流(从上而下),父组件传递数据和函数方法至子组件,子组件通过props显示数据和调用父组件方法更新数据。
5.组件生命周期
1.初始化state和props;
2.组件挂载阶段: componentWillMount ----> render -----> componentDidMount(组件渲染完成,进行ajax请求);
3.组件更新阶段: componentWillReceiverProps(props变化) ---> shouldComponentUpdate(props, nextProps)(返回true更新, false则不更新) ---> componentWillUpdate ---> render ----> componentDidUpdate;
4.组件卸载:componentWillUnMount(清除定时器,事件监听);
新的生命周期添加了getDerivedStateFromProps(代替componentWillReceiveProps), getSnapshotBeforeUpdate(代替componentWillUpdate
6.函数组件和类组件
- 纯函数,输入props,输出JSX
- 没有实例,没有生命周期,state
- 不能扩展其他方法
7.受控组件和非受控组件
(1)受控组件:setState更新组件视图;
(2)非受控组件:必须手动操作DOM元素,setState实现不了,列如文件上传,富文本编辑器。
8.Portals
react中组件会按照既定层次进行渲染,当标签内容嵌套过多时,如果想组件渲染到父组件外,可以使用portals。 用法:
ReactDOM.createPartal{
<div className="modal">{this.props.children}</div>,
document.body // 目标DOM节点
}
应用场景:
- overflow: hidden;
- 父组件z-index太小
- fixed需要放在body第一层级
9.Context
Context 通过组件树提供了一个传递数据的方法,从而避免了在每一个层级手动的传递 props 属性。
父组件通过createContext创建context,provider传递value值,子组件通过指定contextType获取最外层传递的value值。
import React from "react";
const ThemeContext = React.createContext("light");
// 子组件接受context
class ThemeButton extends React.Component {
// 指定 contextType读取当前的ThemeContext
static contextType = ThemeContext;
render() {
const theme = this.context;
return <div>Button is {theme}</div>;
}
}
// 父组件传递context
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
theme: "light",
};
}
render() {
return (
// provider传递context
<ThemeContext.Provider value={this.state.theme}>
<ThemeButton></ThemeButton>
</ThemeContext.Provider>
);
}
}
10.异步组件
异步按需加载组件,主要用到了React.lazy和React.Suspense特性;
// 异步加载需要的组件
const AsyncDemo = React.lazy(() => import("./asyncDemo"));
class App extends React.Component {
render() {
return (
<div>
{/* fallbackAsyncDemo组件未加载前显示 */}
<React.Suspense fallback={<div>loading...</div>}>
<AsyncDemo />
</React.Suspense>
</div>
);
}
}
11.高阶组件和render Props
当项目中不同组件共用相同逻辑时,可以使用HOC或者render Props来进行公共逻辑的抽离,提高复用性。
1.高阶组件(HOC)
接受一个目标组件,返回处理后新的组件。
// 高阶组件不是一种功能,而是一种模式
const HOCFactory = (Component) => {
class HOC extends React.Component {
// 此处定位多个组件的公共逻辑
render() {
return <Component {...this.props} />;
}
}
return HOC;
};
const TestComponent1 = HOCFactory(WrappedComponent1);
const TestComponent2 = HOCFactory(WrappedComponent2);
2.render Props
// 通过一个函数将class公共组件的state值作为参数传递给目标组件,并调用目标组件的render函数
class RenderFactory extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return <div>{this.props.render(this.state)}</div>;
}
}
const App = () => {
<RenderFactory render={(props) => <p>{props}</p>} />;
};
12.react性能优化
(1)合理使用shouldComponentUpdate
通过比较新旧props来判断数据是否变化,如果没有变化则返回false,不更新组件,反之则重新渲染页面。在这里如果props中数据结构较为复杂,则不推荐进行深度比较,相比于更新组件,深度比较更加消耗性能。
(2)使用PureComponent(函数组件使用memo)
在shouldComponentUpdate进行进行了浅比较(Object.is),即比较了第一层的数据,适用于大部分情况
(3)bind函数位置
当我们在 React 中创建函数时,我们需要使用 bind 关键字将函数绑定到当前上下文。绑定可以在构造函数中完成,也可以在我们将函数绑定到 DOM 元素的位置上完成。
但是当我们将函数绑定到 DOM 元素的位置后,每次render的时候都会进行一次bind,这将会有一些不必要的性能损耗,而且还有可能导致子组件不必要的渲染。所以我们可以在构造函数中绑定,也可以直接写箭头函数。同理,我们尽量不写内联函数和内联属性
(4)列表渲染添加key,原因见上标题3