前端面试题详解整理78|useEffect 垂直居中,flex,cookie,sessionStorage,状态码。强制缓存,协商缓存,cnd加速,hooks,

107 阅读29分钟

二面

1. 垂直居中的几种方式

在Web开发中,实现垂直居中可以采用多种方式,以下是几种常见的方式:

  1. 使用Flex布局: 使用Flex布局是实现垂直居中的一种简单有效的方式。通过设置容器的display: flex;align-items: center;属性,即可实现容器内部项目的垂直居中。

    .container {
      display: flex;
      align-items: center; /* 垂直居中 */
    }
    
  2. 使用表格布局: 使用表格布局也是一种常见的实现垂直居中的方式。通过将父容器设置为display: table;,并将子容器设置为display: table-cell; vertical-align: middle;,即可实现垂直居中。

    .container {
      display: table;
    }
    
    .child {
      display: table-cell;
      vertical-align: middle; /* 垂直居中 */
    }
    
  3. 使用绝对定位和transform: 使用绝对定位和transform属性也可以实现垂直居中。将子容器绝对定位于父容器的中间位置,然后通过top: 50%; transform: translateY(-50%);将其垂直居中。

    .container {
      position: relative;
    }
    
    .child {
      position: absolute;
      top: 50%;
      transform: translateY(-50%); /* 垂直居中 */
    }
    
  4. 使用表格和margin: 通过将子容器设置为display: table-cell;,然后使用margin: auto;将其水平和垂直居中。

    .container {
      display: table;
      width: 100%;
      height: 100%;
    }
    
    .child {
      display: table-cell;
      vertical-align: middle; /* 垂直居中 */
      text-align: center; /* 水平居中 */
    }
    

这些方法各有优劣,具体使用哪种方法取决于项目的实际情况和需求。Flex布局是目前最为推荐的实现垂直居中的方式,因为它简单、灵活且兼容性良好。

3. flex 布局的几个属性及其含义

Flex布局是一种强大的布局方式,可以帮助开发者实现灵活的布局效果。以下是Flex布局中常用的几个属性及其含义:

  1. flex-direction

    • 含义:定义了Flex容器的主轴方向。
    • 可选值:row(主轴为水平方向)、row-reverse(主轴为水平方向,但是方向与row相反)、column(主轴为垂直方向)、column-reverse(主轴为垂直方向,但是方向与column相反)。
  2. justify-content

    • 含义:定义了Flex容器中各个项目在主轴上的对齐方式。
    • 可选值:flex-start(默认值,项目位于容器的起始位置)、flex-end(项目位于容器的结束位置)、center(项目位于容器的中间位置)、space-between(项目位于容器内,两端对齐,项目之间的间隔相等)、space-around(项目位于容器内,两侧的间隔相等)。
  3. align-items

    • 含义:定义了Flex容器中各个项目在交叉轴上的对齐方式。
    • 可选值:flex-start(项目在交叉轴起始位置对齐)、flex-end(项目在交叉轴结束位置对齐)、center(项目在交叉轴中间位置对齐)、baseline(项目的第一行文字的基线对齐)、stretch(默认值,项目被拉伸以适应容器)。
  4. align-self

    • 含义:定义了Flex容器中单个项目在交叉轴上的对齐方式,覆盖align-items属性。
    • 可选值:同align-items。
  5. flex-wrap

    • 含义:定义了Flex容器中项目是否换行。
    • 可选值:nowrap(默认值,项目不换行)、wrap(项目换行,第一行在上)、wrap-reverse(项目换行,第一行在下)。
  6. flex-grow

    • 含义:定义了Flex项目的放大比例,默认为0,即不放大。
    • 可选值:一个数字,表示放大比例,如果所有项目的flex-grow都为1,则它们将等分剩余空间;如果其中一个项目的flex-grow为2,其他为1,则前者占据的剩余空间将是后者的两倍。
  7. flex-shrink

    • 含义:定义了Flex项目的缩小比例,默认为1,即可以缩小。
    • 可选值:一个数字,表示缩小比例,如果所有项目的flex-shrink都为1,则它们将等比例缩小;如果其中一个项目的flex-shrink为0,其他为1,则它不会缩小,剩余空间将由其他项目等比例缩小。

这些属性可以结合使用,灵活地实现各种复杂的布局效果。Flex布局的主要特点是简洁、灵活,并且适用于各种设备和屏幕尺寸。

3. 0.1 + 0.2 为什么不等于 0.3

在JavaScript中,0.1 + 0.2 不等于 0.3 的原因是浮点数的精度问题。这是因为JavaScript使用的是双精度浮点数表示法(64位),在这种表示法下,无法精确地表示所有的小数,特别是十进制小数。

例如,0.1 和 0.2 在二进制表示中是无限循环的小数,而 JavaScript 中的数字类型只能存储一定位数的二进制小数,因此在进行浮点数运算时会导致精度损失。

在实际运算中,0.1 + 0.2 的结果并不是精确的 0.3,而是一个非常接近 0.3 的数值,通常是一个极小的舍入误差。这种舍入误差是由于计算机对浮点数进行二进制近似表示的方式所导致的,这种情况在大多数编程语言中都会出现。

为了避免由于浮点数精度问题而产生的意外错误,通常建议在比较浮点数时不要直接使用相等性判断,而是通过设置一个误差范围来进行比较。例如,可以将浮点数相减的结果与一个很小的误差范围进行比较,来判断它们是否相等。

  1. 如何理解闭包, 闭包的使用场景
  2. 什么是宏任务和微任务
  3. 事件循环机制, 并看代码说结果

6. cookie , LocalStorage, SessionStorage 的区别

Cookie、LocalStorage和SessionStorage都是用于在客户端存储数据的Web存储机制,但它们之间有一些区别:

  1. Cookie

    • 存储容量:每个Cookie的存储容量通常限制在4KB左右。
    • 发送方式:每次HTTP请求都会自动携带Cookie,包括请求和响应的头部。
    • 过期时间:可以通过设置过期时间或持久化Cookie来控制Cookie的生存周期。
    • 安全性:可以设置HttpOnly和Secure属性来增强Cookie的安全性,防止被XSS和CSRF攻击。
  2. LocalStorage

    • 存储容量:LocalStorage的存储容量通常限制在5MB左右。
    • 生命周期:数据会永久存储在客户端,除非被显示删除。
    • 发送方式:LocalStorage的数据不会随着HTTP请求自动发送到服务器。
    • 安全性:LocalStorage中的数据可以被JavaScript直接读取,存在安全风险,因此不适合存储敏感数据。
  3. SessionStorage

    • 存储容量:SessionStorage的存储容量通常限制在5MB左右。
    • 生命周期:数据只在当前会话期间有效,关闭浏览器窗口或标签页时会被删除。
    • 发送方式:SessionStorage的数据也不会随着HTTP请求自动发送到服务器。
    • 安全性:与LocalStorage类似,SessionStorage中的数据可以被JavaScript直接读取。

总的来说,Cookie主要用于在客户端和服务器之间传递数据,LocalStorage和SessionStorage用于在客户端长期或临时存储数据。Cookie的存储容量较小,但可以被服务器端设置过期时间和安全属性;LocalStorage和SessionStorage的存储容量较大,但不适合存储敏感数据,并且数据在浏览器关闭后可能会丢失。

3. http 状态码

HTTP状态码是由服务器响应到客户端的数字代码,用于表示特定HTTP请求的处理结果。状态码分为五类,每类都有不同的范围和含义。以下是常见的HTTP状态码及其含义:

  1. 1xx(信息性状态码):表示服务器已经接收到请求,但需要进一步处理。

    • 100 Continue:表示服务器已经接收到了客户端的部分请求,但还没有接收完整的请求,客户端应该继续发送剩余的请求。
  2. 2xx(成功状态码):表示服务器成功处理了请求。

    • 200 OK:表示请求成功,服务器已经成功处理了请求。
    • 201 Created:表示请求已经成功处理,并在服务器上创建了新的资源。
    • 204 No Content:表示请求已经成功处理,但响应中不包含任何内容。
  3. 3xx(重定向状态码):表示需要客户端采取进一步的操作才能完成请求。

    • 301 Moved Permanently:永久重定向,表示请求的资源已经被永久移动到新的URL。
    • 302 Found:临时重定向,表示请求的资源临时移动到了新的URL。
    • 304 Not Modified:表示客户端的缓存资源是最新的,可以直接使用本地缓存的副本。
  4. 4xx(客户端错误状态码):表示客户端发送的请求有误,服务器无法处理。

    • 400 Bad Request:表示客户端发送的请求有语法错误,服务器无法理解。
    • 401 Unauthorized:表示客户端未经授权,需要进行身份验证。
    • 404 Not Found:表示请求的资源不存在。
  5. 5xx(服务器错误状态码):表示服务器在处理请求时发生了错误。

    • 500 Internal Server Error:表示服务器在处理请求时发生了意外的内部错误。
    • 503 Service Unavailable:表示服务器当前无法处理请求,通常是由于服务器过载或维护。

这些HTTP状态码帮助客户端和服务器之间进行通信,使得客户端能够根据不同的情况采取相应的操作,提高了网络通信的可靠性和可用性。

3. 由 304 状态码引发浏览器缓存机制, 协商缓存与强缓存

当浏览器向服务器发送请求时,如果服务器返回的响应状态码是304(Not Modified),则表示资源未被修改,浏览器可以直接使用本地缓存的副本,而不需要重新下载资源。304状态码通常与浏览器的缓存机制以及协商缓存和强缓存相关联。

强缓存:

强缓存是指浏览器在请求资源时,首先根据响应头中的Cache-Control或Expires字段判断是否命中强缓存。如果资源命中了强缓存,则浏览器直接从本地缓存中加载资源,不会向服务器发送请求。强缓存可以通过两种方式来设置:

  • Cache-Control字段:通过设置Cache-Control的max-age或s-maxage指令来定义资源的缓存时间,单位是秒。例如:Cache-Control: max-age=3600表示资源在3600秒内不会过期。

  • Expires字段:通过设置Expires字段来指定资源的过期时间,是一个绝对时间,格式为GMT格式的日期时间字符串。例如:Expires: Wed, 21 Oct 2024 07:28:00 GMT表示资源在这个日期时间之前都是有效的。

协商缓存:

如果资源未命中强缓存,浏览器会向服务器发送请求,并在请求头中携带上一次请求时服务器返回的响应头中的一些字段,如If-Modified-Since或If-None-Match等。服务器收到请求后会根据这些字段判断资源是否发生了变化。如果资源未发生变化,则返回304状态码,表示资源未修改,浏览器可以继续使用本地缓存的副本。这种缓存方式称为协商缓存,因为浏览器与服务器之间需要进行一次“协商”,判断资源是否需要重新加载。

  • Last-Modified / If-Modified-Since:服务器在响应头中返回Last-Modified字段,表示资源的最后修改时间。浏览器在下一次请求时会在请求头中携带If-Modified-Since字段,值为上一次响应中的Last-Modified值。如果服务器判断资源未发生变化,则返回304状态码。

  • ETag / If-None-Match:服务器在响应头中返回ETag字段,表示资源的唯一标识符。浏览器在下一次请求时会在请求头中携带If-None-Match字段,值为上一次响应中的ETag值。如果服务器判断资源未发生变化,则返回304状态码。

综上所述,浏览器的缓存机制包括了强缓存和协商缓存两种方式,它们可以减少不必要的网络请求,提高网页加载速度,同时节省服务器资源。

3. 哪些资源需要进行 CDN 加速

CDN(内容分发网络)可以加速对静态资源的访问,提高网站的加载速度和性能。以下是一些常见的静态资源,适合进行CDN加速:

  1. 图片文件:包括JPEG、PNG、GIF等格式的图片文件。通过CDN加速,可以加快图片加载速度,提升用户体验。

  2. CSS文件:网站的样式表文件,包括CSS、LESS、SASS等格式的文件。通过CDN加速,可以加快页面渲染速度,提高用户体验。

  3. JavaScript文件:网站的脚本文件,包括JavaScript、TypeScript等格式的文件。通过CDN加速,可以加快脚本加载速度,提高网站的交互性和动态性。

  4. 字体文件:网站使用的字体文件,包括TTF、WOFF、WOFF2等格式的字体文件。通过CDN加速,可以加快字体加载速度,提升页面的美观度和可读性。

  5. 视频文件:网站的视频文件,包括MP4、WebM等格式的视频文件。通过CDN加速,可以加快视频加载速度,提高用户体验。

  6. 音频文件:网站的音频文件,包括MP3、OGG等格式的音频文件。通过CDN加速,可以加快音频加载速度,提高用户体验。

  7. 静态文件:其他静态资源文件,如JSON、XML、SVG等格式的文件。通过CDN加速,可以加快这些文件的加载速度,提高网站的整体性能。

总的来说,任何需要通过网络传输的静态资源文件都可以考虑使用CDN加速,从而提高网站的加载速度和性能。但对于动态生成的内容、用户个性化的内容等动态资源,由于无法缓存在CDN节点上,不适合使用CDN加速。

3. React 中为什么 hooks 不能在条件语句中使用?

在React中,Hooks(例如useStateuseEffect等)是基于函数组件的一种新特性,它们通过使用闭包来管理状态和副作用。由于Hooks的工作原理,它们需要按照相同的顺序在每次组件渲染时调用,这就导致了Hooks不能在条件语句中使用的问题。

具体来说,Hooks需要按照相同的顺序被调用,这样React才能正确地将它们与组件的状态进行关联。如果Hooks在条件语句中使用,那么条件的结果可能会导致Hooks的调用顺序发生变化,从而导致React无法正确地管理组件的状态。

举个例子,如果一个组件中的某个条件分支中使用了useState来管理状态,而另一个条件分支中没有使用,那么在不同条件下组件的状态管理方式将不一致,可能导致状态的不稳定和不可预测性。

为了解决这个问题,React规定了Hooks只能在函数组件的最顶层进行调用,不能嵌套在条件语句、循环语句或嵌套函数中。这样可以确保Hooks的调用顺序是固定的,从而保证了组件状态的稳定性和可预测性。

如果需要在条件语句中使用Hooks,可以通过将条件逻辑移到Hooks调用之前,或者在自定义Hook中将条件逻辑封装起来,以确保Hooks的调用顺序不会受到条件的影响。

3. useEffect 使用场景, dependencies 的作用, 返回值的作用

在React中,useEffect是一个用于执行副作用操作的Hook,它在组件渲染完成后执行一些额外的操作,比如数据获取、订阅事件、手动操作DOM等。以下是关于useEffect的使用场景、dependencies的作用以及返回值的作用:

使用场景:

  1. 数据获取和订阅事件:可以在useEffect中执行数据获取或订阅事件的操作,确保这些操作在组件渲染后立即执行。

  2. 手动操作DOM:如果需要手动操作DOM元素,可以将相关操作放在useEffect中,确保DOM已经被渲染完成。

  3. 清理副作用useEffect还可以返回一个清理函数,用于清理副作用产生的一些资源或订阅。这在组件卸载或重新渲染时特别有用。

Dependencies 的作用:

useEffect接受第二个参数,称为dependencies,它是一个数组,用于指定在哪些依赖发生变化时触发useEffect中的副作用操作。如果dependencies为空数组,表示副作用仅在组件挂载时执行一次,类似于类组件中的componentDidMount。如果dependencies不为空,则只有指定的依赖发生变化时,useEffect才会执行。

使用dependencies可以确保副作用的执行是在需要的时候,且仅在需要的时候执行,避免不必要的重复执行和性能问题。

返回值的作用:

useEffect可以返回一个函数,用于清理副作用产生的一些资源或订阅。这个清理函数在组件卸载或重新渲染时执行,用于清理和释放之前创建的资源,防止内存泄漏和其他问题。

总之,useEffect是一个非常有用的Hook,可以用于执行各种副作用操作,如数据获取、订阅事件、手动操作DOM等。通过合理地使用dependencies和返回值,可以确保副作用的执行时机和清理机制,从而提高组件的可靠性和性能。

3. React 父子组件通信的方式

在React中,父子组件通信是一种常见的场景。React提供了几种方式来实现父子组件之间的通信:

  1. Props(属性): 这是React中最常见的一种父子组件通信方式。通过在父组件中传递props(属性)给子组件,子组件可以通过props来接收和使用数据。父组件可以将状态作为props传递给子组件,子组件可以根据props的值来渲染不同的内容或执行特定的操作。

  2. 回调函数: 父组件可以向子组件传递一个回调函数作为props,子组件可以在需要的时候调用该回调函数来向父组件传递数据或触发特定的行为。这种方式常用于子组件需要向父组件传递数据或触发事件的场景。

  3. Context API: Context API是React提供的一种跨组件通信的方式,它可以让我们在组件树中传递数据,而不需要一级一级地手动传递props。通过在父组件中创建一个Context,子组件可以通过contextTypeuseContext hook来访问该Context中的数据。

  4. Refs(引用): Refs可以用来在React中访问DOM元素或组件实例。父组件可以通过refs向子组件传递引用,子组件可以通过props接收并使用该引用。这种方式通常用于需要直接操作DOM或子组件实例的场景。

  5. 事件总线: 可以使用事件总线来实现组件之间的通信,即在应用的顶层创建一个事件总线对象,然后在需要通信的组件中订阅和发布事件。这种方式适用于父子组件之间的通信以及非直接关联的组件之间的通信。

以上是几种常见的React父子组件通信方式,每种方式都有其适用的场景和优缺点,可以根据具体需求选择合适的方式来实现通信。

3. Vuex 的使用场景与基本原理

Vuex是一个专门为Vue.js应用程序开发的状态管理模式。它主要用于管理应用程序中的共享状态,这些状态可以在应用的所有组件中被访问。以下是Vuex的使用场景和基本原理:

使用场景:

  1. 大型单页面应用(SPA):在大型的SPA中,组件之间的数据共享和状态管理变得复杂。Vuex可以帮助你管理所有组件共享的状态,使得数据流更加清晰和可预测。

  2. 多个组件需要共享状态:当多个组件需要访问和修改同一个状态时,可以使用Vuex来集中管理这些状态,避免了组件之间的混乱和耦合。

  3. 跨组件的通信:如果需要在不同组件之间传递数据或触发特定行为,Vuex提供了统一的状态管理机制,可以方便地实现跨组件的通信。

  4. 数据持久化:有时候需要将状态持久化到本地存储中,以便在页面刷新后保持数据。Vuex可以与浏览器的本地存储(如localStorage)结合使用,实现数据的持久化。

基本原理:

  1. 单一状态树:Vuex使用单一状态树管理应用的所有状态,即将所有的状态集中存储在一个对象中。这样做的好处是状态的变化变得可追踪和可调试。

  2. 状态是响应式的:Vuex使用Vue的响应式系统来跟踪状态的变化。当状态发生变化时,相关联的视图会自动更新。

  3. 通过mutation改变状态:在Vuex中,状态只能通过提交mutation来进行更改,而不能直接修改。这样做的目的是为了确保所有的状态变化都是可追踪的,从而更容易调试和理解应用程序的行为。

  4. 通过action提交mutation:在Vuex中,action用于提交mutation,而不是直接改变状态。这样可以将异步操作和逻辑处理从组件中抽离出来,使得代码更加清晰和可维护。

  5. 插件机制:Vuex提供了插件机制,允许开发者在状态发生变化时执行额外的逻辑。这些插件可以用来记录状态变化的日志、持久化状态到本地存储中等。

综上所述,Vuex通过一系列的机制和约定,实现了对应用程序状态的集中管理和统一控制,从而使得数据流更加清晰和可预测。

3. Vuex 状态变更如何引起视图更新, 原理是什么

在Vue.js中,Vuex是一种状态管理模式,它可以帮助我们管理应用的所有组件的状态。当Vuex中的状态发生变化时,会触发视图的更新。这是因为Vuex采用了响应式的数据结构,类似于Vue中的响应式数据,当状态发生变化时,与之相关联的视图会自动更新。

具体来说,当你在组件中通过this.$store.commit()this.$store.dispatch()提交mutation或action来改变状态时,Vuex会在内部检测到状态的变化,然后通知所有依赖于该状态的组件进行更新。

Vuex的原理主要包括以下几个方面:

  1. 单一状态树:Vuex中的所有状态都集中存储在单一状态树中,这使得状态的变化变得可追踪和可调试。

  2. 响应式更新:Vuex使用Vue的响应式系统来跟踪所有状态的变化。当状态发生变化时,相关的视图会自动更新。

  3. 严格的状态管理:Vuex要求通过mutation来改变状态,这样可以确保所有的状态变化都是可追踪的。在开发环境下,如果直接修改状态而不通过mutation,Vuex会发出警告。

  4. 插件机制:Vuex提供了插件机制,允许开发者在状态发生变化时执行额外的逻辑,比如记录状态变化的日志或持久化状态到本地存储中。

总之,Vuex通过响应式的状态管理机制,确保了当状态发生变化时,相关联的视图会自动更新,从而实现了状态变更引起视图更新的功能。

3. 虚拟 DOM

虚拟 DOM(Virtual DOM)是虚拟的 DOM 结构,它是真实 DOM 的 JavaScript 对象表示形式。虚拟 DOM 在前端框架(如 React、Vue 等)中被广泛使用,主要有以下作用和优势:

  1. 性能优化

    • 虚拟 DOM 可以在内存中对 DOM 结构进行操作,减少了直接操作真实 DOM 带来的性能消耗。
    • 通过虚拟 DOM 的批量更新和合并操作,可以减少实际 DOM 操作的次数,提高页面渲染的性能。
  2. 跨平台兼容性

    • 虚拟 DOM 可以在不同平台上运行,不依赖于具体的浏览器环境,从而提高了跨平台的兼容性。
    • 通过虚拟 DOM,可以实现在浏览器、Node.js 等不同环境下对 DOM 结构进行统一的处理。
  3. 简化开发流程

    • 虚拟 DOM 提供了一种更加抽象和简化的 DOM 操作方式,使开发者可以更专注于应用的逻辑和业务需求。
    • 开发者可以通过虚拟 DOM 的声明式 API(如 JSX、模板语法等)来描述 UI 的结构和状态,而无需直接操作 DOM。
  4. 实现高效的 diff 算法

    • 虚拟 DOM 可以实现高效的 diff 算法,通过比较两个虚拟 DOM 树的差异,计算出最小的更新操作,并将更新应用到真实 DOM 上。
    • 通过 diff 算法,可以尽可能地减少不必要的 DOM 操作,提高页面渲染的效率。

总的来说,虚拟 DOM 技术在前端开发中起到了重要的作用,它可以提高页面渲染性能、简化开发流程,并且具有跨平台兼容性等优势。虚拟 DOM 技术的出现和应用,使得前端开发变得更加高效、灵活和可靠。

3. webpack 中的 loader 与 plugin 的区别

在 webpack 中,loader 和 plugin 是两个不同的概念,它们分别用于不同的功能和场景,具有以下区别:

  1. Loader

    • Loader 用于对模块源代码进行转换和处理,将不同类型的文件(如 JavaScript、CSS、图片等)转换为 webpack 可以处理的模块。
    • Loader 在 webpack 配置中的使用是通过 module.rules 字段来配置的,每个 loader 配置都包含了 test、use 等属性,用于匹配文件和指定转换规则。
    • 一个文件可以经过多个 loader 的处理,loader 会按照链式顺序依次执行,最终将处理结果交给 webpack 进行打包。
    • 例如,使用 babel-loader 将 ES6+ 的 JavaScript 代码转换为 ES5,使用 style-loader 和 css-loader 将 CSS 文件转换为 JavaScript 模块。
  2. Plugin

    • Plugin 用于扩展 webpack 的功能,它可以在 webpack 构建过程的各个阶段执行自定义操作,如打包优化、资源管理、环境变量注入等。
    • Plugin 是一个带有 apply 方法的 JavaScript 对象,可以在 webpack 配置中通过 plugins 字段进行配置。
    • 一个 webpack 配置中可以配置多个 plugin,它们会按照配置的顺序依次执行。
    • 例如,使用 HtmlWebpackPlugin 自动生成 HTML 文件,使用 DefinePlugin 定义全局变量。

综上所述,loader 和 plugin 在 webpack 中分别用于处理模块源代码和扩展 webpack 功能,它们在功能和用法上有着明显的区别。loader 主要用于文件转换和处理,而 plugin 则用于扩展 webpack 的功能和自定义构建过程。在实际项目中,通常会同时使用 loader 和 plugin 来完成对模块的处理和对 webpack 构建过程的定制。

3. sourceMap 的作用

sourceMap 是一种用于将压缩、合并后的代码映射回原始源代码的技术。它的作用主要体现在以下几个方面:

  1. 调试

    • 在开发过程中,通常我们会使用未压缩、未合并的源代码进行调试,以方便定位和解决问题。
    • 然而,在生产环境中,为了提高页面加载速度和减少带宽消耗,我们会对代码进行压缩和合并处理,这样的代码在调试时很难阅读和定位问题。
    • 使用 sourceMap 可以在浏览器开发者工具中将压缩后的代码映射回原始的源代码,从而在生产环境中进行方便的调试。
  2. 错误报告

    • 当网站出现错误时,通常会收集用户端的错误报告,这些报告包含了错误发生的位置和堆栈信息。
    • 如果没有 sourceMap,那么错误报告中的位置信息将会指向压缩后的代码,不利于定位问题。
    • 使用 sourceMap 可以将错误报告中的位置信息映射回原始的源代码,有助于更准确地定位问题。
  3. 性能分析

    • 在优化网站性能时,我们需要了解代码中的瓶颈和优化空间,通常会使用性能分析工具进行分析。
    • 如果没有 sourceMap,分析工具将会显示压缩后的代码,不利于理解和优化。
    • 使用 sourceMap 可以将性能分析工具中的代码映射回原始的源代码,有助于更准确地进行性能优化。

总的来说,sourceMap 的作用是提高开发效率和代码质量,帮助开发者更方便地进行调试、错误定位和性能优化。

3. 打包方面的性能优化

打包性能优化在前端开发中非常重要,特别是对于大型项目或者需要频繁部署的项目。以下是一些常见的打包性能优化策略:

  1. 代码拆分(Code Splitting)

    • 将代码拆分成多个小模块,按需加载,可以减少初始加载时间。
    • 使用动态导入(Dynamic Import)或者 webpack 的 SplitChunksPlugin 来实现代码拆分。
  2. 懒加载(Lazy Loading)

    • 将页面中不需要立即加载的模块延迟加载,可以提高页面的初次加载速度。
    • 在路由配置中使用懒加载,或者使用动态导入来实现懒加载。
  3. Tree shaking

    • 利用 ES6 模块的静态特性,去除未使用的代码,减少打包体积。
    • 使用 webpack 的 UglifyJsPlugin 或者 TerserPlugin 来进行代码压缩和 Tree shaking。
  4. 按需加载第三方库

    • 如果项目中使用了大型的第三方库,可以按需加载其中的部分模块,减少打包体积。
    • 使用 lodash 的按需加载功能或者使用 babel-plugin-lodash 来实现按需加载。
  5. 图片优化

    • 对图片进行压缩和转换,选择合适的图片格式(如使用 WebP 格式),减少图片大小。
    • 使用图片压缩工具(如 TinyPNG)或者 webpack 的 image-webpack-loader 来实现图片优化。
  6. 缓存

    • 利用浏览器缓存机制,对静态资源进行缓存,减少重复加载。
    • 使用 webpack 的文件名哈希或者 contenthash 来实现文件缓存。
  7. 生产环境优化

    • 在生产环境中关闭开发环境的调试功能,减少打包体积。
    • 使用 webpack 的 DefinePlugin 来定义环境变量,去除开发环境中的调试代码。
  8. 优化构建工具

    • 使用最新版本的 webpack、Babel 等构建工具,利用其优化功能。
    • 根据项目需求选择合适的插件和配置,减少不必要的构建时间和打包体积。

以上是一些常见的打包性能优化策略,根据项目的具体情况,可以综合运用多种优化手段来提高打包性能和用户体验。

  1. 最近做的项目中遇到的难点, 如何解决的

  2. 青训营项目

3. 算法:二叉树的层序遍历

二叉树的层序遍历是一种广度优先搜索(Breadth First Search, BFS)的方法,它按照树的层次顺序,从根节点开始逐层遍历节点。

实现二叉树的层序遍历通常使用队列来辅助操作。具体步骤如下:

  1. 首先将根节点入队。
  2. 当队列不为空时,循环执行以下步骤:
    • 出队当前节点,并将其值存储到结果数组中。
    • 将当前节点的左右子节点(如果存在)依次入队。
  3. 返回结果数组。

下面是 JavaScript 实现的示例代码:

function TreeNode(val) {
    this.val = val;
    this.left = this.right = null;
}

function levelOrder(root) {
    if (!root) return []; // 如果根节点为空,则返回空数组

    const result = []; // 存储层序遍历结果的数组
    const queue = [root]; // 使用队列辅助遍历,初始时将根节点入队

    while (queue.length) {
        const levelSize = queue.length; // 当前层的节点个数
        const currentLevel = []; // 存储当前层节点值的数组

        // 遍历当前层的所有节点
        for (let i = 0; i < levelSize; i++) {
            const node = queue.shift(); // 出队当前节点
            currentLevel.push(node.val); // 将当前节点值存入数组

            // 将当前节点的左右子节点(如果存在)依次入队
            if (node.left) queue.push(node.left);
            if (node.right) queue.push(node.right);
        }

        result.push(currentLevel); // 将当前层节点值数组存入结果数组
    }

    return result;
}

// 示例用法
const root = new TreeNode(3);
root.left = new TreeNode(9);
root.right = new TreeNode(20);
root.right.left = new TreeNode(15);
root.right.right = new TreeNode(7);

console.log(levelOrder(root)); // 输出:[[3], [9, 20], [15, 7]]

在这个示例中,我们定义了一个 TreeNode 类来表示二叉树的节点,然后实现了 levelOrder 函数来进行层序遍历。最后,我们创建了一个二叉树并调用 levelOrder 函数来打印出层序遍历的结果。

  1. 反问, 提升空间: 工程化与框架原理

作者:zbwer
链接:www.nowcoder.com/discuss/595…
来源:牛客网