1,React 组件之间如何通讯?
props、context
2,JSX本质是什么
JSX(JavaScript XML)本质上是一种 JavaScript 语法扩展,用于在 React 中描述用户界面的结构。它的本质可以分为以下几个方面:
-
语法糖:JSX 是一种语法糖,它允许您以一种更像是 HTML 的方式来编写 React 组件的结构。这使得编写和阅读组件的代码更加直观和容易。
-
JavaScript 表达式:JSX 允许您在花括号
{}
内嵌入 JavaScript 表达式。这意味着您可以在 JSX 中执行任何有效的 JavaScript 表达式,并将其结果插入到渲染的输出中。 -
React 元素:JSX 最终会被转译成 React 元素。React 元素是描述组件结构的纯 JavaScript 对象,它包含有关要渲染的组件类型、属性以及子元素的信息。JSX 的编译过程将 JSX 语法转换为 React 元素,然后 React 将这些元素渲染到 DOM 中。
-
可嵌套性:JSX 允许您像 HTML 标记一样嵌套元素,构建出复杂的组件结构。这种可嵌套性使得构建组件树变得非常容易,类似于编写常规的 HTML 结构。
-
总之,JSX 是一种用于编写 React 组件的语法糖,它允许将组件结构以声明性的方式描述,并在编译时转换为纯 JavaScript,以便 React 可以渲染它们到页面上。这种方式使得编写和维护 React 应用程序变得更加容易和直观
3,Context 是什么,如何应用
Context 提供了一种在组件之间共享这些值的方法,而无需显式地通过树的每一层传递 prop
- 基本使用
- 创建和注入context
context一般在顶层组件创建,方便数据的全局注入和全局共享
import React,{Component,createContext} from 'react'
//使用createContext创建context的初始默认值
//可以是对象,字符串等任意类型的值
export const GlobalContext = createContext({name:'scw'})
//App为顶层组件
class App extends Component{
constructor(props) {
super(props);
}
handleClick = (e) => {
console.log('组件B点击了');
}
render() {
return (
//使用Provider进行context全局注入
//这里的value表示对context重新赋值
<GlobalContext.Provider value={{name:'scw1',onClick:this.handleClick.bind(this)}}>
<B />
</GlobalContext.Provider>
)
}
}
export default App;
作者:药师kabuto
链接:https://juejin.cn/post/6969188665532612639
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
2.消费Context
import { GlobalContext } from './App'
const B = class extends Component{
render() {
return <GlobalContext.Consumer>
{(globalContext)=>
//消费Context的数据
<span onClick={globalContext.onClick}>
{globalContext.name}
</span>
}
</GlobalContext.Consumer>
}
}
export default B
- 使用Provider在顶层组件注入后,里层的所有子组件及其后代组件均可访问到对应的context数据
- 调用creatContext时可以传入默认值,但该值不是Provider的默认值,而是作为Consumer的默认值,仅当没有Provider的时候才生效。换句话说,只有在没有使用Provider的情况下使用了Consumer,Consumer消费的才是默认值。如果注入Provider时没有设置value值,则Consumer拿到的value为undefined
- 多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。
- 当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染。Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数,因此当 consumer 组件在其祖先组件退出更新的情况下也能更新。
// Theme context,默认的 theme 是 “light” 值
const ThemeContext = React.createContext('light');
// 用户登录 context
const UserContext = React.createContext({
name: 'Guest',
});
class App extends React.Component {
render() {
const {signedInUser, theme} = this.props;
// 提供初始 context 值的 App 组件
return (
<ThemeContext.Provider value={theme}>
<UserContext.Provider value={signedInUser}>
<Layout />
</UserContext.Provider>
</ThemeContext.Provider>
);
}
}
function Layout() {
return (
<div>
<Sidebar />
<Content />
</div>
);
}
// 一个组件可能会消费多个 context
function Content() {
return (
<ThemeContext.Consumer>
{theme => (
<UserContext.Consumer>
{user => (
<ProfilePage user={user} theme={theme} />
)}
</UserContext.Consumer>
)}
</ThemeContext.Consumer>
);
}
3.contextType
给顶层class组件(即Provider注入的那个组件中)设置静态属性contextType并赋值为新建的context,则你可以在任何生命周期中通过this.context访问到它,包括 render 函数中
const MyContext = React.createContext(defaultValue);
class MyClass extends React.Component {
static contextType = MyContext
componentDidMount() {
let value = this.context;
/* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */
}
componentDidUpdate() {
let value = this.context;
/* ... */
}
componentWillUnmount() {
let value = this.context;
/* ... */
}
render() {
let value = this.context;
/* 基于 MyContext 组件的值进行渲染 */
}
}
如果是函数组件,则在props后面,会多出一个参数context
const Title = (props, context) => {
const {textColor} = context.theme;
return (
<p style={{color: color}}>
我是标题
</p>
);
};
Title.contextTypes = {
theme: PropTypes.object
};
我并不建议大量使用 context,因为尽管它可以减少逐层传递,但当组件结构复杂的时候,我们并不知道 context 是从哪里传过来的。Context 就像一个全局变量一样,而全局变量正是导致应用走向混乱的罪魁祸首之一,给组件带来了外部依赖的副作用。在大部分情况下,我们并不推荐使用 context 。使用 context 比较好的场景是真正意义上的全局信息且不会更改,例如界面主题、用户信息等
4.说一下shouldComponentUpdate的用途
shouldComponentUpdate
是 React 组件生命周期中的一个方法,用于控制组件是否需要重新渲染。它的主要用途是优化性能,避免不必要的组件渲染。
默认情况下,当组件的状态或属性发生变化时,React 会重新渲染该组件及其所有子组件。然而,在某些情况下,组件的重新渲染可能是不必要的,特别是当组件的状态或属性没有实际变化时。这时,可以使用 shouldComponentUpdate
来告诉 React 是否需要重新渲染组件。
shouldComponentUpdate
方法接收两个参数:nextProps
和 nextState
,它们分别表示组件即将接收的新属性和新状态。该方法应返回一个布尔值,指示组件是否应该重新渲染。如果返回 true
,组件将重新渲染;如果返回 false
,组件将跳过重新渲染。
通过在 shouldComponentUpdate
方法中实现自定义的比较逻辑,您可以根据需要决定是否重新渲染组件。这可以帮助您避免不必要的渲染,提高应用程序的性能。
以下是 shouldComponentUpdate
的一些常见用途:
-
性能优化: 当组件的属性或状态没有发生实际变化时,可以返回
false
,避免不必要的重新渲染。这在组件的渲染开销较大或频繁更新时特别有用。 -
避免子组件的不必要渲染: 如果组件的属性或状态变化不会影响子组件的渲染结果,可以在
shouldComponentUpdate
中返回false
,以避免子组件的不必要渲染。 -
比较复杂对象或数组: 当组件的属性或状态是复杂对象或数组时,可以在
shouldComponentUpdate
中实现自定义的比较逻辑,只有在实际变化时才返回true
。
需要注意的是,如果不实现 shouldComponentUpdate
方法,或者该方法返回 true
,则组件将始终重新渲染。因此,只有在需要优化性能时才应该使用 shouldComponentUpdate
,并确保实现正确的比较逻辑,以避免出现意外的渲染问题。
5.说一下redux单向数据流的机制
Redux 是一种用于管理应用程序状态的 JavaScript 库,它遵循单向数据流的机制。单向数据流是指数据在应用程序中的流动方向是单向的,从而使状态管理更加可控和可预测。
Redux 的单向数据流机制包含以下几个主要部分:
-
Store(存储): Redux 中的存储是应用程序的单一状态源。它包含了整个应用程序的状态数据。存储是只读的,唯一改变状态的方式是通过派发(dispatch)一个 action。
-
Action(动作): Action 是一个简单的 JavaScript 对象,用于描述对状态进行何种操作。它必须包含一个
type
属性,用于指示要执行的操作类型。根据需要,Action 还可以携带其他自定义的数据。 -
Reducer(归约器): Reducer 是一个纯函数,用于根据接收到的 Action 来更新存储中的状态。它接收当前的状态和要执行的 Action,并返回一个新的状态。Reducer 应该是纯函数,即相同的输入应该始终产生相同的输出,不应该有副作用。
-
Dispatch(派发): Dispatch 是一个函数,用于将 Action 发送到存储中。当应用程序的某个部分需要更新状态时,它会派发一个 Action,然后 Reducer 会根据 Action 来更新存储中的状态。
-
Subscribe(订阅): Subscribe 是一个函数,用于注册一个监听器,以便在存储中的状态发生变化时得到通知。通过订阅,可以在状态发生变化时执行相应的操作,例如更新用户界面。
整个过程可以简单概括为:组件通过派发 Action 来请求对状态进行操作,然后 Reducer 根据 Action 来更新存储中的状态,最后订阅的监听器可以得到通知并执行相应的操作,例如更新用户界面。
通过这种单向数据流的机制,Redux 提供了一种可预测和可维护的状态管理方式,使得状态的变化和应用程序的行为更加可控。它也使得应用程序的状态变化历史可追溯,方便调试和回溯。
6.React 类组件的setState 是同步操作还是异步操作?
在 React 类组件中,setState
方法是异步操作。这意味着在调用 setState
后,React 并不会立即更新组件的状态,而是将更新放入队列中,并在稍后的时间点进行批量处理。
React 之所以选择异步更新状态的方式,是为了优化性能和提高效率。当多次连续调用 setState
时,React 会将这些更新合并为一个更新,从而减少不必要的组件重新渲染次数,提高性能。
由于 setState
是异步操作,所以不能立即在调用后获取到更新后的状态值。如果需要在 setState
更新后执行某些操作,可以使用回调函数作为 setState
的第二个参数。
需要注意的是,虽然 setState
是异步操作,但在某些情况下,React 可能会选择同步更新状态。例如,在 React 生命周期方法、事件处理程序或异步操作的回调函数中,setState
可能会同步更新状态。但是,为了保持一致性和可预测性,最好将 setState
视为异步操作,并在需要获取最新状态时使用回调函数。
7.什么是纯函数
纯函数是指在相同的输入下,始终返回相同的输出,并且没有副作用的函数。纯函数不会修改传入的参数,也不会对外部环境产生任何可观察的影响,例如修改全局变量或发起网络请求。
纯函数具有以下特点:
-
确定性:给定相同的输入,纯函数总是返回相同的输出。它不受外部状态的影响,只依赖于输入参数。
-
无副作用:纯函数不会对外部环境产生任何可观察的影响,例如修改传入的参数、修改全局变量、发起网络请求等。它只通过返回值来提供结果。
纯函数的优点包括:
-
可测试性:由于纯函数的输出仅由输入决定,因此很容易编写测试用例来验证其行为。
-
可缓存性:由于纯函数对于相同的输入始终返回相同的输出,可以使用缓存来提高性能。可以将函数的输入和输出进行缓存,以避免重复计算。
-
可组合性:纯函数可以方便地组合成更复杂的函数,因为它们不依赖于外部状态。
在函数式编程中,纯函数是非常重要的概念。通过编写纯函数,可以减少代码的复杂性、提高代码的可读性和可维护性,并且更容易进行并发处理和调试。在 React 中,使用纯函数可以更好地管理组件的状态和副作用,以及实现更高效的渲染。
8.介绍React组件生命周期
React 组件生命周期是指组件在被创建、更新和销毁过程中所经历的一系列阶段。通过这些生命周期方法,我们可以在不同的阶段执行特定的操作,例如初始化状态、处理数据更新、清理资源等。
React 组件生命周期可以分为三个主要阶段:
-
组件的创建阶段:
constructor()
:组件的构造函数,在组件被创建时调用,用于初始化状态和绑定方法。static getDerivedStateFromProps(props, state)
:在组件实例化和接收新的属性时调用,用于根据新的属性更新状态。render()
:必须实现的方法,返回组件的 JSX 结构。componentDidMount()
:在组件被渲染到 DOM 后调用,可以进行异步操作、数据获取等。
-
组件的更新阶段:
static getDerivedStateFromProps(props, state)
:在组件接收新的属性时调用,用于根据新的属性更新状态。shouldComponentUpdate(nextProps, nextState)
:在组件更新前调用,用于判断是否需要重新渲染组件,默认返回true
。render()
:重新渲染组件的 JSX 结构。getSnapshotBeforeUpdate(prevProps, prevState)
:在组件更新前调用,返回一个值作为componentDidUpdate
的第三个参数。componentDidUpdate(prevProps, prevState, snapshot)
:在组件更新后调用,可以进行 DOM 操作、网络请求等。
-
组件的卸载阶段:
componentWillUnmount()
:在组件即将被销毁时调用,可以进行清理操作,如取消订阅、清除定时器等。
此外,还有一些其他的生命周期方法,如 componentDidCatch()
用于处理组件内部的错误,componentDidUpdate()
用于处理组件更新后的操作等。
需要注意的是,React 16.3 版本之后,一些生命周期方法已经被标记为过时,推荐使用新的生命周期方法或钩子函数来替代。
通过合理使用组件生命周期方法,我们可以在不同的阶段执行适当的操作,从而实现更好的组件控制和优化。
9.React发起ajax应该在哪个生命周期
应该在React组件的哪个生命周期函数中发起AJAX请求?我们推荐你在componentDidMount这个生命周期函数中发起AJA×请求。这样做你可以拿到AJAX请求返回的数据并通过setState来更新组件。 应该在反应组件的哪个生命周期函数中发起AJAX请求?我们推荐你在组分这个生命周期函数中发起AJA×请求。这样做你可以拿到ajax请求返回的数据并通过setState来更新组件。
10.渲染列表,为何使用key?
key 属性能够帮助 React 更快速地识别哪些列表项需要更新,从而提高渲染性能; 避免重复渲染:如果没有指定 key 属性,React 会在每次更新列表时重新渲染所有的列表项,而不仅仅是新增、删除或更改的列表项;
11.函数组件和class组件的区别
组件定义方式不同; 2. (因为组件定义方式不同)生命周期不同:class组件有,函数组件无; 3. (因为生命周期不同)副作用操作执行不同:class组件通过生命周期函数,函数组件用hook的useEffect; 4. state的定义、读取、修改方式不同:函数组件用hook的useState;
12.什么是受控组件、什么是非受控组件
受控组件:即通过setState的形式控制输入的值及更新,非受控组件:即通过dom的形式更新值,要获取其值可以通过ref的形式去获取。
13.什么时候使用异步组件
1.在可能发生等待的情况
2.等待过程中不能像alert一样阻塞程序的时候
3.因此,所有的“等待的情况”都需要异步
一句话总结就是需要等待但是又不能阻塞程序的时候需要使用异步
4.定时任务:setTimeout,setInverval
5.网络请求:ajax请求,img图片的动态加载
6.事件绑定或者叫DOM事件,比如一个点击事件,我不知道它什么时候点,但是在它点击之前,我该干什么还是干什么。用addEventListener注册一个类型的事件的时候,浏览器会有一个单独的模块去接收这个东西,当事件被触发的时候,浏览器的某个模块,会把相应的函数扔到异步队列中,如果现在执行栈中是空的,就会直接执行这个函数。
7.ES6中的Promise
14.多个组件有公共逻辑。如何抽离
正向HOC:模式简单,容易理解,但会增加组件层级,也就是我们上面说的类组件,所以会增加属性透传成本
Render Props:代码会相对简单,但比较难理解
15.React 如何进行异步请求
除了使用原生的fetch API,我们还可以使用第三方库axios来进行数据请求。 axios是一个流行的基于Promise的HTTP客户端,可以在浏览器和Node.js环境中使用。 本文介绍了在React中进行异步数据请求的两种常见方法:使用fetch API和使用axios库。 钩子发起异步请求,并在请求完成后更新组件的状态。 钩子发起异步请求,并在请求完成后更新。
16.React-router如何配置懒加载
第一种
import React, { Suspense } from 'react'
import { Route, Routes } from 'react-router-dom'
import router from '@/router'
class MyMain extends React.Component{
render() {
return (
<main>
<Routes>
{
router.map((item, i) => {
return (
<Route key={i} path={item.path} element={
<Suspense fallback={
<div>路由懒加载...</div>
}>
< item.component />
</Suspense>
} />
)
})
}
</Routes>
</main>
)
}
}
export default MyMain
路由表
import React, { Suspense } from 'react'
import { Route, Routes } from 'react-router-dom'
import router from '@/router'
class MyMain extends React.Component{
render() {
return (
<main>
<Routes>
{
router.map((item, i) => {
return (
<Route key={i} path={item.path} element={
<Suspense fallback={
<div>路由懒加载...</div>
}>
< item.component />
</Suspense>
} />
)
})
}
</Routes>
</main>
)
}
}
export default MyMain
第二种
使用react-loadable
进行路由懒加载
需要安装依赖
yarn add react-loadable 或者 npm i react-loadable
如果你是typescript
你还需要额外安装一个依赖
yarn add @types/react-loadable 或者 npm i @types/react-loadable
我这边使用的是yarn
yarn add react-loadable @types/react-loadable
import React, { Suspense } from 'react'
import { Route, Routes } from 'react-router-dom'
import router from '@/router'
class MyMain extends React.Component{
render() {
return (
<main>
<Routes>
{
router.map((item, i) => {
return (
<Route
key={i}
path={item.path}
element={ < item.component /> }
/>
)
})
}
</Routes>
</main>
)
}
}
export default MyMain
路由表
import React from 'react'
import loadable from 'react-loadable' //引入这个loadable,使用这个来加载路由
// 如果你是js就直接无视这个interface的定义
interface Router {
name?: string,
path: string,
children?: Array<Router>,
component: any
}
const LoadingTip = () => <div>懒加载路由ing...</div>
// 如果你是js就直接无视这个: Array<Router>的类型限定
const router: Array<Router> = [
{
path: '/',
component: loadable({
loader: () => import('@/views/home'), // 需要异步加载的路由
loading: LoadingTip // 这是一个的提示
})
},
{
path: '/about',
component: loadable({
loader: () => import('@/views/about'),
loading: LoadingTip
})
}
]
export default router
17.什么是pureComponent?
React15.3中新加了一个 PureComponent 类,顾名思义, pure 是纯的意思, PureComponent 也就是纯组件,取代其前身 PureRenderMixin , PureComponent 是优化 React 应用程序最重要的方法之一,易于实施,只要把继承类从 Component 换成 PureComponent 即可,可以减少不必要的 render 操作的次数,从而提高性能,而且可以少写 shouldComponentUpdate 函数(shouldComponentUpdate通过判断props和state是否发生变化来决定需不需要重新渲染组件),主要目的就是防止不必要的子组件渲染更新。
18.React事件和DOM事件有什么区别
React事件是React库中的自定义事件,用于处理用户与应用程序交互的行为。 React事件具有统一的语法和行为,无论浏览器环境如何,它们在不同的浏览器中都具有一致的表现。 DOM事件是由浏览器提供的原生事件,用于处理用户与网页交互的行为。 DOM事件包括点击、滚动、键盘输入等。 在React中,可以使用DOM事件来处理React组件中的交互行为。 React事件和DOM事件之间的主要区别在于事件处理的语法和绑定方式。
19.React性能优化方式有那些
1 Reac.memo 缓存组件
2 使用 useMemo 缓存大量的计算
3 避免使用 内联对象
4 避免使用 匿名函数
5 延迟加载不是立即需要的组件
6 调整CSS而不是强制组件加载和卸载
7 使用React.Fragment避免添加额外的DOM
8 使用React.PureComponent , shouldComponentUpdate
20。说一下React和Vue 的区别
vue的思想是响应式的,也就是基于是数据可变的,实现了数据的双向绑定,react整体是函数式的思想,是单向数据流,推崇结合immutable来实现数据不可变 vue 采用了template, react采用了jsx (本质上都是模版) React依赖Virtual DOM,而Vue.js使用的是DOM模板。 React采用的Virtual DOM会对渲染出来的结果做脏检查。