【React】高频面试题
1. 简述下 React 的事件代理机制?
React使用了一种称为“事件代理”(Event Delegation)的机制来处理事件。事件代理是指将事件处理程序绑定到组件的父级元素上,然后在需要处理事件的子元素上触发事件时,事件将被委托给父级元素进行处理。
React的事件代理机制有以下几个特点:
-
事件委托:React将事件绑定到组件的父级元素上,而不是直接绑定到每个子元素。这样,不论子元素的数量如何,只需要在父级元素上绑定一个事件处理程序,就可以处理所有子元素的事件。
-
事件冒泡:当子元素上的事件触发时,事件会沿着DOM树从子元素冒泡到父级元素。React利用事件冒泡机制来实现事件代理,将事件从子元素传递到父级元素进行处理。
-
合成事件:React提供了合成事件(Synthetic Event)来封装底层浏览器的原生事件。合成事件是跨浏览器兼容的,并且提供了一致的接口,使开发者可以方便地处理事件。
-
组件实例存储:React会在组件实例上存储合成事件,以便在处理事件时能够准确地访问到组件的相关数据和方法。
通过事件代理机制,React实现了高效和灵活的事件处理方式,同时减少了内存消耗。它使得开发者能够更方便地处理事件,并且能够避免一些潜在的性能问题,特别是在处理大量子元素的情况下。
需要注意的是,React的事件代理机制并不是与原生的事件代理完全相同。React并不是通过在父级元素上使用事件委托来实现性能优化,而是使用合成事件和组件实例存储来提供一种更高效和便捷的事件处理方式。
2. 使用 redux 有哪些原则?
Redux是一个用于JavaScript应用程序状态管理的开源库。在使用Redux时,有几个原则需要遵循:
-
单一数据源:Redux要求整个应用的状态(state)被存储在一个单一的存储库(store)中。这意味着应用的所有状态都被存储在一个JavaScript对象中。
-
状态是只读的:Redux中的状态是只读的,即不能直接修改状态。要修改状态,需要通过派发一个动作(action)来触发状态的变化。
-
使用纯函数进行状态更新:Redux中的状态更新由纯函数(reducer)处理。纯函数接收先前的状态和动作,返回一个新的状态对象。纯函数应该是没有副作用的,即相同的输入永远会得到相同的输出。
-
使用纯函数处理副作用:对于具有副作用(如异步请求、日志记录等)的操作,应使用Redux中间件来处理。通过使用中间件,可以确保状态更新仍然是纯函数,并且副作用的操作被封装在中间件中。
-
使用动作类型常量:为了保持一致性和避免拼写错误,建议使用动作类型常量来定义动作的类型。这样可以在应用的不同部分共享常量,减少错误和调试的难度。
-
使用选择器(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的一些主要新特性:
-
Concurrent Mode(并发模式):Concurrent Mode是React 18中最重要的新特性之一。它引入了一种新的渲染方式,使得React应用能够更高效地处理大型组件树,提高应用的性能和响应能力。
-
新的渲染器(新的渲染引擎):React 18引入了新的渲染器,名为React Server Components,用于在服务器上渲染React组件。它可以加速服务器渲染,并提供更好的开发体验。
-
自动批处理(Automatic Batching):React 18引入了自动批处理机制,它可以自动将多个状态更新批处理为一次,减少不必要的重渲染,提高性能。
-
事件处理改进:React 18改进了事件处理机制,引入了新的API(如useEvent和useEffect),使得事件处理更加灵活和易用。
-
生命周期改进:React 18引入了新的生命周期方法,如useEffect的onRender回调,用于更精确地控制组件的渲染和副作用。
-
组件级别的错误边界(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的过程:
-
解析JSX代码:React使用Babel等工具将JSX代码转换为JavaScript代码。这个过程将JSX中的标签、属性和内容转换为等效的JavaScript语法。
-
创建虚拟DOM:在运行时,React会使用转换后的JavaScript代码来创建虚拟DOM(Virtual DOM)。虚拟DOM是一个JavaScript对象树,它和真实DOM具有相似的结构。
-
Diff算法比较变化:当组件的状态或属性发生变化时,React会使用Diff算法比较新的虚拟DOM和旧的虚拟DOM,找出差异(即需要更新的部分)。
-
更新真实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中的标签在导航功能上有一些区别。
-
SPA导航:组件用于在单页应用中实现导航。它通过React Router管理路由,并使用JavaScript来更新应用程序的状态和渲染。相反,标签通常是用于传统多页应用中的导航,会导致整个页面的刷新。
-
阻止浏览器默认行为:使用组件时,React Router会拦截点击事件,阻止浏览器默认的页面刷新行为,以避免整个页面的重新加载。这样可以实现无刷新的页面切换。而标签会触发浏览器默认行为,导致页面的重新加载。
-
路由匹配:组件会自动处理路由的匹配和URL的生成。它会根据设置的to属性生成对应的URL,并与当前路由进行匹配以添加活动状态的类名。相反,标签只是一个普通的HTML标签,不会自动处理路由匹配。
-
动态路由参数:组件可以方便地处理动态路由参数。通过在to属性中传递参数,可以动态生成对应的URL。而标签需要手动拼接URL参数。
总之,组件是React Router提供的导航组件,可以与React Router一起使用来实现无刷新的页面切换和路由导航。而标签是传统HTML中的导航标签,会触发页面的重新加载。
在使用React Router时,建议使用组件来处理导航和路由链接,以便获得更好的性能和用户体验。
12. React 中的 ref 有什么用?
在React中,ref是一个特殊的属性,用于获取组件或DOM元素的引用。通过ref,你可以在组件中访问和操作对应的实例或DOM元素。
ref的主要用途有以下几个:
-
访问组件实例:使用ref可以获取到组件的实例,从而可以直接调用组件的方法、访问组件的属性,或者获取组件的状态。这在某些情况下非常有用,比如需要手动触发组件的方法或访问组件中的数据。
-
操作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. 单页应用如何提高加载速度?
- 使用代码分割:将代码拆分成小块并按需加载(懒加载),以避免不必要的网络请求和减少加载时间。
- 缓存资源:利用浏览器缓存来存储重复使用的文件,例如 CSS 和 JS 文件、图片等。
- 预加载关键资源:在首次渲染之前,先提前加载关键资源,例如首页所需的 JS、CSS 或数据,以保证关键内容的快速呈现。
- 使用合适的图片格式:选择合适的图片格式(例如 JPEG、PNG、WebP 等),并根据需要进行压缩以减少文件大小。对于一些小图标, 可以使用 iconfont 等字体文件来代替。
- 启用 Gzip 压缩:使用服务器端的 Gzip 压缩算法对文件进行压缩,以减少传输时间和带宽消耗。
- 使用 CDN:使用内容分发网络(CDN)来缓存和传递文件,以提高文件的下载速度和可靠性。
- 优化 API 请求:尽可能地减少 API 调用的数量,并使用缓存和延迟加载等技术来优化 API 请求的效率。
- 使用服务器端渲染:使用服务器端渲染(SSR)来生成 HTML,以减少客户端渲染所需的时间和资源。但需要注意,SSR 也可能增加了服务器的负担并使网站更复杂。
14. React 中,怎么实现父组件调用子组件中的方法?
在React中,实现父组件调用子组件中方法的方式在函数组件和类组件之间有一些区别。
- 对于函数组件,你可以使用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()来调用子组件中的方法。
- 对于类组件,可以使用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 的事件代理机制? React使用了一种称为“事件代理”(Event Delegation)的机制来处理事件。事件代理是指将事件处理程序绑定到组件的父级元素上,然后在需要处理事件的子元素上触发事件时,事件将被委托给…...

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

迅为RK3568开发板RTMP推流之视频监控
1 搭建 RTMP 媒流体服务器 nginx-rtmp 是一个基于 nginx 的 RTMP 服务模块,是一个功能强大的流媒体服务器模块, 它提供了丰富的功能和灵活的配置选项,适用于构建各种规模的流媒体平台和应用。无论是搭建实时视频直播平台、点播系统或多屏互…...
利用CSRF或XSS攻击网站的例子
利用 CSRF 攻击网站的简单示例: 假设有一个在线银行应用,用户可以在其中执行转账操作。用户登录后,系统会生成一个包含转账信息的表单,用户需要填写表单来发起转账。这个表单如下所示: <form action"https:/…...

LeetCode讲解篇之113. 路径总和 II
文章目录 题目描述题解思路题解代码 题目描述 题解思路 深度优先遍历二叉树,遍历的同时记录路径,直到遍历到叶节点,若路径和为targetSum则添加到结果集中 题解代码 func pathSum(root *TreeNode, targetSum int) [][]int {var res make([…...
中国HR从业者现状是怎样的?应如何提升自己?
HR(Human Resource)解释为人力资源,现在统称为人力资源顾问,跟传统人事有本质区别。传统人事一般是和行政部做相类似的工作,比如招聘,培训,职员的考核,职员的薪酬,职员调动等。现代人力资源&…...

Promise笔记-同步回调-异步回调-JS中的异常error处理-Promis的理解和使用-基本使用-链式调用-七个关键问题
Promise笔记 1. 预备知识1.1 实例对象与函数对象1.2 两种类型的回调函数1. 同步回调2. 异步回调 1.3 JS中的异常error处理1. 错误的类型2. 错误处理(捕获与抛出)3. 错误对象 2.Promise的理解和使用2.1 Promise是什么1.理解Promise2.Promise 的状态3. Pro…...
计算机考研自命题(2)
1、C语言-字符串交替拼接 1、用C编程,将两个字符串数组存储实现交替连接如aaa和bbb两个字符连接成ababab 如aaa和baba 两个字符,连接成 abaaaba #include<stdio.h>/* 解题思路:将两个字符串交替拼接,定义三个数组࿰…...

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…...

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

今年这行情,不会自动化的要做好心理准备了
李强是一名软件测试工程师,入行之后在一家小型公司工作了五年。这段时间里,他主要负责手工测试和一些简单的自动化测试工作。由于公司项目也相对简单,他逐渐陷入了工作的舒适区,没有积极追求新的知识和技能。 然而随着身边朋友发展…...
汽车保养笔记
汽车保养笔记 汽车小保养汽车大保养五油:机油变速箱油刹车油转向助力油离合器油 四滤:机油滤芯更换空气滤芯更换空调滤芯更换汽油滤芯更换 三水防冻液(水)玻璃水电瓶水 其他刹车片球头减震器火花塞 4S店的4大套路---没必要清洗节气门更换火花塞和高压线圈…...

【斗破年番】官方改编用心了,彩鳞怀孕并未删,萧潇肯定登场,真相在丹药身上
【侵权联系删除】 【文/郑尔巴金】 斗破苍穹年番动画已经更新了,相信不少人都感觉到不可思议,萧炎跟随美杜莎女王回蛇人族的剧情,居然魔改成这样。好好的腹中孕育出新生命,变成了陨落心炎残余能量,不及时处理的话&…...

英语——分享篇——每日200词——3201-3400
3201——air-conditioning——[eərkəndɪʃnɪŋ]——n.空调设备;vt.给…装上空调——air-conditioning——air-condition空调(熟词)ing鹰(谐音)——空调设备的噪音让鹰不得安宁——The trains dont even have proper air-conditioning, grumbles Mr So. ——地铁…...
合并区间(C++解法)
题目 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。 示例 1: 输入:intervals …...

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

使用MFC创建一个SaleSystem
目录 1、项目的创建: 2、项目的配置: 3、设置窗口属性: (1)、设置图标 1)、添加导入资源 2)、代码初始化图标 (2)、设置标题 (3)、设置窗口…...

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

Python+Requests+PyTest+Excel+Allure 接口自动化测试实战
本文主要介绍了PythonRequestsPyTestExcelAllure 接口自动化测试实战,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 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、安…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...

DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...
6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙
Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...

相关类相关的可视化图像总结
目录 一、散点图 二、气泡图 三、相关图 四、热力图 五、二维密度图 六、多模态二维密度图 七、雷达图 八、桑基图 九、总结 一、散点图 特点 通过点的位置展示两个连续变量之间的关系,可直观判断线性相关、非线性相关或无相关关系,点的分布密…...

Mac flutter环境搭建
一、下载flutter sdk 制作 Android 应用 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter 1、查看mac电脑处理器选择sdk 2、解压 unzip ~/Downloads/flutter_macos_arm64_3.32.2-stable.zip \ -d ~/development/ 3、添加环境变量 命令行打开配置环境变量文件 ope…...