浏览器相关
# 跨域
原因: 跨域主要是因为浏览器同源策略引起的,同源策略要求域名、洗衣、端口一致
解决方案:
- cors(全局资源共享)(CROS 中的简单请求和非简单请求)
- jsonp
- postMessage
- 代理服务器
# 从输入URL到页面展示发生了什么?
首先在浏览器中输入URL
查找缓存:浏览器先查看浏览器缓存-系统缓存-路由缓存中是否有该地址页面,如果有则显示页面内容。如果没有则进行下一步。
- 浏览器缓存:浏览器会记录DNS一段时间,因此,只是第一个地方解析DNS请求;
- 操作系统缓存:如果在浏览器缓存中不包含这个记录,则会使系统调用操作系统, 获取操作系统的记录(保存最近的DNS查询缓存);
- 路由器缓存:如果上述两个步骤均不能成功获取DNS记录,继续搜索路由器缓存;
- ISP缓存:若上述均失败,继续向ISP搜索。
DNS域名解析:浏览器向DNS服务器发起请求,解析该URL中的域名对应的IP地址。DNS服务器是基于UDP的,因此会用到UDP协议。。
建立TCP连接:解析出IP地址后,根据IP地址和默认80端口,和服务器建立TCP连接
发起HTTP请求:浏览器发起读取文件的HTTP请求,,该请求报文作为TCP三次握手的第三次数据发送给服务器
服务器响应请求并返回结果:服务器对浏览器请求做出响应,并把对应的html文件发送给浏览器
关闭TCP连接:通过四次挥手释放TCP连接
浏览器渲染:客户端(浏览器)解析HTML内容并渲染出来,浏览器接收到数据包后的解析流程为:
- 构建DOM树:词法分析然后解析成DOM树(dom tree),是由dom元素及属性节点组成,树的根是document对象
- 构建CSS规则树:生成CSS规则树(CSS Rule Tree)
- 构建render树:Web浏览器将DOM和CSSOM结合,并构建出渲染树(render tree)
- 布局(Layout):计算出每个节点在屏幕中的位置
- 绘制(Painting):即遍历render树,并使用UI后端层绘制每个节点。
JS引擎解析过程:调用JS引擎执行JS代码(JS的解释阶段,预处理阶段,执行阶段生成执行上下文,VO,作用域链、回收机制等等)
# 浏览器对页面进行渲染,是怎么渲染的
- 渲染过程:
- (1)构建DOM树,将浏览器无法直接理解和使用的HTML,转换为浏览器能够理解的结构--DOM 树。
- (2)构建CSSOM,把 CSS 转换为浏览器能理解的结构(styleSheets),并转换样式表中的属性值,使其标准化,计算出 DOM 树中每个节点的具体样式(根据继承规则和层叠规则)。
- (3)创建 Layout 布局树,确定DOM 元素的几何位置信息,遍历 DOM 树中的所有可见节点,加入到布局树(display:none不包含),并计算布局树节点的坐标位置。
- (4)构建图层树,如果页面有复杂的效果,如常见的页面滚动,或者使用 z 轴排序等,为了更加方便地实现这些效果,渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree)。
- (5)Paint 图层绘制,把一个图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表(联想自己画画)。
- (6)tiles:将图层转换成图块。
- (7)光栅化:通过进程实现图块转换成位图。
- (8)display:浏览器进程拿到 DrawQuad 信息生成页面显示。
# 浏览器重绘与重排的区别?
重排/回流(Reflow)
:当DOM的变化影响了元素的几何信息,浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做重排。表现为重新生成布局,重新排列元素。重绘(Repaint)
: 当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘。表现为某些元素的外观被改变。
『重绘』不一定会出现『重排』,『重排』必然会出现『重绘』。
# 如何触发重排和重绘?
任何改变用来构建渲染树的信息都会导致一次重排或重绘:
- 添加、删除、更新DOM节点
- 通过display: none隐藏一个DOM节点-触发重排和重绘
- 通过visibility: hidden隐藏一个DOM节点-只触发重绘,因为没有几何变化
- 移动或者给页面中的DOM节点添加动画
- 添加一个样式表,调整样式属性
- 用户行为,例如调整窗口大小,改变字号,或者滚动。
# 如何避免重绘或者重排?
集中改变样式,不要一条一条地修改 DOM 的样式。
不要把 DOM 结点的属性值放在循环里当成循环里的变量。
为动画的 HTML 元件使用 fixed 或 absoult 的 position,那么修改他们的 CSS 是不会 reflow 的。
不使用 table 布局。因为可能很小的一个小改动会造成整个 table 的重新布局。
尽量只修改position:absolute或fixed元素,对其他元素影响不大
动画开始GPU加速,translate使用3D变化
提升为合成层
# 浏览器缓存策略
# 目的
是为了节约网络的资源加速浏览,浏览器在用户磁盘上对最近请求过的文档进行存储, 当访问者再次请求这个页面时,浏览器就可以从本地磁盘显示文档,这样就可以加速页面的阅览
# 强制缓存、协商缓存、缓存机制
强制缓存:
- Expires 是具体的时间点,修改客户端时间会影响
- Cache-Control 是时间段
- 两者区别:
- Expires 是http1.0的产物,Cache-Control是http1.1的产
- Cache-Control优先级高于Expires
- Expires其实是过时的产物,现阶段它的存在只是一种兼容性的写法
- Expires是一个具体的服务器时间,Cache-Control是一个时间段,控制就比较容易
协商缓存:
- ETag和If-None-Match 是对该资源的一种唯一标识,只要资源有变化,Etag就会重新生成
- Last-Modified和If-Modified-Since 该资源文件最后一次更改时间,以秒为单位
- 两者区别:
- 方式:Etag是对资源的一种唯一标识,而Last-Modified是该资源文件最后一次更改时间
- 精确度:Etag精确度要优于Last-Modified。因为一秒变化多次Last-Modified监测不到,负责均衡的服务器生成的Last-Modified也不一样
- 性能:Etag要逊于Last-Modified,毕竟Last-Modified只需要记录时间,而Etag需要服务器通过算法来计算出一个hash值。
- 优先级:服务器校验优先考虑Etag。
缓存机制:
- 强制缓存优先于协商缓存
- 流程:
- 先比较当前时间和上一次返回200时的时间差,如果没有超过cache-control设置的max-age,则没有过期,命中强缓存。
- 如果时间过期,则向服务器发送header带有If-None-Match和If-Modified-Since的请求。
- 服务器收到请求后,优先根据Etag的值判断被请求的文件有没有做修改,Etag值一致则没有修改,命中协商缓存,返回304;如果不一致则有改动,直接返回新的资源文件带上新的Etag值并返回200;若无ETag则则将If-Modified-Since和被请求文件的最后修改时间做比对,一致则命中协商缓存,返回304;不一致则返回新的last-modified和文件并返回200。
# 储存位置
- service: 可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的。
- Memory : 读取高效,缓存时间短
- Disk Cache: 读取速度慢点,但容量大,存储时效性长
- Push Cache: 它只在会话(Session)中存在,一旦会话结束就被释放,并且缓存时间也很短暂,在Chrome浏览器中只有5分钟左右
# 用户行为
- 地址栏访问,链接跳转是正常用户行为,将会触发浏览器缓存机制;
- F5刷新,浏览器会设置max-age=0,跳过强缓存判断,会进行协商缓存判断;
- ctrl+F5刷新,跳过强缓存和协商缓存,直接从服务器拉取资
# xss 和 csrf 攻击
# XSS
XSS(Cross Site Scripting):跨域脚本攻击。
攻击原理: 不需要你做任何的登录认证,它会通过合法的操作(比如在url中输入、在评论框中输入),向你的页面注入脚本(可能是js、hmtl代码块等)。
XSS的攻击方式
1、反射型: 发出请求时,XSS代码出现在url中,作为输入提交到服务器端,服务器端解析后响应,XSS代码随响应内容一起传回给浏览器,最后浏览器解析执行XSS代码。这个过程像一次反射,所以叫反射型XSS。
2、存储型: 存储型XSS和反射型XSS的差别在于,提交的代码会存储在服务器端(数据库、内存、文件系统等),下次请求时目标页面时不用再提交XSS代码。
XSS的防范措施(encode + 过滤)
1、编码:
2、过滤:
3、校正:
# CSRF
CSRF(Cross-site request forgery):跨站请求伪造。
- CSRF的防范措施
方法一、Token 验证:(服务器发送给客户端一个token;户端提交的表单中带着这个token。如果这个 token 不合法,那么服务器拒绝这个请求。
方法二:隐藏令牌: 把 token 隐藏在 http 的 head头中。(方法二和方法一有点像,本质上没有太大区别,只是使用方式上有区别。)
方法三、Referer 验证: Referer 指的是页面请求来源。意思是,只接受本站的请求,服务器才做响应;如果不是,就拦截。
# 事件循环机制
目的
:JS是单线程的,为了防止一个函数执行时间过长阻塞后面的代码,所以会先将同步代码压入执行栈中,依次执行,将异步代码推入异步队列,异步队列又分为宏任务队列和微任务队列,因为宏任务队列的执行时间较长,所以微任务队列要优先于宏任务队列。
微任务
队列的代表就是,Promise.then,MutationObserver,
宏任务
的话就是setImmediate setTimeout setInterval。
事件环的运行机制
: 先会执行栈中的内容,栈中的内容执行后执行微任务,微任务清空后再执行宏任务,先取出一个宏任务,再去执行微任务,然后在取宏任务清微任务这样不停的循环。
事件循环可以简单的描述为以下四个步骤
:
- 函数入栈,当Stack中执行到异步任务的时候,就将他丢给WebAPIs,接着执行同步任务,直到Stack为空;
- 此期间WebAPIs完成这个事件,把回调函数放入队列中等待执行(微任务放到微任务队列,宏任务放到宏任务队列)
- 执行栈为空时,Event Loop把微任务队列执行清空;
- 微任务队列清空后,进入宏任务队列,取队列的第一项任务放入Stack(栈)中执行,执行完成后,查看微任务队列是否有任务,有的话,清空微任务队列。重复4,继续从宏任务中取任务执行,执行完成之后,继续清空微任务,如此反复循环,直至清空所有的任务。
# JS垃圾回收与V8垃圾回收
JavaScript 在运行过程中数据主要是存储在栈空间和堆空间,原始类型的数据是存放在栈中,引用类型的数据是存放在堆中的。堆中的数据是通过引用和变量关联起来的。 有些数据被使用之后,可能就不再需要了,我们把这种数据称为垃圾数据。如果这些垃圾数据一直保存在内存中,那么内存会越用越多,所以我们需要对这些垃圾数据进行回收,以释放有限的内存空间。
项目中,如果存在大量不被释放的内存(堆/栈/上下文),页面性能会变得很慢。当某些代码操作不能被合理释放,就会造成内存泄漏。我们尽可能减少使用闭包,因为它会消耗内存。
浏览器垃圾回收机制/内存回收机制:
标记清除
: 在js中,最常用的垃圾回收机制是标记清除:当变量进入执行环境时,被标记为“进入环境”,当变量离开执行环境时,会被标记为“离开环境”。垃圾回收器会销毁那些带标记的值并回收它们所占用的内存空间。谷歌浏览器
:“查找引用”,浏览器不定时去查找当前内存的引用,如果没有被占用了,浏览器会回收它;如果被占用,就不能回收。IE浏览器
:“引用计数法”,当前内存被占用一次,计数累加1次,移除占用就减1,减到0时,浏览器就回收它。
优化手段
:内存优化 ; 手动释放:取消内存的占用即可。- (1)堆内存:fn = null 【null:空指针对象】
- (2)栈内存:把上下文中,被外部占用的堆的占用取消即可。
内存泄漏
在 JS 中,常见的内存泄露主要有 4 种,全局变量、闭包、DOM 元素的引用、定时器
# 浏览器存储
相同点:
存储在客户端。
不同点:
- cookie数据大小不能超过4k;sessionStorage和localStorage的存储比cookie大得多,可以达到5M+。
- cookie设置的过期时间之前一直有效;localStorage永久存储,浏览器关闭后数据不丢失除非主动删除数据;sessionStorage数据在当前浏览器窗口关闭后自动删除。
- cookie的数据会自动的传递到服务器;sessionStorage和localStorage数据保存在本地。
# 前端性能优化
1.只请求当前需要的资源
- 异步加载
- 懒加载
- polyfill的优化 https://polyfill.io/v3/url-builder/ (opens new window)
2.缩减资源体积
- 打包压缩
- gzip
- 图⽚片格式优化, 压缩, 根据屏幕分辨率展示不不同分辨率的图⽚片尽量量
- 控制cookie⼤大⼩小
3.时序优化
js中promise.all
ssr
prefetch、prerender、preload
<link rel=“dns-prefetch” href=“xxxxxx” /> <link rel=“preconnect” href=“xxxxxxx” /> <link rel=“preload” as=“image” href=“xxxxxxxxx” />
4.合理理利利⽤用缓存
- cdn
- http缓存
- localStorage, sessionStorage
# 参考资料
https://www.webpagetest.org (opens new window) webpagetest工具测试