【React】高频面试题

1. 简述下 React 的事件代理机制?

React使用了一种称为“事件代理”(Event Delegation)的机制来处理事件。事件代理是指将事件处理程序绑定到组件的父级元素上,然后在需要处理事件的子元素上触发事件时,事件将被委托给父级元素进行处理。

React的事件代理机制有以下几个特点:

  1. 事件委托:React将事件绑定到组件的父级元素上,而不是直接绑定到每个子元素。这样,不论子元素的数量如何,只需要在父级元素上绑定一个事件处理程序,就可以处理所有子元素的事件。

  2. 事件冒泡:当子元素上的事件触发时,事件会沿着DOM树从子元素冒泡到父级元素。React利用事件冒泡机制来实现事件代理,将事件从子元素传递到父级元素进行处理。

  3. 合成事件:React提供了合成事件(Synthetic Event)来封装底层浏览器的原生事件。合成事件是跨浏览器兼容的,并且提供了一致的接口,使开发者可以方便地处理事件。

  4. 组件实例存储:React会在组件实例上存储合成事件,以便在处理事件时能够准确地访问到组件的相关数据和方法。

通过事件代理机制,React实现了高效和灵活的事件处理方式,同时减少了内存消耗。它使得开发者能够更方便地处理事件,并且能够避免一些潜在的性能问题,特别是在处理大量子元素的情况下。

需要注意的是,React的事件代理机制并不是与原生的事件代理完全相同。React并不是通过在父级元素上使用事件委托来实现性能优化,而是使用合成事件和组件实例存储来提供一种更高效和便捷的事件处理方式。

2. 使用 redux 有哪些原则?

Redux是一个用于JavaScript应用程序状态管理的开源库。在使用Redux时,有几个原则需要遵循:

  1. 单一数据源:Redux要求整个应用的状态(state)被存储在一个单一的存储库(store)中。这意味着应用的所有状态都被存储在一个JavaScript对象中。

  2. 状态是只读的:Redux中的状态是只读的,即不能直接修改状态。要修改状态,需要通过派发一个动作(action)来触发状态的变化。

  3. 使用纯函数进行状态更新:Redux中的状态更新由纯函数(reducer)处理。纯函数接收先前的状态和动作,返回一个新的状态对象。纯函数应该是没有副作用的,即相同的输入永远会得到相同的输出。

  4. 使用纯函数处理副作用:对于具有副作用(如异步请求、日志记录等)的操作,应使用Redux中间件来处理。通过使用中间件,可以确保状态更新仍然是纯函数,并且副作用的操作被封装在中间件中。

  5. 使用动作类型常量:为了保持一致性和避免拼写错误,建议使用动作类型常量来定义动作的类型。这样可以在应用的不同部分共享常量,减少错误和调试的难度。

  6. 使用选择器(Selector)进行状态访问:为了避免在组件中直接访问状态,Redux推荐使用选择器来获取所需的状态。选择器是纯函数,接收状态作为参数,并返回派生的状态值。

这些原则帮助开发者理解和遵循Redux的设计思想,使得Redux的状态管理更加一致、可预测和可维护。

3. react 中怎么捕获异常?

在React中,可以使用错误边界(Error Boundaries)来捕获和处理组件中的异常。错误边界是一种React组件,它可以捕获其子组件中的错误,并进行处理,以避免整个应用程序崩溃。
官网关于错误边界的描述:https://legacy.reactjs.org/docs/error-boundaries.html#introducing-error-boundaries

以下是使用错误边界来捕获异常的示例:

import React, { Component } from 'react';class ErrorBoundary extends Component {constructor(props) {super(props);this.state = {hasError: false,error: null,errorInfo: null};}componentDidCatch(error, errorInfo) {this.setState({hasError: true,error: error,errorInfo: errorInfo});// 这里可以进行异常处理,例如发送错误报告到服务器}render() {if (this.state.hasError) {return (<div><h2>出现了一个错误</h2><details style={{ whiteSpace: 'pre-wrap' }}>{this.state.error && this.state.error.toString()}<br />{this.state.errorInfo.componentStack}</details></div>);}return this.props.children;}
}class MyComponent extends Component {render() {// 抛出一个异常throw new Error('发生了一个错误');return <div>My Component</div>;}
}function App() {return (<ErrorBoundary><MyComponent /></ErrorBoundary>);
}

在上述示例中,我们创建了一个名为ErrorBoundary的错误边界组件。它通过定义componentDidCatch生命周期方法来捕获子组件中的异常。当子组件抛出异常时,componentDidCatch方法会被调用,并将异常信息存储在state中。

在ErrorBoundary组件的render方法中,我们根据hasError的状态来决定渲染什么内容。如果hasError为true,则渲染错误信息,否则渲染子组件。

在MyComponent组件中,我们使用throw new Error()语句抛出一个异常。这个异常会被ErrorBoundary组件捕获,并进行处理。

最后,在App组件中,我们将MyComponent组件包裹在ErrorBoundary组件中,这样ErrorBoundary就可以捕获MyComponent组件的异常。

需要注意的是,错误边界只能捕获其子组件中的异常,无法捕获事件处理器、异步代码、服务端渲染等其他情况中的异常。

通过使用错误边界,我们可以优雅地处理组件中的异常,并提供友好的错误信息给用户,同时避免整个应用程序的崩溃。

页可以考虑直接使用开源库:react-error-boundary,对开发者来说,只需要关心出现错误后的处理。

import {ErrorBoundary} from 'react-error-boundary'function ErrorFallback({error, resetErrorBoundary}) {return (<div role="alert"><p>Something went wrong:</p><pre>{error.message}</pre><button onClick={resetErrorBoundary}>Try again</button></div>)
}const ui = (<ErrorBoundaryFallbackComponent={ErrorFallback}onReset={() => {// reset the state of your app so the error doesn't happen again}}><ComponentThatMayError /></ErrorBoundary>
)

遗憾的是,error boundaries 并不会捕捉这些错误:

  • 事件处理程序
  • 异步代码 (e.g. setTimeout or requestAnimationFrame callbacks)
  • 服务端的渲染代码
  • error boundaries自己抛出的错误
    其实官方也有解决方案:how-about-event-handlers, 就是 try catch.
  handleClick() {try {// Do something that could throw} catch (error) {this.setState({ error });}}

4. 实现一个 useTimeout Hook

题目:useTimeout 是可以在函数式组件中,处理 setTimeout 计时器函数

解决了什么问题?

如果直接在函数式组件中使用 setTimeout ,会遇到以下问题:

  • 多次调用setTimeout
 function App() {  const [state, setState] = useState(1);  setTimeout(() => {  setState(state + 1);  }, 3000);  return (  // 我们原本的目的是在页面渲染完3s后修改一下state,但是你会发现当state+1后,触发了页面的重新渲染,就会重新有一个3s的定时器出现来给state+1,既而变成了每3秒+1。  <div> {state} </div>  );  }; 
  • hooks 的闭包缺陷
function App() {  const [count, setCount] = useState(0)  const [countTimeout, setCountTimeout] = useState(0)  useEffect(() => {  setTimeout(() => {  setCountTimeout(count)  }, 3000)  setCount(5)  }, [])  return (  //count发生了变化,但是3s后setTimout的count却还是0  <div>  Count: {count}  <br />  setTimeout Count: {countTimeout}  </div>  )  
}

useTimeout 实现

function useTimeout(callback, delay) {const memorizeCallback = useRef();useEffect(() => {memorizeCallback.current = callback;}, [callback]);useEffect(() => {if (delay !== null) {const timer = setTimeout(() => {memorizeCallback.current();}, delay);return () => {clearTimeout(timer);};}}, [delay]);
};

如何使用

  // callback 回调函数, delay 延迟时间useTimeout(callback, delay);

5. React18新特性

React 18是即将发布的React的新版本,它带来了一些令人期待的新特性,旨在改善开发者体验和性能。以下是React 18的一些主要新特性:

  1. Concurrent Mode(并发模式):Concurrent Mode是React 18中最重要的新特性之一。它引入了一种新的渲染方式,使得React应用能够更高效地处理大型组件树,提高应用的性能和响应能力。

  2. 新的渲染器(新的渲染引擎):React 18引入了新的渲染器,名为React Server Components,用于在服务器上渲染React组件。它可以加速服务器渲染,并提供更好的开发体验。

  3. 自动批处理(Automatic Batching):React 18引入了自动批处理机制,它可以自动将多个状态更新批处理为一次,减少不必要的重渲染,提高性能。

  4. 事件处理改进:React 18改进了事件处理机制,引入了新的API(如useEvent和useEffect),使得事件处理更加灵活和易用。

  5. 生命周期改进:React 18引入了新的生命周期方法,如useEffect的onRender回调,用于更精确地控制组件的渲染和副作用。

  6. 组件级别的错误边界(Component-level Error Boundaries):React 18允许开发者在组件级别使用错误边界,使得捕获和处理组件中的错误更加灵活和精确。

这些是React 18的一些主要新特性,希望它们能够提供更好的开发体验和性能优化。需要注意的是,以上列出的特性可能会有所变化,具体的特性和用法应以React 18正式发布时的官方文档和更新日志为准。
补充:https://fe.ecool.fun/topic/6f40b143-3941-44c6-ac90-9bf87795ee2c?orderBy=updateTime&order=asc&tagId=13

6. 使用 useState (const [test, setTest] = useState([]))时,为什么连续调用 setTest({…test, newValue}) 会出现值的丢失?

由于useState是基于浅比较的,所以在连续调用setTest时,直接使用{…test, newValue}的方式更新状态可能会导致值的丢失。这是因为{…test, newValue}仅仅是对原始状态对象的浅拷贝,新的状态对象与旧的状态对象共享相同的引用,所以React无法正确地检测到状态的改变。并且useState是异步执行的,也就是执行 setTest 后,不会立即更新 test 的结果,多次调用时,可能出现了值覆盖的情况。

为了解决这个问题,可以使用函数式的方式更新状态。这样可以确保使用最新的状态值进行更新,而不是依赖于旧的状态对象。可以通过在setTest中传入一个回调函数来实现:

setTest(prevState => ({...prevState, newValue}));

使用这种方式,prevState参数是之前的状态的副本,直接更新这个副本而不是依赖于旧的状态对象。这样可以确保状态的更新是基于最新的状态值进行的,避免了值的丢失。

总结来说,连续调用setTest({…test, newValue})可能会导致值的丢失,是因为这种方式只是浅拷贝了旧的状态对象,新的状态对象与旧的状态对象共享相同的引用。为了避免这个问题,可以使用函数式的方式更新状态,通过回调函数确保基于最新的状态值进行更新。

7. setState是同步还是异步

setState大部分情况下是异步的。在React中,为了提高性能和优化渲染过程,React会对setState进行批处理以减少不必要的渲染。

当你调用setState时,React会将更新添加到一个队列中,并在合适的时机批量处理这些更新。这意味着在调用setState之后,不会立即更新状态和重新渲染组件,而是会等待合适的时机进行批处理。

React使用一种称为"合成事件"的机制来处理用户交互,例如点击按钮。在合成事件中,React会对多个状态更新进行合并,并一次性更新组件的状态。

这种异步的批处理机制有助于提高性能,避免不必要的渲染,并在一次性更新时减少重复渲染的次数。

但是需要注意的是,setState也有一些特殊情况下的同步行为。例如,在React的生命周期方法(如componentDidUpdate)中调用setState时,更新是同步的,不会进行异步批处理。

如果你需要在setState更新之后立即获取更新后的状态,可以使用setState的回调函数来实现:

setState(newState, () => {// 在这里可以获取更新后的状态console.log(this.state);
});

使用回调函数可以确保在状态更新完成后执行相应的操作。

需要注意的是,React 18中的Concurrent Mode引入了新的渲染方式,可能会改变setState的异步行为。具体的变化和细节可以参考React 18的官方文档和更新日志。

8. 说说React Router有几种模式,以及实现原理?

React Router有两种主要的路由模式:HashRouter和BrowserRouter。

HashRouter:在HashRouter模式下,URL中的路由信息会以哈希值的形式出现,如http://example.com/#/home。这种模式的实现原理是使用浏览器的URL的哈希部分(即#后面的部分)来管理路由。当URL的哈希部分发生变化时,React Router会根据新的哈希值匹配对应的路由,并更新相应的组件。

BrowserRouter:在BrowserRouter模式下,URL中的路由信息会以常规的路径形式出现,如http://example.com/home。这种模式的实现原理是使用HTML5的History API来管理路由。它通过修改浏览器历史记录中的URL来实现路由的切换和更新。

在BrowserRouter模式下,需要服务器的支持,以确保在刷新或直接访问具体URL时,能够正确地渲染对应的组件。

React Router的实现原理是通过使用React的Context功能和React组件生命周期来管理路由。它提供了一组高阶组件(如Router、Route、Switch等),用于定义和匹配路由,并在URL变化时进行相应的渲染和组件更新。

React Router的核心机制是监听URL的变化并更新对应的组件。它提供了一种将URL路径与组件关联起来的方式,使得根据URL来切换和渲染不同组件成为可能。

需要注意的是,React Router还提供了其他功能,如嵌套路由、重定向、路由守卫等,以满足更复杂的路由需求。

9. 说说你对React Router的理解?常用的Router组件有哪些?

React Router是一个用于构建单页应用中路由功能的库。它基于React构建,提供了一组组件和API,用于处理应用程序中的路由和导航。

React Router能够将URL路径与组件进行映射,使得在应用程序中切换和渲染不同的组件成为可能。通过React Router,开发者可以构建具有多个页面和路由功能的单页应用,实现无刷新的页面切换和导航。

常用的React Router组件包括:

BrowserRouter:用于在浏览器环境下使用HTML5的History API来管理路由。

HashRouter:用于在浏览器环境下使用URL的哈希部分(#后面的部分)来管理路由。

Route:用于定义路由规则和匹配URL。通过设置path属性指定URL路径,设置component属性指定对应的组件。

Switch:用于在多个路由规则中选择匹配的第一个。一般用于避免多个路由规则同时匹配的情况。

Link:用于创建导航链接,生成可点击的链接到指定的URL路径。

NavLink:类似于Link组件,但在匹配当前URL时可以添加特定的样式或类名。

Redirect:用于重定向到指定的URL路径。

withRouter:一个高阶组件,用于将路由的相关信息(如history、location、match)注入到组件的props中。

这些组件是React Router中常用的组件,用于构建和管理应用程序的路由。通过组合和配置这些组件,可以实现灵活和高效的路由功能。

10. 说说React Jsx转换成真实DOM过程?

在React中,JSX是一种类似HTML的语法扩展,用于描述UI的结构和组件的渲染。当使用JSX编写组件时,React会将JSX代码转换为真实的DOM元素。

下面是React将JSX转换为真实DOM的过程:

  1. 解析JSX代码:React使用Babel等工具将JSX代码转换为JavaScript代码。这个过程将JSX中的标签、属性和内容转换为等效的JavaScript语法。

  2. 创建虚拟DOM:在运行时,React会使用转换后的JavaScript代码来创建虚拟DOM(Virtual DOM)。虚拟DOM是一个JavaScript对象树,它和真实DOM具有相似的结构。

  3. Diff算法比较变化:当组件的状态或属性发生变化时,React会使用Diff算法比较新的虚拟DOM和旧的虚拟DOM,找出差异(即需要更新的部分)。

  4. 更新真实DOM:根据Diff算法的结果,React会生成一系列DOM操作指令,然后将这些指令应用到真实的DOM上,以更新页面的内容。

通过这个过程,React能够高效地更新页面,只更新发生变化的部分,而不是重新渲染整个页面。

需要注意的是,React的虚拟DOM提供了一种高效的方式来描述和操作真实DOM,但它并不是一种直接替代真实DOM的解决方案。虚拟DOM只是React内部的一种数据结构,用于提高渲染性能和优化更新过程。

在前面文章了解中,JSX通过babel最终转化成React.createElement这种形式
例如:

<div><img src="avatar.png" className="profile" /><Hello />
</div>

会被babel转化成如下:

React.createElement("div",null,React.createElement("img", {src: "avatar.png",className: "profile"}),React.createElement(Hello, null)
);

在转化过程中,babel在编译时会判断 JSX 中组件的首字母:

当首字母为小写时,其被认定为原生 DOM 标签,createElement 的第一个变量被编译为字符串

当首字母为大写时,其被认定为自定义组件,createElement 的第一个变量被编译为对象

最终都会通过RenderDOM.render(…)方法进行挂载,如下:

ReactDOM.render(<App />,  document.getElementById("root"));

在react中,节点大致可以分成四个类别:

  • 原生标签节点
  • 文本节点
  • 函数组件
  • 类组件

如下所示:

class ClassComponent extends Component {static defaultProps = {color: "pink"};render() {return (<div className="border"><h3>ClassComponent</h3><p className={this.props.color}>{this.props.name}</p ></div>);}
}function FunctionComponent(props) {return (<div className="border">FunctionComponent<p>{props.name}</p ></div>);
}const jsx = (<div className="border"><p>xx</p >< a href=" ">xxx</ a><FunctionComponent name="函数组件" /><ClassComponent name="类组件" color="red" /></div>
);

这些类别最终都会被转化成React.createElement这种形式

React.createElement其被调用时会传⼊标签类型type,标签属性props及若干子元素children,作用是生成一个虚拟Dom对象,如下所示:

function createElement(type, config, ...children) {if (config) {delete config.__self;delete config.__source;}// ! 源码中做了详细处理,⽐如过滤掉key、ref等const props = {...config,children: children.map(child =>typeof child === "object" ? child : createTextNode(child))};return {type,props};
}
function createTextNode(text) {return {type: TEXT,props: {children: [],nodeValue: text}};
}
export default {createElement
};

createElement会根据传入的节点信息进行一个判断:

  • 如果是原生标签节点, type 是字符串,如div、span
  • 如果是文本节点, type就没有,这里是 TEXT
  • 如果是函数组件,type 是函数名
  • 如果是类组件,type 是类名
    虚拟DOM会通过ReactDOM.render进行渲染成真实DOM,使用方法如下:
ReactDOM.render(element, container[, callback])

当首次调用时,容器节点里的所有 DOM 元素都会被替换,后续的调用则会使用 React 的 diff算法进行高效的更新

如果提供了可选的回调函数callback,该回调将在组件被渲染或更新之后被执行

render大致实现方法如下:

function render(vnode, container) {console.log("vnode", vnode); // 虚拟DOM对象// vnode _> nodeconst node = createNode(vnode, container);container.appendChild(node);
}// 创建真实DOM节点
function createNode(vnode, parentNode) {let node = null;const {type, props} = vnode;if (type === TEXT) {node = document.createTextNode("");} else if (typeof type === "string") {node = document.createElement(type);} else if (typeof type === "function") {node = type.isReactComponent? updateClassComponent(vnode, parentNode): updateFunctionComponent(vnode, parentNode);} else {node = document.createDocumentFragment();}reconcileChildren(props.children, node);updateNode(node, props);return node;
}// 遍历下子vnode,然后把子vnode->真实DOM节点,再插入父node中
function reconcileChildren(children, node) {for (let i = 0; i < children.length; i++) {let child = children[i];if (Array.isArray(child)) {for (let j = 0; j < child.length; j++) {render(child[j], node);}} else {render(child, node);}}
}
function updateNode(node, nextVal) {Object.keys(nextVal).filter(k => k !== "children").forEach(k => {if (k.slice(0, 2) === "on") {let eventName = k.slice(2).toLocaleLowerCase();node.addEventListener(eventName, nextVal[k]);} else {node[k] = nextVal[k];}});
}// 返回真实dom节点
// 执行函数
function updateFunctionComponent(vnode, parentNode) {const {type, props} = vnode;let vvnode = type(props);const node = createNode(vvnode, parentNode);return node;
}// 返回真实dom节点
// 先实例化,再执行render函数
function updateClassComponent(vnode, parentNode) {const {type, props} = vnode;let cmp = new type(props);const vvnode = cmp.render();const node = createNode(vvnode, parentNode);return node;
}
export default {render
};

总结

在react源码中,虚拟Dom转化成真实Dom整体流程如下图所示:
在这里插入图片描述
其渲染流程如下所示:

  • 使用React.createElement或JSX编写React组件,实际上所有的 JSX 代码最后都会转换成React.createElement(…) ,Babel帮助我们完成了这个转换的过程。
  • createElement函数对key和ref等特殊的props进行处理,并获取defaultProps对默认props进行赋值,并且对传入的孩子节点进行处理,最终构造成一个虚拟DOM对象
  • ReactDOM.render将生成好的虚拟DOM渲染到指定容器上,其中采用了批处理、事务等机制并且对特定浏览器进行了性能优化,最终转换为真实DOM

11. react-router 里的 标签和 标签有什么区别?

React Router中的组件和HTML中的标签在导航功能上有一些区别。

  1. SPA导航:组件用于在单页应用中实现导航。它通过React Router管理路由,并使用JavaScript来更新应用程序的状态和渲染。相反,标签通常是用于传统多页应用中的导航,会导致整个页面的刷新。

  2. 阻止浏览器默认行为:使用组件时,React Router会拦截点击事件,阻止浏览器默认的页面刷新行为,以避免整个页面的重新加载。这样可以实现无刷新的页面切换。而标签会触发浏览器默认行为,导致页面的重新加载。

  3. 路由匹配:组件会自动处理路由的匹配和URL的生成。它会根据设置的to属性生成对应的URL,并与当前路由进行匹配以添加活动状态的类名。相反,标签只是一个普通的HTML标签,不会自动处理路由匹配。

  4. 动态路由参数:组件可以方便地处理动态路由参数。通过在to属性中传递参数,可以动态生成对应的URL。而标签需要手动拼接URL参数。

总之,组件是React Router提供的导航组件,可以与React Router一起使用来实现无刷新的页面切换和路由导航。而标签是传统HTML中的导航标签,会触发页面的重新加载。
在使用React Router时,建议使用组件来处理导航和路由链接,以便获得更好的性能和用户体验。

12. React 中的 ref 有什么用?

在React中,ref是一个特殊的属性,用于获取组件或DOM元素的引用。通过ref,你可以在组件中访问和操作对应的实例或DOM元素。

ref的主要用途有以下几个:

  1. 访问组件实例:使用ref可以获取到组件的实例,从而可以直接调用组件的方法、访问组件的属性,或者获取组件的状态。这在某些情况下非常有用,比如需要手动触发组件的方法或访问组件中的数据。

  2. 操作DOM元素:使用ref可以获取到渲染后的DOM元素,并对DOM进行操作。你可以使用ref来获取表单元素的值、改变DOM的样式、添加或移除DOM节点等。
    需要注意的是,通过ref获取到的DOM元素是真实的DOM,而不是虚拟DOM。因此,应该避免直接操作DOM,而是使用React的状态和属性来管理和更新UI。

除了获取组件实例和DOM元素之外,ref还可以用于存储和访问任意的变量。这种情况下,你可以将ref视为一个普通的变量容器,用于在组件中存储信息。

使用ref的方式有两种:

字符串引用(不推荐):在React早期版本中,可以使用字符串来定义ref,但这种方式已经被废弃,不推荐使用。

回调函数引用:使用函数来定义ref,在组件渲染完成后会调用该函数,并将对应的组件实例或DOM元素传递给该函数。可以通过将ref赋值给一个变量,然后通过该变量来访问和操作对应的实例或DOM元素。

以下是一个使用ref的示例:

class MyComponent extends React.Component {constructor(props) {super(props);this.myRef = React.createRef();}componentDidMount() {console.log(this.myRef.current); // 访问组件实例或DOM元素}render() {return <div ref={this.myRef}>Hello, World!</div>;}
}

在上面的例子中,通过React.createRef()创建了一个ref,并将其赋值给myRef属性。在组件的render方法中,将该ref绑定到div元素上。在组件的componentDidMount生命周期方法中,可以通过this.myRef.current来访问和操作对应的DOM元素。

除了获取组件实例和DOM元素之外,ref还可以用于存储和访问任意的变量。这种情况下,你可以将ref视为一个普通的变量容器,用于在组件中存储信息。

class MyComponent extends React.Component {constructor(props) {super(props);this.myRef = React.createRef();this.myVariable = 'Hello, World!';}componentDidMount() {console.log(this.myVariable); // 访问存储的变量}render() {return <div ref={this.myRef}>Hello, World!</div>;}
}

在上面的例子中,除了创建一个ref,还创建了一个myVariable变量,用于存储字符串’Hello, World!'。在组件的componentDidMount生命周期方法中,可以通过this.myVariable来访问和使用存储的变量。

需要注意的是,使用ref存储变量在React中并不是最佳实践,因为组件状态(state)和属性(props)通常更适合用于存储和管理组件的数据。ref的主要目的是用于访问组件实例和DOM元素,而不是用于存储组件数据。

13. 单页应用如何提高加载速度?

  1. 使用代码分割:将代码拆分成小块并按需加载(懒加载),以避免不必要的网络请求和减少加载时间。
  2. 缓存资源:利用浏览器缓存来存储重复使用的文件,例如 CSS 和 JS 文件、图片等。
  3. 预加载关键资源:在首次渲染之前,先提前加载关键资源,例如首页所需的 JS、CSS 或数据,以保证关键内容的快速呈现。
  4. 使用合适的图片格式:选择合适的图片格式(例如 JPEG、PNG、WebP 等),并根据需要进行压缩以减少文件大小。对于一些小图标, 可以使用 iconfont 等字体文件来代替。
  5. 启用 Gzip 压缩:使用服务器端的 Gzip 压缩算法对文件进行压缩,以减少传输时间和带宽消耗。
  6. 使用 CDN:使用内容分发网络(CDN)来缓存和传递文件,以提高文件的下载速度和可靠性。
  7. 优化 API 请求:尽可能地减少 API 调用的数量,并使用缓存和延迟加载等技术来优化 API 请求的效率。
  8. 使用服务器端渲染:使用服务器端渲染(SSR)来生成 HTML,以减少客户端渲染所需的时间和资源。但需要注意,SSR 也可能增加了服务器的负担并使网站更复杂。

14. React 中,怎么实现父组件调用子组件中的方法?

在React中,实现父组件调用子组件中方法的方式在函数组件和类组件之间有一些区别。

  1. 对于函数组件,你可以使用React Hooks中的useRef来引用子组件的实例,并通过该引用调用子组件中的方法。
import React, { useRef } from 'react';function ParentComponent() {const childRef = useRef();const handleClick = () => {childRef.current.childMethod(); // 调用子组件中的方法};return (<div><ChildComponent ref={childRef} /><button onClick={handleClick}>调用子组件方法</button></div>);
}function ChildComponent() {const childMethod = () => {console.log('子组件方法被调用');};return <div>子组件</div>;
}

在上述代码中,使用useRef创建了childRef引用,我们将其传递给子组件ChildComponent的ref属性。然后在父组件中的handleClick方法中,通过childRef.current.childMethod()来调用子组件中的方法。

  1. 对于类组件,可以使用React.createRef()方法来创建ref引用,并将其传递给子组件的ref属性,然后在父组件中使用ref.current来访问子组件的实例。
import React from 'react';class ParentComponent extends React.Component {constructor(props) {super(props);this.childRef = React.createRef();}handleClick() {this.childRef.current.childMethod(); // 调用子组件中的方法}render() {return (<div><ChildComponent ref={this.childRef} /><button onClick={() => this.handleClick()}>调用子组件方法</button></div>);}
}class ChildComponent extends React.Component {childMethod() {console.log('子组件方法被调用');}render() {return <div>子组件</div>;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/167757.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python —— hou.NetworkItem class

在一个network内&#xff0c;所有可见元素的基类&#xff1b; 此类没有方法&#xff0c;仅作为 hou.NetworkMovabelItem、hou.NodeConnection 基类存在&#xff0c;这两个子类在网络编辑器内均是可见的&#xff0c;是没有真正有意义的基类的&#xff1b;通过提供一个公共的基类…

deque的简单了解

介绍 deque是一种结合了list和vector两者优势的一种容器&#xff0c;它既可以支持下标的随机访问&#xff0c;并且头插头删的效率都不低&#xff0c;但相对也存在一定的缺陷&#xff0c;在中间插入和遍历上&#xff0c;消耗相对较大。 deque并不是真正连续的空间&#xff0c;…

读卡器+芯片

RFID Reader 产品参数 产品型号 RFID Reader 尺寸(mm) 104*70*11.7mm 外观颜色 黑 材质 ABS 接口 MINI 功率 0.1W 电源 5V/1A 读卡距离 40mm 工作温度 -40~85℃ 工作频率 13.56Mhz 串口功能 支持 支持协议 ISO/IEC 14443、ISO/IEC 15693 产品参数 …

分布式存储 vs. 全闪集中式存储:金融数据仓库场景下的性能对比

作者&#xff1a;深耕行业的 SmartX 金融团队 张德敏 近年来随着金融行业的高速发展&#xff0c;经营决策者及监管机构对信息时效性的要求越来越高&#xff0c;科技部门面临诸多挑战。例如&#xff0c;不少金融机构使用数仓业务系统&#xff0c;为公司高层提供日常经营报表&am…

【剑指Offer】36.二叉搜索树与双向链表

题目 输入一棵二叉搜索树&#xff0c;将该二叉搜索树转换成一个排序的双向链表。如下图所示 数据范围&#xff1a;输入二叉树的节点数 0≤n≤1000&#xff0c;二叉树中每个节点的值 0≤val≤1000 要求&#xff1a;O(1)&#xff08;即在原树上操作&#xff09;&#xff0c;时间…

YOLOv5论文作图教程(1)— 软件介绍及下载安装(包括软件包+下载安装详细步骤)

前言:Hello大家好,我是小哥谈。在学习YOLOv5算法的过程中,很多同学都有发表论文的需求。作为文章内容的支撑,图表是最直接的整合数据的工具,能够更清晰地反映出研究对象的结果、流程或趋势。在发表论文的时候,审稿人除了关注论文的内容和排版外,也会审核图表是否清晰美观…

华为数通方向HCIP-DataCom H12-831题库(多选题:21-40)

第21题 网络管理员A希望使用ACL匹配特定的路由条目,请问以下哪些路由条目将被图中的ACL规侧匹配? acl number 2000 rule 10 permit source 10.0.0.0 0.0.6.0A、10.0.0.1/32 B、10.0.0.0/24 C、10.0.1.0/24 D、10.0.2.0/24 答案: 解析: 通配符十进制6转换二进制为00000110,…

从Excel到智能化:智能报表的演进与未来发展趋势

摘要&#xff1a;本文由葡萄城技术团队于CSDN原创并首发。转载请注明出处&#xff1a;葡萄城官网&#xff0c;葡萄城为开发者提供专业的开发工具、解决方案和服务&#xff0c;赋能开发者。 报表的迭代历程 报表工具的诞生与计算机技术的出现和信息技术的进步密不可分。下图是报…

springBoot--web--路径匹配

路径匹配 前言在配置文件中配置路径匹配结果 前言 spring5.3之后加入了更多的请求路径匹配的实现策略 以前只支持antPathMatcher策略&#xff0c;现在提供了PathPatternParse策略&#xff0c;并且可以让我们指定到底使用哪种策略 PathPatternParser: 在jmh基准测试下&#xff…

YOLOv5/v7/v8改进实验(五)之使用timm更换YOLOv5模型主干网络Backbone篇

&#x1f680;&#x1f680; 前言 &#x1f680;&#x1f680; timm 库实现了最新的几乎所有的具有影响力的视觉模型&#xff0c;它不仅提供了模型的权重&#xff0c;还提供了一个很棒的分布式训练和评估的代码框架&#xff0c;方便后人开发。更难能可贵的是它还在不断地更新迭…

QT学习day5(QT实现TCP协议)

作业&#xff1a;利用TCP客户端和服务器实现网络聊天室&#xff08;简单版QQ&#xff09; 1.服务器代码 widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QTcpServer> //服务器头文件 #include<QTcpSocket> …

“香蕉大王”的转型升级,能否扩大市场份额?

佳农食品控股 ( 集团 ) 股份有限公司,于2023年10月11日同海通证券签署上市辅导协议&#xff0c;计划登陆上交所主板。据了解这已经不是佳农食品第一次IPO了&#xff0c;2019 年&#xff0c;佳农集团曾向上交所递交过招股说明书&#xff0c;当时的招股书披露&#xff0c;佳农集团…

磁盘分区如何分? 电脑磁盘分区免费软件指南!

列出并比较顶级免费磁盘分区管理器软件&#xff0c;以选择适用于 Windows 的最佳分区软件&#xff1a; 系统分区在现代计算机设备中起着非常重要的作用。它们可以存储数据&#xff0c;使系统文件远离用户数据&#xff0c;并在同一台设备上安装多个操作系统。但是&#xff0c;这…

阿里云对象存储OSS怎么停止扣费

阿里云对象存储OSS一直扣费如何停止&#xff1f;如何关闭对象存储OSS&#xff1f;阿里云对象存储OSS没有关闭功能&#xff0c;如果不再使用对象存储OSS可以删除存储空间Bucket下的所有文件&#xff0c;详细说下阿里云对象存储OSS停止收费的方法&#xff1a; 阿里云对象存储OSS…

TS和JS的区别

1.TS和JS的区别 ts 是js的超集。 从执行环境上来看&#xff0c;浏览器、node.js 可以直接执行js,但不能执行ts;编译层面&#xff0c;Ts 有编译阶段&#xff0c;js 没有&#xff0c;只有转译阶段和lint阶段&#xff1b;ts更难写一点&#xff0c;但类型更安全。ts 代码写出来就是…

绿盾加密软件支持文件 CAD图纸加密 多平台Windows、Linux、Mac

绿盾加密软件支持文件CAD图纸加密&#xff0c;并且可以在多平台如Windows、Linux、Mac上运行。适用于各种商务服务、软件开发以及行业专用软件。 PC访问地址&#xff1a; https://isite.baidu.com/site/wjz012xr/2eae091d-1b97-4276-90bc-6757c5dfedee 绿盾加密软件的具体功能…

【三维重建】DreamGaussian:高斯splatting的单视图3D内容生成(原理+代码)

文章目录 摘要一、前言二、相关工作2.1 3D表示2.2 Text-to-3D2.3 Image-to-3D 三、本文方法3.1生成式 高斯 splitting3.2 高效的 mesh 提取3.3 UV空间的纹理优化 四. 实验4.1实施细节4.2 定性比较4.3 定量比较4.4 消融实验 总结&#xff08;特点、局限性&#xff09; 五、安装与…

C++ 中的模型预测路径积分 (MPPI) 控制

一、说明 模型预测路径积分控制&#xff08;MPPI&#xff09;是一种基于采样的模型预测控制算法。是MPC控制模型的延申和拓宽&#xff0c;要了解MPPI需要先理解MPC&#xff0c;参见文章&#xff1a;MPC预测控制概述和C 中的模型库-CSDN博客 二、模型预测路径积分 (MPPI) 控制 模…

如何破解压缩包密码,CTF压缩包处理

I. 引言 压缩包我们经常接触&#xff0c;用于对文件进行压缩存储/传输。压缩包处理在CTF比赛中是非常重要的一块&#xff0c;因为压缩包中可能包含重要信息&#xff1a;许多CTF题目会将关键信息隐藏在压缩包中&#xff0c;参赛者需要解压并查看其中的内容才能获取有用的线索。…

Day7力扣打卡

打卡记录 合法分组的最少组数&#xff08;贪心&#xff09; 链接 举例说明&#xff0c;假设 c n t [ x ] 32 cnt[x]32 cnt[x]32&#xff0c; k 10 k10 k10&#xff0c;那么 32 10 10 10 2 321010102 321010102&#xff0c;多出的 2 2 2 可以分成两个 1 1 1&#xf…