前端面试题详解整理110| useReducerhistory路由,单页面路由,Auth 权限组件写一个React的render组件深浅拷贝,找到文件所有依赖,

41 阅读27分钟

字节前端笔试代码题

小红的区间(等价于给一个数字序列,求最长的两个相同数之间的距离)(这题我用set全做出来了)
如果你已经使用集合(Set)解决了这个问题,那么恭喜你!但如果你想使用更传统的方法来解决这个问题,可以尝试以下的 JavaScript 实现:

function longestDistance(nums) {
    const indexMap = new Map(); // 创建一个 Map 用于存储每个数字出现的最后位置
    let maxDistance = 0; // 最大距离初始化为 0

    for (let i = 0; i < nums.length; i++) {
        if (indexMap.has(nums[i])) { // 如果数字已经在 Map 中存在
            const lastPosition = indexMap.get(nums[i]); // 获取该数字上次出现的位置
            const distance = i - lastPosition; // 计算当前位置与上次出现位置的距离
            maxDistance = Math.max(maxDistance, distance); // 更新最大距离
        }
        indexMap.set(nums[i], i); // 更新数字的最后位置
    }

    return maxDistance;
}

// 示例用法
const nums = [1, 3, 5, 2, 3, 4, 6, 3, 5, 6, 7];
console.log(longestDistance(nums)); // 输出:5

这个函数接受一个数字序列 nums,然后使用一个 Map 来存储每个数字最后出现的位置。在遍历序列过程中,我们计算每个数字与其上次出现的位置之间的距离,并更新最大距离。最后返回最大距离即可。

小红的手链(三个红珠子,n个白珠子,一次移动一个,求至少三个红珠子的距离为k的移动次数)(弱弱只过了40%)

下面是详细注释的 JavaScript 代码:

function minMovesToGetRedBeads(necklace, k) {
    let minMoves = Infinity; // 初始化最小移动次数为无穷大,用于记录最终结果
    
    // 遍历手链,从左到右依次作为窗口的左边界
    for (let left = 0; left < necklace.length; left++) {
        let redCount = 0; // 初始化当前窗口内的红珠子数量为 0
        let right = left; // 初始化窗口的右边界为左边界

        // 在窗口范围内统计红珠子数量
        while (right < necklace.length && right - left < k) {
            if (necklace[right] === 'R') {
                redCount++; // 如果当前珠子是红色,则红珠子数量加一
            }
            right++; // 右边界向右移动一位
        }

        // 如果当前窗口内有三个红珠子,则更新最小移动次数
        if (redCount === 3) {
            minMoves = Math.min(minMoves, right - left - 3); // 计算当前移动次数并更新最小移动次数
        }
    }

    // 如果最小移动次数没有更新过,则返回 -1,表示无法找到符合条件的移动次数
    return minMoves === Infinity ? -1 : minMoves;
}

// 示例用法
const necklace = ['R', 'W', 'W', 'W', 'R', 'W', 'W', 'W', 'R']; // 手链
const k = 3; // 至少三个红珠子的距离
console.log(minMovesToGetRedBeads(necklace, k)); // 输出:4

以上代码中,我对每个关键步骤都进行了详细的注释说明,包括遍历手链、滑动窗口内的红珠子统计、移动次数更新等。这样可以帮助你更好地理解代码的逻辑和实现过程。 这个问题可以使用滑动窗口的方法来解决。具体步骤如下:

  1. 创建一个滑动窗口,窗口的大小为 k。
  2. 遍历手链,将窗口中的红珠子数量统计出来。
  3. 如果窗口中的红珠子数量小于 3,则将窗口右边界向右移动,直到窗口中的红珠子数量等于 3。
  4. 统计移动次数,每次窗口右移一步,移动次数加一。
  5. 如果窗口中的红珠子数量等于 3,则记录当前移动次数。
  6. 将窗口左边界向右移动一步,继续遍历手链。
  7. 重复步骤 3 到步骤 6,直到遍历完整个手链。

下面是使用 JavaScript 实现的代码:

function minMovesToGetRedBeads(necklace, k) {
    let minMoves = Infinity;
    
    for (let left = 0; left < necklace.length; left++) {
        let redCount = 0;
        let right = left;

        while (right < necklace.length && right - left < k) {
            if (necklace[right] === 'R') {
                redCount++;
            }
            right++;
        }

        if (redCount === 3) {
            minMoves = Math.min(minMoves, right - left - 3);
        }
    }

    return minMoves === Infinity ? -1 : minMoves;
}

// 示例用法
const necklace = ['R', 'W', 'W', 'W', 'R', 'W', 'W', 'W', 'R'];
const k = 3;
console.log(minMovesToGetRedBeads(necklace, k)); // 输出:4

这个函数接受一个字符串数组 necklace 和一个整数 k,表示手链和至少三个红珠子的距离。然后使用滑动窗口来统计符合条件的移动次数,并返回最小移动次数。

QAQ,60分编程题只拿了39分,我大概是没了(我不配说我是acm竞赛的,太弱了)😇

作者:bookfox
链接:www.nowcoder.com/feed/main/d…
来源:牛客网

字节前端实习 一面

  • 自我介绍
  • 聊实习期间的工作,bug怎么修,项目有没有遇到困难

- video转canvas,会压缩吗? 这我是真不清楚

将视频转换为画布(Canvas)上的图像通常不会引起压缩,因为它们是不同的媒体类型。下面是一些关键的说明:

  1. 视频压缩:视频通常以压缩格式存储(如MP4、AVI等),这有助于减小文件大小并提高传输效率。在转换视频文件时,如果选择了压缩选项,则输出的视频文件可能会经过压缩处理。

  2. Canvas绘图:将视频绘制到Canvas上通常不会引起压缩。Canvas绘图是基于像素的,它会根据视频的每一帧创建一个图像,并将其呈现在Canvas上。这个过程不会改变图像的质量或大小,而是将视频的像素直接映射到Canvas上。

  3. Canvas尺寸:绘制视频到Canvas时,Canvas的尺寸可能会影响图像的显示效果。如果Canvas尺寸与视频的尺寸不匹配,可能会发生拉伸或压缩。因此,在绘制视频到Canvas之前,需要确保Canvas的尺寸与视频的尺寸相匹配,以避免图像失真。

总的来说,将视频绘制到Canvas上通常不会引起压缩,但在视频转换过程中可能会选择进行压缩处理。因此,如果希望保持视频的原始质量和大小,可以选择不进行额外的压缩操作。

react严格模式会发生什么

React 严格模式(Strict Mode)是一种工具,用于识别应用中的一些潜在问题,并提供一种改善开发体验的方式。在 React 应用中使用严格模式会发生以下几件事情:

  1. 识别不安全的生命周期方法:严格模式会检测和报告不安全的生命周期方法使用。例如,它会警告使用不推荐的 componentWillMountcomponentWillUpdatecomponentWillReceiveProps 等生命周期方法。

  2. 检测副作用:严格模式会检测副作用,包括在函数组件中的副作用,以及在渲染期间对组件状态和 props 进行更改的情况。

  3. 发现过时的 context API:严格模式会发现过时的 Context API 使用。如果你使用了过时的 Context API(contextTypecontext.Consumer),它会在控制台发出警告。

  4. 检测不安全的生产模式代码:严格模式会检测在生产模式下可能会产生问题的代码。例如,在生产环境中使用 development 特性或检测到不安全的模式时,它会发出警告。

  5. 检测意外的副作用:严格模式会检测到不期望的副作用,例如对 React 对象进行修改。

总的来说,React 严格模式旨在帮助开发者编写更安全、更可靠的代码,并发现一些在开发过程中可能会被忽略的潜在问题。通过启用严格模式,开发者可以更早地发现这些问题,并采取适当的措施来解决它们,从而提高应用的质量和可维护性。

react cm mode

在 React 中,"Concurrent Mode"(并发模式)是一种实验性的特性,旨在提高应用程序的性能和用户体验。Concurrent Mode 允许 React 在不阻塞主线程的情况下处理高优先级的更新,以确保页面保持响应性。

具体来说,Concurrent Mode 提供了以下几个方面的改进:

  1. 优先级调度:Concurrent Mode 允许 React 将任务划分为不同的优先级,并在不同的优先级之间动态切换,以确保高优先级的任务能够尽快得到处理。这样可以保证用户交互和动画等任务的响应速度。

  2. 暂停和恢复:Concurrent Mode 允许 React 在处理任务时暂停和恢复,以处理更高优先级的任务。这种暂停和恢复的机制有助于避免不必要的计算和渲染,提高了应用程序的性能。

  3. 异步渲染:Concurrent Mode 允许 React 在多个事件循环周期内进行渲染,并在渲染过程中中断和恢复。这样可以提高页面的流畅性和响应性,减少页面卡顿和掉帧的现象。

总的来说,Concurrent Mode 是 React 提供的一种实验性的特性,旨在优化应用程序的性能和用户体验。虽然它目前还处于实验阶段,并且可能存在一些限制和不稳定性,但它为开发者提供了一种新的方式来处理高优先级的任务,并提高应用程序的响应速度和流畅性。

vue 响应式怎么做的

  • 2,3

Vue 的响应式是通过 Vue 实例的数据属性来实现的。Vue 使用了一种称为 "响应式对象" 的技术,它利用了 JavaScript 的 getter 和 setter 函数以及依赖追踪来实现数据的变化检测和视图更新。

  1. 数据属性:在 Vue 实例中声明的数据属性会被自动转换为响应式属性。例如:
data() {
  return {
    message: 'Hello, Vue!'
  }
}

在上面的例子中,message 属性是一个响应式属性。当 message 的值发生变化时,相关的视图会自动更新。

  1. getter 和 setter:Vue 在内部使用了 getter 和 setter 函数来实现数据的监视和更新。当访问响应式属性时,Vue 会在 getter 函数中添加依赖,以便在属性变化时能够通知相关的视图进行更新。当修改响应式属性时,Vue 会在 setter 函数中触发更新,通知相关的视图进行重新渲染。

  2. 依赖追踪:Vue 使用了一种称为 "依赖追踪" 的技术来跟踪响应式属性与视图之间的依赖关系。当访问响应式属性时,Vue 会在当前的计算上下文中收集依赖,并建立响应式依赖关系。这样,当响应式属性发生变化时,Vue 就能够自动通知相关的视图进行更新。

总的来说,Vue 的响应式是通过 getter 和 setter 函数以及依赖追踪来实现的。Vue 在内部使用了这些技术来实现数据的变化检测和视图更新,从而让开发者可以更加轻松地构建响应式的用户界面。

proxy怎么劫持,为什么能劫持

Proxy 是 JavaScript 中的一个内置对象,用于创建一个代理对象,可以对目标对象进行拦截和修改。Proxy 提供了一种强大而灵活的机制,可以在目标对象的操作上添加自定义的行为。

Proxy 能够劫持对象的操作,是因为它可以拦截目标对象上的各种操作,比如获取属性、设置属性、删除属性等。当我们使用 Proxy 创建一个代理对象时,可以为代理对象设置一个 handler 对象,该 handler 对象中定义了一系列的拦截器(handler traps),这些拦截器会在代理对象的各种操作上被触发。

以下是一些常见的 Proxy 拦截器:

  1. get:拦截属性的读取操作。
  2. set:拦截属性的设置操作。
  3. deleteProperty:拦截属性的删除操作。
  4. apply:拦截函数的调用操作。
  5. construct:拦截对象的构造函数调用。

通过定义这些拦截器,我们可以在代理对象上对目标对象的操作进行拦截和修改,从而实现对目标对象的劫持。例如,我们可以在 get 拦截器中对属性的读取进行记录或修改,或者在 set 拦截器中对属性的设置进行校验或修改。

Proxy 能够劫持对象的操作,是因为它提供了一种灵活而强大的机制,可以在目标对象的操作上添加自定义的行为。通过定义适当的拦截器,我们可以在代理对象上对目标对象的操作进行拦截和修改,从而实现对目标对象的劫持。

生命周期,每一个,具体发生了什么

在 Vue 2.x 中,组件的生命周期钩子函数可以分为以下几个阶段:创建阶段、挂载阶段、更新阶段和销毁阶段。下面是每个阶段的具体生命周期钩子函数以及它们发生的时机:

  1. 创建阶段

    • beforeCreate:在实例初始化之后,数据观测 (data observer) 和事件配置 (event/watcher setup) 之前被调用。此时组件实例的属性和方法还未初始化,无法访问数据和方法。
    • created:在实例创建完成后被立即调用。此时组件已经完成了数据观测 (data observer) 和事件配置 (event/watcher setup),但尚未挂载到 DOM 上。
  2. 挂载阶段

    • beforeMount:在挂载开始之前被调用:相关的 render 函数首次被调用。
    • mounted:在挂载完成后被调用:el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用此生命周期函数。如果使用了服务端渲染,mounted 将在服务端渲染期间不被调用。
  3. 更新阶段

    • beforeUpdate:数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。
    • updated:由于数据更改导致的虚拟 DOM 重新渲染和打补丁后调用。
  4. 销毁阶段

    • beforeDestroy:在实例销毁之前调用。此时组件实例仍然完全可用。
    • destroyed:在实例销毁后调用。此时组件实例已经从 DOM 中删除,所有的事件监听器和子实例也被销毁。

以上是 Vue 2.x 中的生命周期钩子函数及其具体发生的时机。每个钩子函数都有其特定的作用,可以在对应的阶段执行相关的操作,如数据初始化、DOM 操作、事件监听等。通过合理利用生命周期钩子函数,可以实现更加灵活和高效的组件开发。

computed支持异步吗,为什么

在 Vue 中,computed 属性是同步的,不支持异步操作。这是因为 computed 属性的计算是基于它所依赖的响应式数据的变化而触发的,而响应式数据的变化是同步更新的。

如果 computed 属性支持异步操作,那么就会导致计算结果的不确定性,因为无法确定异步操作何时完成以及何时触发计算属性的更新。这与 Vue 的响应式机制相违背,因为 Vue 的响应式数据变化和更新是同步进行的。

如果需要在 Vue 中进行异步操作,可以使用 Vue 提供的 watch 和异步操作(如 Promise)结合来实现。在 watch 中监听数据变化,并在数据变化时触发异步操作,最终更新组件中的响应式数据。这样可以保证数据的更新是可控的,避免了计算属性中的不确定性。

深浅拷贝,浅拷贝会带来什么问题

深拷贝和浅拷贝是在 JavaScript 中常用的两种对象复制方法。它们的主要区别在于对于嵌套对象的处理方式。

  1. 浅拷贝

    • 浅拷贝会创建一个新的对象,然后将原始对象的属性复制到新对象中。但是,如果原始对象中的属性值是对象或数组等引用类型,那么浅拷贝只会复制这些引用类型的引用,而不是创建它们的副本。
    • 这意味着,浅拷贝后的对象与原始对象共享相同的引用类型数据,如果修改了其中一个对象中的引用类型数据,那么另一个对象也会受到影响。这可能导致意外的副作用,因为它们指向了相同的内存地址。
  2. 深拷贝

    • 深拷贝会创建一个新的对象,并递归地将原始对象的所有属性以及属性值复制到新对象中。这意味着,深拷贝会创建原始对象及其所有嵌套对象的完整副本,而不是共享引用类型数据。
    • 深拷贝可以避免浅拷贝带来的共享引用类型数据的问题,保证了对象之间的独立性,即使其中一个对象发生了变化,其他对象也不会受到影响。

浅拷贝会带来的问题主要是在处理嵌套对象时可能导致意外的副作用,因为多个对象共享相同的引用类型数据。如果不注意对共享数据的修改,可能会影响到其他对象,导致程序行为不稳定或出现 bug。因此,在需要复制对象时,特别是处理嵌套对象时,最好使用深拷贝来确保对象之间的独立性和稳定性。

  • usememo,compued,实现,怎么知道他更新,浅拷贝的话,变化,是否会变化,为什么
  • 实现深拷贝,并且自测

首先,让我们分别解释 useMemocomputed,然后回答你提出的问题。

  1. useMemo

    • useMemo 是 React 提供的一个钩子函数,用于在渲染过程中缓存计算结果。它接收一个函数和一个依赖数组作为参数,函数返回的值会被缓存,并在依赖数组发生变化时重新计算。
    • useMemo 的作用是避免在每次渲染时都重新计算相同的值,从而提高性能。
  2. computed

    • computed 是 Vue 提供的一个属性,用于声明一个计算属性。计算属性的值会根据它所依赖的响应式数据动态计算得出,并且会缓存结果,只在依赖的响应式数据发生变化时重新计算。
    • computed 的作用是将复杂的计算逻辑封装成一个属性,并在数据变化时自动更新计算结果。

现在来回答你的问题:

  • 怎么知道 useMemo 和 computed 更新

    • useMemocomputed 都会在它们依赖的数据发生变化时重新计算,并返回新的值。因此,只要依赖的数据发生了变化,它们就会被触发更新。
  • 浅拷贝的话,变化是否会变化,为什么

    • 如果你在 useMemocomputed 中使用浅拷贝,即依赖的数据是一个引用类型,并且使用浅拷贝复制了这个引用类型数据,那么只有在引用类型数据的引用发生变化时,useMemocomputed 才会感知到变化并重新计算。
    • 原因是浅拷贝只会复制对象的引用,而不会复制对象本身。如果对象的属性值发生了变化,但对象本身的引用并未改变,那么 useMemocomputed 不会感知到变化,也不会重新计算。

接下来,让我们实现一个深拷贝函数,并进行自测。深拷贝函数的作用是将一个对象完全复制一份,包括对象的所有属性和嵌套对象。我将为您提供一个简单的深拷贝函数,并为您展示如何使用它来进行自测。

function deepCopy(obj) {
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }

  const newObj = Array.isArray(obj) ? [] : {};
  
  for (let key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      newObj[key] = deepCopy(obj[key]);
    }
  }

  return newObj;
}

// 测试
const originalObj = {
  a: 1,
  b: { c: 2 },
  d: [3, 4, 5]
};

const copiedObj = deepCopy(originalObj);
console.log(copiedObj); // 输出深拷贝后的对象
console.log(copiedObj === originalObj); // 输出 false,说明是深拷贝

这段代码定义了一个名为 deepCopy 的函数,它接收一个对象作为参数,并返回该对象的深拷贝副本。然后我们对一个对象进行深拷贝,并进行了自测,输出了深拷贝后的对象以及对比了原始对象和拷贝后的对象的引用是否相同,验证了深拷贝的效果。

全程被拷打,问的很深入,不是点到为止那种😭

作者:十八岁没拿过offer
链接:www.nowcoder.com/discuss/519…
来源:牛客网

字节前端实习二面 60min

  1. 问项目 (20min)
  2. 使用React的感受

1. 场景题 如何找到一个文件的所有依赖

要找到一个文件的所有依赖,你可以按照以下步骤操作:

  1. 分析文件的内容:首先,你需要分析目标文件的内容,特别是查找导入语句和依赖声明。对于 JavaScript 文件,你需要查找 importrequire 等语句;对于 CSS 文件,你需要查找 @import 等语句;对于其他类型的文件,也需要根据其具体语法规则来查找依赖。

  2. 解析依赖:根据分析得到的依赖语句,你需要解析这些语句,获取到所引入的模块路径或文件路径。这可能涉及到路径解析、别名解析、模块解析等操作,具体取决于你的项目配置和依赖管理方式。

  3. 递归查找:一旦你获取到了依赖的路径,你需要递归地对每个依赖进行相同的分析和解析操作。这样可以确保找到目标文件的所有直接或间接依赖。

  4. 记录依赖关系:在递归查找过程中,你可以记录下每个文件和其依赖之间的关系,可以使用数据结构如图或对象来表示依赖关系。这样可以帮助你在后续操作中更加方便地管理依赖。

  5. 处理循环依赖:在进行依赖分析时,可能会遇到循环依赖的情况,即 A 文件依赖于 B 文件,而 B 文件又依赖于 A 文件。在处理这种情况时,你需要考虑是否需要忽略循环依赖、报错提示或其他方式进行处理。

  6. 考虑动态导入:如果目标文件中使用了动态导入语法(如 import()),则需要特殊处理这种情况。你可能需要模拟执行动态导入语句,获取到实际导入的模块路径,并将其作为依赖进行分析。

总之,找到一个文件的所有依赖是一个复杂的过程,需要对文件内容进行分析、依赖解析和递归查找等操作。具体的实现方式会根据项目的需求、开发环境和具体文件类型而有所不同。

  1. http的理解

4. 单页面路由是如何实现不刷新更新页面的

单页面应用(SPA)的路由实现通常基于客户端路由和前端路由库来实现,不会导致整个页面的刷新。以下是单页面路由实现的一般步骤和原理:

  1. 客户端路由

    • 在传统的多页面应用(MPA)中,每次点击链接或提交表单都会导致浏览器向服务器发送请求,服务器返回新的页面内容,页面进行整体刷新。而在单页面应用中,页面的路由控制是在客户端完成的,不需要向服务器发送新的请求,页面只需要局部刷新。
  2. 前端路由库

    • 单页面应用通常使用前端路由库来管理页面的路由状态和视图的切换。这些路由库(如 React Router、Vue Router 等)提供了路由规则的配置、路由状态的管理和页面组件的加载等功能,使得页面之间的切换变得更加灵活和高效。
  3. Hash 路由和 History 路由

    • 前端路由库通常支持两种路由模式:Hash 路由和 History 路由。Hash 路由通过 URL 中的哈希值(#)来表示不同的路由状态,页面的切换不会导致整个页面的刷新;而 History 路由利用浏览器的 History API 来实现页面的切换,同样也不会导致整个页面的刷新。
  4. 视图更新

    • 当用户进行页面切换时,前端路由库会根据当前的路由状态匹配相应的页面组件,并进行加载和渲染。页面的切换只会更新页面中的局部内容,不会触发整个页面的刷新,从而实现了不刷新更新页面的效果。

总之,单页面路由实现的核心原理是在客户端通过前端路由库来管理页面的路由状态和视图的切换,从而实现页面的局部刷新而不导致整个页面的刷新。这种方式能够提升用户体验,减少页面加载时间,提高页面的响应速度。

6. history路由的原理

History 路由是一种前端路由的实现方式,它利用浏览器的 History API 来管理页面的路由状态,实现页面的切换而不导致整个页面的刷新。下面是 History 路由的基本原理:

  1. History API

    • 浏览器提供了一组 History API,包括 pushState()replaceState()popstate 事件等。通过这些 API,可以操作浏览器的历史记录,并在不刷新页面的情况下改变 URL。
  2. pushState() 和 replaceState()

    • pushState(state, title, url) 方法用于向浏览器的历史栈中添加一个新的状态,并改变当前的 URL;replaceState(state, title, url) 方法用于修改当前历史记录的状态,并修改当前的 URL。这两个方法可以改变浏览器地址栏中的 URL,但不会导致页面的刷新。
  3. popstate 事件

    • 当浏览器的历史记录发生变化时(如用户点击浏览器的前进或后退按钮),会触发 popstate 事件。通过监听 popstate 事件,可以在页面的 URL 发生变化时进行相应的处理,如更新页面内容或重新加载数据。
  4. 路由库封装

    • 前端路由库(如 React Router、Vue Router 等)通常会封装 History API,提供更友好的 API 来管理页面的路由状态和路由跳转。这些路由库会监听浏览器的 popstate 事件,根据 URL 的变化进行相应的页面渲染和组件加载。
  5. 注意事项

    • 在使用 History API 进行页面跳转时,需要注意不要破坏浏览器的历史记录,避免使用 location.href 直接改变 URL,而是应该使用 pushState()replaceState() 方法。同时,需要正确处理 popstate 事件,以确保页面状态与 URL 的一致性。

总之,History 路由的原理是通过浏览器的 History API 来管理页面的路由状态,利用 pushState()replaceState()popstate 事件等实现页面的切换和导航。这种方式能够实现不刷新页面的路由跳转,提升用户体验和页面性能。

8. useReducer useContext与redux

useReduceruseContext 和 Redux 都是在 React 应用中用于状态管理的工具,它们之间有一些共同点和区别:

  1. 共同点

    • 都用于管理 React 应用的全局状态。
    • 都提供了一种将状态和操作状态的逻辑分离的方式,使得状态管理更加清晰和可维护。
    • 都能够帮助解决状态在组件之间共享和传递的问题,避免了 props drilling(属性传递)的繁琐性。
  2. 区别

    • Redux
      • Redux 是一个独立于 React 的状态管理库,它使用单一的全局状态树(即 Store)来管理应用的状态。
      • Redux 提供了一种专门的架构模式(Flux 架构),包括 Reducer、Action 和 Store 等概念,使得状态管理变得更加结构化和规范化。
      • Redux 需要安装 Redux 库,并配合 React-Redux 库来与 React 应用集成,提供了 connect 函数来连接 React 组件和 Redux 的状态。
    • useReducer
      • useReducer 是 React 提供的一个 Hooks 函数,用于在函数式组件中管理状态。
      • useReducer 的工作原理类似于 Redux 的 Reducer,它接收一个 Reducer 函数和初始状态作为参数,返回当前状态和一个 dispatch 函数,用于触发状态更新。
      • useReducer 可以方便地将状态和操作状态的逻辑封装在一起,并传递给子组件。
    • useContext
      • useContext 也是 React 提供的一个 Hooks 函数,用于在函数式组件中获取全局的上下文对象。
      • useContext 可以用于从上层组件向下层组件传递状态,实现状态的共享。
      • useReducer 不同,useContext 主要用于获取上下文对象,而不负责状态的更新逻辑。
  3. 选择

    • 如果你的应用状态较为简单,并且状态管理逻辑较为简单,可以考虑使用 useStateuseReducer 来管理状态。
    • 如果你的应用状态较为复杂,并且需要多个组件共享状态或进行异步操作时,可以考虑使用 Redux 来管理状态。

总之,useReduceruseContext 和 Redux 都是 React 应用中常用的状态管理工具,选择适合自己项目需求的工具可以提高开发效率和代码质量。

10. 写一个Auth 权限组件

下面是一个简单的权限组件 Auth 的示例,它接收 allowedRoles 属性,用于指定允许访问该组件的角色列表。如果当前用户的角色包含在 allowedRoles 中,则渲染 children;否则,渲染一个提示信息。

import React from 'react';

const Auth = ({ allowedRoles, userRole, children }) => {
  // 检查当前用户角色是否在允许访问的角色列表中
  const isAllowed = allowedRoles.includes(userRole);

  return (
    <React.Fragment>
      {isAllowed ? (
        // 如果用户角色允许访问,则渲染子组件
        <React.Fragment>{children}</React.Fragment>
      ) : (
        // 如果用户角色不允许访问,则渲染权限提示信息
        <div>您没有权限访问此页面。</div>
      )}
    </React.Fragment>
  );
};

export default Auth;

使用示例:

import React from 'react';
import Auth from './Auth';

const App = () => {
  // 假设当前用户的角色是 'admin'
  const userRole = 'admin';

  return (
    <div>
      {/* 允许 'admin' 和 'manager' 角色访问的组件 */}
      <Auth allowedRoles={['admin', 'manager']} userRole={userRole}>
        <div>欢迎访问管理员页面。</div>
      </Auth>

      {/* 允许 'user' 角色访问的组件 */}
      <Auth allowedRoles={['user']} userRole={userRole}>
        <div>欢迎访问普通用户页面。</div>
      </Auth>
    </div>
  );
};

export default App;

在上面的示例中,根据当前用户的角色和指定的允许访问的角色列表,动态决定是否渲染子组件或权限提示信息。这种方式可以轻松实现权限控制,确保用户只能访问其具有权限的页面或组件。

12. 写一个React的render组件

下面是一个简单的 Render 组件的示例,它接收一个 renderFunc 属性,用于指定渲染内容的函数。该组件会将 renderFunc 函数的返回值作为子组件进行渲染。

import React from 'react';

const Render = ({ renderFunc }) => {
  return (
    <React.Fragment>
      {/* 调用 renderFunc 函数并将其返回的内容作为子组件进行渲染 */}
      {renderFunc()}
    </React.Fragment>
  );
};

export default Render;

使用示例:

import React from 'react';
import Render from './Render';

const App = () => {
  return (
    <div>
      {/* 使用 Render 组件并传入一个渲染函数 */}
      <Render
        renderFunc={() => (
          <div>
            <h1>Hello, Render Component!</h1>
            <p>This is the content rendered by Render component.</p>
          </div>
        )}
      />
    </div>
  );
};

export default App;

在上面的示例中,Render 组件接收一个 renderFunc 属性,该属性是一个函数,用于返回需要渲染的内容。当 Render 组件被渲染时,它会调用 renderFunc 函数,并将其返回的内容作为子组件进行渲染。这种方式可以实现更加灵活和动态的内容渲染。

作者:躺平了的菜鸡很卖萌
链接:www.nowcoder.com/discuss/422…
来源:牛客网