浏览器工作原理

79 阅读4分钟

一、浏览器JS的执行态

  1. ECMAScript - 基础逻辑、数据处理
  2. DOM - 对于浏览器是视窗内,文本的对应操作
  3. BOM - 对于浏览器本身区域能力的处理

二、BOM

1. location

  • .href => 'www.baidu.com/search?clas…' => 路径栏所有
  • .orgin => 'www.baidu.com' => 域名部分
  • .protocol => 'https:' => 协议部分
  • .host => 'www.baidu.com' => 域名不包括协议部分
  • .port => '' => 端口号
  • .pathname => '/search' => 域名之后的路径名
  • .search => '?class=1' => 搜索部分
  • .hash => '#comment' => 哈希值

方法

  • assign('') // 跳转到制定path => 替换pathname
  • replace('') //不仅替换pathname,同事替换浏览历史(浏览器不能返回)
  • reload('') //刷新
  • toString() //产出当前地址字符串

2. history

  • history.state => 储存当前页面的状态

浏览器不会刷新,刷新了模板区域

  • history.pushState()
  • history.replaceState()

路由方向history和hash的模式的区别

  1. hash 模式带有 # 较丑,history 模式较简洁
  2. pushState 设置的新 URL 可以是与当前 URL 同源的任意 URL;而 hash 只可修改 # 后面的部分,故只可设置与当前同文档的 URL
  3. pushState 设置的新 URL 可以与当前 URL 一模一样,这样也会把记录添加到栈中;而 hash 设置的新值必须与原来不一样才会触发记录添加到栈中
  4. pushState 通过 stateObject 可以添加任意类型的数据到记录中;而 hash 只可添加短字符串
  5. pushState 可额外设置 title 属性供后续使用
  6. hash 兼容IE8以上,history 兼容 IE10 以上
  7. history 模式需要后端配合将所有访问都指向 index.html,否则用户刷新页面,会导致 404 错误

3.navigator

浏览器系统信息集合
  • userAgent //获取用户的环境信息
    • 读取信息 => 浏览器兼容、上报信息
    • 剪切板、键盘

4.screen

表征显示区域 - 荧幕

判断区域大小:

  • window视窗判断:

    • 全局入口处:
      • window.innerHeight
      • window.innerWidth
    • 文本处获取:
      • document.documentElement.clinetHeight
      • document.documentElement.clinetWidth
  • 网页视图的size -> offsetHeight = clientHeight + 滚动条 + 边框:

    • document.documentElement.offsetHeight
    • document.documentElement.offsetWidth
  • 动态定位:

    • scrollLeft / scrollTop -距离常规左 / 上滚动的距离
    • offsetLeft / offfsetTop -距离常规左 / 上的距离
  • el.getBoundingClientRect().

    • el.getBoundingClientRect().top
    • el.getBoundingClientRect().left
    • el.getBoundingClientRect().bottom
    • el.getBoundingClientRect().right

兼容性问题 IE会多出来2像素

三、 Event事件模型

    <div id='app'>
        <p id='dom'/>
    </div>
    
    // 冒泡 -ms: p => div => body => HTML => document
    // 捕获 -ns: document => HTML => body => div => p
    
    el.addEventListener(event, function, useCapture) //默认false (冒泡)
    
    // 追问
    // 1. 如何阻止事件的传播
    event.stopPropogation() 阻止冒泡 (阻止传递行为 无法阻止默认事件)
    event.preventDefault() 阻止捕获、默认事件
    event.stopImmediatePropagation() 相同节点绑定多个同类事件 会立刻阻止该节点上的所有同类事件
    
   // 兼容性
   // 2. 手写兼容性事件绑定
   // IE -attachEvent vs addEventListener
   // 区别
   // a. 传参 attachEvent 事件名需要加上'on'
   // b. 执行顺序 attachEvent:后绑定先执行 addEventListener:先绑定先执行
   // c. 解绑: detchEvent vs removeEventListener
   // d. 阻断 event.cancelBubble vs event.stopPropogatio
   // e. 默认事件拦截 event.returnValue vs event.preventDefault()
   
   // 3.性能优化 - 事件代理
   <ul class='list'>
       <li>1</li>
       <li>1</li>
       <li>1</li>
       <li>1</li>
   </ul>
   
   var list = document.querySelector('list');
   var li = list.getElementsByTagName('li');
   
   // 硬碰硬 动态列表或者很多li性能有问题
   for(var n = 0; n < li.length; n++){
       li[n].addEventListener('click', function(){});
   }
   
   // 代理后 - 利用事件传递
   function onClick(e){
       var e = e || window.event;
       if(e.target.nodeName.toLowCase() === 'li'){
           var list = this.querySelevctiorAll('li');
           // 业务逻辑 对比点击的是哪个li
       }
   }
   list.addEventListener('click', onClick);

四、网络层

    // 实例化
    const xhr = new XMLHttpRequest();
    
    // 初始化建立
    xhr.open(method, url, async) // get/post; 请求地址; 是否为异步请求
    
    // 方法的发送请求 - send
    xhr.send(data) // get - 可以不传火传入null, post -encodeURIComponent编码拼接
    
    // 接收
    // xhr.readyStatus: 0 - 尚未建立open; 1 - 已经调用open; 2 - 已经调用send; 3 - 已经收到请求返回的数据; 4 - 请求已经完成。   经常用4判断, 为什么用4
    
    xhr.onreadystatechange = () => {
        if(xhr.readyStatus === 4){
            // 判断http状态码
            if(xhr.status >= 200 && xhr.status < 300 || xhr.status === 300){
                //接口返回成功
            }
        }
    }
    
    // 超时时间
    xhr.timeout = 30000;
    xhr.ontimeout = () => {
        //超时后
    }
    
    // 封装手写
    ajax({
        url: 'reqUrl',
        methor: 'get',
        async: true,
        timeout: 30000,
        data: {
            payload: 'text'
        }
    }).then(
        res => {}
        err => {}
    )
    
    // 实现手写
    function ajax(options){
        const {
            url, 
            method,
            async,
            data,
            timeout
        } = options;
        
        const chr = new XMLHttpRequest();
        
        // 配置超时时间
        
        if(timeout){
            chr.timeout = timeout;
        }
        
        return new Promise((resolve, reject) => {
            // 成功
            chr.onreadystatechange = () => {
                if(chr.readyStatus === 4){
                // 判断http状态码
                if(chr.status >= 200 && chr.status < 300 || chr.status === 300){
                    //接口返回成功
                    resolve(chr.responseText)
                } else {
                    reject && reject();
                    }
                }
            }
            
            // 失败
            chr.onerror = err => reject(err)
            
            // 传参处理
            let _params = [];
            let encodeData = '';
            if(data instanceof Object){
                for(let key in data){
                    // 参数编码
                    _params.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
                }
                encodeData = _params.join('&')
            }
            
            // method 判断连接
            if(method === 'get'){
                const index = url.indexOf('?');
                if(index === -1){
                    url += '?';
                } else if(index !== url.length -1){
                    url += '&';
                }
                url += encodeData;
            }
            
            // 建立连接
            chr.open(methor, url, async);
            
            // 发送请求
            if(method === 'get'){
                xhr.send(null);
            } else {
                xhr.setRequestHeader(
                    'content-type': 'application/x-www-form-urlencoded'
                )
                xhr.send(encodeData)
            }
        })
    }

面试方向

  1. TCP => HTTP/HTTPS
  2. 状态码 => 2xx(成功) 4xx(客户端出错) 5xx(服务端错误) | 3xx => 浏览器缓存 => 强缓存(Expires + cache-control) / 协商缓存 (last-modified + Etag)
  3. content-type => 内容类型 => 浏览器 => ff chrome

五、浏览器原理

面试题: 从url输入到页面展示发生了什么 - 获取到资源 =》 渲染出页面

浏览器渲染流程
  1. DOM
  2. CSSOM - CSS解析成树形数据结构
  3. Render Tree: DOM + CSSOM生成树
  4. Layout module: 计算Render Tree每个节点具体的状态和位置
  5. Painting: 呈现到屏幕上

URL => HTML解析 - JS + DOM + CSSOM => render tree / JS + css执行 => layout module => painting

纵向切分

bytes => characters() => Tokens(解析成抽象语法树 tag tree) => Nodes(html|head|body) characters => Tokens 解析成计算机读得懂的语言