常见浏览器对象和常见api的使用

525 阅读8分钟

常见浏览器 JS 对象和 常见API的 使用

浏览器的体系

不仅是执行环境、而且是用户交互操作的一个应用

浏览器模型

提供了独立于内容、可以与浏览器窗口进行交互的对象结构。

主要对象有:

1.window 对象, js访问浏览器的接口

2.location 对象: 提供当前窗口的加载有关的信息和一些导航功能

3.navigation 获取浏览器的系统信息

4.screen 用来表示浏览器外部窗口的显示器信息

5.history 保存用户上网的浏览信息

window 对象

window 对象是整个浏览器对象模型的核心,其中扮演即使接口又是全局对象的角色

    alert()
    confirm()
    prompt()

    open() // 打开一个新的页面

    onerror()  // 前端监控js的报错
    addEventListnner('error')

    setTimeout()
    setInterval()

    - 窗口位置 (浏览器相对于屏幕的位置的一些操作,不常用)
     screenLeft
     screenTop
     screenX
     screeY
     moveBy(x,y)
     moveTo(x,y)

    - 窗口大小
     innerWidth
     innerHeight

     outerWidth
     outerHeight
     resizeTo(x,y)

    - location 
     hash  url的hash值
     host  服务器名称+端口
     hostname 只含服务名字
     herf     当前加载页面完整的url
     search 参数

    - history
     保存用户上网信息
     go() 
     back() 后退
     forword() 前进
     length

    - document
      document 表示当前页面
      document.cookie 
      querySelector
      querySelectorAll
    
    - e 事件监听的e
      e.clientX: 鼠标的坐标到页面左侧的距离
      e.clientY: 鼠标的坐标到页面顶部的距离

      e.offsetX:鼠标坐标到元素的左侧的距离
      e.offsetY:鼠标坐标到元素的顶部的距离

      e.clientWidth:可视区域的宽度
      e.clientHeight:可视区域的高度

      e.target.offsetLeft: 该元素外边框距离包含元素内边框左侧的距离 
      e.target.offsetTop:该元素外边框距离包含元素内边框顶部的距离

      e.target.offsetWidth: width + padding-left + padding-right + border-left + border-right
      e.target.offsetHeight: height + padding-top + padding-bottom + border-top + border-bottom

      element 可以获取  offsetLeft offsetTop  offsetWidth offsetHeight

location

  • 操作方法
location.herf
     .origin
     .host
     .port
     .pathname
     .search
     .hash
     
      .assign() // 跳转到指定的path,替换pathname
      .replace() // 跳转到指定的path,会替换历史
      .reload()
  • 如果需要跳转到第三方路由,需要返回如何解决?

    携带参数跳转

  • url处理

    qs库或者自己封装

history

重要的api:

history.pushState(state,title,url); 往路由栈里面添加一个记录;适用于跳转路由 history.replaceState(state,title,url); 替换当前路径,不发生刷新;适用于在路径上动态添加删除参数

<body>
<a href="toA()">A页面</a>
<a href="toB()">B页面</a>
<div id="app"></div>
<script>
  function render() {
    app.innerHTML = window.location.pathname
  }
  function toA() {
    history.pushState({},null,'/a');
  }

  function toB() {
    history.pushState({},null,'/b')
  }
  window.addEventListener('popstate', render)
  render()
</script>
</body>

hash

#后面代表就是hash, 通过点击,在路径上动态改变hash,通过监听hashchange 事件,做一些处理; 不会刷新路由;

 <body>
  <a href="#/a">A页面</a>
  <a href="#/b">B页面</a>
  <div id="app"></div>
  <script>
    function render() {
      app.innerHTML = window.location.hash
    }
    window.addEventListener('hashchange', render)
    render()
  </script>
</body>

history vs hash 单页面路由原理

hash和history实现前端路由优缺点:

hash:

  • 兼容低版本浏览器,

  • 只有#符号之前的内容才会包含在请求中被发送到后端,也就是说就算后端没有对路由全覆盖,但是不会返回404错误

  • hash值的改变,都会在浏览器的访问历史中增加一个记录,所以可以通过浏览器的回退、前进按钮控制hash的切换

  • 会覆盖锚点定位元素的功能

  • 不太美观,#后面传输的数据复杂的话会出现问题

history:

  • 使用简单,比较美观
  • 因为是h5的新api,会有兼容性
  • 需要服务器支持,刷新可能出现404(路径要和服务端一致)

浏览器事件模型

浏览器事件冒泡和捕获

捕获阶段 ---> 目标阶段 ----> 冒泡阶段

捕获: window ---> body ---> div
冒泡: div ---> body ---> window

1.如何监听捕获阶段 和冒泡阶段

监听事件的第三个参数,true:捕获阶段 false :冒泡阶段

  <div>
    <son>div</son>
  </div>
    div.addEventLister('click',function(e) {
      // .target.nodeName 当前点击的元素   点了son son ; 点击div 是div
      // 绑定事件监听的元素 e.currentTarget.nodeName   div
      console.log(e.target.nodeName,e.currentTarget.nodeName)
    },true)
    son.addEventLister('click',function(e) {
      console.log(e.target.nodeName)
    },true)
    son.addEventLister('click',function(e) {
      console.log(e.target.nodeName)
    })
    div.addEventLister('click',function(e) {
      console.log(e.target.nodeName)
    })
  1. 如何阻止事件的传播 e.stopPropagation(); // 阻止的是事件的传播,可以阻止冒泡,也可以阻止捕获

    在捕获阶段:使用这个的时候,向下的往后的捕获阶段不执行了,对应的冒泡也不会执行了

    在冒泡阶段:使用,不会向上冒泡了,但是完整的捕获阶段会执行

  2. 场景

  • 一个页面上,有很多元素,div ,p ,button 等,每个元素上都有自己的绑定事件,都不相同; 当用户进入页面,有个状态bannd, true :表示封禁,提示封禁; false 表示,不做处理。 当点击任何位置都提示

       window.addEventLister('click',function(e) {
         if(bannd) {
           e.stopPropagation();
           alert('你被封禁了')
           return 
         }
       },true)
    

    全局封禁适用什么场景?

    游客未登录;接口风控处理;替换蒙层(一般不需要,因为后台会有检验);低成本的人机校验

  • 事件代理

  1. 第一个好处是效率高,比如,不用for循环为子元素添加事件了
  2. 第二个好处是,js新生成的子元素也不用新为其添加事件了,程序逻辑上比较方便 举例:实现一个多选功能,可不选
 <div id ='list'>
    <span class="item">1</span>
    <span class="item">2</span>
    <span class="item">3</span>
    <span class="item">4</span>
 </div>
 <script>
   const listId = document.getElementById('list');
   const itemList = document.querySelectorAll('item');
   listId.addEventListener('click',function(e) {

     if( e.target.style.background === 'red') {
       e.target.style.background = '#fff';
       return
     }
     if(e.target.nodeName.toLowerCase() === 'span') { // 加判断是为了防止点击到list,导致全部变红色,变成小些是为了防止浏览器兼容性
           e.target.style.background = 'red';
     }
   })
 </script>
  1. 哪些事件有冒泡事件? onkeydown onclick onkeyup等

不会冒泡的:onmouseenter onmouseleve onblur onfocus onscroll onresize 等

阻止默认行为

e.preventDefault(); 拖拽一个图片到页面,会自动打开; 点击按钮,自动提交表单; 点击跳转;都算默认行为

阻止事件区分

 event.stopPropgation(); // 阻止事件行为,无法阻止默认事件
 event.preventDefault();// 阻止默认事件
 event.stopImmediatePropgation();// 相同节点绑定的多个同类事件

事件兼容性

  // 手写事件兼容性
  // IE attachEvent vs addEventListener
  // 区别:
  // a 传递参数:attachEvent 对于事件名字要加上on
  // 执行顺序: attachEvent 后绑定的事件先执行; addEventListener 先绑定先执行
  // 解绑: detachEvent vs removeEventListener
  // 阻断: event.cancleBubble = true  vs event.stopPropgation
  // 默认事件拦截 event.returnValue = false  vs event.preventDefault()

        class bindEvent {
          constructor(ele) {
            this.ele = ele;
          }
          addEventListener(type,handler) {
              if(this.ele.addEventListener) {
                this.ele.addEventListener(type,handler,false);
              } else if(this.ele.attachEvent) {
                this.ele.attachEvent([`on${type}`],() => {
                  handler.call(ele);
                })
              } else {
                this.ele[`on${type}`] = handler;
              }
          }
          removeEventListener(type,handler) {
              if(this.ele.removeEventListener) {
                this.ele.removeEventListener(type,handler,false);
              } else if (this.ele.detachEvent) {
                  this.ele.detachEvent(`on${type}`,() => {
                    handler.call(ele);
                  })
              } else {
                this.ele[`on${type}`] = null;
              }
          }

          static stopPropgation(e) {
              if(e.stopPropgation) {
                e.stopPropgation();
              } else {
                e.cancleBubble  =false;
              }
          }

          static preventDefault(e) {
              if(e.preventDefault) {
                e.preventDefault();
              } else {
                e.returnValue = false;
              }
          }

        }  

从浏览器输入到页面完成,一共经历了哪些事情

  • 输入 这个阶段会发生http请求

Url - 资源定位符

  常见的url的形式
  // http://
  // httpls://
  //file:// 指向本地文件

http 与 tcp是什么?

  • http 是应用层网络协议; tcp是传输层的协议;

  • http是基于tcp的建立连接, http的一次请求,会发生tcp发送、断开 优化点:1.0 1.1 2.0 UDP:发送的动作,更快(不保证是发送成功的) tcp:保证发送是正确的(三次握手和四次挥手)

     keep-alive 可以保持Tcp的联系畅通,不同反复建立连接;
    
     1.1 会一条发送,结束会进行下一次连接发送(chrome最大6条);
    
     2.0 多条并发请求,复用同一条通路,无并发限制
    
  • http是无状态的连接(每次的连接是相互不干扰的) ; Tcp是有状态的

http 和 https 的区别?

   1.http 无状态明文传输,速度更快;https加密传输,速度慢一些

   2.http 无使用成本;https 需要第三方证书,需要额外付费

   3.http 默认端口80,https默认是443

   https = http + ssl(tls) => 位于tcp协议与各种协议应用层协议之间

协商缓存和强缓存

命中强缓存:expries cache-control
协商缓存:Last-Modified(if-modified-since) etag(if-none-match)

状态码

304 命中缓存
403 禁止访问
404 请求地址不对或者无内容
  • 域名解析(ARP)

    1. ip 和 域名是有对应关系的,放到浏览器缓存中,缓存DNS一段时间,先从浏览器中找
    2. 如果找不到就会找系统缓存,系统中找缓存 (举例改变host指向)
    3. 如果找不到,就从各种路由器缓存域名信息
    4. 如果找不到,就会从地方站点的缓存信息
    5. 如果找不到,从跟域名服务器找

    优化:CDN(Content Delivery Network) 1. 为同一个主机配置多个ip地址 2. 负载均衡

  • 浏览器渲染

浏览器渲染时,浏览器执行顺序

主线:HTML => DOm +Cssom => renderTRee + js => layout => paint

支线: repaint(重绘) 改变文本,颜色等 reflow(回流,重排) 元素尺寸改变了 优化:减少repaint ,避免reflow

  • js 脚本执行时

    Event Loop

    GUI绘制等

手写并发

场景:优化并发,一个页面可能有20个请求,如果同时发送,可能对服务端造成压力,如果同步等待执行,又比较耗时。现在要求一次只能请求3个。

封装请求

ajax在浏览器不刷新的情况下,可以跟服务端进行数据交换。封装一个通用的请求。

   // 使用
    ajax({
    url:"url",
    method:'get',
    async:true,
    timeout:3000,
    data:{

    }
  }).then(res ={},err => {})
  
//  封装:
   function ajax(options={}) {
    const {url,method,async,timeout,data} = options;
     const xhr = new XMLHttpRequest();
    return new Promise((resolve,reject) => {
       // 监测成功
       xhr.onreadystatuschange =() => {
            // 当服务端全部返回时处理
           if(xhr.readyStatus ===4 ) {
             if(xhr.status >= 200 && xhr.status <300 || xhr.status === 304) { // 状态码
               resolve(xhr.responseText);
             } else {
               reject('数据未拿到');
             }
           }
       }
       
       // 检测失败
       xhr.onerror =(err) => reject(err);

       // 监测超时
       xhr.ontimeout = () => reject('超时了');
  
       // 要对参数进行处理
       let sendData = data;
       if(method === 'get') {
           let getArr = [];
           (Object.keys(data) || []).forEach(item => {
             getArr.push(`${item}=${data[item]}`)
           });
           senData = getArr.join('&');

           if(url.includes('?')) {
               url +=senData;
           } else {
               url +=`?${senData}`
           }
       } 
       
       // 建立连接
           xhr.open(method,url,async);
       });
      // 发送数据
      xhr.send(sendData);
  }