前端面试题详解整理6| 服务端渲染页面和客户端渲染页面,数据类型,判断怎么减少重排和重绘为什么要减少HTTP请求,前端上提高性能的方法,重构需要从哪些方面考虑,

106 阅读17分钟

字节前端一面凉经

  1. 重构需要从哪些方面考虑?
  2. 重构的目的是什么? 重构的目的是改善现有代码的结构、设计和可读性,以提高代码的质量、可维护性和可扩展性,同时保持其原有功能不变。重构通常包括消除代码中的坏味道(code smells)、简化复杂的逻辑、优化性能、增强可测试性等方面的工作。通过重构,可以使代码更易于理解、修改和维护,从而提高开发效率、降低维护成本,并为未来的功能迭代和改进奠定良好的基础。

重构是对现有代码进行修改,以改善其设计、结构、可读性和可维护性,而不改变其外部行为的过程。在考虑重构时,我们需要从以下几个方面来思考:

  1. 重构的目的:首先要明确重构的目的是什么。可能的目的包括改善代码的可读性、可维护性、性能优化、减少代码重复、消除代码坏味道等。明确目的有助于确定重构的方向和范围。

  2. 代码质量:评估当前代码的质量,包括代码结构、命名规范、注释质量、代码复杂度等。了解代码质量的现状可以帮助确定哪些部分需要重构。

  3. 设计原则:考虑是否遵循了常用的设计原则,如单一职责原则、开闭原则、依赖倒置原则等。重构时可以尝试根据设计原则对代码进行改进,以提高其灵活性和可扩展性。

  4. 代码复杂度:评估代码的复杂度,包括函数长度、嵌套层级、依赖关系等。重构可以帮助简化复杂的代码结构,提高代码的可读性和可维护性。

  5. 测试覆盖率:确保代码有足够的测试覆盖率,以保证重构后的代码不会引入新的 bug。在重构过程中,需要确保已有的测试用例能够覆盖重构后的代码。

  6. 团队合作:在团队协作的情况下,需要与团队成员充分沟通,共同决定重构的方向和优先级。确保团队成员都理解并同意重构的目的和计划。

综上所述,重构需要从明确目的、评估代码质量、遵循设计原则、简化代码复杂度、保证测试覆盖率和与团队合作等方面考虑。通过合理的重构,可以提高代码的质量和可维护性,从而提升团队的开发效率和产品的稳定性。

  1. 前端上提高性能的方法?

提高前端性能可以通过以下几个方面来实现:

  1. 优化资源加载

    • 合并和压缩文件:将多个 CSS 或 JavaScript 文件合并成一个,减少 HTTP 请求次数,并对文件进行压缩以减小文件大小,提高加载速度。
    • 使用 CDN 加速:将静态资源(如图片、CSS、JavaScript 文件)托管在 CDN 上,提高资源的加载速度,减轻服务器负载。
    • 使用异步加载:将不必要立即加载的资源延迟加载,例如使用<script>标签的asyncdefer属性,或者通过动态创建<link><script>标签进行加载。
  2. 优化图片资源

    • 使用合适的图片格式:根据图片内容选择合适的图片格式,例如使用 JPEG 格式的图片来存储照片,使用 PNG 格式的图片来存储图标和透明图片。
    • 图片压缩:使用图片压缩工具(如 TinyPNG)来减小图片文件的大小,减少网络传输时间。
    • 懒加载和预加载:对于长页面或包含大量图片的页面,可以使用懒加载技术延迟加载图片,或者使用预加载技术在页面加载完毕后提前加载图片。
  3. 优化 CSS 和 JavaScript

    • 删除无用代码:删除不必要的 CSS 和 JavaScript 代码,减小文件大小。
    • 避免使用过多的库和框架:只引入项目需要的库和框架,避免引入过多不必要的代码,减小文件大小。
    • 使用缓存和优化技术:利用浏览器缓存和服务端缓存技术,减少重复的 CSS 和 JavaScript 文件的加载,提高页面加载速度。
  4. 优化页面渲染

    • 减少重排和重绘:通过合理的 CSS 布局和样式设计,尽量减少页面的重排和重绘,提高页面渲染性能。
    • 使用 CSS3 动画和 transform:使用 CSS3 动画和 transform 属性来实现动画效果,而不是使用 JavaScript 动画,减少 CPU 的消耗,提高动画的流畅度。
  5. 优化代码结构和逻辑

    • 优化 JavaScript 执行:避免使用过多的嵌套循环和递归函数,减少不必要的计算和内存消耗。
    • 优化事件绑定:避免频繁的事件绑定和解绑,合理使用事件委托来减少事件处理器的数量。

综上所述,通过优化资源加载、图片资源、CSS 和 JavaScript、页面渲染、代码结构和逻辑等方面,可以有效提高前端性能,提升用户体验。 3. 为什么要减少HTTP请求?(这里应该是要回答请求的过程和性能之间的关系) 减少HTTP请求是为了提高网页加载性能和用户体验。HTTP请求是客户端向服务器请求资源的过程,它涉及到建立连接、发送请求、等待响应等步骤,每个请求都需要消耗时间和网络资源。因此,减少HTTP请求可以降低网页加载时间,提高页面的响应速度,从而提升用户体验。

具体来说,减少HTTP请求可以带来以下几方面的性能优势:

  1. 减少网络延迟:每个HTTP请求都需要经过网络传输,而网络延迟是影响页面加载速度的重要因素之一。减少HTTP请求意味着减少了网络传输的次数,可以降低整体的网络延迟,加快页面加载速度。

  2. 提高并发性:在HTTP/1.1协议中,每个域名下的并发连接数有限制,当页面中存在大量的HTTP请求时,可能会导致部分请求需要等待其他请求完成才能发送,降低了并发性。减少HTTP请求可以减轻服务器的负载,提高页面的并发性,加快页面加载速度。

  3. 减少服务器压力:每个HTTP请求都需要服务器处理和响应,当请求过多时,可能会导致服务器负载过高,影响服务器的响应速度和稳定性。减少HTTP请求可以降低服务器的负载,提高服务器的性能和稳定性。

  4. 节省带宽成本:在某些情况下,如移动端用户使用流量访问网页时,减少HTTP请求可以节省用户的带宽成本,提升用户体验。

综上所述,减少HTTP请求可以有效提高网页加载性能,加快页面响应速度,提升用户体验,是优化网页性能的重要手段之一。

  1. 怎么减少重排和重绘? 重排(Reflow)和重绘(Repaint)是浏览器渲染页面时的两个重要概念。重排指的是改变页面布局或者尺寸等引起浏览器重新计算元素的位置和大小,而重绘指的是元素的外观样式改变引起浏览器重新渲染元素。

减少重排和重绘可以提高页面的性能和流畅度,主要有以下几个方法:

  1. 批量操作DOM:减少对DOM的操作次数,尽可能通过操作文档碎片(Document Fragment)或字符串来一次性添加多个节点到DOM中,而不是多次单个添加。这样可以减少重排和重绘的次数。

  2. 避免强制同步布局:当读取一些会触发浏览器强制同步布局的属性时(如offsetWidthoffsetHeightclientWidthclientHeight等),尽量避免在循环中频繁调用这些属性,因为每次调用都会导致浏览器重新计算元素的位置和大小,引发重排。如果需要多次获取这些属性,可以先将它们缓存起来,减少不必要的计算。

  3. 使用 CSS 动画和 transform:使用 CSS3 动画代替 JavaScript 动画,因为 CSS3 动画在现代浏览器中会优化为合成线程(Composite Thread),不会触发重排和重绘。另外,使用 transform 属性进行动画变换,可以使用硬件加速,进一步提高性能。

  4. 避免频繁更改样式:尽量避免频繁更改元素的样式属性,尤其是那些会导致页面重排和重绘的属性(如宽度、高度、位置等)。可以通过修改元素的类名来一次性改变多个样式,或者使用requestAnimationFrame来优化频繁的样式改变。

  5. 使用 CSS3 动画替代 JavaScript 动画:CSS3 动画是由浏览器内部优化的,性能更高,不会触发 JavaScript 引擎,从而避免了 JavaScript 执行时可能导致的重排和重绘。

  6. 使用 will-change 属性:将会发生变化的元素提前通知浏览器,可以通过设置 will-change 属性来提前通知浏览器,告诉它哪些属性将要发生变化,从而优化性能。

通过合理设计和优化页面的结构、样式和交互,可以有效减少页面的重排和重绘,提升页面的性能和用户体验。

  1. 如果不把DOM属性不放在循环里面,具体应该怎么做

如果不希望在循环中重复获取 DOM 属性,可以在循环外部获取一次 DOM 属性,并将其存储在一个变量中,然后在循环中重复使用该变量。这样可以减少 DOM 操作的次数,提高代码的性能。

下面是一个示例代码,演示了如何在循环外部获取 DOM 属性并在循环中重复使用:

// 获取 DOM 元素
const container = document.getElementById('container');

// 获取并存储 DOM 属性
const containerWidth = container.offsetWidth;
const containerHeight = container.offsetHeight;

// 循环操作
for (let i = 0; i < 10; i++) {
    // 使用存储的 DOM 属性
    const newElement = document.createElement('div');
    newElement.style.width = containerWidth + 'px';
    newElement.style.height = containerHeight + 'px';
    container.appendChild(newElement);
}

在上面的代码中,我们首先在循环外部获取了容器 container 的宽度和高度属性,并将它们存储在 containerWidthcontainerHeight 变量中。然后在循环中,我们重复使用这两个变量来设置新创建的 div 元素的宽度和高度,而不是每次都重复获取容器的宽度和高度属性。这样可以提高代码的效率和性能。

  1. 从敲下URL到服务端渲染的页面最终能够去进行连接交互之间,发生了哪些步骤? 以下是从敲下 URL 到服务端渲染的页面最终能够进行连接交互之间可能发生的一些步骤:

  2. URL 解析

    • 浏览器根据用户输入的 URL 进行解析,获取主机名、路径和查询参数等信息。
  3. DNS 解析

    • 浏览器根据主机名查询 DNS 服务器,获取主机名对应的 IP 地址。
  4. 建立 TCP 连接

    • 浏览器通过获取到的 IP 地址建立 TCP 连接到服务器。
  5. 发起 HTTP 请求

    • 浏览器向服务器发送 HTTP 请求,请求特定路径的资源。
  6. 服务器处理请求

    • 服务器接收到请求后,根据路径和其他信息进行处理,可能包括路由解析、控制器调用、数据查询等。
  7. 生成响应内容

    • 服务器根据请求的处理结果生成响应内容,通常包括 HTML、CSS、JavaScript、图片等资源。
  8. HTML 渲染

    • 如果是服务端渲染的页面,服务器将生成的 HTML 内容发送到客户端,浏览器接收到 HTML 后开始渲染页面。
  9. 资源加载

    • 浏览器解析 HTML 页面,发现页面中引用的 CSS、JavaScript、图片等资源,并向服务器发起相应的资源请求。
  10. 客户端渲染

    • 如果页面中包含客户端渲染的部分(例如通过 JavaScript 动态生成的内容),浏览器会执行 JavaScript 代码,动态生成页面内容。
  11. 连接交互

    • 页面渲染完成后,用户可以与页面进行交互,例如点击链接、填写表单、发送请求等操作。
  12. 事件处理

    • 当用户执行交互操作时,浏览器会触发相应的事件,例如点击事件、表单提交事件等,页面会根据事件触发相应的处理逻辑。
  13. 数据交互

    • 页面与服务器之间可能会进行数据交互,例如通过 AJAX 请求获取数据、提交表单数据等。
  14. 响应处理

    • 浏览器接收到服务器的响应后,可能会更新页面的部分内容,例如更新页面中的列表、显示新的数据等。
  15. 状态更新

    • 页面的交互可能会导致状态的变化,例如页面的 URL 发生变化、页面的滚动位置发生变化等。
  16. 页面更新

    • 页面的状态变化可能会触发页面的重新渲染或部分更新,用户会看到页面内容的变化。

这些步骤描述了从敲下 URL 到页面最终能够进行连接交互之间可能发生的主要过程,具体情况可能会根据页面的具体内容和交互方式有所不同。

  1. 服务端渲染页面和客户端渲染页面有哪些区别?

服务端渲染页面(SSR)和客户端渲染页面(CSR)是两种不同的页面渲染方式,它们之间有以下区别:

  1. 渲染位置

    • 服务端渲染页面:页面的 HTML 内容是在服务器上生成的,服务器将完整的 HTML 页面发送到客户端浏览器。
    • 客户端渲染页面:页面的 HTML 内容是通过 JavaScript 在客户端浏览器中生成的,浏览器首先接收到一个空白的 HTML 页面,然后通过 JavaScript 在页面加载后动态地渲染内容。
  2. 页面加载速度

    • 服务端渲染页面:因为服务器直接生成完整的 HTML 页面并发送到客户端,所以页面的首次加载速度较快,用户能够更快地看到页面内容。
    • 客户端渲染页面:由于浏览器需要首先下载 HTML 页面,然后再通过 JavaScript 请求数据并渲染页面,所以页面的首次加载速度可能较慢,用户可能会看到一个空白的页面或加载指示器。
  3. SEO(搜索引擎优化)

    • 服务端渲染页面:搜索引擎能够直接抓取并索引完整的 HTML 页面内容,有利于页面的搜索引擎优化。
    • 客户端渲染页面:由于搜索引擎爬虫可能无法执行 JavaScript,所以对于基于客户端渲染的页面,搜索引擎可能无法获取到完整的页面内容,导致页面的 SEO 效果不佳。
  4. 页面交互性

    • 服务端渲染页面:在页面首次加载时,所有的页面内容都已经在服务器上生成,因此页面的交互性可能较差,需要通过页面的重新加载来获取更新的内容。
    • 客户端渲染页面:页面的内容是通过 JavaScript 动态生成的,可以实现更多的交互功能,例如无刷新加载、动态更新等。
  5. 缓存机制

    • 服务端渲染页面:由于页面的内容是在服务器上生成的,可以使用传统的 HTTP 缓存机制,例如 HTTP 缓存头、CDN 缓存等。
    • 客户端渲染页面:页面的内容是动态生成的,因此缓存机制较为复杂,通常需要使用更复杂的缓存策略,例如客户端缓存、数据缓存等。

综上所述,服务端渲染页面和客户端渲染页面各有优劣,选择合适的页面渲染方式取决于项目的需求和特点。 3. JS中有哪些数据类型? JavaScript 中有以下几种基本数据类型:

  1. 字符串(String):用于表示文本数据,例如 "Hello, World!"

  2. 数字(Number):用于表示数值,包括整数和浮点数,例如 423.14

  3. 布尔值(Boolean):用于表示逻辑值,只有两个取值:truefalse

  4. 空值(Null):用于表示空值或空对象引用,只有一个取值:null

  5. 未定义(Undefined):用于表示未定义的值或变量,只有一个取值:undefined

除了基本数据类型外,JavaScript 还有一个复杂数据类型:

  1. 对象(Object):用于表示复杂数据结构,包括键值对、函数、数组等。对象是一种无序的集合,例如 { key: 'value' }

此外,ES6 新增了两种复杂数据类型:

  1. Symbol(符号):用于创建唯一的标识符,通常用作对象的键。每个 Symbol 值都是唯一的,例如 Symbol('foo')

  2. BigInt(大整数):用于表示任意精度的整数,可以表示比 Number 类型更大的整数。例如 BigInt(123456789012345678901234567890)

这些数据类型可以用于构建各种类型的数据和数据结构,JavaScript 中的变量可以在运行时动态地根据需要进行类型转换。

  1. 如果判断一个变量是否是一个数组?

要判断一个变量是否是数组,可以使用 Array.isArray() 方法。这个方法接受一个参数,并返回一个布尔值,指示该参数是否是数组。

以下是一个示例:

const arr = [1, 2, 3];
const obj = { key: 'value' };

console.log(Array.isArray(arr)); // 输出: true
console.log(Array.isArray(obj)); // 输出: false

在这个示例中,Array.isArray() 方法用于检查变量 arr 是否是数组,返回 true。而对于变量 obj,返回 false,因为它不是一个数组。 3. 题目:用async/await实现1秒后打印 使用 async/await 实现延迟执行并打印的方法是结合 setTimeout 函数和 Promise 对象。下面是一个使用 async/await 的示例函数,该函数将在 1 秒后打印消息:

async function printAfterDelay() {
    // 使用 Promise 封装 setTimeout
    const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
    
    // 使用 await 延迟执行
    await delay(1000);
    
    // 在延迟后打印消息
    console.log("1 second has passed");
}

// 调用函数
printAfterDelay();

在这个示例中,printAfterDelay 函数使用 async 关键字声明为异步函数。在函数内部,使用 await 关键字等待 delay 函数返回的 Promise 对象,在等待 1 秒后,再打印消息。 delay 函数用于将 setTimeout 函数封装成一个返回 Promise 的延迟执行函数。

  1. 题目:判断是否是“回文”字符串

判断一个字符串是否是回文字符串的方法是将其反转并与原始字符串进行比较。如果反转后的字符串与原始字符串相同,则原始字符串是回文字符串。

以下是一个JavaScript函数,用于判断一个字符串是否是回文字符串:

function isPalindrome(str) {
    // 去除字符串中的非字母字符并转换为小写
    const formattedStr = str.replace(/[^a-zA-Z]/g, '').toLowerCase();
    
    // 反转字符串
    const reversedStr = formattedStr.split('').reverse().join('');
    
    // 比较反转后的字符串和原始字符串
    return formattedStr === reversedStr;
}

// 示例用法
console.log(isPalindrome("A man, a plan, a canal: Panama")); // 输出: true
console.log(isPalindrome("race a car")); // 输出: false

这个函数首先通过正则表达式去除字符串中的非字母字符,并将所有字母转换为小写。然后,它将字符串反转,并与原始字符串进行比较,最终返回比较结果。

作者:爱编程的小鱼
链接:www.nowcoder.com/discuss/519…
来源:牛客网