React前端面试题(持续更新...) 1

简介: React前端面试题(持续更新...)

1.说说对 React 的理解?有哪些特性?

raect是什么

React是一个简单的 javascript UI库,用于构建高效、快速的用户界面。

它是一个轻量级库,因此很受欢迎。它遵循组件设计模式、声明式编程范式和函数式编程概念,以使前端应用程序更高效。

它使用虚拟 DOM来有效地操作 DOM。

它遵循从高阶组件到低阶组件的单向数据流。

特性

React 特性有很多,如:

JSX 语法

单向数据绑定

虚拟 DOM

声明式编程

Component

优势

高效灵活

声明式的设计,简单使用

组件式开发,提高代码复用率

单向响应的数据流会比双向绑定的更安全,速度更快

2.说说Real diff算法是怎么运作的?

diff算法


在某一时间节点调用 React 的 render() 方法,会创建一棵由 React 元素组成的树。在下一次 state 或 props 更新时,相同的 render() 方法会返回一棵不同的树。React 需要基于这两棵树之间的差别来判断如何有效率的更新 UI 以保证当前 UI 与最新的树保持同步。


React diff策略


1.Web UI 中 DOM 节点跨层级的移动操作特别少,可以忽略不计。【永远只比较同层节点,不会跨层级比较节点。】

2.拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结构。

3.对于同一层级的一组子节点,它们可以通过唯一 key 进行区分

基于以上三个前提策略,React 分别对 tree diff、component diff** 以及 element diff 进行算法优化,事实也证明这三个前提策略是合理且准确的,它保证了整体界面构建的性能。


tree层:


tree层对DOM节点的跨层级移动的操作忽略不计,只对相同层级的DOM节点进行比较(即同一个父节点下的所有子节点),一旦发现节点不存在,直接删除掉该节点以及之下的所有子节点。


component层:

component层是组件间的比较,有三种策略:


遇到同一类型的组件遵循 tree diff,进行层级对比

遇到不同类型的组件,直接将这个不同的组件判断为脏组件,并替换该组件和之下所有的子节点

在同一类型的两个组件中,当知道这个组件的虚拟dom没有任何变化,就可以手动使用shouldComponentUpdate()来判断组件是否需要进行diff,进一步的提升了diff效率和性能

注意:


避免使用结构相同但是类型不同的组件,由于类型不同的原因,diff会直接销毁该组件并重建,会造成的性能浪费;

对于同一类型并且没有变化的组件,合理使用 shouldComponentUpdate() 进行优化


element层:

element层对同一层级的元素节点进行比较,有三种情况:


面对全新的节点时,执行插入操作

面对多余的节点时,执行删除操作

面对换位的节点时,执行移动操作

react中key值的作用


key值就是每个元素节点对应的唯一标识,要进行数据的更新,需要进行新旧虚拟dom的对比,就需要对比子元素的key值是否有匹配项,如果有则会进行数据的更新;如果没有就需要进行删除和重新创建


3.说说React生命周期有哪些不同的阶段?每个阶段对应的方法是?

生命周期: React整个组件生命周期包括从创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程


一共有三个阶段,分别为


挂载阶段(Mounting):已插入真实的Dom阶段


更新阶段(Updating):正在被重新渲染的阶段


卸载阶段(Unmounting):已移出真是dom阶段


旧版生命周期

c3f2dd9f4fac426a90dc4074acdb3431.png

新版生命周期

4eab3ae973a1444484a02d7e8288379f.png


挂载阶段:


constructor(): 在 React 组件挂载之前,会调用它的构造函数。


componentWillMount(): 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。


componentDidMount(): 在组件挂载后(插入 DOM 树中)立即调用


更新运行阶段:


componentWillReceiveProps(): 在接受父组件改变后的props需要重新渲染组件时用到的比较多,外部组件传递频繁的时候会导致效率比较低


shouldComponentUpdate():用于控制组件重新渲染的生命周期,state发生变化,组件会进入重新渲染的流程,在这里return false可以阻止组件的更新


render(): render() 方法是 class 组件中唯一必须实现的方法。


componentWillUpdate(): shouldComponentUpdate返回true以后,组件进入重新渲染完成之前进入这个函数。


componentDidUpdate(): 每次state改变并重新渲染页面后都会进入这个生命周期


卸载或销毁阶段:


componentWillUnmount (): 在此处完成组件的卸载和数据的销毁。


4.说说你对react hook的理解?

React 中通常使用 类定义 或者 函数定义 创建组件:


在类定义中,我们可以使用到许多 React 特性,例如 state、 各种组件生命周期钩子等,但是在函数定义中,我们却无能为力,因此 React 16.8 版本推出了一个新功能 (React Hooks),通过它,可以更好的在函数定义组件中使用 React 特性。


优势


代码逻辑聚合,逻辑复用

HOC嵌套地狱

代替class

好处:


跨组件复用: 其实 render props / HOC 也是为了复用,相比于它们,Hooks 作为官方的底层 API,最为轻量,而且改造成本小,不会影响原来的组件层次结构和传说中的嵌套地狱;

类定义更为复杂

---- 不同的生命周期会使逻辑变得分散且混乱,不易维护和管理;

---- 时刻需要关注this的指向问题;

---- 代码复用代价高,高阶组件的使用经常会使整个组件树变得臃肿;

状态与UI隔离: 正是由于 Hooks 的特性,状态逻辑会变成更小的粒度,并且极容易被抽象成一个自定义 Hooks,组件中的状态和 UI 变得更为清晰和隔离

注意:


避免在 循环/条件判断/嵌套函数 中调用 hooks,保证调用顺序的稳定;

只有 函数定义组件 和 hooks 可以调用 hooks,避免在 类组件 或者 普通函数 中调用;

不能在useEffect中使用useState,React 会报错提示;

类组件不会被替换或废弃,不需要强制改造类组件,两种方式能并存;

常用的 hooks


useState()

useEffect()

useCallback()

useMemo()

useRef()

useContext()

useReducer()

Hooks的用法


useState():状态钩子

纯函数组件没有状态,useState()用于设置和使用组件的状态属性。

const [state, setState] = useState(initialValue);
// state:初始的状态属性,指向状态当前值,类似this.state
// setState:修改状态属性值的函数,用来更新状态,类似setState
// initialValue:状态的初始值,该值会赋给state

注意:setState的命名为:set+State(初始状态名),并且采用小驼峰命名法。例如[count, setCount]、[name, setName]


示例:使用Hooks重写计数器

const Count = () => {
    const [count, setCount] = useState(0); // 将0设置为count的初始值
    const addCount = () => {
        let newCount = count;
        setCount(newCount += 1);
    }
    return (
        <div>
            <p>{count}</p>
            <button onClick={addCount}>加1</button>
        </div>
    )
}

用函数组件实现了一个功能完全一样的计数器,代码看起来更加的轻便简洁,没有了继承,没有了渲染逻辑,没有了生命周期等。这就是hooks存在的意义。


useEffect():副作用钩子

useEffect()是副作用的钩子,可以实现特定的功能,如异步请求。语法如下:

useEffect(() => {
    // 回调函数,其中是要进行的异步操作代码
}, [array])
// [array]:useEffect执行的依赖,当该数组的值发生改变时,回调函数中的代码就会被指向
// 如果[array]省略,则表示不依赖,在每次渲染时回调函数都会执行
// 如果[array]是空数组,即useEffect第二项为[],表示只执行一次

示例:通过useEffect()模拟异步加载数据。

const AsyncPage = () => {
    // 首先设置loading状态为true
    const [loading, setLoading] = useState(true);
    useEffect(() => {
        // 2秒后将loading状态设置为false
        setTimeout(() => {
            setLoading(false);
        }, 2000);
    })
    return (
        // 判断loading是否为true,是就显示loading,不是就显示异步请求完成
        loading ? <p>loading...</p> : <p>异步请求完成</p>
    )
}

示例:useEffect()依赖第二项数组变化

const AsyncPage = ({name}) => {
    const [loading, setLoading] = useState(true); // 设置loading状态为true
    const [person, setPerson] = useState({}); // 设置person状态为空对象
    useEffect(() => {
        // 首先设置loading为true,2秒后改为false,name改成传过来的参数
        setLoading(true);
        setTimeout(() => {
            setLoading(false);
            setPerson({name});
        }, 2000);
    }, [name]); // 表示当name修改才会执行回调函数
    return (
        <>
            {loading ? <p>Loading...</p> : <p>{person.name}</p>}
        </>
    )
}
const PersonPage = () => {
    // 设置初始state为空字符串
    const [state, setState] = useState("");
    const changeName = (name) => { // 修改name的函数
        setState(name);
    }
    return (
        <>
            {/*首先将state传给name*/}
            <AsyncPage name={state}/>
            <button onClick={() => { // 点击按钮后将张三传给name
                changeName("张三")
            }}>张三
            </button>
            <button onClick={() => {
                changeName("李四")
            }}>李四
            </button>
        </>
    )
}

useEffect和useLayoutEffect的区别

useEffect()和useLayoutEffect()主要的区别是调用时机不同。


useLayoutEffect()和componentDidMount()及componentDidUpate()一致,再react完成DOM更新后马上同步调用代码,它会阻塞页面的渲染,而useEffect()则会在页面渲染完后才会异步调用。


在实际使用中如果项避免页面抖动,可以把需要操作DOM的代码放在useLayoutEffect()中,在该函数中做DOM操作,这些DOM修改会和react做出的更改一起被一次性渲染到屏幕上,只有一次回流重绘的代价。


useCallback(): 记忆函数

useCallback()为记忆函数,它可以防止因为组件重新渲染,导致方法被重新创建,起到缓存作用。语法如下:

useCallback(() => {
    // 回调函数,当array改变后,该函数才会重新声明
}, [array])
// 如果[array]为空数组,那么就是第一次创建后就被缓存,如果后期array改变了,拿到的还是老的array
// 如果不传入第二个参数,每次都会重新声明一次,拿到的就是最新的array

比如说下面一段代码中,我们可以看到有很多的函数,当我们在return中修改一个状态,就会导致整个页面重新渲染,那么这些函数(handleChange1、handleChange2…)也会被重新创建,这样会造成性能的浪费,因此可以使用useCallback将这些函数缓存下来,这样下一次页面重新渲染的时候,某些函数就不会重新被创建了。

        const UseCallback = function () {
            const handleChange1 = () => {
                // 具体代码
            }
            const handleChange2 = () => {
                // 具体代码
            }
            const handleChange3 = () => {
                // 具体代码
            }
            const handleChange4 = () => {
                // 具体代码
            }
            return (
                <div>
                    {/*具体代码*/}
                </div>
            )
        }

使用useCallback()时,只需要将其写在整个函数外部即可,上面代码使用useCallback()后效果如下,每当依赖项改变时,该函数才会被重新创建,如果依赖项不变,则不会重新创建。

        const UseCallback = function () {
            const handleChange1 = useCallback(
                () => {
                    // 具体代码
                }, [依赖项]
            )
            const handleChange2 = useCallback(
                () => {
                    // 具体代码
                }, [依赖项]
            )
            const handleChange3 = useCallback(
                () => {
                    // 具体代码
                }, [依赖项]
            )
            const handleChange4 = useCallback(
                () => {
                    // 具体代码
                }, [依赖项]
            )
            return (
                <div>
                    {/*具体代码*/}
                </div>
            )
        }

**useMemo():**记忆组件

useCallback()的功能可以由useMemo()所替代,useMemo()也可以返回一个记忆函数,语法如下:

useMemo(() => fn, [])
// useCallback(fn, []) = useMemo(() => fn, [])

useCallback()与useMemo()的区别:


useCallback()不会执行第一个参数函数,而是将其返回,useMemo()会执行第一个函数并且将函数执行结果返回给你。useCallback()常用记忆时间按函数,生成记忆后的时间函数传递给子组件使用,useMemo()更适合经过函数计算得到一个确定的只,比如记忆组件。


**useRef():**保存引用值

useRef()等价于类组件中的React.createRef(),语法如下:

const loginRef = useRef();

使用useRef()创建了一个值后,就可以将其绑定到DOM节点上,给DOM节点增加一个ref属性,将loginRef传入,则可以通过其current属性获取到DOM节点的值,语法如下:

<input ref={loginRef}/>

除此之外,我们都知道useState()可以保存一个状态,那么另一个方法就是使用useRef(),为useRef()传入一个初始值,它可以帮助我们记住这个状态。


useContext():共享状态钩子

useContext()可以共享状态,作用是进行状态的分发(React16.x以后的版本支持),避免了使用Props进行数据的传递。语法如下:

// 第一步:创建全局的Context
const AppContext = React.createContext([初始化参数])
// 第二步:通过全局的Context进行状态值的共享
<AppContext.Provider value={{ 属性名: 值 }}>
    <组件1 />
    <组件2 />
</AppContext>

示例:A组件和B组件共享一个状态


const Count = () => {
    const AppContext = React.createContext({});
    const A = () => {
        const {name} = useContext(AppContext);
        return (
            <div>
                我是A组件,我的名字是:{name}
            </div>
        )
    }
    const B = () => {
        const {name} = useContext(AppContext);
        return (
            <div>
                我是B组件,我的名字是:{name}
            </div>
        )
    }
    return (
        <AppContext.Provider value={{name: "橘猫吃不胖"}}>
            <A/>
            <B/>
        </AppContext.Provider>
    )
}

useReducer():Action钩子

在使用React的过程中,如遇到状态管理,一般会用到Redux,而React本身是不提供状态管理的。而useReducer()提供了状态管理。


useReducer()是useState()的替代方案。首先,关于redux我们都知道,其原理是通过用户在页面中发起action,从而通过reducer方法来改变state,从而实现页面和状态的通信。而Reducer的形式是(state, action) => newstate,返回当前的 state 以及与其配套的 dispatch 方法。。Hooks的useReducer()是这样的:

const [state, dispatch] = useReducer(reducer, initialState)

它接受reducer函数和状态的初始值作为参数,返回一个数组,其中第一项为当前的状态值,第二项为发送action的dispatch函数。


在某些场景下,useReducer 会比 useState 更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为可以向子组件传递 dispatch 而不是回调函数。


例如:使用useReducer()实现一个计数器

const Count = () => {
    const reducer = (state, action) => {
        if (action.type == "add") {
            return {
                ...state,
                count: state.count + 1
            }
        } else {
            return state
        }
    }
    const addCount = () => {
        dispatch({
            type: "add"
        })
    }
    const [state, dispatch] = useReducer(reducer, {count: 0});
    return (
        <>
            <p>{state.count}</p>
            <button onClick={addCount}>加1</button>
        </>
    )
}

通过代码可以看到,使用useReducer()代替了Redux的功能,但useReducer无法提供中间件等功能,假如有这些需求,还是需要用到redux。

相关文章
|
2天前
|
Web App开发 前端开发 Linux
「offer来了」浅谈前端面试中开发环境常考知识点
该文章归纳了前端开发环境中常见的面试知识点,特别是围绕Git的使用进行了详细介绍,包括Git的基本概念、常用命令以及在团队协作中的最佳实践,同时还涉及了Chrome调试工具和Linux命令行的基础操作。
「offer来了」浅谈前端面试中开发环境常考知识点
|
4天前
|
前端开发 JavaScript API
React、Vue.js 和 Angular前端三大框架对比与选择
前端框架是用于构建用户界面的工具和库,它提供组件化结构、数据绑定、路由管理和状态管理等功能,帮助开发者高效地创建和维护 web 应用的前端部分。常见的前端框架如 React、Vue.js 和 Angular,能够提高开发效率并促进团队协作。
21 4
|
18天前
|
前端开发 JavaScript 开发者
Express.js与前端框架的集成:React、Vue和Angular的示例与技巧
本文介绍了如何将简洁灵活的Node.js后端框架Express.js与三大流行前端框架——React、Vue及Angular进行集成,以提升开发效率与代码可维护性。文中提供了详细的示例代码和实用技巧,展示了如何利用Express.js处理路由和静态文件服务,同时在React、Vue和Angular中构建用户界面,帮助开发者快速掌握前后端分离的开发方法,实现高效、灵活的Web应用构建。
32 3
|
17天前
|
JavaScript 前端开发 API
如何在前端开发中有效管理状态:React vs. Vue
在现代前端开发中,状态管理是一个关键因素,它直接影响到应用的性能和可维护性。React 和 Vue 是当前最流行的前端框架,它们在状态管理方面各有优势和劣势。本文将深入探讨 React 和 Vue 在状态管理中的不同实现,分析它们的优缺点,并提供实际应用中的最佳实践,以帮助开发者选择最适合他们项目的解决方案。
|
2月前
|
前端开发 Java Spring
Spring与Angular/React/Vue:当后端大佬遇上前端三杰,会擦出怎样的火花?一场技术的盛宴,你准备好了吗?
【8月更文挑战第31天】Spring框架与Angular、React、Vue等前端框架的集成是现代Web应用开发的核心。通过RESTful API、WebSocket及GraphQL等方式,Spring能与前端框架高效互动,提供快速且功能丰富的应用。RESTful API简单有效,适用于基本数据交互;WebSocket支持实时通信,适合聊天应用和数据监控;GraphQL则提供更精确的数据查询能力。开发者可根据需求选择合适的集成方式,提升用户体验和应用功能。
67 0
|
2月前
|
前端开发 Java JSON
Struts 2携手AngularJS与React:探索企业级后端与现代前端框架的完美融合之道
【8月更文挑战第31天】随着Web应用复杂性的提升,前端技术日新月异。AngularJS和React作为主流前端框架,凭借强大的数据绑定和组件化能力,显著提升了开发动态及交互式Web应用的效率。同时,Struts 2 以其出色的性能和丰富的功能,成为众多Java开发者构建企业级应用的首选后端框架。本文探讨了如何将 Struts 2 与 AngularJS 和 React 整合,以充分发挥前后端各自优势,构建更强大、灵活的 Web 应用。
40 0
|
2月前
|
前端开发 Java UED
JSF 面向组件开发究竟藏着何种奥秘?带你探寻可复用 UI 组件设计的神秘之路
【8月更文挑战第31天】在现代软件开发中,高效与可维护性至关重要。JavaServer Faces(JSF)框架通过其面向组件的开发模式,提供了构建复杂用户界面的强大工具,特别适用于设计可复用的 UI 组件。通过合理设计组件的功能与外观,可以显著提高开发效率并降低维护成本。本文以一个具体的 `MessageComponent` 示例展示了如何创建可复用的 JSF 组件,并介绍了如何在 JSF 页面中使用这些组件。结合其他技术如 PrimeFaces 和 Bootstrap,可以进一步丰富组件库,提升用户体验。
44 0
|
2月前
|
开发者 安全 UED
JSF事件监听器:解锁动态界面的秘密武器,你真的知道如何驾驭它吗?
【8月更文挑战第31天】在构建动态用户界面时,事件监听器是实现组件间通信和响应用户操作的关键机制。JavaServer Faces (JSF) 提供了完整的事件模型,通过自定义事件监听器扩展组件行为。本文详细介绍如何在 JSF 应用中创建和使用事件监听器,提升应用的交互性和响应能力。
22 0
|
17天前
|
SpringCloudAlibaba JavaScript 前端开发
谷粒商城笔记+踩坑(2)——分布式组件、前端基础,nacos+feign+gateway+ES6+vue脚手架
分布式组件、nacos注册配置中心、openfegin远程调用、网关gateway、ES6脚本语言规范、vue、elementUI
谷粒商城笔记+踩坑(2)——分布式组件、前端基础,nacos+feign+gateway+ES6+vue脚手架
|
2月前
|
存储 前端开发 JavaScript
前端语言串讲 | 青训营笔记
前端语言串讲 | 青训营笔记
28 0

热门文章

最新文章

下一篇
无影云桌面