前端面试题详解整理64| redux相关,useref 不建议使用, SWR的实现方案,为什么不能用它处理post请求, 函数式编程 immutable,

323 阅读18分钟

阿里前端面经

记录一下历时很久的阿里暑期实习面试,3.11投递,5.9收到意向

0320 一面

  1. 基本围绕项目展开,没有自我介绍
  2. 课程有哪些工程相关的东西
  3. 做了哪些工作 有哪些亮点

1. useref 不建议使用

在React中,useRef是一个用于创建对DOM元素或其他React组件的引用的Hook。虽然useRef是React提供的一个强大的工具,但在某些情况下确实可能不建议过度使用它,主要基于以下考虑:

  1. 副作用隐藏useRef主要用于处理与DOM相关的副作用,但过度使用useRef可能会导致副作用在组件中的分散和隐藏,使得组件的行为变得不可预测。

  2. 破坏React数据流:React更倾向于通过props和state来管理组件的状态和数据流,而不是直接操纵DOM。过度使用useRef可能导致React组件之间的数据流不清晰,增加代码的复杂度。

  3. 难以调试:过多的useRef引用可能会导致代码的可读性和维护性下降,因为需要在组件中跟踪和理解许多引用,这可能会增加调试的难度。

  4. 性能问题:虽然useRef本身没有性能问题,但过度使用它可能导致组件渲染性能下降。因为useRef创建的引用不会触发组件的重新渲染,如果在组件中频繁地使用useRef来绑定DOM元素或其他组件,可能会导致React在更新时无法正确地检测到变化,从而影响性能。

尽管useRef可能不适合在所有情况下使用,但它仍然是一个非常有用的工具,特别是在处理DOM操作、焦点管理、动画控制等方面。因此,开发者在使用useRef时应该权衡利弊,避免过度使用,保持代码的简洁性和可维护性。同时,应该优先考虑使用React的props和state来管理组件的状态和数据流,只在必要时才使用useRef

3. 函数式编程 immutable

函数式编程和不可变性(immutable)是两个密切相关的概念,它们通常一起使用来构建更加健壮、可维护和可预测的程序。下面简要介绍这两个概念:

  1. 函数式编程

    • 函数式编程是一种编程范式,它将计算视为数学函数的求值,强调函数的纯粹性和不变性。
    • 函数式编程的核心思想包括不可变性、高阶函数、纯函数、惰性求值等。
    • 主要特点包括函数是一等公民(函数可以作为参数传递、作为返回值返回)、无状态和无副作用等。
  2. 不可变性(Immutable)

    • 不可变性是指数据一旦创建就不能被修改的特性,任何修改操作都会产生一个新的数据副本,原始数据保持不变。
    • 在函数式编程中,不可变性是很重要的概念,因为它确保了数据的不变性,使得程序更容易理解、测试和调试。
    • 不可变性可以通过使用不可变数据结构(如列表、映射、集合)或函数式编程语言的特性来实现。

在实践中,函数式编程和不可变性通常会一起使用,特别是在JavaScript等支持函数式编程范式的语言中。在这些语言中,可以使用一些库(如Immutable.js)来实现不可变性,从而提高代码的健壮性和可维护性。通过使用不可变数据结构,可以避免许多常见的错误,如意外的数据修改、并发访问问题等。同时,函数式编程的思想也能够帮助开发者编写更加清晰和易于理解的代码。 5. 有没有读过相关的书 6. 反问 7. 代码题

interface examScore {
    // 学号
    id: string;
    // 课程代号
    course: string;
    // 成绩
    score: number;
}

// 根据学生各科考试成绩,输出每个学生的总成绩。输出格式为:
// {{id: '1', total: 100 }, {id: '2', total: 101}}
const examScores = [
    {
        id: '1',
        course: 'cs001',
        socre: 90,
    },
    {
        id: '1',
        course: 'cs002',
        socre: 79,
    },
    {
        id: '2',
        course: 'cs002',
        socre: 57,
    },
    {
        id: '3',
        course: 'cs001',
        socre: 85,
    },
    {
        id: '2',
        course: 'cs001',
        socre: 74,
    },
]

以下是一个用于计算每个学生总成绩的 TypeScript 代码:

interface ExamScore {
    // 学号
    id: string;
    // 课程代号
    course: string;
    // 成绩
    score: number;
}

// 根据学生各科考试成绩,输出每个学生的总成绩。输出格式为:
// [{id: '1', total: 100 }, {id: '2', total: 101}]
const examScores: ExamScore[] = [
    {
        id: '1',
        course: 'cs001',
        score: 90, // 修改 socre 为 score
    },
    {
        id: '1',
        course: 'cs002',
        score: 79, // 修改 socre 为 score
    },
    {
        id: '2',
        course: 'cs002',
        score: 57, // 修改 socre 为 score
    },
    {
        id: '3',
        course: 'cs001',
        score: 85, // 修改 socre 为 score
    },
    {
        id: '2',
        course: 'cs001',
        score: 74, // 修改 socre 为 score
    },
];

// 计算每个学生的总成绩
const totalScores: { id: string; total: number }[] = [];
examScores.forEach((examScore) => {
    const existingStudentIndex = totalScores.findIndex((item) => item.id === examScore.id);
    if (existingStudentIndex !== -1) {
        // 学生已存在,累加总成绩
        totalScores[existingStudentIndex].total += examScore.score;
    } else {
        // 学生不存在,添加新的总成绩记录
        totalScores.push({ id: examScore.id, total: examScore.score });
    }
});

console.log(totalScores);

这段代码首先定义了一个ExamScore接口来描述考试成绩的数据结构。然后,遍历了examScores数组,累加每个学生的成绩得到总成绩,并存储在totalScores数组中。最后,输出了每个学生的总成绩。

0324 二面

  1. 面试官自我介绍
  2. 介绍一个你比较熟悉的项目
  3. 项目里比较困难的点

1. SWR的实现方案,聊了很久相关(为什么不能用它处理post请求?

SWR(Stale-While-Revalidate)是一种用于数据获取和缓存的前端库,通常用于React应用中。它提供了一种简单而强大的方式来管理数据的获取、缓存和更新,可以有效地提高应用的性能和用户体验。

虽然SWR在处理GET请求方面非常有效,但不建议将其用于处理POST请求的原因如下:

  1. GET请求和POST请求的语义不同

    • GET请求用于获取资源,通常不对服务器端数据进行更改,因此可以进行缓存和重用。
    • POST请求用于向服务器提交数据,通常会对服务器端数据进行修改,因此不能直接进行缓存和重用。
  2. POST请求的幂等性

    • GET请求是幂等的,即多次请求相同的资源会返回相同的结果,不会对服务器端数据产生影响。
    • POST请求不是幂等的,多次请求相同的资源可能会对服务器端数据产生不同的影响,因此不能直接进行缓存和重用。
  3. SWR的设计目的

    • SWR的设计目的是为了提高数据获取的效率和用户体验,它通过在组件渲染时从缓存中获取数据来避免不必要的网络请求,并在后台进行数据的重新验证(revalidate)以确保数据的新鲜性。
    • POST请求通常用于对数据进行修改,与SWR的设计目的不符合,因为POST请求不能简单地从缓存中获取数据,而需要重新获取最新的数据以确保修改的效果。

因此,尽管SWR在处理GET请求方面非常有效,但不适合用于处理POST请求。对于POST请求,通常建议使用其他适合的数据管理方案,例如直接使用Fetch API或Axios等库来发送POST请求,并根据需要手动管理数据的缓存和更新。

3. redux相关,优缺点,其他替代方案(讲了useContent+useReducer)

Redux是一个用于管理JavaScript应用状态的库,它提供了一种可预测的状态管理方案,使得应用状态的变化变得易于追踪和调试。下面是Redux的一些优缺点以及其他替代方案:

优点:

  1. 单一数据源:Redux采用单一数据源的模式,将应用的状态保存在一个统一的store中,方便统一管理和维护。

  2. 可预测的状态变化:Redux通过使用纯函数来处理状态变化,使得状态变化变得可预测和可控,从而降低了出错的可能性。

  3. 方便的调试和追踪:Redux提供了强大的开发者工具,如Redux DevTools,可以方便地追踪状态变化、重播用户操作等,有助于快速定位和解决问题。

  4. 适用于大型应用:Redux适用于需要管理大量状态和复杂数据流的大型应用,可以帮助开发者更好地组织和管理应用的状态。

缺点:

  1. 繁琐的样板代码:Redux在使用时需要编写大量的样板代码,如action、reducer等,使得代码量较大,维护成本较高。

  2. 学习曲线较陡:Redux的概念和使用方法相对复杂,需要一定的学习成本,特别是对于新手来说可能会感到困难。

  3. 不适用于小型应用:对于简单的小型应用,使用Redux可能会显得过度复杂,增加了开发的复杂性和成本。

其他替代方案:

  1. useContext + useReducer:React提供了useContext和useReducer两个Hooks,可以用于实现简单的状态管理,特别适合于小型应用或组件间简单的状态共享。

  2. MobX:MobX是另一个流行的状态管理库,它采用响应式编程的方式来管理状态,使得状态变化变得更加自然和直观,适用于需要处理复杂数据流的应用。

  3. Recoil:Recoil是Facebook开发的一个状态管理库,它提供了一种简单而强大的方式来管理React组件之间的状态共享,支持原子性的状态更新和异步数据流。

  4. Context API + Immer:可以结合React的Context API和Immer库来实现简单的状态管理,Immer可以帮助简化状态更新的过程,使得状态管理变得更加简单和直观。

总之,Redux是一个强大而灵活的状态管理库,适用于需要处理复杂数据流的大型应用,但对于小型应用或简单的状态管理需求,可以考虑使用其他替代方案来简化开发流程。

5. react hooks 结合实现逻辑说一下怎么用的,和class的区别,为什么不能用在条件语句里

React Hooks是React 16.8版本引入的一项重要功能,它允许函数组件中使用状态(State)和其他React特性,从而实现了函数组件的状态管理和生命周期等功能。下面是使用React Hooks的一些常见场景以及与类组件的区别:

  1. 使用React Hooks的常见场景

    • useState:用于在函数组件中添加状态管理。
    • useEffect:用于在函数组件中执行副作用操作,如数据获取、订阅、手动操作DOM等。
    • useContext:用于在函数组件中获取上下文。
    • useReducer:用于在函数组件中实现复杂的状态逻辑,类似于Redux的Reducer。
    • useCallback和useMemo:用于优化性能,避免不必要的函数创建和计算。
  2. 与类组件的区别

    • 语法:使用Hooks时,不再需要编写类声明和扩展React.Component,而是直接编写函数组件,并在函数内部使用Hooks。
    • 状态管理:使用useState和useReducer来管理组件的状态,不再需要使用this.state和this.setState。
    • 副作用操作:使用useEffect来替代生命周期方法,用于执行副作用操作,如数据获取、订阅等。不再需要componentDidMount、componentDidUpdate和componentWillUnmount等生命周期方法。
    • 上下文访问:使用useContext来获取上下文,不再需要使用this.context。
    • 性能优化:使用useCallback和useMemo来优化性能,避免不必要的函数创建和计算,不再需要使用shouldComponentUpdate方法。

3. 不能在条件语句中使用的原因

React Hooks必须在函数组件的顶层使用,不能在条件语句、循环语句或嵌套函数中使用。这是因为React Hooks的调用顺序必须保持一致,React会根据Hooks的调用顺序来确定各个状态之间的对应关系。如果在条件语句中使用Hooks,可能会导致Hooks的调用顺序发生变化,从而导致状态管理出现问题。为了确保Hooks的正确使用,React规定Hooks必须在函数组件的顶层使用,不能在条件语句中使用。

7. 扫码登录(移动端、pc端、服务端 流程)

扫码登录是一种常见的身份验证方式,它通过用户使用移动设备或PC端扫描特定的二维码来完成登录过程。以下是扫码登录的一般流程,包括移动端、PC端和服务端的交互:

1. 移动端流程:

  • 用户打开需要登录的移动App,并选择使用扫码登录。
  • 移动端App生成一个随机的唯一标识(token),并将其与用户的身份信息(如用户ID或手机号)一起发送到服务端。
  • 服务端接收到请求后,将随机生成的token与用户身份信息进行关联,并生成一个对应的二维码,将二维码的图片地址返回给移动端。
  • 移动端App将二维码显示给用户,用户使用移动设备的相机扫描二维码。
  • 扫描成功后,移动端App开始轮询服务端,检查用户是否已经完成登录。
  • 服务端在用户完成登录后,向移动端发送登录成功的消息,并返回用户的登录状态或其他必要的信息。

2. PC端流程:

  • 用户访问需要登录的网站,并选择使用扫码登录。
  • 网站生成一个随机的唯一标识(token),并将其与用户的身份信息(如用户ID或邮箱)一起发送到服务端。
  • 服务端接收到请求后,将随机生成的token与用户身份信息进行关联,并生成一个对应的二维码,将二维码的图片地址返回给网站。
  • 网站将二维码显示给用户,用户使用移动设备的扫码工具(如手机支付宝或微信)扫描二维码。
  • 扫描成功后,移动设备上的扫码工具向服务端发送确认登录请求,并附带用户的身份信息。
  • 服务端验证用户身份信息,并在验证通过后返回登录成功的消息给PC端网站,网站更新用户登录状态并完成登录过程。

3. 服务端流程:

  • 接收到移动端或PC端发送的扫码登录请求,包括用户身份信息和随机生成的token。
  • 将token与用户身份信息进行关联,并生成一个二维码。
  • 在用户完成扫码登录之前,定期检查token是否已经被绑定,如果绑定则等待用户完成登录,如果超时则取消登录请求。
  • 当用户完成登录后,验证用户身份信息,更新token对应的用户登录状态,并返回登录成功的消息给移动端或PC端。

9. 前端安全问题(XSS,CSRF)

前端安全问题主要包括跨站脚本攻击(XSS)和跨站请求伪造(CSRF)。下面分别对这两种安全问题进行说明:

  1. 跨站脚本攻击(XSS)

    XSS攻击是一种常见的Web安全漏洞,攻击者通过在Web页面中插入恶意的脚本代码,使得用户在访问页面时执行恶意脚本,从而盗取用户信息、劫持用户会话、篡改页面内容等。XSS攻击通常分为三种类型:

    • 存储型XSS:攻击者将恶意脚本上传到服务器端,当用户访问包含恶意脚本的页面时,恶意脚本会从服务器端加载并执行,造成用户信息泄露或被篡改。
    • 反射型XSS:攻击者将恶意脚本作为URL参数发送给受害者,当受害者点击包含恶意脚本的链接时,恶意脚本会被浏览器执行,从而导致XSS攻击。
    • DOM型XSS:攻击者利用浏览器解析URL参数或页面中的DOM元素来触发XSS漏洞,从而执行恶意脚本。

    防范XSS攻击的方法包括对用户输入进行过滤和转义、使用Content Security Policy(CSP)来限制页面加载的资源、避免使用eval()等动态执行代码的函数、对于cookie设置HttpOnly属性等。

  2. 跨站请求伪造(CSRF)

    CSRF攻击是一种利用受害者的身份发起恶意请求的攻击方式,攻击者通过欺骗用户点击恶意链接或访问恶意网站,使得用户在登录状态下发送恶意请求,从而实施攻击。CSRF攻击的特点是攻击者无法获取用户的信息,但可以以用户的身份发送请求。

    防范CSRF攻击的方法包括:

    • 使用CSRF Token:服务器在返回表单页面时生成一个随机的Token,并将Token存储在用户的会话中,用户提交表单时需要同时提交这个Token,服务器验证Token的有效性来确定请求是否合法。
    • 使用同源策略:浏览器的同源策略可以限制页面加载的资源只能来自同一来源,可以有效防止第三方网站发送恶意请求。
    • 使用Cookie属性:合理设置Cookie的属性,如使用HttpOnly属性可以防止JavaScript代码访问Cookie,使用SameSite属性可以限制跨站点的Cookie传输。

综上所述,前端安全问题主要包括XSS攻击和CSRF攻击,防范这些安全问题需要开发者在设计和开发Web应用时充分考虑用户输入的安全性、合理设置HTTP头部和Cookie属性、使用安全的开发框架和工具等措施。

11. 函数式编程,怎么用的

函数式编程(Functional Programming)是一种编程范式,它将计算视为数学函数的求值,并强调函数的纯粹性、不变性和无副作用。函数式编程通常包括以下几个核心概念和技术:

  1. 纯函数(Pure Functions)

    • 纯函数是指具有以下两个特点的函数:对于相同的输入,始终返回相同的输出;函数的执行过程中没有副作用(不会修改外部状态或产生其他可观察的行为)。
    • 纯函数的优点是可以提高代码的可读性、可维护性和可测试性,因为它们的行为完全由输入决定,而不会受到外部环境的影响。
  2. 不可变性(Immutability)

    • 不可变性是指数据一旦创建就不能被修改,任何修改操作都会产生一个新的数据副本,原始数据保持不变。
    • 不可变性可以避免并发访问问题、简化状态管理和优化性能,因为不可变数据结构是线程安全的,并且可以避免因为状态修改而导致的副作用。
  3. 高阶函数(Higher-order Functions)

    • 高阶函数是指接受一个或多个函数作为参数,并且/或者返回一个函数作为结果的函数。
    • 高阶函数可以帮助实现函数的复用和组合,使得代码更加灵活和可复用。
  4. 函数组合(Function Composition)

    • 函数组合是指将多个函数组合在一起形成一个新的函数,新函数的输出是前一个函数的输入。
    • 函数组合可以帮助将复杂的问题分解为简单的问题,然后组合成一个解决方案,提高代码的可读性和可维护性。
  5. 惰性求值(Lazy Evaluation)

    • 惰性求值是指在需要时才计算表达式的值,而不是在表达式被定义时就立即计算。
    • 惰性求值可以提高程序的性能和效率,避免不必要的计算和资源浪费。

函数式编程通常用于解决复杂的计算问题和处理大量数据的场景,它的主要优点包括简洁、表达力强、易于理解和测试等。在JavaScript等支持函数式编程的语言中,可以使用函数式编程的技术和思想来编写更加简洁和健壮的代码。

  1. 工作or读研 为什么选前端
  2. 反问

0407 hr面

  1. 自我介绍

  2. 每个项目聊一聊个人贡献和难点

  3. 为什么会做那些项目,学到了什么

  4. 一些随便聊聊的问题

作者:希望offer多多的马里奥很苦闷
链接:www.nowcoder.com/discuss/487…
来源:牛客网