【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.mzph.cn/news/115394.shtml

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

相关文章

软件外包开发迭代管理工具

软件迭代的管理工具有助于团队有效地规划、跟踪和管理迭代开发过程&#xff0c;确保项目按时交付&#xff0c;并与团队成员之间进行协作。以下是一些常用的软件迭代管理工具&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#…

10.23归并排序

课上 归并排序 最大时&#xff0c;就是两个都是完全倒序&#xff0c;但注意一定有一个序列先用完&#xff0c;此时剩一个序列只有一个元素&#xff0c;不用比较&#xff0c;直接加入&#xff0c;所以就是nn-1, 最小时&#xff0c;是都是完全有序&#xff0c;且一个序列中的元…

Python合并同类别且相交的矩形框

Python合并同类别且相交的矩形框 前言前提条件相关介绍实验环境Python合并同类别且相交的矩形框代码实现 前言 由于本人水平有限&#xff0c;难免出现错漏&#xff0c;敬请批评改正。更多精彩内容&#xff0c;可点击进入Python日常小操作专栏、YOLO系列专栏、自然语言处理专栏或…

大语言模型(LLM)综述(二):开发大语言模型的公开可用资源

A Survey of Large Language Models 前言3. RESOURCES OF LLMS3.1 公开可用的模型CheckPoints或 API3.2 常用语料库3.3 库资源 前言 随着人工智能和机器学习领域的迅速发展&#xff0c;语言模型已经从简单的词袋模型&#xff08;Bag-of-Words&#xff09;和N-gram模型演变为更…

图论04-【无权无向】-图的广度优先遍历BFS

文章目录 1. 代码仓库2. 广度优先遍历图解3.主要代码4. 完整代码 1. 代码仓库 https://github.com/Chufeng-Jiang/Graph-Theory 2. 广度优先遍历图解 3.主要代码 原点入队列原点出队列的同时&#xff0c;将与其相邻的顶点全部入队列下一个顶点出队列出队列的同时&#xff0c;将…

基础课6——计算机视觉

1.计算机视觉的概念与原理 1.1概念 计算机视觉&#xff08;CV&#xff09;是人工智能的一个重要发展领域&#xff0c;属于计算机科学的一个分支&#xff0c;它企图让计算机能像人类一样通过视觉来获取和理解信息。计算机视觉的应用非常广泛&#xff0c;包括但不限于图像识别、…

Typora的相关配置(Typora主题、字体、快捷键、习惯)

Typora的相关配置(Typora主题、字体、快捷键、习惯) 文章目录 Typora的相关配置(Typora主题、字体、快捷键、习惯)[toc]一、主题配置二、字体配置查看字体名称是否可以被识别&#xff1a;如果未能正确识别&#xff1a; 三、习惯配置四、快捷键配置更改提供的功能的快捷键&#…

前端react入门day01-了解react和JSX基础

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 React介绍 React是什么 React的优势 React的市场情况 开发环境搭建 使用create-react-app快速搭建…

python【多线程、单线程、异步编程】三个版本--在爬虫中的应用

并发编程在爬虫中的应用 之前的课程&#xff0c;我们已经为大家介绍了 Python 中的多线程、多进程和异步编程&#xff0c;通过这三种手段&#xff0c;我们可以实现并发或并行编程&#xff0c;这一方面可以加速代码的执行&#xff0c;另一方面也可以带来更好的用户体验。爬虫程…

概念解析 | 毫米波雷达与计算机视觉的融合

注1:本文系“概念解析”系列之一,致力于简洁清晰地解释、辨析复杂而专业的概念。本次辨析的概念是:毫米波雷达与计算机视觉的融合。 毫米波雷达与计算机视觉的融合 Sensors | Free Full-Text | MmWave Radar and Vision Fusion for Object Detection in Autonomous Driving: A …

FFMPEG之example编译

FFMPEG源码下载:Download FFmpeg 编译需配置的库: sudo apt-get install yasm sudo apt-get install libsdl1.2-dev sudo apt-get install libsdl2-dev 编译流程: ./configure --disable-x86asm --prefix=路径 --enable-shared 按照提示添加 --dis…

FDWS9510L-F085车规级 PowerTrench系列 P沟道增强型MOS管

PowerTrench MOSFET 是优化的电源开关&#xff0c;可提高系统效率和功率密度。 它们组合了小栅极电荷 (Qg)、小反向恢复电荷 (Qrr) 和软性反向恢复主体二极管&#xff0c;有助于快速切换交流/直流电源中的同步整流。 采用屏蔽栅极结构&#xff0c;可提供电荷平衡。 利用这一先进…

RHCE8 资料整理(三)

RHCE8 资料整理 第三篇 网络相关配置第11章 网络配置11.1 网络基础知识11.2 查看网络信息11.3 图形化界面修改11.4 通过配置文件修改11.5 命令行管理11.6 主机名的设置 第12章 ssh12.1 ssh基本用法12.2 打开远程图形化界面12.3 ssh无密码登录12.4 ssh安全设置12.5 ssh限制用户1…

【2024秋招】2023-8-5-小红书-数据引擎团队后端开发提前批面经

1 面试官介绍 OLAP引擎&#xff0c;离线引擎&#xff0c;大数据分析中间件 2 自我介绍 缺点&#xff1a; &#xff08;1&#xff09;面试官让重点介绍自己最在行的项目&#xff0c;我真的在自我介绍上扯了一些别的东西… &#xff08;2&#xff09;在面试的时候因为想看简…

Webots将节点复制到不同工程中

我们要将A工程的节点复制到B工程中。 先将两个工程调成未开始仿真模式 将A中的节点复制 选中节点wall&#xff0c;右击选择Export然后保存为.wbo格式 打开B工程 点击新增&#xff0c;再点击Import&#xff0c;选择刚刚导出的文件 成功导入

k8s kubernetes 1.23.6 + flannel公网环境安装

准备环境&#xff0c;必须是同一个云服务厂商&#xff0c;如&#xff1a;华为&#xff0c;阿里、腾讯等&#xff0c;不要存在跨平台安装K8S&#xff0c;跨平台安装需要处理网络隧道才能实现所有节点在一个网络集群中&#xff0c;这里推荐使用同一家云服务厂商安装即可 这里使用…

计算属性和侦听属性以及方法有什么区别,本文以计算一个数组中所有偶数的和为例

计算属性(computed)是Vue中的一个特殊属性&#xff0c;它根据依赖的数据进行计算&#xff0c;并返回计算结果。计算属性的值会根据其相关依赖项的变化而自动更新&#xff0c;类似于一个响应式的缓存。计算属性可以用来处理一些复杂的逻辑计算&#xff0c;避免在模板中编写过多的…

GitHub和Gitee的区别以及具体使用

文章目录 GitHub和GiteeGitHub和Gitee区别GitHub的使用Gitee的使用 GitHub和Gitee GitHub和Gitee区别 速度不同&#xff1a;GitHub位于美国&#xff0c;而Gitee位于中国。这意味着在中国使用Gitee可能会有更快的访问速度和更好的稳定性。如果我们希望体验Git飞一般的速度&…

Mysql如何确定执行计划是最优开销?Mysql优化器!

1. 什么是 MySQL 优化器&#xff1f; MySQL 优化器是 MySQL 中的一个核心组件。MySQL 优化器的主要职责在于确定查询的执行计划。在数据库中&#xff0c;同样的查询可以有多种不同的执行方式&#xff0c;如使用不同的索引&#xff0c;使用不同的连接顺序等。每种执行方式都有其…

抛硬币有连续x次正面的期望

首先来看期望的基本公式 其中x是抛x次&#xff0c;p是在抛第x次后出现连续正面的概率 先来看出现1次正面的期望 按照公式来算是 抛1次 1/2的概率 抛2次 1/4的概率 抛3次 1/8的概率... 我们可以写一个程序试一下 signed main() {ios_base::sync_with_stdio(0); cin.tie(0…