React 学习
高阶组件
作用:
- 类似一个装饰器,为原有的组件提供额外的功能。
- 抽象出原始组件的共有方法,并将原始组件作为子组件传入高阶组件。
函数组件中绑定this指向(隐式)的方式
- 通过箭头函数
// 方式一
function App() {
let [count, setCount] = useState(0);
function test(){
setCount(count+1);
}
// 在事件中使用箭头函数绑定为当前实例
return (
<div className="App">
<p>{count}</p>
<button onClick={() => test()}>点击</button>
</div>
);
}
// 方式二
function App() {
let [count, setCount] = useState(0);
// 使用箭头函数将方法创建在实例上
let test = () => {
setCount(count+1);
}
return (
<div className="App">
<p>{count}</p>
<button onClick={test}>点击</button>
</div>
);
}
- 通过bind方法
function App() {
let [count, setCount] = useState(0);
function test(){
setCount(count+1);
}
// 在事件中使用bind绑定为当前实例
return (
<div className="App">
<p>{count}</p>
<button onClick={test.bind(test)}>点击</button>
</div>
);
}
React 生命周期
// 使用 类组件 来获取各个阶段的执行顺序
// constructor phase
// render phase
// componentDidMount phase
// 点击按钮更新后的情况
// render phase
// componentDIdUpdate phase
interface State {
count: number;
}
class App extends React.Component<any, State> {
constructor(props: any) {
super(props);
this.state = {
count: 0,
};
console.log("constructor phase");
}
test = () => {
this.setState({
count: this.state.count + 1,
});
};
// 挂载之后
componentDidMount() {
console.log("componentDidMount phase");
}
// 更新之后
componentDidUpdate() {
console.log("componentDIdUpdate phase");
}
// 卸载之前
componentWillUnmount() {
console.log("componentWillUnmount phase");
}
render() {
console.log("render phase");
return (
<div className="App">
<p>{this.state.count}</p>
<button onClick={this.test}>点击</button>
</div>
);
}
}
受控组件与非受控组件
注意:受控组件与非受控组件都是针对表单而言的。
受控组件
定义:表单元素中的value
值交由组件中的state
来托管,即将React中的state
属性与表单元素建立依赖关系,再通过setState
与 绑定事件(onChange
)来更新state
中的属性,就可以控制用户输入过程中表单发生的操作。
一种常见的操作就是进行数据的双向绑定(视图层与模型层相互影响)。
function App() {
let [value, setValue] = useState("");
// 通过事件监听来获取value并用于更改React中的state
let changeValue = (e:React.ChangeEvent<HTMLInputElement>) =>{
setValue(e.target.value);
}
return (
<div className="App">
<input value={value} onChange={changeValue}></input>
<div>
{value}
</div>
</div>
);
}
非受控组件
定义:表单元素中的value
值不再交由组件中的state
托管,而是由DOM
节点来处理。
function App() {
// 通过ref来获取DOM中的value
const input = useRef<HTMLInputElement>(null);
let submit = (e:React.FormEvent<EventTarget>) =>{
console.log(input.current?.value);
e.preventDefault();
}
return (
<div className="App" onSubmit={submit}>
<form>
<input type="text" ref={input}></input>
<input type="submit"></input>
</form>
</div>
);
}
组件优化
只渲染产生变化的部分
场景:父组件会根据用户点击按钮而重新渲染,而子组件没有产生变化时,可以进行组件优化,在父组件重新渲染时不再渲染子组件部分。
方法:
- 使用
shouldComponentUpdate(nextProps, nextState)
钩子函数,返回false时表示组件不进行渲染。(针对类组件) - 将子组件继承
PureComponent
(针对类组件)。 - 将子函数组件包裹一层
memo()
(高阶组件,可以通过第二个参数设置来自定义比较)。但是此方法无法解决传递函数重新渲染的情况,需要增加useCallback()
方法来处理
// 包裹一层memo实现 PureComponent 的效果
// 在点击按钮后,父组件会更新并重新渲染,但是子组件不会再次渲染
const Sub = memo((props:Props) => {
console.log("sub render");
return <div>sub</div>;
});
function App() {
let [count, setCount] = useState({a:1, b:12});
let [name] = useState("test");
let test = () =>{
setCount({...count, a: count.a+1});
}
// 使用 useCallback 解决传递相同函数时重复渲染的问题
let transmit = useCallback(() =>{
console.log(name);
}, [name])
console.log("parent render")
return (
<div className="App">
<Sub test={transmit}/>
<p>{count.a}</p>
<button onClick={test}>点击</button>
</div>
);
}
前端路由
browser路由
使用 history.pushState(Object, null, url)
来实现
hash路由
使用 a
标签中的锚点来实现
Portals
组件默认会挂载到DOM树中离其最近的父节点上,事件触发遵循冒泡机制。
Portals 则提供了一种将子节点渲染到父组件以外的DOM节点的方案。(可以是父组件指向节点的兄弟节点)使用了组件树中的事件冒泡机制。
应用场景:对话框、提示框等(antd中的Modal)。
函数式编程
纯函数:根据输入的参数,返回组件的HTML代码的函数。
副效应:改变应用状态、生成日志等操作。
钩子:React函数组件副效应的解决方案。用于为函数组件引入副效应。
useEffect
使用函数式组件来模拟React生命周期
function App() {
let [count, setCount] = useState(0);
let test = () =>{
setCount(count+1);
}
useEffect(() =>{
console.log("componentDidMount phase");
}, [])
useEffect(() =>{
console.log("componentDidUpdate phase");
// 会在副效应函数执行前执行一次,清楚上一次渲染的副效应
return () =>{
console.log("componentWillUnmount phase");
}
},[count])
console.log("render phase");
return (
<div className="App">
<p>{count}</p>
<button onClick={test}>点击</button>
</div>
);
}
diff算法
用途:计算虚拟DOM中真正变化的部分,并且只针对该部分进行实际的DOM操作。
- tree diff:只进行同层比较,对于跨层的操作,React会直接删除,并重构。
- component diff:同类型组件,通过
shouldComponentUpdate()
来判断是否需要继续比较;不同类型,则直接替换整个组件下的所有子节点。 - element diff:通过唯一的key来进行diff优化,通过复用已有节点,减少节点的删除和创建操作。
https://juejin.cn/post/6844903944796258317
虚拟DOM
React 会将代码转化为一个JS对象(即虚拟DOM),然后再将这个对象转化为真实DOM。
优点:
- 使得开发人员将重心放在业务逻辑而非DOM操作上,这点可以大大提升开发效率。
- 对于首次渲染,使用虚拟DOM不具备任何优势。但是在进行页面更新时,React通过Diff算法和批处理策略,进行更高效地更新。
https://segmentfault.com/a/1190000018891454