💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
性能优化的题目也是面试常考的,这类题目有很大的扩展性,能够扩展出来很多小细节,而且对个人的技术视野和业务能力有很大的挑战。这部分笔者会重点讲下常用的性能优化方案。 > 题目:总结前端性能优化的解决方案 ### 优化原则和方向 性能优化的原则是**以更好的用户体验为标准**,具体就是实现下面的目标: 1. 多使用内存、缓存或者其他方法 2. 减少 CPU 和GPU 计算,更快展现 优化的方向有两个: * **减少页面体积,提升网络加载** * **优化页面渲染** ### 减少页面体积,提升网络加载 * 静态资源的压缩合并(JS 代码压缩合并、CSS 代码压缩合并、雪碧图) * 静态资源缓存(资源名称加 MD5 戳) * 使用 CDN 让资源加载更快 ### 优化页面渲染 * CSS 放前面,JS 放后面 * 懒加载(图片懒加载、下拉加载更多) * 减少DOM 查询,对 DOM 查询做缓存 * 减少DOM 操作,多个操作尽量合并在一起执行(`DocumentFragment`) * 事件节流 * 尽早执行操作(`DOMContentLoaded`) * 使用 SSR 后端渲染,数据直接输出到 HTML 中,减少浏览器使用 JS 模板渲染页面 HTML 的时间 ### 详细解释 #### 静态资源的压缩合并 如果不合并,每个都会走一遍之前介绍的请求过程 ~~~ <script src="a.js"></script> <script src="b.js"></script> <script src="c.js"></script> ~~~ 如果合并了,就只走一遍请求过程 ~~~ <script src="abc.js"></script> ~~~ #### 静态资源缓存 通过链接名称控制缓存 ~~~ <script src="abc_1.js"></script> ~~~ 只有内容改变的时候,链接名称才会改变 ~~~ <script src="abc_2.js"></script> ~~~ 这个名称不用手动改,可通过前端构建工具根据文件内容,为文件名称添加 MD5 后缀。 #### 使用 CDN 让资源加载更快 CDN 会提供专业的加载优化方案,静态资源要尽量放在 CDN 上。例如: ~~~ <script src="https://cdn.bootcss.com/zepto/1.0rc1/zepto.min.js"></script> ~~~ #### 使用 SSR 后端渲染 可一次性输出 HTML 内容,不用在页面渲染完成之后,再通过 Ajax 加载数据、再渲染。例如使用 smarty、Vue SSR 等。 #### CSS 放前面,JS 放后面 上文讲述浏览器渲染过程时已经提过,不再赘述。 #### 懒加载 一开始先给为`src`赋值成一个通用的预览图,下拉时候再动态赋值成正式的图片。如下,`preview.png`是预览图片,比较小,加载很快,而且很多图片都共用这个`preview.png`,加载一次即可。待页面下拉,图片显示出来时,再去替换`src`为`data-realsrc`的值。 ~~~ <img src="preview.png" data-realsrc="abc.png"/> ~~~ 另外,这里为何要用`data-`开头的属性值?—— 所有 HTML 中自定义的属性,都应该用`data-`开头,因为`data-`开头的属性浏览器渲染的时候会忽略掉,提高渲染性能。 #### DOM 查询做缓存 两段代码做一下对比: ~~~ var pList = document.getElementsByTagName('p') // 只查询一个 DOM ,缓存在 pList 中了 var i for (i = 0; i < pList.length; i++) { } ~~~ ~~~ var i for (i = 0; i < document.getElementsByTagName('p').length; i++) { // 每次循环,都会查询 DOM ,耗费性能 } ~~~ 总结:DOM 操作,无论查询还是修改,都是非常耗费性能的,应尽量减少。 #### 合并 DOM 插入 DOM 操作是非常耗费性能的,因此插入多个标签时,先插入 Fragment 然后再统一插入 DOM。 ~~~ var listNode = document.getElementById('list') // 要插入 10 个 li 标签 var frag = document.createDocumentFragment(); var x, li; for(x = 0; x < 10; x++) { li = document.createElement("li"); li.innerHTML = "List item " + x; frag.appendChild(li); // 先放在 frag 中,最后一次性插入到 DOM 结构中。 } listNode.appendChild(frag); ~~~ #### 事件节流 例如要在文字改变时触发一个 change 事件,通过 keyup 来监听。使用节流。 ~~~ var textarea = document.getElementById('text') var timeoutId textarea.addEventListener('keyup', function () { if (timeoutId) { clearTimeout(timeoutId) } timeoutId = setTimeout(function () { // 触发 change 事件 }, 100) }) ~~~ #### 尽早执行操作 ~~~ window.addEventListener('load', function () { // 页面的全部资源加载完才会执行,包括图片、视频等 }) document.addEventListener('DOMContentLoaded', function () { // DOM 渲染完即可执行,此时图片、视频还可能没有加载完 }) ~~~ #### 性能优化怎么做 上面提到的都是性能优化的单个点,性能优化项目具体实施起来,应该按照下面步骤推进: 1. 建立性能数据收集平台,摸底当前性能数据,通过性能打点,将上述整个页面打开过程消耗时间记录下来 2. 分析耗时较长时间段原因,寻找优化点,确定优化目标 3. 开始优化 4. 通过数据收集平台记录优化效果 5. 不断调整优化点和预期目标,循环2~4步骤 性能优化是个长期的事情,不是一蹴而就的,应该本着先摸底、再分析、后优化的原则逐步来做。