当前位置: 首页 > news >正文

【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>;}
}

相关文章:

【React】高频面试题

1. 简述下 React 的事件代理机制&#xff1f; React使用了一种称为“事件代理”&#xff08;Event Delegation&#xff09;的机制来处理事件。事件代理是指将事件处理程序绑定到组件的父级元素上&#xff0c;然后在需要处理事件的子元素上触发事件时&#xff0c;事件将被委托给…...

Java数据结构之稀疏数组

目录 线性结构与非线性结构线性结构非线性结构 稀疏数组应用场景 代码实现二维数组转稀疏数组稀疏数组转二维数组 线性结构与非线性结构 线性结构 数据结构分两种&#xff0c;线性与非线性&#xff0c;线性结构的数据元素之间存在一对一的关系。 一对一指的是每个数据元素都…...

迅为RK3568开发板RTMP推流之视频监控

1 搭建 RTMP 媒流体服务器 nginx-rtmp 是一个基于 nginx 的 RTMP 服务模块&#xff0c;是一个功能强大的流媒体服务器模块&#xff0c; 它提供了丰富的功能和灵活的配置选项&#xff0c;适用于构建各种规模的流媒体平台和应用。无论是搭建实时视频直播平台、点播系统或多屏互…...

利用CSRF或XSS攻击网站的例子

利用 CSRF 攻击网站的简单示例&#xff1a; 假设有一个在线银行应用&#xff0c;用户可以在其中执行转账操作。用户登录后&#xff0c;系统会生成一个包含转账信息的表单&#xff0c;用户需要填写表单来发起转账。这个表单如下所示&#xff1a; <form action"https:/…...

LeetCode讲解篇之113. 路径总和 II

文章目录 题目描述题解思路题解代码 题目描述 题解思路 深度优先遍历二叉树&#xff0c;遍历的同时记录路径&#xff0c;直到遍历到叶节点&#xff0c;若路径和为targetSum则添加到结果集中 题解代码 func pathSum(root *TreeNode, targetSum int) [][]int {var res make([…...

中国HR从业者现状是怎样的?应如何提升自己?

HR(Human Resource)解释为人力资源&#xff0c;现在统称为人力资源顾问&#xff0c;跟传统人事有本质区别。传统人事一般是和行政部做相类似的工作&#xff0c;比如招聘&#xff0c;培训&#xff0c;职员的考核&#xff0c;职员的薪酬&#xff0c;职员调动等。现代人力资源&…...

Promise笔记-同步回调-异步回调-JS中的异常error处理-Promis的理解和使用-基本使用-链式调用-七个关键问题

Promise笔记 1. 预备知识1.1 实例对象与函数对象1.2 两种类型的回调函数1. 同步回调2. 异步回调 1.3 JS中的异常error处理1. 错误的类型2. 错误处理&#xff08;捕获与抛出&#xff09;3. 错误对象 2.Promise的理解和使用2.1 Promise是什么1.理解Promise2.Promise 的状态3. Pro…...

计算机考研自命题(2)

1、C语言-字符串交替拼接 1、用C编程&#xff0c;将两个字符串数组存储实现交替连接如aaa和bbb两个字符连接成ababab 如aaa和baba 两个字符&#xff0c;连接成 abaaaba #include<stdio.h>/* 解题思路&#xff1a;将两个字符串交替拼接&#xff0c;定义三个数组&#xff0…...

ZKP6.1 Discrete-log-based Polynomial Commitments (Preliminary)

ZKP学习笔记 ZK-Learning MOOC课程笔记 Lecture 6: Discrete-log-based Polynomial Commitments (Yupeng Zhang) Recall How to build an efficient SNARK? A polynomial commitment scheme A polynomial interactive oracle proof (IOP) SNARK for general circuits Plo…...

五金经营小程序商城的作用体现在哪

对消费者而言&#xff0c;如今线上购买五金是很多人的选择&#xff0c;传统线下购买&#xff0c;不仅需要跑路&#xff0c;而且店内未必有所需品&#xff0c;但线上平台则一目了然购买所需品&#xff0c;本地/外地均可以触达到&#xff0c;同时还可对用户/会员进行高效管理&…...

今年这行情,不会自动化的要做好心理准备了

李强是一名软件测试工程师&#xff0c;入行之后在一家小型公司工作了五年。这段时间里&#xff0c;他主要负责手工测试和一些简单的自动化测试工作。由于公司项目也相对简单&#xff0c;他逐渐陷入了工作的舒适区&#xff0c;没有积极追求新的知识和技能。 然而随着身边朋友发展…...

汽车保养笔记

汽车保养笔记 汽车小保养汽车大保养五油&#xff1a;机油变速箱油刹车油转向助力油离合器油 四滤&#xff1a;机油滤芯更换空气滤芯更换空调滤芯更换汽油滤芯更换 三水防冻液(水)玻璃水电瓶水 其他刹车片球头减震器火花塞 4S店的4大套路---没必要清洗节气门更换火花塞和高压线圈…...

【斗破年番】官方改编用心了,彩鳞怀孕并未删,萧潇肯定登场,真相在丹药身上

【侵权联系删除】 【文/郑尔巴金】 斗破苍穹年番动画已经更新了&#xff0c;相信不少人都感觉到不可思议&#xff0c;萧炎跟随美杜莎女王回蛇人族的剧情&#xff0c;居然魔改成这样。好好的腹中孕育出新生命&#xff0c;变成了陨落心炎残余能量&#xff0c;不及时处理的话&…...

英语——分享篇——每日200词——3201-3400

3201——air-conditioning——[eərkəndɪʃnɪŋ]——n.空调设备&#xff1b;vt.给…装上空调——air-conditioning——air-condition空调(熟词)ing鹰(谐音)——空调设备的噪音让鹰不得安宁——The trains dont even have proper air-conditioning, grumbles Mr So. ——地铁…...

合并区间(C++解法)

题目 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 示例 1&#xff1a; 输入&#xff1a;intervals …...

CUDA学习笔记(十四) Constant Memory

转载至https://www.cnblogs.com/1024incn/tag/CUDA/ CONSTANT MEMORY constant Memory对于device来说只读但是对于host是可读可写。constant Memory和global Memory一样都位于DRAM&#xff0c;并且有一个独立的on-chip cache&#xff0c;比直接从constant Memory读取要快得多…...

使用MFC创建一个SaleSystem

目录 1、项目的创建&#xff1a; 2、项目的配置&#xff1a; 3、设置窗口属性&#xff1a; &#xff08;1&#xff09;、设置图标 1&#xff09;、添加导入资源 2&#xff09;、代码初始化图标 &#xff08;2&#xff09;、设置标题 &#xff08;3&#xff09;、设置窗口…...

grafana v10.1版本设置告警

1. 相关概念概述 如图所示&#xff0c;点击切换菜单标志&#xff0c;可以看到警报相关子选项。 警报规则&#xff1a;通过PromQL语句定义告警规则&#xff0c;即达到怎样的状态触发告警。 联络点&#xff1a; 设置当警报规则实例触发时&#xff0c;如何通知联系人&#xff0c;…...

Python+Requests+PyTest+Excel+Allure 接口自动化测试实战

本文主要介绍了PythonRequestsPyTestExcelAllure 接口自动化测试实战&#xff0c;文中通过示例代码介绍的非常详细&#xff0c;对大家的学习或者工作具有一定的参考学习价值&#xff0c;需要的朋友们下面随着小编来一起学习学习吧 Unittest是Python标准库中自带的单元测试框架…...

日志分析系统——ELK

目录 一、ELK概述 ELK的组成 1、ElasticSearch 2、Logstash 3、Kiabana 完整日志采集系统基本特征 ELK的工作原理 二、ELK的部署 1、环境准备 2、部署ElasticSearch软件 3、安装Elasticsearch-head插件 4、Logstash部署 5、Kibana部署 三、FilebeatELK部署 1、安…...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

基于Uniapp开发HarmonyOS 5.0旅游应用技术实践

一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架&#xff0c;支持"一次开发&#xff0c;多端部署"&#xff0c;可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务&#xff0c;为旅游应用带来&#xf…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

MySQL 部分重点知识篇

一、数据库对象 1. 主键 定义 &#xff1a;主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 &#xff1a;确保数据的完整性&#xff0c;便于数据的查询和管理。 示例 &#xff1a;在学生信息表中&#xff0c;学号可以作为主键&#xff…...

android13 app的触摸问题定位分析流程

一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...

WebRTC从入门到实践 - 零基础教程

WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC&#xff1f; WebRTC&#xff08;Web Real-Time Communication&#xff09;是一个支持网页浏览器进行实时语音…...

Linux系统部署KES

1、安装准备 1.版本说明V008R006C009B0014 V008&#xff1a;是version产品的大版本。 R006&#xff1a;是release产品特性版本。 C009&#xff1a;是通用版 B0014&#xff1a;是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存&#xff1a;1GB 以上 硬盘&#xf…...