URL从输入到页面展示的过程
版本1
1.用户在浏览器的地址栏输入访问的URL地址。浏览器会先根据这个URL查看浏览器缓存-系统缓存-路由器缓存,若缓存中有,直接跳到第6步操作,若没有,则按照下面的步骤进行操作。
2.浏览器根据输入的URL地址解析出主机名。
3.浏览器将主机名转换成服务器ip地址。浏览器先查找本地DNS缓存列表,看缓存里面是否存在这个ip,如果有则进入第4步,如果缓存中不存在这个ip地址,就再向浏览器默认的DNS服务器发送查询请求,同时缓存当前这个ip到DNS缓存列表中。
4.拿到ip地址后,浏览器再从URL中解析出端口号。
5.拿到ip和端口后,浏览器会建立一条与目标Web服务器的TCP连接,也就是传说中的三次握手。
6.浏览器向服务器发送一条HTTP请求报文。
7.服务器向浏览器返回一条HTTP响应报文。
8.关闭连接,浏览器解析HTML、CSS、JS等资源,最后进行页面渲染,呈现给用户。
版本2
先进行DNS域名解析,先查看本地hosts文件,查看有没有当前域名对应的ip地址,若有直接发起请求,没有的话会在本地域名服务器去查找,该查找属于递归查找,如果本地域名服务器没查找到,会从根域名服务器查找,该过程属于迭代查找,根域名会告诉你从哪个与服务器查找,最后查找到对应的ip地址后把对应规则保存到本地的hosts文件中。
如果想加速以上及之后的http请求过程的话可以使用缓存服务器CDN,CDN过程如下:
用户输入url地址后,本地DNS会解析url地址,不过会把最终解析权交给CNAME指向的CDN的DNS服务器
CDN的DNS服务器会返回给浏览器一个全局负载均衡IP
用户会根据全局负载均衡IP去请求全局负载均衡服务器
全局负载均衡服务器会根据用户的IP地址,url地址,会告诉用户一个区域负载均衡设备,让用户去请求它。
区域负载均衡服务器会为用户选择一个离用户较近的最优的缓存服务器,并把ip地址给到用户
用户想缓存服务器发送请求,如果请求不到想要的资源的话,会一层层向上一级查找,知道查找到为止。
进行http请求,三次握手四次挥手建立断开连接
服务器处理,可能返回304也可能返回200
返回304说明客户端缓存可用,直接使用客户端缓存即可,该过程属于协商缓存
返回200的话会同时返回对应的数据
客户端自上而下执行代码
其中遇到CSS加载的时候,CSS不会阻塞DOM树的解析,但是会阻塞DOM树的渲染,并且CSS会阻塞下面的JS的执行
然后是JS加载,JS加载会影响DOM的解析,之所以会影响,是因为JS可能会删除添加节点,如果先解析后加载的话,DOM树还得重新解析,性能比较差。如果不想阻塞DOM树的解析的话,可以给script添加一个
defer或者
async的标签。
defer:不会阻塞DOM解析,等DOM解析完之后在运行,在
DOMContentloaed
之前async: 不会阻塞DOM解析,等该资源下载完成之后立刻运行
进行DOM渲染和Render树渲染
获取html并解析为Dom树
解析css并形成一个cssom(css树)
将cssom和dom合并成渲染树(render树)
进行布局(layout)
进行绘制(painting)
回流重绘
回流必将引起重绘,重绘不一定引起回流
什么是重绘?回流(重排)?
1. 当页面中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流。每个页面至少需要一次回流,就是在页面第一次加载的时候。
2. 当页面中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。
注:回流必将引起重绘,而重绘不一定会引起回流。
//什么时候会发生回流?
当页面布局和几何属性改变时候就需要回流。下面的情况会引发浏览器的回流:
1:添加或者删除可见的DOM元素
2:元素位置改变;
3:元素尺寸改变----边距、填充、边框、高度和宽度
4:内容改变---比如文本改变或者图片大小改变而引起的计算值宽度和高度改变
5:页面渲染初始化
6:浏览器窗口尺寸改变---resize事件发生时
//什么时候发生重绘?
1.改变字体
2.增加或者移除样式表
3.内容变化(input框输入文字)
4.激活css伪类 eg :hover
5.计算offsetWidth、offsetHeigth属性(浏览器的可见高度)
6.设置style属性的值
//如何减少回流、重绘
1.使用DocumentFragment进行缓存操作,引发一次回流和重绘
2. csstext(利用cssText属性合并所有改变,然后一次性写入)
3.使用trsansform使用改变位置(margin-left等)这些更加流畅
4.如果你要改变多个属性,最好将这些属性定义在一个class中,直接修改class名,这样只用引起一次回流。
防抖和节流
//函数防抖:当事件被触发一段时间后再执行回调,如果在这段时间内事件又被触发,则重新计时。
在事件触发时,开始计时,在规定的时间(delay)内,若再次触发事件,将上一次计时(timer)清空,然后重新开始计时。保证只有在规定时间内没有再次触发事件之后,再去执行这个事件。 1次
//函数节流(throttle):指定时间间隔内,若事件被多次触发,只会执行一次
在事件触发之后,开始计时,在规定的时间(delay)内,若再次触发事件,不对此事件做任何处理。保证在规定时间内只执行一次事件.
//函数防抖的应用场景
连续的事件,只需触发一次回调的场景有:
搜索框搜索输入。只需用户最后一次输入完,再发送请求
手机号、邮箱验证输入检测
窗口大小Resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。
//函数节流的应用场景
间隔一段时间执行一次回调的场景有:
滚动加载,加载更多或滚到底部监听
谷歌搜索框,搜索联想功能
高频点击提交,表单重复提交
深/浅拷贝
浅拷贝--新旧互相影响,改变的是地址
新值===原值, 只能拷贝一层
> 1. 数组方法 `slice截取`, `concat拼接`, `filter过滤` , `map`,
> 2. 对象方法 `Object.assign({ },obj)`,
> 3. 展开运算符`{…obj}`
深拷贝--新旧互不影响,改变的是值
新值=/=原值 , 可以拷贝多层
> 1. JSON序列化 **JSON.parse( JSON.stringify(obj) )** 对象-->字符串-->对象
缺陷:1.如果对象里有RegExp对象,则序列化的结果将只得到空对象;
2.如果对象里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
3.对象中含有NaN,则序列化的结果会变成null
> 2. 原生实现
> - 递归
// 递归实现深拷贝
function cloneDeep(obj) {
// 1.根据obj为对象或者数组,产生一个空的对象或数组,存放数据
var newobj = obj instanceof Array ? [] : {},
// 2.for...in进入循环
for (var k in obj) {
// 3.判断对象的第一个属性是否为数组或者对象,如果是,则进入递归
if (typeof obj[k] === 'object') {
newobj[k] = cloneDeep(obj[k])
} else {
// 4.如果数据为基本类型,则直接赋值
newobj[k] = obj[k]
}
}
// 5.把存放了数据的新对象返回出去
return newobj
}
> 3. 工具实现【 第三方封装库 】
> - loadsh _.cloneDeep(obj)
> - 缺点
> - Immutable
4.Object.create()
js数据类型
基本数据类型有:Number、String、Boolean、Null、Undefined、Symbol(ES6新增数据类型)、bigInt
引用数据类型统称为Object类型,细分有:Object、Array、Date、Function、RegExp
基本数据类型的数据直接存储在栈中;而引用数据类型的数据存储在堆中,每个对象在堆中有一个引用地址。引用类型在栈中会保存他的引用地址,以便快速查找到堆内存中的对象。
栈内存是自动分配内存的。而堆内存是动态分配内存的,不会自动释放。所以每次使用完对象的时候都要把它设置为null,从而减少无用内存的消耗
判断数据类型有几种方法:
typeof:
缺点是typeof null
的值为Object
,无法分辨是null
还是Object
instanceof:
缺点是只能判断对象是否存在于目标对象的原型链上(原理是实际上就是查找目标对象的原型链)
constructor
Object.prototype.toString.call()
一种最好的基本类型检测方式
Object.prototype.toString.call()
;它可以区分 null 、 string 、boolean 、 number 、 undefined 、 array 、 function 、 object 、 date 、 math 数据类型。
缺点:不能细分为谁谁的实例
什么是闭包,闭包的作用?
函数执行,形成私有的执行上下文,使内部私有变量不受外界干扰,起到保护和保存的作用
作用:
保护:避免命名冲突
保存:解决循环绑定引发的索引问题
变量不会销毁:可以使用函数内部的变量,使变量不会被垃圾回收机制回收
应用:
设计模式中的单例模式
for循环中的保留i的操作
防抖和节流
函数柯里化
缺点:会出现内存泄漏的问题
https和http有什么区别,https的实现原理?
http无状态无连接,而且是明文传输,不安全
https传输内容加密,身份验证,保证数据完整性
https实现原理:
首先客户端向服务端发起一个随机值,以及一个加密算法
服务端收到后返回一个协商好的加密算法,以及另一个随机值
服务端在发送一个公钥CA
客户端收到以后先验证CA是否有效,如果无效则报错弹窗,有过有效则进行下一步操作
客户端使用之前的两个随机值和一个预主密钥组成一个会话密钥,在通过服务端传来的公钥加密把会话密钥发送给服务端
服务端收到后使用私钥解密,得到两个随机值和预主密钥,然后组装成会话密钥
客户端在向服务端发起一条信息,这条信息使用会话秘钥加密,用来验证服务端时候能收到加密的信息
服务端收到信息后返回一个会话秘钥加密的信息
都收到以后SSL层连接建立成功
http缓存
缓存分为强缓存和协商缓存
强缓存
在浏览器加载资源时,先看看
cache-control
里的max-age
,判断数据有没有过期,如果没有直接使用该缓存 ,有些用户可能会在没有过期的时候就点了刷新按钮,这个时候浏览器就回去请求服务端,要想避免这样做,可以在cache-control
里面加一个immutable
.public
允许客户端和虚拟服务器缓存该资源,cache-control中的一个属性
private
只允许客户端缓存该资源
no-storage
不允许强缓存,可以协商缓存
no-cache
不允许缓存
协商缓存
浏览器加载资源时,没有命中强缓存,这时候就去请求服务器,去请求服务器的时候,会带着两个参数,一个是
If-None-Match
,也就是响应头中的etag
属性,每个文件对应一个etag
;另一个参数是If-Modified-Since
,也就是响应头中的Last-Modified
属性,带着这两个参数去检验缓存是否真的过期,如果没有过期,则服务器会给浏览器返回一个304状态码,表示缓存没有过期,可以使用旧缓存。 etag的作用
有时候编辑了文件,但是没有修改,但是
last-modified
属性的时间就会改变,导致服务器会重新发送资源,但是etag
的出现就完美的避免了这个问题,他是文件的唯一标识缓存位置:
内存缓存Memory-Cache
离线缓存Service-Worker
磁盘缓存Disk-Cache
推送缓存Push-Cache
面试官:localStorage、SessionStorage、cookie、session 之间有什么区别
localStorage:
关闭浏览器后数据依然保留,除非手动清除,否则一直在,相同浏览器的不同标签在同源情况下可以共享localStorage
sessionStorage:
关闭浏览器或者标签后即失效,只在当前标签可用,当前标签的iframe中且同源可以共享
cookie:
是保存在客户端的,一般由后端设置值,可以设置过期时间
储存大小只有4K
一般用来保存用户的信息的
在http下cookie是明文传输的,较不安全
cookie属性有
http-only:不能被客户端更改访问,防止XSS攻击(保证cookie安全性的操作)
Secure:只允许在https下传输
Max-age: cookie生成后失效的秒数
expire: cookie的最长有效时间,若不设置则cookie生命期与会话期相同
session:
session是保存在服务端的
session的运行依赖sessionId,而sessionId又保存在cookie中,所以如果禁用的cookie,session也是不能用的,不过硬要用也可以,可以把sessionId保存在URL中
session一般用来跟踪用户的状态
session 的安全性更高,保存在服务端,不过一般为使服务端性能更加,会考虑部分信息保存在cookie中
localstorage存满了怎么办?
划分域名,各域名下的存储空间由各业务组统一规划使用
跨页面传数据:考虑单页应用、采用url传输数据
最后兜底方案:调别人的存储
怎么使用cookie保存用户信息
document.cookie(“名字 = 数据;expire=时间”)
怎么删除cookie
目前没有提供删除的操作,但是可以把它的Max-age设置为0,也就是立马失效,也就是删除了