【前端面试题】前端面试题总结2023-7-13(十一)

简介: 【前端面试题】前端面试题总结2023-7-13(十一)

46、React性能优化的方法

提高组件渲染的效率:减少不必要的render方法:

  • shouldComponentUpdate
  • PureComponent
  • React.memo
    性能优化的方案:
  • 避免使用内联函数:使用内联函数,则每次调用render函数时都会创建一个新的函数实例
  • 使用 React Fragments 避免额外标记:用户创建新组件时,每个组件应具有单个父标签。父级不能有两个标签,所以顶部要有一个公共标签,所以我们经常在组件顶部添加额外标签div
    这个额外标签除了充当父标签之外,并没有其他作用,这时候则可以使用fragement
    其不会向组件引入任何额外标记,但它可以作为父级标签的作用
  • 使用 Immutable
  • 懒加载组件:react中使用到了Suspenselazy组件实现组件延迟加载
  • 事件绑定方式:性能方面考虑,在render方法中使用bindrender方法中使用箭头函数这两种形式在每次组件render的时候都会生成新的方法实例,性能欠缺,而constructorbind事件与定义阶段使用箭头函数绑定这两种形式只会生成一个方法实例,性能方面会有所改善
  • 服务端渲染

47、虚拟DOM和真实DOM的区别

虚拟DOM和真实DOM的最大区别在于它们所代表的对象不同。

虚拟DOM是一个轻量级的JavaScript对象,它是React框架中的核心概念,用于描述页面UI的状态和结构。它将所有的元素和组件都抽象成了JavaScript对象和属性,这些对象和属性之间构成了一棵虚拟的树形结构(Virtual DOM Tree)。在每一次渲染时,React会比较前后两棵虚拟DOM树的差异,然后只对有变化的部分进行重新渲染,从而提高渲染效率。

真实DOM则是页面上实际存在的元素对象,它由浏览器根据HTML标记解析后生成,并与页面上其他元素形成复杂的关联关系。真实DOM渲染性能较低,因为每次更改都会触发浏览器的重新渲染,而且它的操作和属性都是直接作用于页面的,比较麻烦。

因此,虚拟DOM和真实DOM的区别主要体现在:

  1. 虚拟DOM是JavaScript对象,真实DOM是浏览器中的页面元素。
  2. 虚拟DOM渲染效率更高,真实DOM操作性更强,但渲染性能相对较低。

48、说说你对React中refs的理解?作用?

在 React 中,ref 是一个特殊的属性,可以用来获取组件或 DOM 元素的引用。使用 ref 可以让我们直接从 DOM 元素获取数据或对其进行操作,而不需要通过 props 或 state 来传递数据,这样可以提高程序的性能和效率。

在 React 中,有两种方式使用 ref:一种是使用字符串作为 ref 的值,另一种则是使用回调函数。使用字符串作为 ref 的值已经过时,建议使用回调函数。

使用回调函数的方式定义 ref,可以在组件挂载和卸载时获取到对应的 DOM 元素或组件实例,并将其保存到一个变量中,然后在需要使用时,可以通过访问该变量来获取或操作对应的 DOM 元素或组件实例。例如:

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  handleClick = () => {
    console.log(this.myRef.current); // 输出 <input type="text" />
    this.myRef.current.focus();      // 将 input 元素聚焦
  }
  render() {
    return (
      <div>
        <input type="text" ref={this.myRef} />
        <button onClick={this.handleClick}>Focus</button>
      </div>
    );
  }
}

在上面的代码中,我们使用 React.createRef() 方法创建了一个 ref 对象,并将其保存在组件的实例对象中。在 render() 方法中,将 ref 对象绑定到一个 <input> 元素上。然后,我们可以在组件的其他方法中访问该 ref 对象,从而获取或操作对应的 DOM 元素。

使用 ref 可以解决一些特殊场景下的问题,例如:

  • 获取某个 DOM 元素的大小、位置、滚动等信息;
  • 控制某个输入框的光标位置;
  • 获取组件实例并调用其方法。

需要注意的是,使用 ref 应该尽量避免过度使用,因为这会使组件变得难以理解、调试和维护。在使用 ref 时,需要权衡其方便性和可维护性,确保代码的可读性和健壮性。

49、说说你对Redux的理解

Redux 是一个状态管理库,用于管理 JavaScript 应用的状态和数据流。Redux 通过建立一个单一的 Store 来存储整个应用的状态,并使用纯函数来处理 State 的变化,以保证应用的可预测性和可维护性。Redux 的设计思想主要包括三个方面:单一数据源、状态不可变、纯函数处理状态变化。

  1. 单一数据源
    Redux 应用只有一个单一的 Store 来保存整个应用的状态,而且该状态是一个不可变对象,不能被直接修改。因此,在 Redux 应用中,所有的 State 变化都是由 Action 触发的,并通过 Reducer 来处理。这种单一数据源的设计使得 Redux 应用的状态变化变得可预测、可控、易于调试和维护。
  2. 状态不可变
    在 Redux 应用中,State 是不可变的,不能被直接修改。当应用需要更新某个状态时,必须创建一个新的 State 对象,并在其中修改需要更新的部分数据,返回一个新的 State 对象。这种状态不可变的设计可以使得 Redux 应用的状态变化变得非常清晰和可控,避免了出现一些难以发现和调试的问题。
  3. 纯函数处理状态变化
    在 Redux 应用中,所有的状态变化都是通过纯函数来处理的。这些纯函数被称为 Reducer,接收当前 State 和 Action 作为参数,返回新的 State 对象。Reducer 必须是纯函数,不能有副作用,也不能修改参数。这种纯函数的设计使得 Redux 应用的状态变化变得易于理解、可测试和可维护。

Redux 的核心思想可以概括为:单一数据源、状态不可变、纯函数处理状态变化。它可以帮助我们管理整个应用的状态,并让应用的状态变化变得可预测、可控、易于调试和维护。在使用 Redux 时,需要注意一些细节,例如如何设计 Reducer、如何组织代码、如何管理异步操作等。但总体来说,Redux 是一个非常好用、灵活、可扩展的状态管理库,可以帮助我们构建高质量的 JavaScript 应用。

50、React和Vue的区别,相同点和不同点?

React 和 Vue 是目前最流行的前端框架之一,它们都具有很多优点和适用场景,但是也存在一些明显的区别和不同之处。

相同点:

  1. 都是基于组件化开发的思想,将 UI 界面抽象成一个个可复用的组件,方便代码的重用和维护。
  2. 都支持虚拟 DOM 技术,能够高效地渲染页面,并且通过数据的双向绑定,实现了数据和 UI 界面的自动同步。
  3. 都具有丰富的生命周期函数,可以方便地控制组件的加载、更新、卸载等过程,同时也能够进行一些逻辑处理和异步请求操作。
  4. 都有非常丰富的社区和生态系统,提供了大量的插件、组件和工具,方便开发者进行项目开发和维护。
    不同点:
  5. React 更加注重灵活性和可扩展性,提供了 JSX 语法和函数式编程模式,通过高阶函数和组合等方式来实现复杂功能。而 Vue 更加注重易用性和开发效率,提供了模板语法和单文件组件的写法,使得开发者可以更快速地创建界面。
  6. React 采用的是单向数据流的架构模式,父子组件之间通过 props 和回调函数来进行数据的传递和事件的处理。而 Vue 采用的是双向数据绑定的架构模式,通过 v-model 来实现数据的双向绑定,同时也提供了自定义事件和 $emit 方法来进行事件处理。
  7. React 更加注重配合使用其他库和框架,例如 Redux、React Router、Webpack 等,通过组合方式来实现复杂应用的架构。而 Vue 更加注重集成性和全家桶,例如 Vuex、Vue Router、Vue CLI 等,提供了完整的开发工具链。

总之,React 和 Vue 都是非常优秀的前端框架,具有各自的优点和适用场景。在项目开发中,需要根据具体需求和团队的技术水平选择合适的框架,并且充分利用其提供的特性和生态系统,提高开发效率和项目质量。

8、其他

1、说说你对webSocket的理解?

Websocket 是一种在客户端和服务器之间建立持久连接的协议,可以实现双向通信。它基于类似于 HTTP 的握手协议,但是建立连接后是一种全双工的通信协议,而不是像 HTTP 一样只能以请求响应的方式交互。

Websocket 的优点在于能够实现低延迟、高效率的双向通信。通过 Websocket,可以在客户端和服务器之间传输实时数据,同时可以减少网络带宽和服务器负载,从而提高应用程序的性能和用户体验。

Websocket 协议的实现需要客户端和服务器均支持该协议,通常使用 JavaScript 在客户端实现。Websocket 的 API 相对简单,通过创建 WebSocket 对象可以与服务器建立连接,发送和接收数据。Websocket 可以通过保持连接来保持实时性,而不需要基于 HTTP 协议进行轮询,因此可以减少因频繁连接和断开而产生的网络开销和延迟。

Websocket 的应用范围非常广泛,如在线聊天、实时游戏、实时数据展示等场景,可以提供更加流畅、稳定的用户体验。

2、什么是强缓存和协商缓存?

强缓存是指浏览器在第一次请求数据时,会将数据的缓存时间记录下来,然后在下一次请求时根据这个时间判断是否需要重新请求,如果缓存时间没有过期,则直接从缓存中获取数据,不需要再向服务器发送请求。

协商缓存是指浏览器在第一次请求数据时,服务器会返回数据的缓存标识(如Etag或者Last-Modified),浏览器会将这个标识和请求头中携带的If-None-Match或If-Modified-Since进行比对,如果相同则表示缓存未过期,可以直接从本地缓存中获取数据,否则需要向服务器发起请求获取最新数据。

强缓存优先级高于协商缓存,当缓存时间未过期时,浏览器不会进行协商缓存。如果强缓存失效了,浏览器才会进行协商缓存,如果协商缓存也失效了,那么浏览器才会重新向服务器请求获取最新数据。

3、说说webpack中常见的loader?解决了什么问题?

Webpack中常见的loader有:

  1. babel-loader:使用Babel进行转译,让项目支持ES6/ES7/ES8等新特性。
  2. css-loader:让Webpack支持加载CSS文件,可以配合style-loader将CSS注入到HTML中。
  3. file-loader:将文件输出到指定路径,并返回该文件的URL,可以用来处理图片、字体等静态资源。
  4. url-loader:与file-loader类似,但可以将文件转换为data URL,减少HTTP请求次数,提高效率。
  5. sass-loader:将Scss或Sass文件转换为CSS。
  6. vue-loader:用于加载.vue单文件组件。
    这些loader的作用是让Webpack支持处理各种类型的文件,解决了在开发中出现的文件类型不兼容的问题。使用这些loader可以实现自动编译、打包、转译等功能,使得我们可以更加专注于业务逻辑的开发。

4、说说如何借助webpack来优化前端性能?

webpack是一个主流的前端构建工具,可用于优化前端性能。以下是一些借助webpack优化前端性能的方法:

  1. 代码压缩:webpack可以通过使用插件和配置选项来压缩代码。代码压缩可以减少文件大小,从而加快应用程序的加载速度。
  2. 文件合并:webpack可以将多个文件合并成一个,从而减少HTTP请求的数量。这样可以加快应用程序的加载速度。
  3. 图像压缩:webpack可以使用图片压缩插件来压缩图像,从而减少图像文件的大小。
  4. 延迟加载:webpack可以使用代码分离插件,将应用程序代码分成多个文件,在需要时按需加载。这样可以减少初始加载时间,并提高应用程序的性能。
  5. 按需加载CSS:webpack可以使用插件和配置选项来拆分CSS文件,并在需要时按需加载。这样可以减少初始加载时间,并提高应用程序的性能。
  6. 缓存处理:webpack可以使用HashedModuleIdsPlugin来生成哈希标识,从而使文件版本保持一致。这可以帮助浏览器缓存文件并减少文件加载时间。
  7. Gzip压缩:webpack可以使用CompressionWebpackPlugin插件来启用gzip压缩。这可以减少文件大小,从而加快应用程序的加载速度。

5、说说webpack中代码分割如何实现?

在Webpack中,代码分割(Code Splitting)是一种将应用程序的代码拆分成多个小块的技术,以实现更好的性能和加载时间。通过代码分割,我们可以将应用程序的代码按需加载,减少初始加载时所需的文件大小,从而提高页面的响应速度。

Webpack提供了多种方式来实现代码分割:

  1. 手动配置:通过手动配置Webpack的entry和output选项,将应用程序拆分成多个入口文件,并为每个入口文件生成独立的输出文件。这样做可以有效地实现代码分割,但需要手动管理入口文件和依赖关系。
  2. 使用动态 import:Webpack支持使用ES6的import语法动态导入模块。通过在代码中使用动态import,Webpack会自动将导入的模块拆分成独立的块,并按需加载。例如:
import("./module")
  .then(module => {
    // 使用模块
  })
  .catch(error => {
    // 处理错误
  });
  1. 使用SplitChunks插件:Webpack内置了SplitChunks插件,可以通过配置进行代码分割。该插件将公共的代码提取到单独的块中,并使得多个入口之间共享这些块。可以通过配置optimization.splitChunks选项来自定义代码分割的行为。例如:
module.exports = {
  // ...其他配置
  optimization: {
    splitChunks: {
      chunks: "all",
      minSize: 30000,
      maxSize: 0,
      minChunks: 1,
      maxAsyncRequests: 6,
      maxInitialRequests: 4,
      automaticNameDelimiter: "~",
      name: true,
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: "vendor",
          chunks: "all"
        }
      }
    }
  }
};

上述配置将会把所有位于node_modules目录下的模块打包到一个名为vendor的输出文件中。

无论使用哪种方式,代码分割都能够帮助我们优化应用程序的加载性能。它可以将应用程序拆分成多个小块,按需加载和缓存,减少了初始加载时所需的文件大小,并提高了页面的渲染速度。

6、说说AMD、CMD、commonJS模块化规范的区别?

AMD(Asynchronous Module Definition)、CMD(Common Module Definition)和CommonJS是JavaScript模块化规范中的三种常见方式,它们在模块定义、加载方式和使用方式等方面存在一些区别,下面是我对它们的区别的解释:

  1. AMD(异步模块定义):
  • 模块定义:AMD使用define函数来定义模块,模块可以通过return语句返回一个值作为模块的输出。
  • 加载方式:AMD采用异步方式加载模块,使用require函数来加载依赖的模块,在模块加载完成后执行回调函数。
  • 使用方式:AMD适用于浏览器环境,它主要用于在浏览器中开发异步模块化的JavaScript应用。
  1. CMD(通用模块定义):
  • 模块定义:CMD也使用define函数来定义模块,但CMD的模块定义方式更加自由,可以根据需要在代码的任意位置定义模块。
  • 加载方式:CMD同样采用异步方式加载模块,使用require函数来加载依赖的模块。不同于AMD,CMD在加载模块时会先执行模块体,而不是等模块加载完再执行。
  • 使用方式:CMD同样适用于浏览器环境,它风格更接近于CommonJS,并且更适合动态导入模块的场景。
  1. CommonJS:
  • 模块定义:CommonJS使用require()函数定义模块,通过module.exports或exports对象导出模块的接口。
  • 加载方式:CommonJS采用同步方式加载模块,即在代码中使用require()函数加载依赖的模块,并立即执行加载后的模块代码。
  • 使用方式:CommonJS主要用于服务器端JavaScript环境(如Node.js),它更适合同步加载的场景,例如文件操作等。

这三种模块化规范都是为了解决JavaScript中模块化开发的问题,提供了模块定义、加载和使用的一些原则和机制。它们各自有着不同的设计思想和适用场景,开发者可以根据具体项目的需求选择适合的模块化规范。近年来,随着ES6模块化的引入,在浏览器环境中可以直接使用import和export语法进行模块化开发,成为了更加推荐的模块化方案。

7、说说package.json中版本号的规则?

在 package.json 文件中,版本号是用来标识软件包版本的字符串。它遵循语义化版本规范(Semantic Versioning),通常由三个数字组成:主版本号(Major)、次版本号(Minor)和修订号(Patch)。

版本号的格式如下:

<major>.<minor>.<patch>
  • 主版本号(Major):当你做了不兼容的 API 修改时,应该将主版本号增加。
  • 次版本号(Minor):当你添加了向后兼容的功能时,应该将次版本号增加。
  • 修订号(Patch):当你进行向后兼容的错误修复时,应该将修订号增加。

除了这三个基本的版本号外,还可以在版本号后面添加预发布版本号(Prerelease)和构建信息(Build Metadata)。

预发布版本号指示软件包发布的开发阶段,常见的标识符有 alpha、beta、rc(Release Candidate)等。

构建信息用于标识特定构建的元数据,比如构建时间戳、提交哈希等。

例如,一个完整的版本号可能是:1.2.3-alpha.1+20230713。

在实际使用中,版本号的变更应该遵循一定的规则和约定,以确保版本号的语义正确传达了软件包的更新内容。这样可以帮助用户理解和判断软件包的向后兼容性,并且对于依赖此软件包的项目,可以更精确地控制其更新和变更。

同时,版本号还可以在 package.json 文件的 dependencies 和 devDependencies 字段中使用,用于指定所依赖的其他软件包的版本范围。这样可以确保项目在安装依赖时能够获得符合要求的合适版本的软件包。常见的版本范围规则包括:

  • 精确版本号:指定一个完整的版本号,例如 “1.2.3”。
  • 范围版本号:使用操作符和通配符定义版本范围,例如 “^1.2.3”、“>=1.0.0 <2.0.0” 等。

总结起来,版本号规则是一种遵循语义化版本规范的约定,用于标识软件包的版本,并且可以在依赖管理中指定合适的版本范围。这样能够提高软件包的可维护性和兼容性,同时便于开发者和用户理解和使用。

8、说说你对koa中洋葱模型的理解?

在 Koa 框架中,洋葱模型(Onion Model)是一种中间件处理流程的设计模式,它通过将中间件按照特定顺序组织在一起,形成多层嵌套的处理管道。这个模型得名于它类似于剥洋葱一样的执行顺序,请求从外到内依次经过每个中间件,然后再从内到外依次返回响应。

洋葱模型的核心思想是利用异步函数的特性,使每个中间件在请求的进入和离开时都能执行相关操作。一个典型的洋葱模型可以包含以下四个阶段:

  1. 请求进入阶段:请求从外部进入洋葱模型,此时会依次经过每个中间件的“洋葱外层”。
  2. 中间件处理阶段:请求从洋葱的外层向内层逐步传递,在每一层中间件中执行相应的操作,比如处理请求、验证身份、记录日志等。
  3. 响应返回阶段:请求在到达中间件的最内层后开始向外部返回响应,此时会依次经过每个中间件的“洋葱内层”。
  4. 响应完成阶段:响应从洋葱模型中完全返回到外部,整个请求-响应周期结束。

洋葱模型的优势在于它可以灵活地控制中间件的执行顺序。每个中间件都可以在请求进入和离开时执行一些前置和后置操作,而无需关心内部中间件的具体实现细节。此外,洋葱模型还能够在中间件之间传递数据和状态,方便实现一些复杂的处理逻辑。

以下是一个简化的 Koa 洋葱模型示例:

const Koa = require('koa');
const app = new Koa();
// 中间件1
app.use(async (ctx, next) => {
  console.log('中间件1 - 请求进入');
  await next();
  console.log('中间件1 - 响应返回');
});
// 中间件2
app.use(async (ctx, next) => {
  console.log('中间件2 - 请求进入');
  await next();
  console.log('中间件2 - 响应返回');
});
// 中间件3
app.use(async (ctx, next) => {
  console.log('中间件3 - 请求进入');
  await next();
  console.log('中间件3 - 响应返回');
});
// 中间件4
app.use(async (ctx) => {
  console.log('中间件4 - 请求进入');
  ctx.body = 'Hello, Koa!';
  console.log('中间件4 - 响应返回');
});
app.listen(3000, () => {
  console.log('服务器已启动,监听端口 3000');
});

在上述示例中,每个中间件按照洋葱模型的顺序组织起来。当请求进入时,依次经过中间件1、中间件2 和中间件3,并且在每个中间件的前后会打印相应的日志。最后,请求从内层的中间件4返回,也会经过中间件3、中间件2 和中间件1,并且会输出相应的日志。

通过洋葱模型,我们可以清晰地看到请求和响应在每个中间件之间的流动过程,方便进行相关的处理和调试。同时,这种模型也能够灵活地添加、删除或修改中间件,以满足不同的业务需求。

9、什么是发布订阅模式,写出其核心实现代码?

发布订阅模式(Publish-Subscribe Pattern)是一种常见的软件设计模式,用于解耦消息发布者和消息订阅者之间的关系。在该模式中,发布者负责发布消息,而订阅者可以选择性地订阅感兴趣的消息,并在发布者发布相应消息时接收到通知。

以下是发布订阅模式的核心实现代码示例:

// 创建一个事件管理器,用于管理消息的发布和订阅
class EventManager {
  constructor() {
    this.eventMap = new Map(); // 存储事件订阅的回调函数
  }
  // 订阅事件
  subscribe(eventName, callback) {
    if (!this.eventMap.has(eventName)) {
      this.eventMap.set(eventName, []);
    }
    this.eventMap.get(eventName).push(callback);
  }
  // 取消订阅事件
  unsubscribe(eventName, callback) {
    if (this.eventMap.has(eventName)) {
      const callbacks = this.eventMap.get(eventName);
      const index = callbacks.indexOf(callback);
      if (index !== -1) {
        callbacks.splice(index, 1);
      }
    }
  }
  // 发布事件
  publish(eventName, data) {
    if (this.eventMap.has(eventName)) {
      const callbacks = this.eventMap.get(eventName);
      callbacks.forEach(callback => {
        callback(data); // 调用订阅者的回调函数,并传递数据
      });
    }
  }
}
// 创建一个事件管理器实例
const eventManager = new EventManager();
// 示例:发布者和订阅者
// 发布者A
const publisherA = {
  publishMessage(message) {
    eventManager.publish('message', message);
  }
};
// 订阅者B
const subscriberB = {
  handleMessage(message) {
    console.log('订阅者B接收到消息:', message);
  }
};
// 订阅者C
const subscriberC = {
  handleMessage(message) {
    console.log('订阅者C接收到消息:', message);
  }
};
// B和C订阅'message'事件
eventManager.subscribe('message', subscriberB.handleMessage);
eventManager.subscribe('message', subscriberC.handleMessage);
// A发布消息
publisherA.publishMessage('Hello, World!');
// 输出:
// 订阅者B接收到消息: Hello, World!
// 订阅者C接收到消息: Hello, World!
// 取消订阅
eventManager.unsubscribe('message', subscriberB.handleMessage);
// A再次发布消息
publisherA.publishMessage('Goodbye!');
// 输出:
// 订阅者C接收到消息: Goodbye!

在上述代码中,事件管理器 EventManager 提供了 subscribeunsubscribepublish 方法用于订阅、取消订阅和发布事件。发布者可以通过调用 publish 方法来发布特定的事件,并传递相应的数据。订阅者可以使用 subscribe 方法订阅感兴趣的事件,并提供一个回调函数来处理收到的消息。当发布者发布对应事件时,订阅者的回调函数将会被触发,从而实现消息的传递和处理。

这只是发布订阅模式的一种简单实现,实际应用中还可以根据需要进行扩展和优化。

10、大文件的断点续传如何实现,写出其核心思路代码,前后端都要写?

实现大文件的断点续传需要前后端配合完成。以下是实现大文件断点续传的核心思路和示例代码:

前端实现思路:

  1. 将大文件切分成小块,并记录每个小块的起始位置。
  2. 通过 XMLHttpRequest 或 Fetch API 发送每个小块的请求到后端。
  3. 根据后端返回的响应,判断是否需要上传该小块,若需要,则将该小块的数据发送给后端。
  4. 若上传成功,继续上传下一个小块,直到所有小块上传完毕。

前端示例代码:

function uploadFile(file) {
  const chunkSize = 1024 * 1024; // 每个小块的大小
  const totalChunks = Math.ceil(file.size / chunkSize); // 总共的小块数
  let currentChunk = 0;
  let start = 0;
  function uploadNextChunk() {
    const end = Math.min(start + chunkSize, file.size);
    const chunk = file.slice(start, end);
    const formData = new FormData();
    formData.append('file', chunk);
    formData.append('start', start);
    formData.append('end', end);
    // 发送请求到后端
    fetch('/upload', {
      method: 'POST',
      body: formData
    })
    .then(response => response.json())
    .then(data => {
      if (data.uploaded) {
        // 该小块上传成功
        currentChunk++;
        start = end;
        if (currentChunk < totalChunks) {
          // 继续上传下一个小块
          uploadNextChunk();
        } else {
          // 所有小块上传完毕
          console.log('文件上传完成');
        }
      } else {
        // 上传失败,可进行相应的重试逻辑
        console.log('上传失败');
      }
    })
    .catch(error => {
      console.error('上传出错:', error);
    });
  }
  // 开始上传第一个小块
  uploadNextChunk();
}
const fileInput = document.getElementById('file-input');
fileInput.addEventListener('change', (event) => {
  const file = event.target.files[0];
  uploadFile(file);
});

后端实现思路:

  1. 后端需要提供一个接口用于接收前端发送的小块数据。
  2. 根据接收到的小块数据,将其写入到指定的文件中,记录已接收的数据长度。
  3. 如果所有小块都接收完毕,则表示文件上传完成。

后端示例代码(使用 Node.js 和 Express 框架):

const express = require('express');
const fs = require('fs');
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.post('/upload', (req, res) => {
  const { start, end } = req.body;
  const fileStream = fs.createWriteStream('uploaded_file.txt', { flags: 'a' });
  req.on('data', (chunk) => {
    fileStream.write(chunk);
  });
  req.on('end', () => {
    fileStream.end();
    const uploadedSize = end - start;
    // 返回上传结果给前端
    res.json({ uploaded: true, uploadedSize });
  });
  req.on('error', (error) => {
    console.error('上传出错:', error);
    res.json({ uploaded: false });
  });
});
app.listen(3000, () => {
  console.log('服务器已启动,监听端口 3000');
});

以上代码为简化示例,实际应用中可能还需要考虑文件的合法性验证、断点记录的存储方式等问题。


相关文章
|
1月前
|
缓存 前端开发 JavaScript
"面试通关秘籍:深度解析浏览器面试必考问题,从重绘回流到事件委托,让你一举拿下前端 Offer!"
【10月更文挑战第23天】在前端开发面试中,浏览器相关知识是必考内容。本文总结了四个常见问题:浏览器渲染机制、重绘与回流、性能优化及事件委托。通过具体示例和对比分析,帮助求职者更好地理解和准备面试。掌握这些知识点,有助于提升面试表现和实际工作能力。
66 1
|
3月前
|
Web App开发 前端开发 Linux
「offer来了」浅谈前端面试中开发环境常考知识点
该文章归纳了前端开发环境中常见的面试知识点,特别是围绕Git的使用进行了详细介绍,包括Git的基本概念、常用命令以及在团队协作中的最佳实践,同时还涉及了Chrome调试工具和Linux命令行的基础操作。
「offer来了」浅谈前端面试中开发环境常考知识点
|
4月前
|
存储 XML 移动开发
前端大厂面试真题
前端大厂面试真题
|
2月前
|
Web App开发 JavaScript 前端开发
前端Node.js面试题
前端Node.js面试题
|
4月前
|
存储 前端开发 JavaScript
44 个 React 前端面试问题
【8月更文挑战第18天】
58 2
|
4月前
|
存储 JavaScript 前端开发
2022年前端js面试题
2022年前端js面试题
46 0
|
4月前
|
存储 前端开发 JavaScript
44 个 React 前端面试问题
44 个 React 前端面试问题
|
2月前
|
存储 人工智能 前端开发
前端大模型应用笔记(三):Vue3+Antdv+transformers+本地模型实现浏览器端侧增强搜索
本文介绍了一个纯前端实现的增强列表搜索应用,通过使用Transformer模型,实现了更智能的搜索功能,如使用“番茄”可以搜索到“西红柿”。项目基于Vue3和Ant Design Vue,使用了Xenova的bge-base-zh-v1.5模型。文章详细介绍了从环境搭建、数据准备到具体实现的全过程,并展示了实际效果和待改进点。
177 2
|
2月前
|
JavaScript 前端开发 程序员
前端学习笔记——node.js
前端学习笔记——node.js
48 0
|
2月前
|
人工智能 自然语言处理 运维
前端大模型应用笔记(一):两个指令反过来说大模型就理解不了啦?或许该让第三者插足啦 -通过引入中间LLM预处理用户输入以提高多任务处理能力
本文探讨了在多任务处理场景下,自然语言指令解析的困境及解决方案。通过增加一个LLM解析层,将复杂的指令拆解为多个明确的步骤,明确操作类型与对象识别,处理任务依赖关系,并将自然语言转化为具体的工具命令,从而提高指令解析的准确性和执行效率。