1、浏览器对象模型
参考资料:知识整理——浏览器对象模型
BOM:Browser Object Model(浏览器对象模型),浏览器模型提供了独立于内容的、可以与浏览器窗口进行滑动的对象结构,就是浏览器提供的API。其主要对象有:
- window对象:BOM的核心,是js访问浏览器的接口,也是ES规定的Global对象
- location对象:提供当前窗口中的加载的文档有关的信息和一些导航功能。既是 window 对象属性,也是document的对象属性
- navigation对象:获取浏览器的系统信息
- screen对象:用来表示浏览器窗口外部的显示器的信息等
- history对象:保存用户上网的历史信息
1、window对象
windows对象是整个浏览器对象模型的核心,其扮演着既是接口又是全局对象的角色
1、window对象的属性和方法
alert(<msg>)/confirm(<msg>)/prompt(<msg>,<val>)
open(url, [target, string, boolean])
/**
* url: 要加载的URL,
* target: 窗口目标
* string: 特定的字符串,以逗号分隔的字符串表示新窗口显示的特性
* boolean: 表示新页面是否取代浏览器历史记录中当前加载页面的布尔值
*/
onerror() // 多用于前端日志和错误采集
/** 事件处理程序,当未捕获的异常传播到调用栈上时就会调用它,并把错误消息输出到浏览器的 JavaScript 控制上。window.onerror(描述错误的一条消息, 字符串--存放引发错误的JavaScript代码所在的文档url, 文档中发生错误的行数)*/
setTimeout(() => {}, value); // 超时调用--在指定的时间过后执行代码
setInterval(() => {}, value); // 间歇调用--每隔指定的时间就执行一次
/** 使用 `setInterval()` 方法的时候, 再不加干涉的情况下, 该方法会一直执行到页面的卸载, 所以一般情况下 `setInterval()` 比较消耗性能。然后 `setTimeout()` 方法可以通过调用自身完成间歇调用的功能。所以说,在一般情况下使用 `setTimeout()` 来完成超时与间歇调用。 */
/** bug:app里嵌入H5时候, setInterval做的倒计时,10次递归 */
2、窗口位置
screenLeft // 窗口相对于屏幕左边的位置, 适用于IE、Safari、Chrome
screenTop
screenX // 窗口相对于屏幕左边的位置, 适用于IE、Safari、Chrome、Firefox
screenY
moveBy(x, y) // 全兼容
moveTo(x, y)
// 跨浏览器获取窗口左边和上边位置
var leftPos = (typeof window.screenLeft == 'number') ? window.screenLeft : window.screenX
var topPost = (typeof window.screenTop == 'number') ? window.screenTop : window.screenY
3、窗口大小
innerWidth // 可见视窗的大小
innerHeight
outerWidth // 浏览器窗口本身的尺寸
outerHeight
resizeTo(width, height)
resizeBy(width, height)
/** 移动IE浏览器:不支持该属性, 当移动IE浏览器将布局视口的信息保存至document.body.clientWidth与document.body.clientHeight中 */
// 获取浏览器视窗大小,可使用 can i use 网站查询
window.innerWidth || document.body.clientWidth
window.innerHeight || document.body.clientHeight
2、location对象
提供当前窗口中的加载的文档有关的信息和一些导航功能。既是 window 对象属性,也是document的对象属性
window.location === document.location // true
location | 对象的主要属性 |
---|---|
hash | #host 返回url中的 hash (#后字符>=0) |
host | juejin.im:80 服务器名称+端口 |
hostname | juejin.im 服务器名称 |
href | 当前加载页面的完整url |
pathname | 返回url的目录和(或)文件名 /book/23sadf234sdf234sd |
port | |
protocol | |
search | 返回url的查询字符串,以问号开头 ?name=haha&age=22 |
location 的应用场景:
1、解析 url 查询字符串参数,并将其返回一个对象,可通过循环、正则来实现,方法有很多,实现的大体思路时:通过 location 的 search 属性来获取当前url传递的参数,如果url中有查询字符串的话就将其问号截取掉,然后再遍历里面的字符串并以等号为断点,使用 decodeURIComponent() 方法来解析其参数 的具体数值,并将其放在对象容器中,并将其返回
2、载入新的文档,也可以说时刷新页面,主要有三个方法:
- **assign(): **location.assign('http://www.xxx.com')就可立即打开新url并在浏览器是我历史中生成一条新的记录,在一个生成了5条浏览记录的页面中,然后使用assign()跳转url后,history记录只剩两条,一条是通过assign跳转的页面,另一条则是上一个页面(使用assign()跳转方法的页面),其余的所有页面都被清除掉了
- **replace(): **location.replace('http://www.bbb.com')只接受url一个参数,通过跳转到的url界面不会在浏览器中生成历史记录,就是history的length不会+1,但是会替代掉当前的页面
- **reload(): **其作用是重新加载当前显示的页面,当不传递参数的时候,如果页面自上次请求以来并没有改变过,页面就会从浏览器中重新加载,如果传递 true ,则会强制从服务器重新加载
3、navigation对象
navigation接口表示用户代理的状态和标识,允许脚本查询它和注册自己进行一些活动,navigation应用场景:
- 检测插件
- 注册处理程序
navigator.onLine: 浏览器是否链接了因特网
4、history对象
go()
back()
forword()
length // 保存历史记录的数量,可用于检测当前页面是否是用户历史记录的第一页(history.length === 0)
5、screen对象
其提供有关窗口显示的大小和可用的颜色输入信息
window.screen.deviceXDPI/deviceYDPI 屏幕实际的水平DPI、垂直DPI
2、浏览器事件捕获和冒泡
浏览器事件模型中的过程主要分为三个阶段:捕获阶段、目标阶段、冒泡阶段。
捕获 -> 目标 ->冒泡
1、第三个参数
window.addEventListener('click', function(e) {
console.log(e.target.nodeName); // 指当前点击的元素
console.log(e.currentTarget.nodeName); // 绑定监听事件的元素
}, false) // 不写默认为false, false 为冒泡, true 为捕获
2、阻止事件传播
- e.stopPropagation()
- 阻止冒泡和捕获阶段的传播
- stopImmediatePropagation() 如果有多个相同类型事件的事件监听函数绑定到同一个元素,当该类型的事件触发时,它们会按照被添加的顺序执行。如果其中某个监听函数执行了event.stopImmediatePropagation()方法,则当前元素剩下的监听函数将不会被执行
3、阻止默认行为
e.preventDefault(): 可以阻止事件的默认行为发生,默认行为是指:点击a标签就转跳到其它页面、拖拽一个图片到浏览器会自动打开、点击表单的提交按钮会提交表单等等,因为有的时候我们并不希望发生这些事情,所以需要阻止默认行为。
4、兼容性
- attachEvent —— 兼容:IE7、IE8;不支持第三个参数来控制在哪个阶段发生,默认是绑定在冒泡阶段
- addEventListener —— 兼容:firefox、chrome、IE、safari、opera;
5、面试题
1、页面为 ul + li 结构,点击每个 li 需 alert 对应的索引
解析:浏览器获取元素方法
- document.getElementById()
- document.getElementByTagName()
- document.querySelector() // css选择符的模式匹配DOM元素,返回单个元素
- document.querySelectorAll() // 返回 nodeList 列表
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul id="ul">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
</ul>
<script>
// 第一种:给每个li绑定事件
const liList = document.getElementsByTagName("li")
for (let i = 0; i < liList.length; i++) {
liList[i].addEventListener('click', function(e) {
alert(`内容为${e.target.innerHTML},索引为${i}`)
})
}
// 第二种,使用捕获绑定事件
const ul = document.querySelector('#ul')
ul.addEventListener('click', function(e) {
const target = e.target
if(target.tagName.toLowerCase() === 'li') {
const liList = this.querySelectorAll('li')
const index = Array.prototype.indexOf.call(liList, target)
alert(`内容为${target.innerHTML},索引为${index}`)
}
})
</script>
</body>
</html>
2、封装一个多浏览器兼容的绑定事件函数
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, function() {
handler.call(element)
})
} else {
this.element['on' + type] = handler;
}
}
removeEvent(type, handler) {
if(this.element.addEventListener) {
this.element.removeEnentListener(type, handler, false)
} else if(this.element.datachEvent) {
this.element.datachEvent('on' + type, handler)
} else {
this.element['on' + type] = null
}
}
}
// 阻止事件(主要是事件冒泡,因为IE不支持事件捕获)
function stopPropagation(ev) {
if(ev.stopPropagation) {
ev.stopPropagation() // 标准w3c
} else {
ev.cancelBubble = true // IE
}
}
// 取消事件的默认行为
function preventDefault(event) {
if(event.preventDefault) {
event.preventDefault() // 标准w3c
} else {
event.returnValue = false // IE
}
}
3、网络请求
1、XMLHttpRequest对象
- 方法
- open(请求方法:"get/post",请求url,是否异步(默认true))
- send(请求体发送数据,无则传入null)
- abort():收到响应之前取消异步请求
- setRequestHeader('MyHeader', 'MyValue')
- getResponseHeader('MyHeader')|getAllResponseHeader()
- 属性
- responseText
- responseXML
- status (响应HTTP状态)
- statusText (响应HTTP状态描述)
- readyState (响应状态,请求/响应过程的哪个阶段):0未初始化|1已打开|2已发送|3接收中|4完成,从一个值变为一个值,会触发readystatechange事件,readystatechange事件处理程序应该在调用open()之前赋值
- timeout超时事件,对应超时事件ontimeout
- 进度事件:loadstart、progress事件:接收数据时反复触发、error、abort、load、loadend
let xhr = new XMLHttpRequest();
xhr.open('GET', 'http://domain/service')
// request state change event
xhr.onreadystatechange = function() {
// request completed?
if(xhr.readyState !== 4) return;
if(xhr.status === 200) {
// request successful - show response
console.log(xhr.responseText);
} else {
// request error
console.log('HTTP error', xhr.status, xhr.statusText);
}
}
xhr.timeout = 3000; // 3 seconds
xhr.ontimeout = () => console.log('timeout', xhr.responseURL);
// progress事件可以报告长时间运行的文件上传
xhr.upload = onprogress = p => {
console.log(Math.round((p.loaded / p.total) * 100) + '%');
}
// start request
xhr.send(null)
2、fetch
- 方法:fetch(url, {}init对象),返回Promise对象,只支持异步
- 响应通过response对象获取:fetch().then((response) => {}).catch(() => {}),response对象混入了body,提供了5个方法,将ReadableStream转存到缓冲区的内存里,将缓冲区转换为js对象,通过Promise返回。
- response.text() // 转为text
- response.json() //转为json
- response.formData()
- response.arrayBuffer()
- response.blob()
- 错误不会reject:当接收到一个代表错误的 HTTP 状态码时,从 fetch() 返回的 Promise 不会被标记为 reject,即使该 HTTP 响应的状态码时 404 或 500。相反,它会将 Promise 状态标记为 resolve (但是会将 resolve 的返回值的 ok 属性设置为 false),仅当网络故障时或请求被阻止时,才会标记为 reject
- 不支持超时设置
- 需要借用AbortController终止fetch
fetch(
'http://domain/service', {
method: 'GET'
}
)
.then(response => response.json())
.then(json => console.log(json))
.catch(error => console.error('error:', error))
// credentials: omit不发送cookie|same-origin同源发送cookie(默认)|include都发送cookie
fetch(
'http://domain/service', {
method: 'GET',
credentials: 'same-origin'
}
)
// 错误不会reject
// HTTP错误(例如404 Page Not Found 或 500 Internal Server Error)不会导致Fetch返回的Promise标记为reject;.catch()也不会被执行。
// 想要精确的判断 fetch是否成功,需要包含 promise resolved 的情况,此时再判断 response.ok 是不是为true
fetch(
'http://domain/service', {
method: 'GET'
}
)
.then(response => {
if(response.ok) {
return response.json()
}
throw new Error('Network response was not ok.')
})
.then(json => console.log(json))
.catch(error => console.error('error:', error))
// 不支持直接设置超时,可以用promise
function fetchTimeout(url, init, timeout = 3000) {
return new Promise((resolve, reject) => {
fetch(url, init)
.then(resolve)
.catch(reject)
setTimeout(reject, timeout)
})
}
// 终止fetch
// signal用于支持AbortController中断请求
const controller = new AbortController()
// AbortController接口表示一个控制器对象,允许你根据需要终止一个或多个 web 请求。
fetch(
'http://domain/service', {
method: 'GET',
signal: controller.signal
}
)
.then(response => response.json())
.then(json => console.log(json))
.catch(error => console.error('Error:',error))
controller.abort()
3、HTTP状态码和首部
1、HTTP状态码
100信息性|200成功|300重定向|400客户端错误|500服务器错误
- 200 get 成功
- 201 post 成功
- 301 永久重定向
- 302 临时重定向
- 307 临时重定向,使用get请求重定向
- 304 协商缓存 服务器文件未修改
- 400 客户端请求有语法错误,不能被服务器识别
- 403 服务器受到请求,但是拒绝提供服务,可能是跨域
- 404 请求的资源不存在
- 500 服务器发生不可预期的错误
2、HTTP首部
- 通用首部:Connection、Date、MIME-Version、Cache-Control
- 请求首部:User-Agent、Accept (MIME类型)、Accept-Encoding、Cookie
- 响应首部:Set-Cookie
- 实体首部:Content-Length、Content-Type、ETag、Expires、Last-Modified
3、body格式
- form-data: 就是http请求中的multipart/form-data,它会将表单的数据处理为一条消息,以标签为单元,用分隔符分开。既可以上传键值对,也可以上传文件。当上传的字段是文件时,会有Content-Type来说明文件类型;content-disposition,用来说明字段的一些信息;由于有boundary隔离,所以multipart/form-data既可以上传文件,也可以上传键值对,它采用了键值对的方式,所以可以上传多个文件。
- x-www-form-urlencoded:就是application/x-www-form-urlencoded,会将表单内的数据转换为键值对,比如:name=java&age=22
- raw:可以上传任意格式的文本,可以上传text、json、xml、html等
- binary:Content-Type:application/octet-stream,从字面意思得知,只可以上传二进制数据,通常用来上传文件,由于没有键值,所以一次只能上传一个文件。
4、封装Ajax请求
interface IOptions {
url: string;
type?: string;
data: any;
timeout?: number;
}
function formatUrl(json) {
let dataArr: any = [];
json.t = Math.random();
for (const key in json) {
dataArr.push(`${key}=${encodeURIComponent(json[key])}`)
}
return dataArr.join('&')
}
export function ajax(options: IOptions) {
return new Promise((resolve, reject) => {
if(!options.url) return;
options.type = options.type || 'GET';
options.data = options.data || {};
options.timeout = options.timeout || 10000;
let dataToUrlstr = formatUrl(options.data);
let timer;
// 1.创建
let xhr;
if((window as any).XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else {
xhr = new ActiveXObject('Microsoft.XMLHTTP');
}
if(options.type.toUpperCase() === 'GET') {
// 2.连接
xhr.open('get', `${options.url}?${dataToUrlstr}`, true);
// 3.发送
xhr.send();
} else if(options.type.toUpperCase() === 'POST') {
// 2.连接
xhr.open('post', options.url);
xhr.setRequestHeader('ContentType', 'application/x-www-form-urlencoded');
// 3.发送
xhr.send(options.data);
}
// 4.接收
xhr.onreadystatechange = () => {
if(xhr.readyState === 4) {
clearTimeout(timer);
if(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
resolve(xhr.responseText);
} else {
reject(xhr.status);
}
}
}
if(options.timeout) {
timer = setTimeout(() => {
xhr.abort();
reject('超时');
}, options.timeout);
}
// xhr.timeout = options.timeout
// xhr.ontimeout = () => {
// reject('超时');
// }
})
}
5、axios
Axios是一个基于promise的HTTP库,可以用在浏览器和node.js 中,有以下特点:
- 从浏览器中创建 XMLHttpRequests
- 从 node.js 创建 http(opens new window)请求
- 支持 Promise(opens new window)API
- 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转换JSON数据
- 客户端支持防御 XSRF
1、常见方法
// import axios from 'axios'
const axios = require('axios')
// 全局请求
// 1、get请求
axios.get('/user?id=1234').then(function(response){}).catch(function(error){})
axios.get('/user',{params:{id:1234}}).then(function(response){}).catch(function(error){})
// 2、post请求
axios.get('/user',{id:1234}).then(function(response){}).catch(function(error){})
// 实例请求
const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
})
instance#request(config)
instance#get(url[, config])
instance#delete(url[, config])
instance#head(url[, config])
instance#options(url[, config])
instance#post(url[, data[, config]])
instance#put(url[, data[, config]])
instance#patch(url[, data[, config]])
2、相关配置
在 lib/defaults.js 找到的库的默认值,然后是实例的 defaults 属性,最后是请求的 config 参数。后者将优先于前者。
- 全局配置
- 实例配置
- 请求的config参数
// import axios from 'axios'
const axios = require('axios')
// 全局配置
axios.defaults.baseURL = 'https://some-domain.com/api/'
// 实例配置
const instance = axios.create()
instance.defaults.timeout = 2500;
// 请求参数
instance.get('/longRequest', {
timeout: 5000
})
1、请求配置
const axios = {
// `url` 是用于请求的服务器 URL
url: 'user',
// `method` 是创建请求时使用的方法
method: 'get', // default
// `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
// 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
baseURL: 'https://some-domain.com/api/',
// `headers` 是即将被发送的自定义请求头
headers: {'X-Requested-With': 'XMLHttpRequest'},
// `timeout` 指定请求超时的毫秒数(0 表示无超时时间)
// 如果请求时间超过 `timeout` 的时间,请求将被中断
timeout: 1000,
// `params` 是即将与请求一起发送的 URL 参数,get
// 必须是一个无格式对象(plain object)或URLSearchParams 对象
params: {
ID: 12345
},
// `data` 是作为请求主体被发送的数据,post
data: {
firstName: 'Fred'
},
// `responseType` 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
responseType: 'json', // default
// `responseEncoding` indicates encoding to use for decoding responses
// Note: Ignored for `responseType` of 'stream' or client-side requests
responseEncoding: 'utf8', // default
// `xsrfCookieName` 是用作 xsrf token 的值的cookie的名称
xsrfCookieName: 'XSRF-TOKEN', // default
// `xsrfHeaderName` is the name of the http header that carries the xsrf token value
xsrfHeaderName: 'X-XSRF-TOKEN', // default
// `onUploadProgress` 允许为上传处理进度事件
onUploadProgress: function(progressEvent) {
// Do whatever you want with the native progress event
},
// `onDownloadProgress` 允许为下载处理进度事件
onDownloadProgress: function(progressEvent) {
// 对原生进度事件的处理
},
// `maxContentLength` 定义允许的响应内容的最大尺寸
maxContentLength: 2000,
// `validateStatus` 定义对于给定的HTTP 响应状态码是 resolve 或 reject promise .如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),promise 将被 resolve;否则,promise将被 reject
validateStatus: function(status) {
return status >= 200 && status < 300; // default
},
// `cancelToken` 指定用于取消请求的 cancel token
cancelToken: new CancelToken(function(cancel) {})
}
2、响应结构
const axios = {
// `data` 由服务器提供的响应
data: {},
// `status` 来自服务器响应的 HTTP 状态码
status: 200,
// `statusText` 来自服务器响应的 HTTP 状态信息
statusText: 'OK',
// `headers` 服务器响应的头
headers: {},
// `config` 是为请求提供的配置信息
config: {},
// `request`
// `request` is the request that generated this response
// It is the last ClientRequest instance in node.js (in redirects)
// and an XMLHttpRequest instance the browser
request: {}
}
注意:默认情况下,axios将JavaScript对象序列化为JSON。要以application/x-www-form-urlencoded格式发送数据,需做处理。
- 使用URLSearchParams API
- 使用qs库编码数据
// const axios = require('axios')
import axios from 'axios'
const params = new URLSearchParams();
params.append('param1', 'value1');
params.append('param2', 'value2');
axios.post('/foo', params);
import qs from 'qs'
const data = { 'bar': 123 }
const options = {
methods: 'POST',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
data: qs.stringify(data),
url,
};
axios(options);
3、错误处理
// const axios = require('axios')
import axios from 'axios'
axios.get('/user/12345')
.catch(function(error) {
if(error.response) {
/**
*
*/
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if(error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// https.ClientRequest in node.js
console.log(error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
}
console.log(error.config);
})
3、取消发送
使用 cancel token 取消请求
- 使用 CancelToken.source 工厂方法创建 cancel token
- 传递一个 executor 函数到 CancelToken 的构造函数来创建 cancel token
// const axios = require('axios')
import axios from 'axios'
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/user/12345', {
cancelToken: source.token
}).catch(function(thrown) {
if(axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// 处理错误
}
});
axios.post('user/12345', {
name: 'new name'
}, {
cancelToken: source.token
})
// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');
// const axios = require('axios')
import axios from 'axios'
const CancelToken = axios.CancelToken;
let cancel;
axios.post('user/12345', {
cancelToken: new CancelToken(function executor(c) {
// executor 函数接收一个 cancel 函数作为参数
cancel = c;
})
})
// cancel the request
cancel();
4、并发处理
- axios.all(iterable)
- axios.spread(callback)
// const axios = require('axios')
import axios from 'axios'
function getUserAccount() {
return axios.get('/user/12345');
}
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function(acct, perms) {
// 两个请求现在都执行完成
// acct方法1的返回值
// perms方法2的放回置
}))
标签:axios,浏览器,xhr,对象,url,详解,error,response
From: https://www.cnblogs.com/DTCLOUD/p/17143189.html