常见浏览器 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)
})
-
如何阻止事件的传播 e.stopPropagation(); // 阻止的是事件的传播,可以阻止冒泡,也可以阻止捕获
在捕获阶段:使用这个的时候,向下的往后的捕获阶段不执行了,对应的冒泡也不会执行了
在冒泡阶段:使用,不会向上冒泡了,但是完整的捕获阶段会执行
-
场景
-
一个页面上,有很多元素,div ,p ,button 等,每个元素上都有自己的绑定事件,都不相同; 当用户进入页面,有个状态bannd, true :表示封禁,提示封禁; false 表示,不做处理。 当点击任何位置都提示
window.addEventLister('click',function(e) { if(bannd) { e.stopPropagation(); alert('你被封禁了') return } },true)
全局封禁适用什么场景?
游客未登录;接口风控处理;替换蒙层(一般不需要,因为后台会有检验);低成本的人机校验
-
事件代理
- 第一个好处是效率高,比如,不用for循环为子元素添加事件了
- 第二个好处是,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>
- 哪些事件有冒泡事件? 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)
- ip 和 域名是有对应关系的,放到浏览器缓存中,缓存DNS一段时间,先从浏览器中找
- 如果找不到就会找系统缓存,系统中找缓存 (举例改变host指向)
- 如果找不到,就从各种路由器缓存域名信息
- 如果找不到,就会从地方站点的缓存信息
- 如果找不到,从跟域名服务器找
优化: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);
}