(3)JS性能优化

194 阅读5分钟

**本文已参加[新人创作礼]活动,一起开启掘金创作之路。
**

前言

      本系列主要是前端高阶课程的笔记整理,所以在讲述知识点的同时会延伸映射一些面试题,内容之间有点跳跃,但是全文的知识点还是循序渐进的。

今天主要围绕那道非常经典的面试题展开--从输入URL到页面展示经历了什么
在这里置个臀,先放一张今天文章的轴图

输入URL

或许你会愿意看回我的上一篇的文章,这样你就能更清楚知道URL和URI 的区别。

url :资源定位符

作为url,通常有三种形式(当然不是说只有这三种)

http:honggege.com   //http协议  [1]追问:http 与TCP有什么关系或者区别
https://honggege.com  
//file///C:Users/class/honggege/document //本地文件目录地址,只在本地打开不涉及网络

面试题 :

  • http与TCP之间的关系

   Q: 首先直面回答面试官问题   
1.http是传输层协议,而TCP是应用层的
http -- 传输层     TCP -- 应用层     2.两者的关联: http是基于TCP实现连接的
3.两者的差异:http是无状态连接,而TCP是有状态的

还记得我们今天的主题是什么吗,性能优化,那么我们如何从以上这几点进行性能优化呢

优化

1.TCP有1.0 1.1 2.0 版本,我们优先使用2.0版本

2.根据自身项目需要,选择UDP 协议(面向连接传输,保障速度) 或者TCP协议(面向连接确认,更准确),当然这个是后端大佬的事
3.打开TCP的keep-alive -- 保持TCP 的连续畅通,不用反复地建立连接(只在TCP1.1协议中有)
2.0比较特殊,是因为2.0可多条并发请求复用同一条通路,即复用通路,无并发限制,而1.1不可以

4.使用socket连接. socket是封装化的TCP。让我们的应用,更加方便地使用调用

追问: http和https

1.HTTPS = http + SSL(TLS) => 位于TCP协议与各种应用层协议之间

2.实现原理,可看回文章开头的原理图
你也可以通过这一篇文章再进一步了解https --<看完这篇 HTTPS,和面试官扯皮就没问题>

3.https多次连接 :导致网络请求加载时间延长;增加开销和功耗

优化

合并请求和长连接

域名解析

1.浏览器缓存中-首先会去我们的浏览器缓存中去找我们的DNS信息 -- 浏览器中会缓存DNS信息一段时间

2.系统缓存-如果没有找到,会从系统中找缓存,如果系统中没找到,就会去HOST中找,而我们平时说的"改HOST"就是在改我们服务器的ip跟我们实际请求的服务地址的映射

3.如果系统缓存也没有的话,我们会在路由器缓存去找 -- 各级路由器缓存域名信息

4.运营商地方站点的缓存信息 (80%都能在这层面找到了)

5.实在没有就会知道根域名服务器上了

优化

CDN-- Content Delivery Network

1.为同一个主机配置多个IP地址

2.LB --负载均衡

面试官跟你聊CDN的时候,其实是在跟你聊缓存

在现实中的一些开发场景;用户在打开页面的时候有时候打开的是一个老的页面,那是因为我们在部署静态资源的时候,我们会放在不同的机器上,用户因为负载均衡的规则去命中,去分配到不同的机子,导致有些用户没有更新到所有的点,所以才会说叫用户去清除缓存,重新加载一遍,问题就在于此.

web服务器

解析完域名之后,终于到了web端服务器

在这个阶段主要有这些知识点

    // apache、ngnix
    // 1. 接收请求 => 传递给服务端代码
    // 2. 通过反向代理 => 传递给其他服务器
    // 3. 不同域名 => 指向相同ip的服务器 => ngnix域名解析 => 引导到不同的服务监听端口

服务器 涉及到网络优化

面试题: 手写并发 -QPS(涉及后端知识,五年经验题)

// 面试:并发优化 10个请求,由于后台或者业务需求只能同时执行三个

还是那句话,在遇到手写代码面试题时,分析思路更重要,在写代码钱可以先把思路先写下来

输入条件: promise数组 、limit数组

//存储;reqpool -并发池

思路:塞入 + 执行 ,(塞入:往并发池里面添加,直到超出我们限制的数量;执行:当数量到了,并发池满了我们就执行方法)

    function qpsLimit(requestPipe, limitMax = 3) {
        let reqPool = []
        let reqMap = new Map()

        // 往并发池里塞入promise
        const add = () => {
            let _req = requestPipe.shift()

            reqPool.push(_req)
        }
    
        // 执行实际请求
        const run = () => {
            if(requestPipe.length === 0) return
            // 池子满了发车后,直接race
            let _finish = Promise.race(reqPool)

            _finish.then(res => {
                // 做一个id整理
                let _done = reqPool.indexOf(_finish)
                reqPool.splice(_done, 1)
                add()
            })
            run()
        }

        while(reqPool.length < limitMax) {
            add()
        }
        run()
    }

浏览器渲染时

浏览器执行顺序 主线:HTML => DOM + CSSOM => renderTree + js => layout => paint 支线: repaint(重绘) - 改变文本、颜色等 展示 reflow(重排或回流) - 元素几何尺寸变了

=> 优化点:减少repaint,避免reflow

reflow => display: none => reflow; visibility:hidden; => repaint

这就是为什么我们平时在说性能优化时说"减少对DOM的操作",其实就是减少repaint

脚本执行时 - JS

mark & sweep => 触达标记,锁定清空、未触达直接抹掉(垃圾回收机制:js大部分都帮我们做了)

    // 内存分配:申明变量、函数、对象
    // 内存使用:读写内存
    // 内存释放

    const zhaowa = {
        js: {
            performance: 'good',
            teacher: '云隐'
        }
    }

    // 建立引用关系
    const _obj = zhaowa

    // 引用源给替换掉了 - 暂未gc
    zhaowa = 'best'

    // 深入层级做引用 - 暂未gc
    const _class = _obj.js

    // 引用方替换 - 暂未gc
    _obj = 'over'

    // gc 完成
    _class = null
    
    // => 1. 对象层级,宜平不宜深 2. 深层引用最好深拷贝,或者用完直接销毁 3. 避免循环引用
    function traverseTree(node1, node2) {
        node1.parent = node2;
        node2.children = node1;
    }

    // 内存泄露
    // 莫名其妙的全局变量
    function foo() {
        bar1 = ''
        this.bar2 = ''
    }

    // 未清理的定时器
    setInterval(() => {
    }, 1000)

    // 使用后的闭包
    function zhaowa() {
        const _no = 1
        return {
            number: _no
        }
    }

打包配置优化

    // 1. 懒加载 - 非必要不加载
    // 2. 按需引入 - 非必要不引入
    // 3. 抽离公共 - 相同项目合并公用