前端内功修炼之浏览器事件模型、ajax和fetch api | 8月更文挑战

224 阅读4分钟

浏览器对象模型

BOM:Browser Object Model(浏览器对象模型),浏览器模型提供了独立于内容的,可以与浏览器窗口进行互动的对象结构。主要的API:

1) window: 是BOM的核心,是js访问浏览器的接口
    常见方法:alert、confirm、prompt、open、onerror、setTimeout、setInterval
2) location对象:提供当前窗口中加载的文档有关的信息和一些导航功能
    常见方法:hash、host、hostname、href、port、protocol、search
3) navigator对象: 获取浏览器的系统信息
4) screen对象: 表示浏览器显示器的相关信息
    常见方法:screenX, screenY, screenLeft、screenTop, moveBy(x, y), moveTo(x, y)
5) history对象: 存储用户上网的历史记录(重点,vue-router和react-router就是基于这个api实现的)
    常见方法:pushState, onPopState, push, back, forward, go

浏览器事件机制

事件委托/事件代理

执行顺序?

捕获阶段 -> 目标阶段 -> 冒泡阶段 
如果触发的是一个button上的click事件
则执行顺序为(默认情况下是先事件捕获后事件冒泡): window -> body -> button -> body -> window
在js中的addEventListener有三个参数,第三个参数用来控制事件是冒泡的阶段触发,还是在捕获的阶段
触发,默认是false

阻止事件的传播

e.stopPropagation() //符合W3C标准的浏览器

阻止默认行为

e.preventDefault() // 标准浏览器

项目中使用场景

在C端小程序中,某个页面只有当有某种权限的时候才能进行一些交互,这种情况下该如何处理?
方案一:
    给整个页面添加一个蒙层,判断是否有访问权限,若有则正常显示,反之则展示蒙层。这样有一个缺陷就是增加了页面元素。
方案二:
    利用事件委托机制,在最外层的DOM结构上绑定事件,在捕获阶段触发,先判断是否有权限。

解决兼容性问题

class BomEvent {
  constructor (element) {
    this.element = element;
  }

  addEvent (type, handler) {
    if (this.element.addEventListener) {
      this.element.addEventListener(type, handler, false);
    } else if (this.element.attachEvent) {
      this.element.attachEvent(`on${type}`, handler);
    } else {
      this.element[`on${type}`] = handler;
    }
  }

  removeEvent (type, handler) {
    if (this.element.removeEventListener) {
      this.element.removeEventListener(type, handler);
    } else if (this.element.detachEvent) {
      this.element.detachEvent(`on${type}`, handler);
    } else {
      this.element[`on${type}`] = null;
    }
  }
}

function stopPropagation (evt) {
  if (evt.stopPropagation) {
    evt.stopPropagation();
  } else {
    // 兼容IE
    evt.cancelBubble = true;
  }
}

function preventDefault (evt) {
  if (evt.preventDefault) {
    evt.preventDefault();
  } else {
    // 兼容IE
    evt.returnValue = false;
  }
}

ajax和 fetch api详解 !important

原生ajax 基于XMLHttpRequest api发送请求

const xhr = new XMLHttpRequest()

xhr.open('GET', 'https://www.baidu.com')

// 这里需要注意的是: 要先建立状态的监听,然后再使用send方法发送请求
xhr.onreadystatechange = () => {
  // readyState为4时代表请求完成
  if(xhr.readyState !== 4) {
    return
  }
  // 请求成功
  if(xhr.status === 200) {
    console.log(xhr.responseText);
  } else {
    // 请求失败
    console.error('error', xhr.statusText);
  }
}

// 原生ajax自带超时的api
xhr.timeout = 3000

xhr.ontimeout = () => {
  console.log('当前请求已超时');
}

xhr.send()

fetch 也是原生发送请求的api,是基于promise封装的,可以.then、.catch。

fetch('http://www.baidu.com',{
  method: 'GET',
  credentials: 'same-origin'
}).then(response =>{
  if(response.ok) {
    return response.json()
  }
  throw new Error('request error')
}).catch(err =>{
  console.error(err)
})

// 这样问题就来了,基于fetch发送的请求如何实现超时呢?这里需要指出的是fetch原生没有支持超时的api
function fetchTimeout(url, init, timeout = 3000) {
  return new Promise((resolve, reject) =>{
    fetch(url, init).then(resolve).catch(reject)
    setTimeout(reject, timeout)
  })
}

summary: 虽然说发送请求可以使用原生的ajax和fetch,但是实际的企业中项目还是使用axios库来封装发送请求的逻辑比较多。

请求头

method: 请求方法
path: 请求路径
cookie: 与用户信息相关的一些信息
referer: 页面来源
user-agent: 用来对不同的设备做不同的处理

响应头

access-control-allow-origin: *  //浏览器同源的问题
content-encoding: gzip
set-cookie:   //例如:login接口可能返回一些认证成功的信息,就是通过这个响应头的字段种到页面

常见的请求状态

200: get请求成功
201: post请求成功
301: 永久重定向
302: 临时重定向
304: 协商缓存: 避免重复发送相同请求的一种策略
400: 客户端请求参数错误:客户端参数与服务端要求参数不同 导致报错
404: 未找到相应请求地址,可以是客户端地址错误,也有可能是域名还没有部署到生产环境
405: 请求方式错误, get或者post、put等请求方式与后端要求不对应