浏览器相关

120 阅读3分钟

浏览器体系

一、浏览器运行态下的JS

BOM、DOM、ECMAScript

    (function(context,undefined){
        const _class = ['js','browser','vue'];
        
        //向全局中挂载
        window.classArr = _class.map(item => item)
        //获取当前页面地址
        const _url = location.href
        
        // 设置tab标签
        document.title = 'hhhhhh';
        
        //获取渲染节点
        document.getElementById('#app');
    })(this)
    
    
    

浏览器JS的执行态

ECMAScripr - 基础逻辑、数据处理

DOM - 对于浏览器视窗内,文本的相应操作

BOM - 对于浏览器本身区域能力的处理

二、 BOM

1. location

  • location.href => 'juejin.cn/editor/draf…' => 路径栏所有东西
  • location.orgin => 'juejin.cn'
  • location.protocol => 'https:'
  • location.host => 'juejin.cn'
  • location.port => ''
  • location.pathname => '/editor/drafts/7232225892214259749'
  • location.search => '?a=1'
  • location.hash => '#hahah'
  • location.assign('') =>跳转到指定path => 替换pathname
  • location.replace('') => 同上,同时替换浏览历史
  • location.reload()
  • location.toString() =>产出当前地址字符串

2. history

  • history.state => 存储当前页面的状态
  • history.pushState()
  • history.replaceState()

3. navigator

  • 浏览器系统信息大集合
    navigator.userAgent //获取用户当前环境信息

4. screen

表征显示区域 - 电脑荧幕

window 视窗判断:

全局入口处:

    window.innerWidth 

    window.innerHeight
    
文本处获取:

    document.documentElement.clientHeight
    
    document.documentElement.clientWidth
    
    document.body.clientHeight
    
    document.body.clientWidth
    

网页视图的size => offsetHeight = clientHeight + 滚动条 + 边框

    document.documentElement.offsetHeight
    
    document.documentElement.offsetWidth
    
    document.body.offsetHeight
    
    document.body.offsetWidth
    

动态定位:

scrollLeft / scrollTop   - 距离常规左/上滚动距离
offsetLeft / offsetTop  -  - 距离常规左/上距离
el.getBoundingClientRect()
    el.getBoundingClientRect().top
    el.getBoundingClientRect().left
    el.getBoundingClientRect().bottom
    el.getBoundingClientRect().right
  • el.getBoundingClientRect()在老版本的IE里会多出两个像素

三、Event事件模型

    <div id='app'>
        <p id='dom'></p>
    </div>
    
    //冒泡 - ms: p => div => body => HTML =>document 
    //捕获 - ns:document => HTML => body => div => p
    el.addEventListener(event,function,useCapture) //默认是捕获false
        
    //阻止事件的传播
    event.stopPropgation() //阻止任何传递行为,但无法阻止默认事件
    event.preventDefault() //阻止默认事件 - 例如a标签点击会跳出来
    event.stopImmediatePropgation()//相同节点绑定多个同类事件
    

兼容性 & 性能(IE - attachEvent VS addEventListener)

区别:

  1. 传参:attachEvent 对于事件名需要加一个‘on’
  2. 执行顺序:attachEvent 后绑定先执行;addEventListener 先绑定先执行
  3. 解绑detachEvent VS removeEventListener
  4. 阻断:e.cancelBubble VS event.stopPropgation()
  5. 默认事件拦截:event.returnValue VS event.preventDefault()
    class bindEvent{
        constructor(element){
            this.element = element
        }
        //绑定
        addEventListener = (type, handler) =>{
            if(this.element.addEventListener){
                this.element.addEventListener(type, handler,false)
            }else if(this.element.attachEvent){
                this.element.attachEvent('on' + type,()=>{
                    handler.call(element);
                })
            }else{
                this.element['on' + type] = handler
            }
        }
        //解绑
        removeEventListener= (type, handler) =>{
            if(this.element.removeEventListener){
                this.element.removeEventListener(type, handler,false)
            }else if(this.element.detachEvent){
                this.element.detachEvent('on' + type,()=>{
                    handler.call(element);
                })
            }else{
                this.element['on' + type] = null;
            }
        }
        //阻断
        static stopPropgation(e){
            if(e.stopPropgation){
                e.stopPropgation()
            }else{
                e.cancelBubble = true
            }
        }
        //默认拦截
        static preventDefault(e){
            if(e.preventDefault){
                e.preventDefault()
            }else{
                e.returnValue = false
            }
        }
    }

性能优化 - 事件代理

    <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
        <li>6</li>
        <li>7</li>
    </ul>
    <div class="content"></div>
    var list = document.querySelector('list');
    var li = list.getElementsByTagName('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 liList = this.querySelectorAll('li');
            ……
        }
    }
    
    list.addEventListener('click',onClick,false)

四、网络层

    //实例化
    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:请求已经完成
    xhr.onreadystatuschange = () => {
        if(xhr.readyStatus === 4){
            //判断http状态码
            if(xhr.status >= 200 && xhr.status <300 || xhr.status ==304){
                
            }
        }
    }
    
    xhr.timeout = 3000
    xhr.ontimeout = () => {
        //超时……
    }
    
    //封装
    ajax({
        url:'reqUrl',
        method:'get',
        async:true,
        timeout:3000,
        data:{
            payload :'text'
        }
    }).then(
        res =>{}
        err =>{}
    )
    
    //实现
    function ajax(options){
        const {
            url,
            method,
            async,
            data,
            timeout
        } = options;
        
        const chr = new XMLHttpRequest()
        //配置超时时间
        if(timeout){
            xhr.timeout = timeout;
        }
        
        return new Promise((resolve,reject) => {
            //成功
            xhr.onreadystatuschange = () => {
                if(xhr.readyStatus === 4){
                    //判断http状态码
                    if(xhr.status >= 200 && xhr.status <300 || xhr.status ==304){
                        resolve(xhr.responseText)
                    }else{
                        reject()
                    }
                }
            }
            //失败
            xhr.onerror = err => reject(err);
            xhr.ontimeout = () => reject('timeout');
            
            //传参处理
            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
            }
            
            //建立连接
            xhr.open(method,url,async)
            
            //发送请求
            if(method === 'get'){
                xhr.send(null)
            }else{
                xhr.setRequestHeader(
                    'content-type':'application/x-www-form-urlencoded'
                )
                xhr.send(encodeData)
            }   
        })
    }

五、浏览器原理

从url输入到页面展示发生了什么(本篇只聊获取到资源到渲染出页面)

DOM

CSSOM:css解析成树形数据结构

Render Tree:DOM + CSSOM生成树

Layout module:计算Render Tree 每个节点具体的状态和位置

Painting: 呈现到屏幕上

流程

url => HTML解析( JS + DOM +CSSOM )=> render tree / JS + css执行 => layout => painting

纵向切分

bytes(62 48 65 2C ……) => characters(<html ></html>) => Tokens(tag tree) =>Nodes(html|head|body) => DOM | CSSOM