前端面试题详解整理65|封装组件,手写Promise.any,导出文件浏览器,为什么需要nexttick,跨域怎么下载为什么选vue3,

161 阅读16分钟

字节前端实习一面

1.自我介绍 + 实习经历介绍
2.选择前端的原因

3.如何解决跨域

跨域(Cross-Origin Resource Sharing,CORS)是由浏览器的同源策略(Same-Origin Policy)引起的安全机制,它限制了从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。要解决跨域问题,可以采用以下几种常见方法:

  1. CORS(跨域资源共享):

    • 在服务器端配置 CORS 头部,允许指定源的请求访问资源。可以通过设置响应头部的 Access-Control-Allow-OriginAccess-Control-Allow-MethodsAccess-Control-Allow-Headers 等字段来指定允许的源、方法和头部信息。
    • 在 Node.js 中可以使用 cors 中间件来简化 CORS 头部的设置,例如 app.use(cors())
  2. JSONP(JSON with Padding):

    • JSONP 是一种利用 <script> 标签的 GET 请求来实现跨域数据传输的技术。在客户端通过动态创建 <script> 标签,将请求发送到目标服务器,服务器返回一段 JavaScript 代码,并在其中调用一个回调函数,并将数据作为参数传递给该函数。
  3. 代理服务器:

    • 可以在同源服务器上设置一个代理服务器,用于转发请求到目标服务器。浏览器向代理服务器发出请求,代理服务器再将请求发送到目标服务器,将响应返回给浏览器。通过这种方式可以绕过浏览器的同源策略限制。
  4. 跨域资源嵌入(Cross-Origin Resource Inclusion):

    • 在服务端通过将跨域资源包含在当前页面中,使得浏览器认为资源是同源的。例如,可以使用 CORS 头部的 Access-Control-Allow-Origin: *,或者在响应中添加 document.domain = "yourdomain.com" 来绕过同源策略。
  5. 反向代理:

    • 在同源服务器上设置一个反向代理服务器,用于接收来自客户端的请求,并将请求转发到目标服务器。反向代理服务器可以修改请求头部,使得目标服务器认为请求是从同源服务器发出的。

需要注意的是,不同的解决方案适用于不同的场景和需求,开发者可以根据具体情况选择合适的方法来解决跨域问题。同时,跨域解决方案也需要考虑安全性和性能等方面的因素。

4.tailwind CSS 这个是我其中一个项目中使用的,但我当时只是当它工具使用的,直接问我实现原理和优势等等。实现原理我没回答好,但这个确实是一个好问题

Tailwind CSS 是一种基于原子类的 CSS 框架,它的实现原理和优势如下:

实现原理: Tailwind CSS 的实现原理主要基于以下几个核心概念:

  1. 原子类: Tailwind CSS 将样式拆分成小的、单一的类,每个类都代表一个特定的样式属性。这些类的命名通常根据其所代表的样式属性进行命名,如 text-blue-500 表示文本颜色为蓝色,颜色深度为 500。

  2. 可配置性: Tailwind CSS 提供了一系列的配置选项,可以根据项目的需要进行自定义。通过配置文件,可以定义颜色、字体、间距、边框、阴影等各种样式属性的值,以及生成自定义的原子类。

  3. 工具类生成: Tailwind CSS 提供了一个工具集合,用于快速生成样式,例如在文本颜色上应用蓝色、背景颜色上应用灰色等。开发者可以通过配置选项来选择要生成的工具类,从而减少样式代码的冗余。

优势: Tailwind CSS 的优势主要体现在以下几个方面:

  1. 灵活性: Tailwind CSS 提供了丰富的样式类和配置选项,可以满足各种项目的样式需求。开发者可以根据需要自定义样式,而不受限于预定义的样式。

  2. 可维护性: 由于每个样式属性都由单一的原子类表示,因此样式的定义变得更加清晰和可维护。开发者可以轻松地理解和修改样式,而不必担心影响其他部分的样式。

  3. 性能优化: Tailwind CSS 的样式类都是预先定义好的,不需要额外的样式文件。这样可以减少样式文件的大小,加快页面加载速度。另外,由于使用了原子类,可以更好地利用浏览器的 CSS 压缩机制,进一步减小样式文件的大小。

  4. 易用性: Tailwind CSS 提供了丰富的文档和工具,使得学习和使用变得更加容易。开发者可以通过查阅文档快速上手,并通过工具类生成样式,提高开发效率。

总的来说,Tailwind CSS 是一种灵活、可维护、性能优化的 CSS 框架,通过原子类的方式提供了丰富的样式定义,使得开发者可以更轻松地管理和定制样式,从而加快项目的开发速度并提升代码质量。

代码题:
1.let var 变量提升题目,作用域相关问题。没全答对应该,说的有错误
2.JavaScript事件循环题目。诶这道题我也没答全对,当时忘了resolve这种的执行顺序

3.管道函数,这道题被最后提示了下用reverse()反转,答对九成九吧哈哈

管道函数(Pipeline function)是一种将多个函数组合在一起,按顺序依次执行的函数。实现一个简单的管道函数可以使用 JavaScript 编写如下:

// 定义管道函数
const pipe = (...fns) => arg => fns.reduce((acc, fn) => fn(acc), arg);

// 示例函数
const add = x => x + 2;
const multiply = x => x * 3;
const subtract = x => x - 4;

// 使用管道函数
const result = pipe(add, multiply, subtract)(5);
console.log(result); // 输出:(5 + 2) * 3 - 4 = 19

上面的代码定义了一个 pipe 函数,它接收任意数量的函数作为参数,并返回一个新的函数。这个新的函数接收一个参数,并将其依次传递给传入的函数列表中的每一个函数,然后返回最终的结果。

在示例中,定义了三个简单的函数 addmultiplysubtract,分别对输入的数字进行加法、乘法和减法操作。然后通过 pipe 函数将这三个函数组合在一起,依次执行,最终得到结果。

虽然最后没有被录取,但是这是我意料之中的——我已经抛弃前端好长时间了,从硕士阶段就一直去在搞深度学习相关的论文。字节给的准备时间也很短两天不到,但代码题确实有点深度,但我感觉问的东西确实不难,是我自己的准备和能力问题。

长时间的拷打项目相关的问题,感觉我说的很多东西不是很确定,这时候就会被反问和再次确

作者:人定勝天
链接:www.nowcoder.com/feed/main/d…
来源:牛客网

字节前端二面

项目难点(封装组件)

在封装组件时,可能会遇到一些难点,下面列举几个常见的项目难点:

  1. 组件设计和抽象: 在封装组件时,需要考虑到组件的设计和抽象,包括组件的功能、接口设计、数据流设计等。需要思考如何将组件拆分成更小的可复用部分,以及如何设计良好的接口和数据传递机制。

  2. 状态管理: 如果组件涉及到复杂的状态管理,如何合理地管理组件的状态会是一个难点。需要考虑到状态的生命周期、作用域、数据的更新和同步等问题,并选择合适的状态管理方案,如 React 的 Context、Redux 等。

  3. 性能优化: 封装组件时,需要考虑到组件的性能问题,如何优化组件的渲染性能、内存占用等。可能需要采取一些性能优化措施,如懒加载、虚拟列表、优化渲染逻辑等。

  4. 兼容性和适配性: 不同的浏览器和设备可能对组件的兼容性和适配性有不同的要求。在封装组件时,需要考虑到不同环境下的兼容性和适配性问题,并做好相应的兼容性处理。

  5. 错误处理和异常情况: 封装组件时,需要考虑到各种可能出现的错误和异常情况,并做好相应的错误处理。可能需要编写一些容错机制或者提供友好的错误提示,以提高组件的健壮性和用户体验。

  6. 文档和测试: 封装组件后,需要编写完善的文档和测试用例,以方便其他开发者使用和维护组件。文档需要清晰地说明组件的功能、用法、参数说明等,测试用例需要覆盖组件的各种情况,确保组件的稳定性和可靠性。

在面对这些难点时,可以采取逐步迭代、分步实现的方式,先从简单的功能开始,逐步完善和优化组件,同时不断地进行测试和验证,确保组件的质量和稳定性。

  • 父子组件传方法

在 React 中,父组件向子组件传递方法通常通过将方法定义在父组件中,然后将方法作为 props 传递给子组件。子组件可以在需要的时候调用这些方法。以下是一个简单的示例:

// 父组件
import React from 'react';
import ChildComponent from './ChildComponent';

class ParentComponent extends React.Component {
  // 定义需要传递的方法
  handleButtonClick = () => {
    console.log('Button clicked!');
  };

  render() {
    return (
      <div>
        {/* 将方法作为 props 传递给子组件 */}
        <ChildComponent onClick={this.handleButtonClick} />
      </div>
    );
  }
}

// 子组件
import React from 'react';

class ChildComponent extends React.Component {
  render() {
    return (
      <button onClick={this.props.onClick}>Click me</button>
    );
  }
}

export default ChildComponent;

在上面的示例中,父组件 ParentComponent 定义了一个名为 handleButtonClick 的方法,该方法用于处理按钮点击事件。然后将这个方法作为 onClick props 传递给子组件 ChildComponent

在子组件中,通过 this.props.onClick 来访问父组件传递的方法,并在按钮的点击事件中调用它。

这样就实现了父组件向子组件传递方法的功能。子组件可以在适当的时候调用父组件传递的方法,实现组件之间的通信和交互。

导出文件浏览器是怎么下载的,回答了FileSaver.saveAs通知浏览器下载

当浏览器接收到服务器返回的文件时,通常会根据文件的 MIME 类型进行处理。如果服务器返回的响应头中包含了 Content-Disposition: attachment,则浏览器会将该文件视为附件并下载。浏览器会自动弹出下载对话框,用户可以选择保存文件的路径和文件名。

FileSaver.saveAs 是一个 JavaScript 库,用于在浏览器中触发文件下载操作。它通过创建一个虚拟的链接,然后将链接的 href 属性设置为文件的 URL,同时设置 download 属性为文件名,从而模拟用户点击下载链接的行为。

虽然使用 FileSaver.saveAs 可以通过 JavaScript 实现文件下载,但它并不是浏览器下载文件的原生机制。浏览器的下载行为通常受到安全策略的限制,例如对于跨域请求或者某些类型的文件,浏览器可能会阻止自动下载。因此,在一些特定的场景下,使用 FileSaver.saveAs 可能无法实现文件下载的功能,需要服务器端进行适当的配置来确保文件可以被下载。

跨域怎么下载

跨域下载文件通常会受到浏览器的安全策略限制,但可以通过以下几种方法来实现:

  1. CORS(跨域资源共享): 如果服务器允许跨域请求,并且在响应头部设置了正确的 CORS 头部信息,浏览器就会允许跨域下载文件。确保服务器端设置了合适的 CORS 头部,例如 Access-Control-Allow-OriginAccess-Control-Allow-Methods 等。

  2. 代理服务器: 可以在同源服务器上设置一个代理服务器,用于转发跨域请求并下载文件。前端代码发送请求给代理服务器,代理服务器再向目标服务器发送请求并将文件返回给前端。这样可以避免浏览器的同源策略限制。

  3. JSONP: 如果目标服务器支持 JSONP,可以通过动态创建 <script> 标签来触发跨域请求,并在服务器返回的 JavaScript 中实现文件下载。这种方式比较简单,但只适用于 GET 请求。

  4. 服务器中转: 将文件先上传到同源服务器,然后通过同源请求下载文件。在前端发送请求时,先将文件上传到同源服务器,然后通过后端生成一个下载链接返回给前端,前端再通过该链接进行文件下载。

  5. 跨域资源嵌入: 在目标服务器返回的响应中包含跨域资源,并将其嵌入到当前页面中。这种方式可以绕过浏览器的同源策略限制,但需要确保目标服务器允许跨域资源共享。

需要注意的是,以上方法中的每一种都有其适用的场景和限制条件,开发者需要根据具体情况选择合适的方法来实现跨域下载文件。同时,为了确保安全性,需要谨慎处理跨域下载文件的过程,避免出现安全漏洞。

项目为什么选vue3

选择 Vue 3 作为项目的框架可能有以下几个原因:

  1. 性能优化: Vue 3 在性能方面进行了许多优化,包括编译器、虚拟 DOM、渲染函数等方面的改进,使得整体性能得到了提升。新的响应式系统也更加高效,减少了不必要的重新渲染,提升了页面的性能表现。

  2. Composition API: Vue 3 引入了 Composition API,使得组件的逻辑更加清晰和灵活。使用 Composition API 可以更好地组织和重用组件的逻辑代码,提高了代码的可读性和可维护性。尤其是在大型项目中,使用 Composition API 可以更好地管理组件的状态和行为。

  3. TypeScript 支持: Vue 3 对 TypeScript 提供了更好的支持,包括完整的 TypeScript 类型定义和更好的类型推断。这使得在项目中使用 TypeScript 更加方便,提高了代码的健壮性和可维护性。

  4. 更好的生态系统: 随着 Vue 3 的发布,越来越多的 Vue 生态库和工具已经升级到了 Vue 3,并且开始提供对 Vue 3 的支持。这使得在项目中使用 Vue 3 可以更加方便地利用丰富的生态资源,提高开发效率。

  5. 未来发展趋势: Vue 3 是 Vue.js 的最新版本,具有更加现代化的设计和更强大的功能。选择 Vue 3 可以使得项目保持在技术发展的前沿,更好地满足未来的需求和挑战。

综上所述,选择 Vue 3 作为项目的框架,可以获得更好的性能、更灵活的开发体验、更好的 TypeScript 支持和更丰富的生态资源,从而提高项目的质量和开发效率。

vue已经可以自动监听数据变化,实现页面渲染,为什么还需要nexttick

虽然 Vue 在数据变化时可以自动触发页面的重新渲染,但有时候我们需要在下一个 DOM 更新周期执行一些操作。这就是使用 nextTick 的场景。

以下是一些常见的情况,我们可能需要使用 nextTick

  1. 操作 DOM: 在数据变化之后立即操作 DOM 可能会导致错误。因为 Vue 是异步更新 DOM 的,数据变化后 DOM 不会立即更新。如果我们希望在 DOM 更新之后执行操作,就需要使用 nextTick

  2. 获取更新后的 DOM 信息: 如果我们想要获取更新后的 DOM 信息(如元素的位置、尺寸等),同样需要在 DOM 更新之后进行操作。此时 nextTick 可以确保我们在获取 DOM 信息之前 DOM 已经更新完毕。

  3. 等待子组件更新: 在某些情况下,我们可能需要等待子组件更新完毕后再执行某些操作。例如,在父组件中修改了子组件的 props 后,我们希望在子组件更新后再执行一些操作,这时候就可以使用 nextTick

  4. 更新后的数据操作: 如果我们想在更新后的数据状态下执行某些操作,而不是在数据更新之后立即执行,就可以使用 nextTick。例如,我们可能需要在数据更新后更新一些依赖于数据的状态或执行一些异步操作。

总之,nextTick 提供了一种在 Vue 更新 DOM 后执行代码的方式,可以确保我们在操作 DOM 或执行其他操作时,DOM 已经更新完毕,数据已经同步到 DOM 上。这样可以避免一些潜在的问题,并提供更好的开发体验。

手写:Promise.any

虽然面完没几分钟就发感谢信了,但字节的面试官还是挺好的 Promise.any 方法接收一个 Promise 可迭代对象(如数组),并返回一个新的 Promise,该 Promise 在传入的 Promise 可迭代对象中的任何一个 Promise 成功时解决,或在所有 Promise 都失败时拒绝。

以下是一个简单的手写实现:

Promise.any = function(promises) {
  return new Promise((resolve, reject) => {
    let resolved = false;
    let rejectedCount = 0;

    for (const promise of promises) {
      Promise.resolve(promise).then(
        value => {
          if (!resolved) {
            resolved = true;
            resolve(value);
          }
        },
        error => {
          rejectedCount++;
          if (rejectedCount === promises.length) {
            reject(new AggregateError("All promises were rejected"));
          }
        }
      );
    }
  });
};

该实现中,我们遍历传入的 Promise 可迭代对象,对每个 Promise 使用 Promise.resolve 包装,以确保处理的都是 Promise 对象。然后使用 then 方法处理每个 Promise 的解决和拒绝状态。如果有任何一个 Promise 解决了,我们将返回一个解决状态的 Promise,并传递该 Promise 的值。如果所有 Promise 都被拒绝,我们将返回一个拒绝状态的 Promise,并传递一个 AggregateError,其中包含了所有 Promise 的拒因。

作者:穆_穆
链接:www.nowcoder.com/discuss/549…
来源:牛客网