赞
踩
setState在16.4版本之前都是同步的,在16版本之后基本都是异步的除了一些特殊事件(原生js、promise、setTImeout、setInterval)
Concurrent模式想做到的事情就是用户可以自定义更新任务优先级并且能够通知到React,React再来处理不同优先级的更新任务,当然,优先处理高优先级任务,并且低优先级任务可以中断
中断批处理,批处理是一个破坏性改动,如果你想退出批量更新,你可以使用 flushSync,建议尽量不要这么做
JSX 是 JavaScript 语法的扩展,它允许编写类似于 HTML 的代码。JSX 可以很好地描述 UI 应该呈现出它应有交互的本质形式
jsx语法可以转换为js形式
- // jsx形式
- <div className="sidebar" />
- // js形式
- React.createElement( 'div', {className: 'sidebar'})
React 认为渲染逻辑本质上与其他 UI 逻辑内在耦合,比如,在 UI 中需要绑定处理事件、在某些时刻状态发生变化时需要通知到 UI,以及需要在 UI 中展示准备好的数据
在每次render方法前调用,第一个参数为即将更新的props,第二个参数为上一个状态的state,可以比较props 和 state来加一些限制条件,防止无用的state更新
该方法需要返回一个新的对象作为新的state或者返回null表示state状态不需要更新
render之后执行
接收两个参数:prevProps(表示更新前的props)和prevState(表示更新前的state)。它的返回值将作为第三个参数传递给componentDidUpdate方法。
不能直接操作DOM,因为DOM尚未更新
React基于浏览器的事件机制自身实现了一套事件机制,包括事件注册、事件的合成、事件冒泡、事件派发等这套机制被称之为合成事件;
合成事件是 React模拟原生 DOM事件所有能力的一个事件对象;根据 W3C规范来定义合成事件,兼容所有浏览器,拥有与浏览器原生事件相同的接口
- // 原生事件绑定方式
- <button onclick="handleClick()">按钮命名</button>
-
- // React 合成事件绑定方式
- const button = <button onClick={handleClick}>按钮命名</button>
- // 原生事件 事件处理函数写法
- <button onclick="handleClick()">按钮命名</button>
-
- // React 合成事件 事件处理函数写法
- const button = <button onClick={handleClick}>按钮命名</button>
props 是传递给组件的(类似于函数的形参),而 state 是在组件内被组件自己管理的(类似于在一个函数内声明的变量)
props 是不可修改的,所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。
state 是在组件中创建的,一般在 constructor中初始化 state, state 是多变的、可以修改,每次setState都异步更新的
Keys 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。
在 React 中渲染集合时,向每个重复的元素添加关键字对于帮助React跟踪元素与数据之间的关联非常重要。key 应该是唯一ID,最好是 UUID 或收集项中的其他唯一字符串
注意事项:
Refs 是 React 提供给我们的安全访问 DOM 元素或者某个组件实例的句柄。
我们可以为元素添加 ref 属性然后在回调函数中接受该元素在 DOM 树中的句柄,该值会作为回调函数的第一个参数返回
- class CustomForm extends Component{
- handleSubmit = () => {
- console.log("Input Value: ", this.input.value)
- }
- render () {
- return (
- <form onSubmit={this.handleSubmit}>
- <input type='text' ref={(input) => this.input = input} />
- <button type='submit'>Submit</button>
-
- </form> ) }
- }
上述代码中的 input 域包含了一个 ref 属性,该属性声明的回调函数会接收 input 对应的 DOM 元素,我们将其绑定到 this 指针以便在其他的类函数中使用
渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”;一般用于表单元素(<input>、 <textarea> 和 <select>等)
- class TestComponent extends React.Component {
- constructor (props) {
- super(props);
- this.state = { username: 'lindaidai' };
- }
- render () {
- return <input name="username" value={this.state.username} />
- }
- }
简单来讲,就是不受我们控制的组件,一般情况是在初始化的时候接受外部数据,然后自己在内部存储其自身状态
- // An highlighted block
- import React, { Component } from 'react';
-
- export class UnControll extends Component {
- constructor (props) {
- super(props);
- this.inputRef = React.createRef();
- }
- handleSubmit = (e) => {
- console.log('我们可以获得input内的值为', this.inputRef.current.value);
- e.preventDefault();
- }
- render () {
- return (
- <form onSubmit={e => this.handleSubmit(e)}>
- <input defaultValue="lindaidai" ref={this.inputRef} />
- <input type="submit" value="提交" />
- </form>
- )
- }
- }
顾名思义,也就是通过使用ES6类的编写形式去编写组件,该类必须继承React.Component
如果想要访问父组件传递过来的参数,可通过this.props的方式去访问;在组件中必须实现render方法,在return中返回React对象
- class Welcome extends React.Component {
- constructor(props) {
- super(props)
- }
- render() {
- return <h1>Hello, {this.props.name}</h1>
- }
- }
函数组件,顾名思义,就是通过函数编写的形式去实现一个React组件,是React中定义组件最简单的方式;函数第一个参数为props用于接收父组件传递过来的参数
- function Welcome(props) {
- return <h1>Hello, {props.name}</h1>;
- }
fiber架构是react16引入的新型框架,Fiber是 React内部所定义的一种数据结构,它是 Fiber树结构的节点单位,也就是 React 16 新架构下的虚拟DOM
1.更好的用户体验:通过使用Fiber架构,React应用程序可以在用户交互事件发生时立即响应,避免了页面卡顿和无响应的情况。
2.增量渲染:Fiber架构将渲染过程划分为多个任务单元,可以根据任务的优先级和时间片进行调度,使得渲染工作可以分布在多个帧中完成,提高了页面的渲染性能。
3.可中断、恢复的机制:Fiber架构允许React在执行渲染任务时进行中断,并在有空闲时间时恢复任务,使得渲染过程可以更好地适应浏览器的调度。
4.更好的错误边界和组件的并发处理:Fiber架构为React引入了新的错误处理机制,可以更好地捕获和处理组件中发生的错误,同时也为React未来支持并发模式奠定了基础。
都支持组件化
都是数据驱动视图
都是vdom操作dom
都支持模块化
react是用于搭建UI界面的javascript库,vue是渐进式框架
react是单向数据流,vue是双向数据绑定
react使用jsx语法,vue使用接近html语法
react拥有更庞大和成熟的生态系统,拥有大量的第三方库和工具,vue相对来说比较少
react更适合大型和复杂的应用程序,使用起来更具灵活性和扩展性,vue则适合快速原型开发和小型项目,更容易上手
- // 父组件
- const element = <EmailInput email="123124132@163.com" />;
-
- // 子组件
- function EmailInput(props) {
- return (
- <label>
- Email: <input value={props.email} />
- </label>
- );
- }
- const PriceContext = React.createContext('price')
- const { Provider, Consumer} = PriceContext;
- export default { Provider, Consumer }
- // 父组件
- <Provider value={100}>
- </Provider>
4.在后代组件内使用Consumer接收数据
- // 后代组件
- <Consumer>
- { /*这里是一个函数*/ }
- {
- price => <div>price:{price}</div>
- }
- </Consumer>
react diff算法使用三大策略将算法复杂度O(n^3)转化为了O(n)
react通过updateDepth(层级修改)对Virtual dom树进行分层级控制,在对比过程中,如果发现节点不存在了会完全删除不会对其他地方进行比较,这样只需要对树遍历一次就可以了
对于同一层级的一组子节点,通过唯一id区分
当节点处于同一层级时,diff提供三种节点操作:删除、插入、移动。
尽量减少类似将最后一个节点移动到列表首部的操作,当节点数量过大或更新操作过于频繁时,会影响React的渲染性能
在React中,高阶组件即接受一个或多个组件作为参数并且返回一个组件,本质也就是一个函数,并不是一个组件
- import React, { Component } from 'react';
-
- export default (WrappedComponent) => {
- return class EnhancedComponent extends Component {
- // do something
- render() {
- return <WrappedComponent />;
- }
- }
- }
- // withauth.js
- import { Navigate } from "react-router-dom";
-
- function AuthComponent(props) {
- const children = props.children;
- const token = localStorage.getItem("token"); // 假设token保存在localStorage中
- if (token) {
- return <>{children}</>;
- } else {
- return <Navigate to="/login" ></Navigate>;
- }
- }
-
- export default AuthComponent;
-
- // app.js
- function App() {
- return (
- <div className="App">
- <div className="app-content">
- <WithAuth>
- <Outlet></Outlet>
- </WithAuth>
- </div>
- </div>
- )
- }
记录每次渲染时间
- function withTiming(Comp) {
- return class extends Comp {
- constructor(props) {
- super(props);
- this.start = Date.now();
- this.end = 0;
- }
- componentDidMount() {
- super.componentDidMount && super.componentDidMount();
- this.end = Date.now();
- console.log(`${WrappedComponent.name} 组件渲染时间为 ${this.end - this.start} ms`);
- }
- render() {
- return super.render();
- }
- };
- }
在React中,数据在组件中是单向流动的,数据从一个方向父组件流向子组件(通过props),所以,两个非父子组件之间通信就相对麻烦,redux的出现就是为了解决state里面的数据问题
Redux是将整个应用状态存储到一个地方上称为store,里面保存着一个状态树store tree,组件可以派发(dispatch)行为(action)给store,而不是直接通知其他组件,组件内部通过订阅store中的状态state来刷新自己的视图
整个应用的state都被存储到一个状态树里面,并且这个状态树,只存在于唯一的store中
state是只读的,唯一改变state的方法就是触发action,action是一个用于描述以发生时间的普通对象
使用纯函数来执行修改,为了描述action如何改变state的,你需要编写reducers
- let createStore = (reducer) => {
- let state;
- //获取状态对象
- //存放所有的监听函数
- let listeners = [];
- let getState = () => state;
- //提供一个方法供外部调用派发action
- let dispath = (action) => {
- //调用管理员reducer得到新的state
- state = reducer(state, action);
- //执行所有的监听函数
- listeners.forEach((l) => l())
- }
- //订阅状态变化事件,当状态改变发生之后执行监听函数
- let subscribe = (listener) => {
- listeners.push(listener);
- }
- dispath();
- return {
- getState,
- dispath,
- subscribe
- }
- }
- let combineReducers=(renducers)=>{
- //传入一个renducers管理组,返回的是一个renducer
- return function(state={},action={}){
- let newState={};
- for(var attr in renducers){
- newState[attr]=renducers[attr](state[attr],action)
-
- }
- return newState;
- }
- }
- export {createStore,combineReducers};
- // 1、首先下载@reduxjs/toolkit 和 react-redux;
- // 创建一个store目录,在当前index.jsx内进行配置;
- import { configureStore } from "@reduxjs/toolkit";
- import CounterSlice from "./counterSlice"; //引入counterSlice文件夹
- // 合并切片
- const store = configureStore({
- // 合并多个slice切片
- reducer: {
- counter: CounterSlice, // 这里的counter就是reducer的名字
- ...
- },
- });
- export default store;
-
- //2、 counterSlice/index.jsx
- // 创建切片
- import { createSlice } from "@reduxjs/toolkit";
-
- export const counterSlice = createSlice({
- name: "counter", //切片名称
- initialState: { value: 0 }, //初始状态
- reducers: {
- //reducer函数
- increment: (state) => {
- state.value += 1; //状态增加
- },
- decrement: (state) => {
- state.value -= 1; //状态减少
- },
- },
- });
- //导出action
- export const { increment, decrement } = counterSlice.actions;
- export default counterSlice.reducer; //导出reducer
-
- // 3、根目录下 //main.jsx;
-
- import store from "./store"; //引入store
- import { Provider } from "react-redux"; //通过react-redux引入Provider
- // 使用Provider包裹最外层,并且挂载store实例;
- ReactDOM.createRoot(document.getElementById("root")).render(
- <Provider store={store}>
- <RouterProvider router={router}>
- <App />
- </RouterProvider>
- </Provider>
- );
- // 4、组件内使用 -- list.jsx
- /* useDispatch用于派发事件,useSelector用于获取数据*/
- import { useDispatch, useSelector } from "react-redux";
- /* increment,decrement 用于修改redux状态的事件 */
- import { increment, decrement } from "../../store/counterSlice/index.jsx";
-
- // 实例化useSelector方法并且获取store中counter切片的value
- const { value } = useSelector((store) => store.counter);
- // 实例化dispatch方法
- let dispatch = useDispatch();
- // 最后操作redux
- <Button type="primary" size="small" onClick={() => dispatch(increment())}>
- +
- </Button>
- {value}
- <Button type="warning" size="small" onClick={() => dispatch(decrement())}>
- -
- </Button>
react中hooks主要用来处理副作用和复杂的逻辑
用于实现state和setState;
在函数组件内本身没有state,函数组件是一个纯函数,执行完即销毁,无法储存state
- //count 为访问的数据,setCount 用来修改count的数据;
- const [count, setCount] = useState(0) // 传入一个初始值
在函数组件内是没有生命周期函数的,执行完则销毁,自己无法生成生命周期;
默认情况纯函数输入参数返回结果无副作用,在函数之外定义全局属性、定时器等等都会产生副作用;
模拟生命周期
componentDidMount ;useEffect依赖[],只在初始化完成时执行一次,后续不会执行;
componentDidUpdate ; useEffect依赖[a,b]或者useEffect(fn)没有写第二个参数,当依赖数据发生变化会重新执行内部函数,不传递任何参数将会一直执行;
componentWillUnmount ; useEffect返回一个函数,return()=>{ 清除定时器以及删除时间等等; }
在react中默认父组件变化时,会更新所有子组件;
使用useMemo会缓存对象,避免子组件重复渲染
- import React, { useState, memo, useMemo } from 'react'
-
- // 子组件
- // function Child({ userInfo }) {
- // console.log('Child render...', userInfo)
-
- // return <div>
- // <p>This is Child {userInfo.name} {userInfo.age}</p>
- // </div>
- // }
- // 类似 class PureComponent ,对 props 进行浅层比较
- const Child = memo(({ userInfo }) => {
- console.log('Child render...', userInfo)
-
- return <div>
- <p>This is Child {userInfo.name} {userInfo.age}</p>
- </div>
- })
-
- // 父组件
- function App() {
- console.log('Parent render...')
-
- const [count, setCount] = useState(0)
- const [name, setName] = useState('test')
-
- // const userInfo = { name, age: 20 }
- // 用 useMemo 缓存数据,有依赖
- // useMemo包裹后返回的对象是同一个,没有创建新的对象地址,不会触发子组件的重新渲染
- const userInfo = useMemo(() => {
- return { name, age: 21 }
- }, [name])
-
- return <div>
- <p>
- count is {count}
- <button onClick={() => setCount(count + 1)}>click</button>
- </p>
- <Child userInfo={userInfo}></Child>
- </div>
- }
-
- export default App
用于缓存函数,避免子组件频繁更新
useCallback需要配合React.memo使用才生效
- import React, { useState, memo, useMemo, useCallback } from 'react'
-
- // 子组件,memo 相当于 PureComponent
- const Child = memo(({ userInfo, onChange }) => {
- console.log('Child render...', userInfo)
-
- return <div>
- <p>This is Child {userInfo.name} {userInfo.age}</p>
- <input onChange={onChange}></input>
- </div>
- })
-
- // 父组件
- function App() {
- console.log('Parent render...')
-
- const [count, setCount] = useState(0)
- const [name, setName] = useState('test')
-
- // 用 useMemo 缓存数据
- const userInfo = useMemo(() => {
- return { name, age: 21 }
- }, [name])
-
- // function onChange(e) {
- // console.log(e.target.value)
- // }
- // 用 useCallback 缓存函数,避免在组件多次渲染中多次创建函数导致引用地址一致
- const onChange = useCallback(e => {
- console.log(e.target.value)
- }, [])
-
- return <div>
- <p>
- count is {count}
- <button onClick={() => setCount(count + 1)}>click</button>
- </p>
- <Child userInfo={userInfo} onChange={onChange}></Child>
- </div>
- }
-
- export default App
用于获取dom节点,以及逻辑处理
- import React, { useRef, useEffect } from 'react'
-
- function UseRef() {
- const btnRef = useRef(null) // 初始值
-
- // const numRef = useRef(0)
- // numRef.current
-
- useEffect(() => {
- console.log(btnRef.current) // DOM 节点
- }, [])
-
- return <div>
- <button ref={btnRef}>click</button>
- </div>
- }
-
- export default UseRef
用于处理上下文传递数据
- import React, { useContext } from 'react'
-
- // 主题颜色
- const themes = {
- light: {
- foreground: '#000',
- background: '#eee'
- },
- dark: {
- foreground: '#fff',
- background: '#222'
- }
- }
-
- // 创建 Context
- const ThemeContext = React.createContext(themes.light) // 初始值
-
- function ThemeButton() {
- // 在后代组件内使用useContext来接收App组件传递的数据;
- const theme = useContext(ThemeContext)
- // 展示数据
- return <button style={{ background: theme.background, color: theme.foreground }}>
- hello world
- </button>
- }
-
- function Toolbar() {
- return <div>
- <ThemeButton></ThemeButton>
- </div>
- }
-
- function App() {
- // 在父组件内使用Provider来向所有子组件传递数据
- return <ThemeContext.Provider value={themes.dark}>
- <Toolbar></Toolbar>
- </ThemeContext.Provider>
- }
-
- export default App
注意事项:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。