1-5、浏览器相关
原理知识:1-3、Promise知识与使用
浏览器下的 JS
包含:
- ECMAScript:基础逻辑与数据处理
const a = [1,2,3].map(i => i*2)
- BOM:对浏览器功能的处理
location.href = 'www.a.com'
- DOM:HTML 文本的操作
document.title = 'hello'
JS 与 ES 的区别:ES 是普通话,JS 是方言;
官方话术:ECMAScript 是一种语言标准,而 JavaScript 是对这个标准的一种具体实现(额外扩展了一些浏览器交互操作)。
BOM
location
与浏览器的地址相关的
基础知识
举例:
location.href = https://www.yuque.com/u53094/ux9b3x/frct1y9u1fppzpqf?q=1#aXLrY
location 组成
- 协议 protocol => location.protocol,
https:
- 域名 host => location.host/location.hostname,
www.yuque.com
- 端口 port => location.port,
''
- 路径 path => location.pathname,
/u53094/ux9b3x/frct1y9u1fppzpqf
- 参数 query => location.search,
[?
,#)
之间的,?q=1
- 锚点 anchor => location.hash,
[#
之后的,#aXLrY
进阶知识
location.assgin(url)
跳转到指定页面,会创建新的浏览器历史,跟location.href
效果一样
location.replace(url)
跳转到指定页面,不会创建新的浏览器历史,并将之前地址的历史替换为新地址
location.reload()
刷新当前页面
location.toString()
获取当前地址,等价于location.href
的值
面试常问
- location 的 API
- 路由相关:跳转、参数、操作等
- 实际场景分析:
- 项目路由模式:
- history:
https://www.example.com/user/profile
地址不带#
;需要服务器支持(因为/user/profile
是直接访问服务器资源的,所以需要服务器处理通配让项目只访问主 html 文件) - hash:
https://www.example.com/#/user/profile
地址带#
;不需要服务器支持(因为#
后面的不会发送给服务器,所以可以永远只访问主 html)
- history:
- 项目路由模式:
- url 的处理
- 如何将地址参数转为对象?
1 |
|
- 使用正则判断是否为 url?
- 核心是要知道
location
的组成,然后一步步判断即可
- 核心是要知道
1 |
|
监听地址的变化
1 |
|
history
与浏览器的地址历史相关的,允许你在浏览器的历史记录中向前和向后导航,以及在历史记录中添加新的条目。
基础知识
属性
history.length
返回历史记录的数量,number
类型history.state
返回当前页面状态,object
类型,值为null
或
方法
history.back()
跳转到历史记录上一页,等价于浏览器后退一步的操作history.forward()
跳转到历史记录下一页,等价于浏览器前进一步的操作history.go(n)
跳转到历史记录的第 n 页,n 为正数表示向前,负数表示向后
1 |
|
history.pushState(state, title, url)
跳转到指定 url,并新增历史记录。不会触发页面的重新加载,history 路由模式使用它来进行页面跳转的
1 |
|
history.replaceState(state, title, url)
跳转到指定 url,不会新增历史记录,并将之前地址的历史替换为新地址。不会触发页面的重新加载
1 |
|
监听地址的变化
1 |
|
navigator
包含浏览器信息的对象
基础知识
属性
navigator.userAgent
返回浏览器信息,string
类型,包含浏览器类型、版本、操作系统信息等
1 |
|
navigator.userAgentData
返回浏览器信息,object
类型,包含浏览器类型、版本、操作系统信息等,更精确
1 |
|
navigator.language
返回用户首选语言,string
类型,我的值为zh-CN
navigator.platform
返回浏览器运行的平台,string
类型,我的值为MacIntel
navigator.cookieEnable
返回浏览器是否启用了 cookie,boolean
类型
面试常问
- UA 的获取与解读
- 获取:
navigator.userAgent
或navigator.userAgentData
- 解读:按需解读即可,也可以借鉴 platform.js 库
- 获取:
- 如何获取剪切板的内容?
- 借用
navigator.clipbord
实现读取剪切板:readText()
- 借用
补充
navigation
通常出现在 Web APIs 中,用于提供有关浏览器导航的信息。一个常见的使用场景是在 Service Worker 中使用
screen(有可视化经历必问)
浏览器设备的屏幕相关的
基础知识
属性
screen.height/screen.width
:电脑屏幕的宽高,随浏览器设备的分辨率变化screen.availHeight/screen.availWidth
:电脑屏幕的可用宽高(减去了 mac/window 系统的上下任务栏的高度),随浏览器设备的分辨率变化screen.orientation
:屏幕方向对象,如screen.orientation.type
横向(landscape)或纵向(portrait)
参考:如何在 JavaScript 中获取屏幕,窗口和网页大小 - 掘金
进阶知识
获取屏幕的大小有哪些方式?
- 使用
screen.width/screen.height
获取电脑屏幕的宽高,随浏览器设备的分辨率变化 - 使用
window.innerWidth/window.innerHeight
获取浏览器窗口的宽高(包括滚动条),随浏览器窗口大小而变化 - 使用
document.documentElement.clientWidth/document.documentElement.clientHeight
获取 html元素的宽高(不包括滚动条) - 使用
document.body.clientWidth/document.body.clientHeight
获取 body 元素的宽高(不包括滚动条)
这几个值之间的关系?
1
>=2
==3
==4
这几个值分别受什么影响?
1
受设备分辨率影响2
受浏览器窗口大小影响3
受给 html元素设置的 CSS 宽高影响4
受给 body元素设置的 CSS 宽高影响
如何监听浏览器的大小变化?
监听 resize 事件
1 |
|
offsetWidth/offsetHeight
与clientWidth/clientHeight
的关系
clientWidth/clientHeight
=元素可视区域的宽/高 + padding(若有)-滚动条(若有)offsetWidth/offsetHieght
=clientWidth/clientHeight
+边框(若有)+滚动条(若有)
滚动条宽高计算方式有哪些?
全局入手
window.innerWidth
(浏览器宽度+滚动条(若有)) - document.documentElement.clientWidth
(浏览器宽度)
元素入手-1(自己想的)
元素(无边框+无 padding+可滚动时).offsetWidth - 元素(无边框+无 padding+可滚动时).clientWidth
元素入手-2(iView 的做法)
元素(可滚动时).offsetWidth - 元素(未滚动前).offsetWidth
常见的scroll*
属性
scrollWidth/scrollHeight
元素的实际宽高,非可视区域的
若无滚动条,则等于clientWidth/clientHeight
若有滚动条,则等于实际内容宽高(一般跟子元素宽高有关)
+padding(若有)
scrollLeft/scrollTop
元素最左端/最上端与窗口最左端/最上端的距离,即滚动条左/上滚的距离
scrollX/scrollY
===pageXOffset/pageYOffset
返回元素滚动了的距离
举例:window.scrollX
===pageXOffset
===document.documentElement.scrollLeft
offsetLeft/offsetTop
返回元素的左/上端与最近定位元素(默认为 body)的左/上边的距离
如何元素监听滚动事件?
一般滚动的 HTML 结构为:父元素高度小于子元素高度并且父元素允许滚动,这样才能滚动
所以监听的滚动事件是设置在父元素上面的
1 |
|
如何判断元素是否在可视区域内?
计算法-父元素为任意元素
对比父元素.scrollTop
与子元素.offsetTop+子元素.offsetHeight(若需要)
的差值,大于 0 表明还在,小于 0 表明不在
计算法(getBoundingClientRect)-父元素仅为窗口
元素.getBoundingClientRect()
获取元素与可视窗口(body 0,0)之间的关系,返回值为object
类型
- width:元素宽度
- height:元素高度
- left:元素左边与可视窗口的左边距离
- right:元素右边与可视窗口的左边距离 = left + width
- top:元素上边与可视窗口的上边距离
- botton:元素下边与可视窗口的上边距离 = top + height
- x:元素鼠标的 X 坐标,等价于 left
- y:元素鼠标的 Y 坐标,等价于 top
- 兼容性问题:IE 会多出 2px(边框计算有差异)
当元素同时满足以下 4 个条件时,则表明在可视窗口内:
元素.left
>= 0元素.top
>= 0元素.right
<=窗口宽度(document.body.clientWidth)
元素.bottom
<=窗口高度(document.body.clientHeight)
API 法-IntersectionObserver(性能最好)
采用IntersectionObserver
来观察两个元素是否重叠
基本语法:new IntersectionObserver(callback, options)
,其中的 callback 将会在满足和不满足重叠时分别调用一次
官网文档:
Intersection Observer API:Intersection Observer API - Web API 接口参考 | MDN
IntersectionObserver:IntersectionObserver - Web API 接口参考 | MDN
entry 对象:IntersectionObserverEntry - Web API 接口参考 | MDN
1 |
|
面试常问
如何 Tab 实现吸顶效果
思路:监听父元素的滚动事件,实时判断父元素的 scrollTop 是否大于 Tab 的 offsetTop 高度,若大于则说明该 Tab 将滚出可视区域了,所以马上设置固定定位,定位的 Top/Left 需要根据父元素的 offsetLeft/offsetTop
1 |
|
事件模型
常说的事件队列、EventLoop 是 JS 语言的机制
普通浏览器
概念
而这里的浏览器事件模型讲的是:浏览器处理事件传播的方式,分为三个阶段
- 捕获:先从文档根节点向下传播到目标元素
- 目标:到达目标元素,触发其绑定的事件(若有)
- 冒泡:最后再从目标元素向上传播到文档根节点
事件注册
使用 DOM Level 0 事件处理程序:
1 |
|
使用 DOM Level 2 事件处理程序:
1 |
|
事件解绑
使用 DOM Level 0 事件处理程序:
1 |
|
使用 DOM Level 2 事件处理程序:
1 |
|
阻止事件传播
event.stopPropagation()
阻止当前元素上的所有本事件的传播,不在触发捕获与冒泡,但已注册未执行的事件都会按序触发
白话:我自己执行完后,只是断掉传播,别人的还是能执行
假设:一个元素上绑定了 3 个 click 事件,若在之一写了event.stopPropagation()
,其他两个的 click 事件会正常触发,但都没法传播了,但其他事件(scroll)不受影响
event.stopImmediatePropagation()
阻止当前元素上的所有本事件的传播,不在触发捕获与冒泡,并且已注册未执行的事件不会再触发
白话:我自己执行完后,不仅要断掉传播,并且让别人也没法执行,做事更绝。
假设:一个元素上绑定了 3 个 click 事件,若在之一写了event.stopImmediatePropagation()
,其他未执行的 click 事件不会正常触发,并且从我开始阻断传播,但其他事件(scroll)不受影响
IE 浏览器(了解即可)
概念
- IE 8 及更早版本只支持事件冒泡,不支持事件捕获。
- IE 9 及之后的版本开始支持事件冒泡和捕获
事件注册
element.attachEvent('onclick', callback)
,必须带上on
attachEvent 是后绑定先执行
1 |
|
事件解绑
element.detachEvent('onclick', callback)
,必须带上on
,并且 callback 必须与 attachEvent 的一致,否则将无法解绑
阻止事件传播
event.cancelBubble = true
使用场景
- 事件代理:
- 基于冒泡,可将列表 item 的点击事件绑定到父级上,在父级上通过
event.target
获取到触发事件的元素,然后进行对应操作。节省子元素的 item 的获取与绑定效率
- 基于冒泡,可将列表 item 的点击事件绑定到父级上,在父级上通过
面试常问
区分不同的阻止
event.stopPropagation()
/event.stopImmediatePropagation()
/event.preventDefault()
手写多浏览器兼容的事件注册、解绑、阻止等
1 |
|
网络
ajax - XMLHttpRequest 对象
Async JavaScript adn XML:通过异步方式与服务器进行数据交互
基本步骤
1 |
|
进阶设置
1 |
|
详细解释
xhr.readyState
的状态有:
- 0:未初始化,尚未调用
open
方法 - 1:启动,
open
方法已调用 - 2:发送,
send
方法已调用 - 3:接收,正在接收服务器数据
- 4:完成,数据接收完毕,可以在浏览器用了
xhr.status
HTTP 的状态码,服务器返回的
- 1xx:提示信息
- 2xx:成功,200(请求成功)
- 3xx:重定向,301(永久重定向),302(临时重定向)
- 4xx:客户端错误,400(请求语法有错),401(请求未授权),403(服务器拒绝服务),404(请求资源不存在)
- 5xx:服务器错误,500(服务器错误),503(服务器已不能处理请求)
自行封装
1 |
|
浏览器缓存
访问资源时,不必重新请求。
强缓存
明确告知浏览器一定时间内使用本地缓存Cach-Control
- max-age: 定义资源被认为是新鲜的最长时间(秒)。
- s-maxage: 覆盖 max-age,但仅适用于共享缓存(比如代理服务器)。
- public: 允许所有内容缓存,包括代理服务器。
- private: 仅允许终端用户缓存。
Expries
资源过期时间,服务器返回的 HTTP 头
协商缓存
浏览器向服务器发请求验证缓存是否有效
- Last-Modified / If-Modified-Since:
- 服务器通过 Last-Modified 头告诉浏览器资源的最后修改时间。
- 浏览器通过 If-Modified-Since 头将最后的修改时间发送给服务器,如果时间仍然一致,服务器返回 304,表示资源未被修改,可以使用本地缓存。
- ETag / If-None-Match:
- 服务器通过 ETag 头生成一个唯一的标识符(通常是文件内容的哈希值)。
- 浏览器通过 If-None-Match 头将该标识符发送给服务器,如果标识符仍然一致,服务器返回 304。
面试常问
RESTFUL 请求的区别 - GET、POST、DELETE、PUT、OPTION
GET | POST | DELETE | PUT | OPTION |
---|---|---|---|---|
获取数据 | 提交数据 | 删除数据 | 更新数据 | 预检,询问服务器支持哪些请求类型,通常在 CORS 请求中进行预检 |
参数在 url 上,有长度限制 | 参数在请求体上,浏览器/服务器有大小限制 | 参数在 url 上,有长度限制 | 参数在请求体上,浏览器/服务器有大小限制 | 无参数 |
跨域问题
同源策略:协议、域名、端口都一致
跨域的原因是:浏览器的同源策略导致的,非同源的不允许访问
解决办法:
- 本地环境:可配置代理
- 生产环境:服务端设置响应头,允许跨域(CORS-Cross Origin Resource Share)
补充知识
window.requestIdleCallback(callback,options)
允许在浏览器空闲时执行一些任务,以避免阻塞用户界面的流畅性。通常用于拆分大型任务(任务分片),确保不会在主线程执行太久而导致影响用户体验
用之前检查兼容性
1 |
|
1 |
|
实际场景举例:
- 性能监测和分析:空闲时可以做
- 自动保存和同步:空闲时可以做
- 前端性能优化:空闲时预加载资源、图片等
window.requestAnimationFrame(callback)
可在浏览器下一次重绘之前执行指定的回调函数,以确保动画的平滑运行
1 |
|
阻止默认事件
event.preventDefault()
阻止浏览器的默认行为,比如:a 标签点击后会默认跳转envnt.returnValue = false
IE 阻止浏览器的默认行为