react版本发布时间
18.0.0版本 2022
17.0.0版本 2020
16.0.0版本 2017
15.0.0版本 2016
0.3.0 版本 2013
vue 版本发布时间
3.0.0版本 2020
2.0.0版本 2016
1.0.0版本 2015
0.6.0版本 2013
react资源链接
react英文官网:可用edge翻译
react中文官网
react版本日志
hook api 索引
hook api
react
react-dom
react-router-dom
react-redux
创建react项目
官网提供create-react-app建项目
引入ant design
配置文件暴露出来
1.npm run eject 暴露所有的配置文件 不可逆的,在发射前需要提交git文件
2.安装react-app-rewired 这个包需要把pakage.json中的脚本配置修改为下面代码,再在根目录下创建config-overrides.js的配置
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
react18是函数组建好,还是类组件好
React 18 并没有对类组件和函数组件进行任何变更或优化,而是着眼于改进渲染性能和开发体验等方面。因此,React 18 无论是对类组件还是函数组件,都可以得到支持和优化。
然而,随着 React 16+ 版本的推出,函数组件逐渐成为了主流,而类组件渐渐地被认为是更加繁琐和复杂,官方也更加推荐使用函数组件。因此,在 React 18 中,推荐使用函数式组件来开发新的页面和组件。具体的原因包括:
-
性能方面:函数组件在处理逻辑和渲染上更加快速,因为函数组件没有实例化、生命周期等开销。
-
简洁性方面:函数式组件可以使代码更加简洁、易读、易维护。这些组件不需要复杂的类继承,没有也不需要 constructor、render、setState 等。
-
状态管理方面:随着 React Hooks 的广泛使用,我们可以更方便地在函数组件中引入和管理状态。
需要注意的是,如果已经使用类组件实现了一些特定需求,那么不一定需要全部改写为函数组件。一方面,类组件仍然可以正常使用,也可以通过 React 对其进行优化以提高性能。另一方面,我们不应该为了追求新技术而盲目迁移,而是应该根据实际需求进行选择。
setState 和 useState 区别
setState
适用于类组件,具有合并更新和异步更新的特点;
useState
适用于函数组件,具有直接替换和同步更新的特点。
setState
和useState
是React中用于管理组件状态的两种常用方式,它们存在一些区别:
- 语法:
setState
是类组件中的方法,通过调用this.setState()
来更新组件的状态。在类组件中,状态通常是通过定义一个类属性state
来管理。useState
是函数组件中的钩子函数,通过调用const [state, setState] = useState(initialState)
来定义和更新组件的状态。在函数组件中,状态通常是通过调用useState
创建的。
- 定义状态的方式:
setState
在类组件中使用this.setState({stateName: value})
来定义和更新状态。它可以接收一个对象,也可以接收一个回调函数作为参数,用于基于当前状态值进行更新。useState
在函数组件中使用const [state, setState] = useState(initialState)
来定义和更新状态。它返回一个状态值和一个更新状态的函数。初始状态可以通过useState
的参数进行设置,通常是一个基本类型或一个对象。
- 更新状态的方式:
setState
对状态的更新是合并的。这意味着当使用setState
更新状态时,React将当前状态与新状态合并,并触发组件重新渲染。useState
更新状态时,需要调用返回的状态更新函数。该函数接收新的状态值作为参数,并完全替换之前的状态值。使用该函数更新状态后,React将重新渲染组件。
- 异步更新:
setState
是异步的,多个setState
调用会被合并为一个更新操作,从而提高性能。因此,如果在多个setState
之后读取组件的状态,可能会得到旧的状态值。useState
是同步的,状态更新是立即生效的。在函数组件中,每次调用状态更新函数,组件都会在同一渲染周期内重新渲染,并使用新的状态值。
总结起来,setState
适用于类组件,具有合并更新和异步更新的特点;而useState
适用于函数组件,具有直接替换和同步更新的特点。根据自己的项目需求和使用场景,选择适合的方式来管理组件的状态。
React.StrictMode是什么?
StrictMode
检查项目中潜在问题。
与 Fragment
一样,StrictMode 不会渲染任何可见的 UI。它为其后代元素触发额外的检查和警告。
Fragment
类似于 vue
中的template
Profiler是干啥的??
使用 <Profiler>
组件包裹 React
树以测量其渲染性能。
这需要两个属性:id(字符串)和 onRender
回调函数(函数),每当 React
树中的任何组件“提交”更新时都将调用该函数。
// Profiler组件可以使用多次
<Profiler id="Sidebar" onRender={onRender}>
<Sidebar />
</Profiler>
Suspense是干啥的???
<Suspense>
是 React v16.6+
新增的一个组件,它可以在懒加载组件时显示一个 loading
界面,直到所需的组件加载完成才渲染该组件。
具体来说,当使用 React.lazy()
对某个组件进行懒加载时,如果需要该组件的代码还没有被加载,则会显示由 <Suspense>
提供的占位符(如 loading
动画),等到组件代码加载完成后,再渲染具体的组件。这样就可以优化应用的性能和用户体验。
使用 <Suspense>
的示例代码如下:
import React, { lazy, Suspense } from 'react';
const SomeComponent = lazy(() => import('./SomeComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<SomeComponent />
</Suspense>
</div>
);
}
在上面的代码中,<Suspense>
提供了一个 fallback
属性,它接收一个 React
组件或元素作为占位符,这个占位符会在所依赖的组件加载完成前显示。一般来说,会在 fallback
中显示一些 loading
动画或者文字提示,以便提高用户体验。
Fragment是干啥的???
Fragment
组件用于包裹多个元素,而无需在 DOM
中添加额外的节点。这对于需要返回多个兄弟元素的组件非常有用。
<Fragment> (<>...</>)
<Fragment> 通常使用 <>...</>
代替,它们都允许你在不添加额外节点的情况下将子元素组合。
import React, { Fragment } from 'react';
function App() {
return (
<Fragment>
<h1>Title</h1>
<p>Content</p>
</Fragment>
);
}
详细介绍React.Portal、React.Children、React.createContext例子
当使用React来构建应用程序时,React库提供了一些有用的API和组件来帮助我们进行组件交互和状态管理。其中包括React.Portal、React.Children和React.createContext。
- React.Portal:
React.Portal组件允许我们在React组件树之外渲染子组件。通常情况下,React组件在父组件的上下文中渲染,但是有时我们可能希望将子组件渲染到DOM结构中的其他位置,而不是其直接父组件。这可以在应用程序中创建模态框、对话框、弹出窗口等功能时非常有用。
下面是一个使用React.Portal的例子:
import React from 'react';
import ReactDOM from 'react-dom';
function Modal({ children }) {
return ReactDOM.createPortal(
<div className="modal">
{children}
</div>,
document.getElementById('modal-root') // 将子组件渲染到指定的DOM元素中
);
}
function App() {
return (
<div>
<h1>我的应用程序</h1>
<Modal>
<p>这是一个模态框!</p>
</Modal>
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
在上面的例子中,Modal
组件使用ReactDOM.createPortal
将其子组件渲染到modal-root
元素中,而不是其直接父组件。这样可以确保模态框始终出现在组件树之外。
- React.Children:
React.Children提供了一些方法来处理React组件的子元素。它们允许我们遍历、映射和处理组件的所有子元素,无论子元素的类型如何。
下面是一个使用React.Children的例子:
import React from 'react';
function List({ children }) {
return (
<ul>
{React.Children.map(children, (child, index) => (
<li key={index}>{child}</li>
))}
</ul>
);
}
function App() {
return (
<List>
<span>项目1</span>
<span>项目2</span>
</List>
);
}
在上面的例子中,List
组件在渲染时使用React.Children.map方法遍历其子元素,并为每个子元素创建一个<li>
元素。这使得我们可以自由地在父组件内部操作和控制子元素。
- React.createContext:
React.createContext允许我们在组件之间共享数据,而不需要通过props层层传递。它创建了一个上下文对象,我们可以在树中的任何位置使用该上下文来访问其提供的数据。
下面是一个使用React.createContext的例子:
import React, { useContext } from 'react';
const MyContext = React.createContext();
function ComponentA() {
const data = useContext(MyContext);
return (
<div>
<h2>组件A</h2>
<p>来自上下文的数据: {data}</p>
</div>
);
}
function ComponentB() {
const data = useContext(MyContext);
return (
<div>
<h2>组件B</h2>
<p>来自上下文的数据: {data}</p>
</div>
);
}
function App() {
return (
<MyContext.Provider value="共享的数据">
<ComponentA />
<ComponentB />
</MyContext.Provider>
);
}
在上面的例子中,MyContext.Provider
组件包装了ComponentA
和ComponentB
,将一个值传递给它们作为共享数据。然后,在每个组件中,我们使用useContext
来获取该上下文中提供的数据,并在渲染时使用它。
这是React.Portal、React.Children和React.createContext的简单例子,它们在构建React应用程序时非常常见且有用。
react项目文件中 为什么都要导入react包
import React from "react";
import ReactDOM from 'react-dom'
let h1 = <h1>123</h1> //babel会编译这个 下面有编译的地址
function App() {
return h1
}
ReactDOM.render(<App />, document.getElementById('root'))
export default App;
let h1 = /*#__PURE__*/React.createElement("h1", null, "123");
可以看到这里用到了React 所以需要导入
用到了jsx语法 所以每个文件都需要导入react包
React16,17,18区别
react16
开始有fiber(expireTimes 十进制一个优先级)
React.createElement(jsx):页面需要强制导入React
事件委托将事件都绑定在document上,触发react事件后,也会触发用户绑定在document上的事件
异步使用event前需要加上event.persit() 这里是为了提高老浏览器性能
useEffect的清理函数 useLayoutEffect的清理函数 都是同步的 阻塞页面切换(如:切换标签)
16.6开始有Hook
16.3开始有getDerivedStateFromProps
16.4 setState 和 forceUpdate也可以触发getDerivedStateFromProps
react17小版本
优化fiber(lanes 二进制多个优先级)
自动从package中引入函数调用处理jsx:页面不需要导入React
事件委托将事件都绑定在root上
onScroll不冒泡
onFocus onBlur onClickCapture 底层切换为原生事件,和浏览器更接近
移除事件池 重用了老浏览器的事件对象 异步使用event 不需要加persit了
保留useLayoutEffect的清理函数同步性,把useEffect的清理函数设置为异步了执行,在屏幕更新完后执行清理
react18
ReactDOM.createRoot(rootDom).render(<App />)
合成事件 + setTimeout0 不再能解决state同步问题
原生事件也不再能解决state同步问题
concurrent:早就出了,现在开始投入使用,用户自定义任务优先级且通知React,如以下
useDeferredValue(text)延迟更新(模糊匹配) --- concurrent减少节流防抖的需求
[inPending,startTransition] = useTransition() 避免Suspense频繁出现loading
SuspenseList可以设置多个suspense展示问题
getDerivedStateFromError:之前就有了,用于捕获子组件错误如(suspense报错)
错误边界
React 16 引入了错误边界的概念。错误边界是指在组件树中,当子组件发生 JavaScript 错误时,不会影响其余子组件的渲染,而是会显示备用 UI。React 17 和 18 中还保留了错误边界的功能,不过没有引入新的语法。
生命周期更新
React 16.3 中引入了新的生命周期函数,例如getDerivedStateFromProps和componentDidCatch,以替换一些过时的生命周期方法。React 17 中取消了两个废弃的组件生命周期方法,即componentWillMount和componentWillReceiveProps。React 18 中有一些新的生命周期方法,如onScopeUpdate和useSyncExternalStore。
React Hooks 和函数组件
React 16.8 引入了 React Hooks,它允许您在无状态函数组件中使用状态管理和其他功能。使用 Hooks 可以帮助您在无需编写类组件的情况下编写可维护的代码。React 17 和 18 版本保留了这些 Hooks 的功能,并在 React 18 中引入了新的批量更新钩子。另外,React 18 还支持了分段渲染,可以让渲染异步化。
渲染模式和ReactDOM.createRoot
React 16 支持了两种渲染模式,即DOM 和 Server。React 17 引入了ReactDOM.createRoot方法,以替换ReactDOM.render方法,用于启用异步渲染和批量更新。React 18 又进一步完善了ReactDOM.createRoot方法,以更好地支持并发模式的渲染。
兼容性
React 16 和 17 向后兼容 React 15 中的代码,但您需要注意生命周期更新和getDefaultProps不再支持。React 18 目前还在测试阶段,而且要求您的主机环境需要一些额外的依赖,例如: Node.js 版本需要 12 或 14,可能需要配合V8 编译选项等。
性能
React 18 引入了一些性能优化,包括渲染器以及调度算法等。React 18 也支持增量更新,可以让组件只更新发生了变化的部分。同时,React 18 还支持了分段渲染,并且可以让渲染异步化,以更好地支持大型项目。
总的来说
React 16, 17 和 18 版本都在持续改进和优化 React 平台的功能,其中一些改进包括错误边界、新的生命周期函数、React Hooks 和函数组件、渲染模式和性能优化等。建议您根据自己的项目和需求,选择合适的 React 版本,并多试一些新特性,以便更好地理解它们并优化自己的项目。
react18常用的钩子语法,和生命周期钩子函数的使用
在 React 18 中,常用的钩子语法包括以下几个:
- useState:用于在函数组件中声明和使用状态。它返回一个状态值和更新状态的函数,可以通过解构赋值的方式获取。
import React, { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
- useEffect:用于在函数组件中执行副作用操作,例如订阅、定时器等。它接受一个函数和一个依赖数组作为参数,依赖数组中的值发生变化时会重新执行传入的函数。
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
- useContext:用于在函数组件中访问上下文数据。它接受一个上下文对象作为参数,并返回上下文对象中的值。
import React, { useContext } from 'react';
import MyContext from './MyContext';
function Example() {
const value = useContext(MyContext);
return <p>Value: {value}</p>;
}
- useRef:用于在函数组件中获取子组件或 DOM 元素的引用。它返回一个可变的 ref 对象,在渲染周期之间保持稳定,可以用于存储任意可变值。
import React, { useRef } from 'react';
function Example() {
const ref = useRef();
const handleButtonClick = () => {
ref.current.focus();
};
return (
<div>
<input ref={ref} />
<button onClick={handleButtonClick}>Focus Input</button>
</div>
);
}
这些是 React 18 中常用的钩子语法。需要注意的是,React 18 保留了之前版本中的生命周期方法,如componentDidMount
、componentDidUpdate
、componentWillUnmount
等,但推荐使用钩子函数来替代类组件的生命周期方法。您可以通过在函数组件中使用相应的钩子函数来实现类似的功能。
例如,使用useEffect
钩子来替代componentDidMount
和componentDidUpdate
方法:
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('Component mounted or updated');
// 清理副作用
return () => {
console.log('Component unmounted');
};
});
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
注意:即使 React 18 引入了新的钩子函数和渲染模式,类组件的生命周期方法和钩子函数仍然可以在 React 18 中使用。这些钩子函数和生命周期方法具有相似但并不完全相同的用途和用法。根据您的喜好和项目需求,选择适合的生命周期或钩子函数即可。
详细介绍useContext和useEffect的使用方法,解释传参的细节
useContext
和useEffect
是React 18中最常用的两个钩子函数。下面,我将分别详细介绍这两个钩子函数的使用方法。
useContext
useContext
是React 18提供的上下文API。它可以让我们在组件中访问某些全局的数据,而不需要通过props一级一级地往下传递,从而提高了组件之间的数据传递效率。使用useContext
需要先创建一个上下文对象,然后在组件中使用useContext
来访问上下文对象中的数据。
// 创建上下文对象
const MyContext = React.createContext();
// 在父组件中提供上下文数据
function Parent() {
return (
<MyContext.Provider value="Hello, World!">
<Child />
</MyContext.Provider>
);
}
// 在子组件中访问上下文数据
function Child() {
const value = useContext(MyContext);
return <p>{value}</p>;
}
通过调用React.createContext
方法可以创建一个上下文对象。在父组件中提供上下文数据时,需要使用<MyContext.Provider>
标签。这里的value
属性就是我们想要共享的数据。在子组件中使用useContext
来获取这个值。
需要注意的是,当上下文数据发生变化时,消费者组件(即使用useContext
的组件)会重新渲染。此时,如果是对象或数组等复杂类型的数据,最好使用useMemo
或React.memo
等工具进行优化。
useEffect
useEffect
是React 18提供的处理副作用的API。副作用指的是那些不依赖于函数输入和不改变函数输出的操作,比如数据请求、订阅、定时器、DOM操作等。使用useEffect
可以让我们在函数组件中执行这些操作。useEffect
需要传入两个参数:
useEffect(effect: EffectCallback, deps?: DependencyList): void;
其中,第一个参数effect
是一个函数,它需要处理副作用操作。这个函数会在组件渲染完成后执行。如果需要在组件更新后执行副作用操作,则需要在effect
中返回一个清理函数。
第二个参数deps
是一个可选的数组,它指定了effect
函数的依赖项。当deps
发生变化时,effect
函数会重新执行。如果没有传递deps
,则effect
函数会在每次组件更新时都执行。如果希望副作用只在组件挂载时执行一次,则可以将deps
设置为空数组[]
。
以下是一个使用useEffect
钩子的例子:
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('Component mounted');
// 在组件卸载时清理副作用
return () => {
console.log('Component unmounted');
};
}, [count]);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
在这个例子中,useEffect
钩子的第一个参数是一个函数,它会在组件渲染完成后执行。这个函数里打印了一条消息以表明副作用处理的开始。在组件卸载时,useEffect
会自动清理这个副作用。由于我们传递了[count]
作为第二个参数,这个副作用函数只会在count
发生变化时才会重新执行。
需要注意的是,在useEffect
的副作用函数中使用的所有变量都应该在deps
数组中列出。如果不这样做,可能会导致不必要的副作用操作和性能瓶颈。
还需要注意useEffect
的执行顺序
useState创建响应式数据,每次只能创建一个,react项目如何同时设置多个响应式数据,就像vue中的reactive一样
在 React
中,每个组件的内部状态(state
)是相互独立的,使用 useState
创建的状态变量也只能对应一个响应式数据。为了管理多个响应式数据,可以使用第三方的状态管理库,比如 Redux
或者 MobX
。
Redux
是 React
最流行的状态管理库,它可以帮助我们将整个应用程序的状态存储在一个中央存储(store)中,从而方便地管理多个响应式数据。在 Redux 中,我们需要定义一个 state 树,然后根据这个 state 树来创建 store 对象。每次更新 state 树的时候,我们都需要调用 store.dispatch 方法来触发更新,最终会通过 store.subscribe 方法通知所有订阅者(即 React 组件)进行更新。在 React 中调用 store.subscribe 方法可以使用 React-Redux 库提供的 useSelector
和 useDispatch
钩子函数。
MobX 是另一个流行的状态管理库,它使用了类似 vue 中 reactive 的响应式数据机制。我们可以创建一个 observable 对象,然后在需要使用响应式数据的地方通过调用 observable 对象上的属性或方法来创建响应式数据。MobX 还提供了多个辅助函数,比如 computed
和 autorun
,用于方便地管理响应式数据。
总而言之,如果需要在 React 项目中同时管理多个响应式数据,推荐使用 Redux 或 MobX 等第三方状态管理库来实现。这些库不仅可以管理多个响应式数据,还能方便地实现数据的共享和组件通信。
详细介绍Redux使用案例和MobX使用案例
Redux 使用案例:
以一个简单的计数器为例,介绍如何使用 Redux。
1.安装 Redux 和 React-Redux:
npm install redux react-redux
2.创建 Reducer:
const initialState = { count: 0 };
function counterReducer(state = initialState, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
}
export default counterReducer;
3.创建 Store:
import { createStore } from 'redux';
import counterReducer from './counterReducer';
const store = createStore(counterReducer);
export default store;
4.在组件中使用:
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
function Counter() {
const count = useSelector(state => state.count);
const dispatch = useDispatch();
const handleIncrement = () => dispatch({ type: 'increment' });
const handleDecrement = () => dispatch({ type: 'decrement' });
return (
<div>
<h1>Count: {count}</h1>
<button onClick={handleIncrement}>Increment</button>
<button onClick={handleDecrement}>Decrement</button>
</div>
);
}
export default Counter;
MobX 使用案例:
以一个简单的 TodoList 为例,介绍如何使用 MobX。
1.安装 MobX
和 MobX React
:
npm install mobx mobx-react
2.创建 Store:
import { makeObservable, observable, action } from 'mobx';
class TodoStore {
todos = [];
constructor() {
makeObservable(this, {
todos: observable,
addTodo: action,
removeTodo: action
});
}
addTodo = (text) => {
this.todos.push({ text });
}
removeTodo = (index) => {
this.todos.splice(index, 1);
}
}
const store = new TodoStore();
export default store;
3.在组件中使用:
import React, { useState } from 'react';
import { observer } from 'mobx-react-lite';
import todoStore from './todoStore';
const TodoList = observer(() => {
const [text, setText] = useState('');
const handleAddTodo = () => {
todoStore.addTodo(text);
setText('');
}
const handleRemoveTodo = (index) => {
todoStore.removeTodo(index);
}
return (
<div>
<input type="text" value={text} onChange={e => setText(e.target.value)} />
<button onClick={handleAddTodo}>Add</button>
{todoStore.todos.map((todo, index) => (
<div key={index}>
<span>{todo.text}</span>
<button onClick={() => handleRemoveTodo(index)}>Remove</button>
</div>
))}
</div>
);
});
export default TodoList;
以上就是 Redux 和 MobX 的简单使用案例,可以根据需求进一步扩展。需要注意的是,这里只是演示了如何在 React 中使用 Redux 和 MobX,如果要在其他框架中使用,可能需要对 API 进行适当调整。
mobx-react-lite 和mobx-react 区别是什么
mobx-react-lite
和 mobx-react
都是用于在 React 应用中集成和使用 MobX 状态管理的库,但它们之间有一些区别。
mobx-react-lite
是 mobx-react
的轻量级替代方案,专注于提供与 React Hooks 的无副作用和无性能损失的集成。它提供了一个名为 observer
的函数,用于创建响应式的 React 组件。observer
函数可以在函数组件和类组件中使用,并且会自动对组件进行订阅和更新,以便响应使用的 MobX 观察数据的变化。由于它的设计精简,mobx-react-lite
提供了更快的运行时性能和更小的包体积,适用于大多数简单或中等复杂度的场景。
而 mobx-react
是 MobX 官方提供的 React 集成库,它提供了一些额外的功能和扩展,例如 Provider
和 inject
这样的高阶组件。mobx-react
在功能和灵活性上更为丰富,适用于对于高度可定制化和复杂的状态管理需求。然而,它的包体积较大,并且在某些特定情况下可能会有一些性能开销。
综上所述,如果你的项目简单而且性能要求较高,可以考虑使用 mobx-react-lite
;如果你需要更多的功能和灵活性,可以选择 mobx-react
。根据具体的项目需求和性能要求,你可以选择适合你的库。
mobx-react-lite的使用需要安装什么
mobx-react-lite
的使用需要安装以下依赖:
mobx
: MobX 状态管理库;mobx-react-lite
: MobX 在 React 中的轻量级集成库。
你可以在项目目录下执行以下命令进行安装:
npm install mobx mobx-react-lite
这将会安装 mobx
和 mobx-react-lite
两个库及其所有依赖。安装完成后,你就可以在 React 组件中使用 mobx-react-lite
提供的 observer
函数来观察和响应 MobX 状态的变化。同时,使用 MobX 提供的 observable
、computed
等方法来定义和更新状态数据。
mobx-react包是涵盖了mobx-react-lite的包吗
是的,mobx-react
包含了 mobx-react-lite
的功能,它提供了与 React 的完整集成,包括更多的高阶组件和工具方法。
mobx-react
提供了一些额外的功能,如 Provider
和 inject
,用于更方便地管理 MobX 的状态和注入机制。它还提供了一些高阶组件,如 Observer
和 disposeOnUnmount
,用于更精细地控制组件的更新和资源的清理。
与此相比,mobx-react-lite
更精简,专注于提供与 React Hooks 的无副作用和无性能损失的集成。它只提供了一个名为 observer
的函数,用于创建响应式的 React 组件。由于其设计的轻量化,mobx-react-lite
相对于 mobx-react
有更快的运行时性能和更小的包体积。
因此,如果你想要更多功能和更高度的自定义能力,可以选择使用 mobx-react
。如果你的项目需求相对简单而且性能要求较高,可以选择使用 mobx-react-lite
。
类组件 和 函数组件
函数组件使用hooks 可以替换类组件
受控组件和 非受控组件
<input value={name} onChange={this.changeName} /> // change = e => this.setState({name:e.target.value})}
<input ref={(el)=>{this.qq = el }} /> // this.qq 就是当前dom
react常用写法
<button onClick={this.fn.bind(this)}>btn</button> //当fn中用到 this时 需要用bind绑定this
<div dangerouslySetInnerHTML={{ __html:'<h2>h2</h2>' }} /> // 渲染字符串dom
{arr.map(item => { })} //渲染数组
{bol ? a : b} // 三元运算符 类似v-if
<div id={id} className={className} style={{ height: '30px' }}>{text}</div>//花括号 可以写js语句
fn = () => {this.props.change([])} // 子组件触发 父组件传过来的方法change 间接修改父组件数据
setState注意事项
react生命周期
getDerivedStateFromProps和老钩子关系
组件复合
redux
react-redux
react-router
1. react-router
2. react-router中⽂⽂档
react-router包含3个库,react-router、react-router-dom和react-router-native。
react-router提供最基本的路由功能,实际使⽤的时候我们不会直接安装react-router,
⽽是根据应⽤运⾏的环境选择安装react-router-dom(在浏览器中使⽤)
或react-router-native(在rn中使⽤)。
react-router-dom和react-router-native都依赖react-router,
所以在安装时,react-router也会⾃动安装,创建web应⽤,使⽤:
npm install --save react-router-dom
import React, { Component } from 'react';
import { BrowserRouter as Router, Link, Route, useParams, Switch, withRouter } from 'react-router-dom';
export default class RouterPage extends Component {
render() {
return (
<div>
<h3>RouterPage</h3>
<Router>
{/* 添加Switch表示仅匹配⼀个 解决下面的EmptyPage*/}
<Switch>
<Link to="/">⾸⻚</Link>
<Link to="/user">⽤户中⼼</Link>
{/* 根路由要添加exact,实现精确匹配 解决/ 和 /user同时显示 */}
<Route
exact
path="/"
component={HomePage}
//children={() => <div>children</div>} 优先级高
//render={() => <div>render</div>}
// children 优先级高 跟其他的Route不是互斥的
// component 优先级中 跟其他的Route互斥的
// render 优先级低 跟其他的Route互斥的
/>
<Route path="/user" component={UserPage} />
<Route component={EmptyPage} />
</Switch>
</Router>
</div>
);
}
}
路由重定向
import { Redirect, Route } from 'react-router'
<Redirect to={{ pathname: '/login', state: { redirect: path } }} />;
const { redirect = '/' } = location.state || {}
动态路由
<Route path="/search/:id" children={<SearchComponent />} />
function SearchComponent(props) {
const { id } = useParams(); // props.match.params;
return (
<div>
<Link to={"/search/" + id + "/detail"}>详情</Link>
<Route
path={"/search/" + id + "/detail"}
children={<DetailComponent />}
/>
</div>
);
}
{/* 渲染component的时候会调用React.createElement,如果使用下面这种匿名函数的形式,每次都会生成一个新的匿名的函数,
导致生成的组件的type总是不相同,这个时候会产生重复的卸载和挂载 */}
{/* 错误举例 课下自己尝试下 观察下child的didMount和willUnmount函数 */}
<Route component={() => <Child count={count} />} /> //state变化会 反复执行 挂载和卸载
<Route component={() => <FunctionChild count={count} />} />//state变化会 反复执行 挂载和卸载
{/* 下面才是正确的示范 */}
<Route render={() => <Child count={count} />} />
<Route render={() => <FunctionChild count={count} />} />
{/* children 呢 */}
<Route children={() => <Child count={count} />} />
<Route children={() => <FunctionChild count={count} />} />
部署不是根文件 服务于子文件夹 就用到了 basename
纯组件
React.PureComponent 与 React.Component 很相似。两者的区别在于 React.Component 并未实
现 shouldComponentUpdate() ,⽽ React.PureComponent 中以浅层对⽐ prop 和 state 的⽅式来
实现了该函数。
如果赋予 React 组件相同的 props 和 state, render() 函数会渲染相同的内容,那么在某些情况下使
⽤ React.PureComponent 可提⾼性能。
React.PureComponent 中的 shouldComponentUpdate() 仅作对象的浅层⽐较。如果对象中
包含复杂的数据结构,则有可能因为⽆法检查深层的差别,产⽣错误的⽐对结果。仅在你的
props 和 state 较为简单时,才使⽤ React.PureComponent ,或者在深层数据结构发⽣变化时
调⽤ forceUpdate() 来确保组件被正确地更新。你也可以考虑使⽤ immutable 对象加速嵌套
数据的⽐较。
此外, React.PureComponent 中的 shouldComponentUpdate() 将跳过所有⼦组件树的 prop
更新。因此,请确保所有⼦组件也都是“纯”的组件。
import React, { Component, PureComponent } from "react";
export default class PureComponentPage extends PureComponent {
constructor(props) {
super(props);
this.state = {
counter: 0,
// obj: {
// num: 2,
// },
};
}
setCounter = () => {
this.setState({
counter: 100,
// obj: {
// num: 200,
// },
});
};
render() {
const { counter, obj } = this.state;
console.log("render");
return (
<div>
<h1>PuerComponentPage</h1>
<div onClick={this.setCounter}>counter: {counter}</div>
</div>
);
}
}
Hook
自定义hook 和 hook规则
<p>{useClock().toLocaleTimeString()}</p>
//⾃定义hook,命名必须以use开头
function useClock() {
const [date, setDate] = useState(new Date());
useEffect(() => {
console.log("date effect");
//只需要在didMount时候执⾏就可以了
const timer = setInterval(() => {
setDate(new Date());
}, 1000);
return () => clearInterval(timer);//清除定时器,类似willUnmount
}, []);
return date;
}
Hook 就是 JavaScript 函数,但是使⽤它们会有两个额外的规则:
只能在函数最外层调⽤ Hook。不要在循环、条件判断或者⼦函数中调⽤。
只能在 React 的函数组件中调⽤ Hook。不要在其他 JavaScript 函数中调⽤。(还有⼀个地⽅可
以调⽤ Hook —— 就是⾃定义的 Hook 中。)
介绍🪝useState,useEffect,useContext,useReducer,useCallback,useMemo,useRef,useImperativeHandle,useLayoutEffect,useDebugValue,useDeferredValue,useTransition,useId,useSyncExternalStore,useInsertionEffect
useState:
import React, { useState } from 'react';
const ExampleComponent = () => {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
};
使用场景:当你需要在函数组件中使用状态来存储数据并且希望在状态更新时重新渲染组件时,可以使用 useState。
useEffect:
import React, { useState, useEffect } from 'react';
const ExampleComponent = () => {
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
const response = await fetch('https://example.com/api/data');
const result = await response.json();
setData(result);
};
fetchData();
}, []);
return (
<div>
<p>{data ? data.title : 'Loading...'}</p>
</div>
);
};
使用场景:当你需要在组件渲染后执行副作用操作,比如获取数据、订阅事件等,可以使用 useEffect。
useContext:
import React, { useContext } from 'react';
const ThemeContext = React.createContext('light');
const ExampleComponent = () => {
const theme = useContext(ThemeContext);
return (
<div>
<p>Current Theme: {theme}</p>
</div>
);
};
使用场景:当你需要在组件中访问全局的状态或配置信息时,可以使用 useContext 来方便地获取上下文中的值。
useReducer:
import React, { useReducer } from 'react';
const initialState = { count: 0 };
const reducer = (state, action) => {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
};
const ExampleComponent = () => {
const [state, dispatch] = useReducer(reducer, initialState);
const increment = () => {
dispatch({ type: 'increment' });
};
const decrement = () => {
dispatch({ type: 'decrement' });
};
return (
<div>
<p>Count: {state.count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
};
使用场景:当你的组件有复杂的状态逻辑,并且需要进行状态的派发和管理时,可以使用 useReducer 来管理和更新状态。
以下是对 useCallback、useMemo、useRef 和 useImperativeHandle 的代码示例和使用场景解释:
useCallback:
import React, { useState, useCallback } from 'react';
const ExampleComponent = () => {
const [count, setCount] = useState(0);
const handleIncrement = useCallback(() => {
setCount(count + 1);
}, [count]);
return (
<div>
<p>Count: {count}</p>
<button onClick={handleIncrement}>Increment</button>
</div>
);
};
使用场景:当你需要将回调函数进行记忆化,并且希望在依赖变化时重新创建新的回调函数时,可以使用 useCallback。
useMemo:
import React, { useState, useMemo } from 'react';
const ExampleComponent = () => {
const [count, setCount] = useState(0);
const expensiveCalculation = useMemo(() => {
return count * 2;
}, [count]);
return (
<div>
<p>Count: {count}</p>
<p>Result of Expensive Calculation: {expensiveCalculation}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
使用场景:当你需要根据依赖值进行计算,并且希望在依赖变化时重新进行计算并返回新值时,可以使用 useMemo。
useRef:
import React, { useEffect, useRef } from 'react';
const ExampleComponent = () => {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus();
}, []);
return (
<div>
<input ref={inputRef} type="text" />
</div>
);
};
使用场景:当你需要在函数组件中创建一个在组件渲染周期保持不变的 mutable ref 对象时,可以使用 useRef。
useImperativeHandle:
import React, { useEffect, useRef, useImperativeHandle, forwardRef } from 'react';
const CustomInput = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return (
<input ref={inputRef} type="text" />
);
});
const ExampleComponent = () => {
const customInputRef = useRef();
useEffect(() => {
customInputRef.current.focus();
}, []);
return (
<div>
<CustomInput ref={customInputRef} />
</div>
);
};
使用场景:当你需要在父组件中自定义子组件暴露的实例值或方法时,可以使用 useImperativeHandle 来控制子组件暴露给父组件的实例值或方法。
以下是对 useLayoutEffect、useDebugValue、useDeferredValue 和 useTransition 的代码示例和使用场景解释:
useLayoutEffect:
import React, { useState, useLayoutEffect } from 'react';
const ExampleComponent = () => {
const [width, setWidth] = useState(0);
useLayoutEffect(() => {
const handleResize = () => {
setWidth(window.innerWidth);
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
return (
<div>
<p>Window Width: {width}</p>
</div>
);
};
使用场景:当你需要在组件渲染后同步执行副作用操作并且在浏览器执行绘制之前同步执行操作时,可以使用 useLayoutEffect。
useDebugValue:
import React, { useState, useDebugValue } from 'react';
const ExampleComponent = () => {
const [count, setCount] = useState(0);
useDebugValue(count > 5 ? 'High Count' : 'Low Count');
const handleIncrement = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={handleIncrement}>Increment</button>
</div>
);
};
使用场景:当你想在 React 开发者工具中为自定义 Hook 提供额外的调试信息时,可以使用 useDebugValue。
useDeferredValue:
import React, { useState, useDeferredValue } from 'react';
const ExampleComponent = () => {
const [count, setCount] = useState(0);
const deferredCount = useDeferredValue(count, { timeoutMs: 1000 });
const handleIncrement = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {deferredCount}</p>
<button onClick={handleIncrement}>Increment</button>
</div>
);
};
使用场景:当你想推迟需要使用的值的更新操作,以减轻主线程负载并提高性能时,可以使用 useDeferredValue。
useTransition:
import React, { useState, useTransition } from 'react';
const ExampleComponent = () => {
const [items, setItems] = useState([]);
const [startTransition, isPending] = useTransition();
const addItem = () => {
startTransition(() => {
setItems([...items, Date.now()]);
});
};
return (
<div>
<button onClick={addItem} disabled={isPending}>
Add Item
</button>
<ul>
{items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</div>
);
};
使用场景:当你在执行可能会产生延迟的操作(如数据加载或动画效果)时,希望在加载期间向用户展示提示或 loading 状态时,可以使用 useTransition。
高阶组件
HOC: 是个函数,接收一个组件,返回一个新的组件
function Child(props) {
return <div>Child</div>;
}
const foo = Cmp => props => {
return (
<div className="border">
<Cmp {...props} />
</div>
);
};
const Foo = foo(foo(Child)) 可以链式调用
<Foo /> 渲染
装饰器
高阶函数写的太繁琐了
装饰器安装方法
经过 eject 后在 package.json 中的 plugins 中配置
在babel对象 修改为下面代码
{
"babel": {
"presets": [
"react-app"
],
"plugins": [
[
"@babel/plugin-proposal-decorators",
{ "legacy": true }
]
]
}
}
create-react-app 脚手架中已经安装了 @babel/plugin-proposal-decorators 插件,如果是自己配置的脚手架,则先要安装插件:`npm install @babel/plugin-proposal-decorators --save-dev
表单组件封装
表单一般使用和装饰器使用# 表单组件装饰器用法 自定义实现
理解到:@a 去装饰b组件 其实a是一个函数,它的参数是组件b,需要返回一个b组件,也可以是一个匿名组件内部render b组件
@kFormCreate
class MyFormPage extends Component {
const {getFieldsValue, getFieldValue, validateFields} = this.props;
validateFields((err, values) => {});
console.log( getFieldsValue(), getFieldValue("name"));
const {getFieldDecorator} = this.props;
{getFieldDecorator("name", {rules: [nameRules]})(
<input type="text" placeholder="please input ur name" />
)}
}
export default function kFormCreate(Cmp) {// Cmp => MyFormPage
return class extends Component {
constructor(props) {
super(props);
this.state = {};
this.options = {};
}
handleChange = e => {
let { name, value } = e.target;
this.setState({ [name]: value });
};
getFieldDecorator = (field, option) => {
this.options[field] = option;
return InputCmp => {
return React.cloneElement(InputCmp, {
name: field,
value: this.state[field] || "",
onChange: this.handleChange
});
};
};
getFieldsValue = () => {
return { ...this.state };
};
getFieldValue = field => {
return this.state[field];
};
validateFields = callback => {
const errors = {};
const state = { ...this.state };
for (let name in this.options) {
if (state[name] === undefined) {
errors[name] = "error";
}
}
if (JSON.stringify(errors) === "{}") {
callback(undefined, state);
} else {
callback(errors, state);
}
};
render() {
return (
<div className="border">
<Cmp
getFieldDecorator={this.getFieldDecorator}
getFieldsValue={this.getFieldsValue}
getFieldValue={this.getFieldValue}
validateFields={this.validateFields}
/>
</div>
);
}
};
}
实现一个dialog组件 使用传送门portal
import Dialog from "../components/Dialog";
export default class DialogPage extends Component {
constructor(props) {
super(props);
this.state = {
showDialog: false
};
}
render() {
const { showDialog } = this.state;
return (
<div>
<button
onClick={() => {
this.setState({ showDialog: !showDialog });
}}>
toggle
</button>
{showDialog && (
<Dialog>
<p>我是一段文本</p>
</Dialog>
)}
</div>
);
}
}
import {createPortal} from "react-dom";
export default class Dialog extends Component {
constructor(props) {
super(props);
const doc = window.document;
this.node = doc.createElement("div");
doc.body.appendChild(this.node);
}
componentWillUnmount() {
window.document.body.removeChild(this.node);
}
render() {
return createPortal(
<div className="dialog">
{this.props.children}
</div>,
this.node
);
}
}
antd4底层实现
context
关键词
let Context = React.createContext()
Context.Provider
Context.Consumer
static contextType = Context 类组件使用context
React.useContext(Context)
compose实现
compose 英[kəmˈpəʊz]
function f1(v) {
return 'f1-' + v + '-f1'
}
function f2(v) {
return 'f2-' + v + '-f2'
}
function f3(v) {
return 'f3-' + v + '-f3'
}
var r = f3(f2(f1('demo')))
console.log(r)
var re = compose(f1, f2, f3)('demo')
console.log(re)
function compose(...params) {
return function (...str) {
return params.reduce((accumulator, onec, index) => {
return onec(accumulator)
}, ...str)
}
}
还有一种方法
function compose(...params) {
return params.reduce(
(accumulator, onec) => {
return (...args) => accumulator(onec(...args))
}
)
}
reduce 不传第二个参数则 accumulator是数组第一项,onec从第二项开始直到最后一项,内部函数的返回值就是accumulator后续值
简写为下面
const compose = (...arr) => arr.reduce((a, b) => (...v) => a(b(...v)))
处理极端情况
function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg;
// return () => {};
}
if (funcs.length === 1) {
return funcs[0];
}
return funcs.reduce((accumu, f) => (...args) => accumu(f(...args)));
}
redux的 combineReducers createStore applymiddleware以及中间件原理
redux默认dispatch 只能传对象
react-redux原理
react-router原理
组件可以使用到 Route内部返回 的 RouterContext.Provider
mini-react
requestIdleCallback
是 Web API
的一部分,它是一种浏览器提供的新的调度 API
,用于在浏览器的空闲时间运行一些低优先级的任务,以便使界面保持流畅并尽可能响应用户的操作。在React中,可以使用这个API来实现延迟一些不紧急的工作,以减少主UI线程的负载,从而提高整个应用程序的性能和流畅度。
在React
中,使用 requestIdleCallback
可以让我们在浏览器空闲时间执行低优先级的任务,例如:
-
执行一些耗时较长的计算。
-
执行一些需要处理大量数据的代码(如排序和筛选)。
-
批处理需要进行的操作,以避免一次性进行许多操作。
-
进行
DOM
操作,例如更新布局或样式。
在React
中,可以使用requestIdleCallback
来让渲染任务分步执行,这样可以避免堵塞UI线程并提高性能。例如,在组件渲染过程中,可以使用requestIdleCallback
来延迟渲染大量数据,这样用户可以立即看到一部分数据,并在后台继续加载剩余数据。
值得注意的是,requestIdleCallback
并不适合所有场景。需要根据实际情况来决定是否使用它,以及如何使用它。如果交互和渲染效果非常重要,那么不应该使用requestIdleCallback
,而是应该优化性能并使用其他方法来提高页面的渲染速度和响应速度。
代码地址
export const NoFlags = 0b00000000000000000000;
export const Placement = 0b0000000000000000000010; // 2
export const Update = 0b0000000000000000000100; // 4
export const Deletion = 0b0000000000000000001000; // 8
export function isStr(s) {
return typeof s === "string";
}
export function isStringOrNumber(s) {
return typeof s === "string" || typeof s === "number";
}
export function isFn(fn) {
return typeof fn === "function";
}
export function isArray(arr) {
return Array.isArray(arr);
}
import { Placement } from "./utils";
export default function createFiber(vnode, returnFiber) {
const fiber = {
type: vnode.type,
key: vnode.key,
props: vnode.props,
stateNode: null, // 原生标签时候指dom节点,类组件时候指的是实例
child: null, // 第一个子fiber
sibling: null, // 下一个兄弟fiber
return: returnFiber, // 父fiber
// 标记节点是什么类型的
flags: Placement,
// 老节点
alternate: null,
deletions: null, // 要删除子节点 null或者[]
index: null, //当前层级下的下标,从0开始
};
return fiber;
}
export function updateNode(node, nextVal) {
Object.keys(nextVal).forEach((k) => {
if (k === "children") {
if (isStringOrNumber(nextVal[k])) {
node.textContent = nextVal[k] + "";
}
} else {
node[k] = nextVal[k];
}
});
}
JSX
在线尝试jsx
JSX(JavaScript XML)
是一种用于在JavaScript
代码中编写HTML
和XML
结构的语法扩展。它是React
框架中的一部分,用于定义组件的UI
。JSX
允许将HTML
标签直接嵌入到JavaScript
代码中,以及在组件中使用动态的JavaScript
表达式。
JSX
的语法类似HTML
,但也有一些区别。以下是一些JSX
的特点:
-
标签:
JSX
使用类似HTML
的标签,例如<div>
、<p>
、<span>
等。这些标签用于定义组件的结构和样式。标签必须以关闭标签的方式使用,或者以自闭合标签的方式结束,例如<img>
或<br>
。 -
表达式插值:可以使用花括号({})将
JavaScript
表达式嵌入到JSX
中。这样可以动态地渲染组件的属性和内容。例如:<h1>Hello, {name}!</h1>
-
属性:可以为
JSX
元素添加属性,就像HTML
标签一样。属性可以传递静态值,也可以使用JavaScript
表达式动态地设置值。例如:<img src={imageUrl} alt="Image" />
-
组件:
JSX
可以用来定义组件。组件是可重用的UI
元素,它们可以被渲染和嵌套在其他组件中。可以使用标签形式调用组件,并通过属性传递数据。例如:function MyComponent(props) { return <div>{props.text}</div>; }
-
样式:可以在
JSX
中直接使用内联样式,使用style
属性传递一个对象,其中每个键名是CSS属性,键值是对应的样式值。例如:<div style={{ color: 'red', fontSize: '14px' }}>Styled Text</div>
值得注意的是,尽管JSX
看起来类似HTML
,但它实际上是一种语法扩展,经过编译后会被转换为正常的JavaScript
代码。React
使用Babel
等工具将JSX
编译为可运行的JavaScript
。这样,可以在浏览器中使用JSX
,并让它与普通的JavaScript
代码一起工作。
react的vscode插件
vscode 代码提示插件
也可以使用自定义的