【浏览器原理】前端应该知道的浏览器相关知识点
引言
浏览器常见缓存有哪些?浏览器渲染引擎的工作原理是什么?近期整理了一些浏览器相关知识,分享给大家一起来学习。如有问题,欢迎指正。
1. Web缓存原理
什么是缓存:
第一次访问页面,服务器是原原本本把所有的数据给浏览器。
第二次访问,是没有必要把所有的资源原原本本返回,将一些请求过得静态资源( CSS/JS/ 图片)无论是存储到本地或者服务端,不再重新获取,这就是缓存。
为什么要有缓存:
CPU计算很快,页面渲染只需要毫秒,比较慢的是网络请求几百毫秒,同时网路请求存在不稳定性。
- 减少请求次数
- 提高页面加载速度
- 减少服务器负担
- 提高网站的性能
2. 强制缓存与协商缓存
强制缓存
浏览器初次请求,服务器返回资源和Cache-control
(资源可以缓存,就加cache-control
)再次请求,判断cache-control
没有过期,没有过期,就会在本地缓存中找资源。如果过期了,再次返回资源和cache-control
,重新设置。
- 服务器端控制,在Response Headers 中
- 例如 Cache-Control:max-age=3153600(单位是s)
Cache-control的值
- Max-age :设置缓存过期时间
- No-cache:不用强制缓存
- No-store:不用强制缓存,不用协商缓存
Expires
- 同在响应头中
- 同为控制缓存过期
- 已被Cache-Control代替(存在于http1.0中)
协商缓存
如果命中强制缓存,我们无需发起新的请求,直接使用缓存内容,如果没有命中强制缓存,如果设置了协商缓存,这个时候协商缓存就会发挥作用。
服务端缓存策略(服务端判断客户端资源,是否和服务端一样)
浏览器初次请求,服务器返回资源和last-Modified与Etag,再次请求在请求头中携带If-Modified-Since(是否修改自从)和If-none-Match(是否没有匹配),服务端进行校验,一致则返回304,否则返回200和最新资源。
Last-Modified
Etag
- 在Response Header中
- Last-Modified 资源的最后修改时间
- Etag资源唯一标识(一个字符串)
- 优先使用Etag,etag根据内容计算会精准一些,Modified只能精确到秒级
二者应用场景:
对于频繁改动的文件,采用协商缓存处理,通过Etag的值来判断当前缓存是否可用。 对于基本不变化的资源,采用强制缓存处理,通过设置cache-control:max-age
3. 三种刷新有什么区别
- 正常操作:地址栏输入url,跳转链接,前进后退
- 手动刷新:f5
- 强制刷新:ctrl+f5
正常操作:强制缓存、协商缓存都有效
手动操作:强制缓存失效,协商缓存有效(可能返回304或200)
强制刷新;强制、协商都失效(相当于请求从来没有来过)
4. 输入URL到页面展示的全过程
大概分三个步骤:
网络请求
- DNS解析:将域名解析为IP地址
- 客户端与服务器建立TCP连接:三次握手
- 客户端发起HTTP请求
- 服务器处理HTTP请求
得到html源代码,遇到静态资源(css/js/图片/视频)继续请求
解析渲染
- 浏览器根据 html 文件构建 DOM 树
- 根据CSS构建CSSDOM书(style tree)
- 两者结合,形成 render tree
- 将Render Tree 渲染到界面
断开连接,四次挥手
渲染过程中遇到JS文件如何处理?
遇到script
则暂停渲染,优先加载并执行JS代码,完成继续渲染。JS线程和和渲染线程共用一个线程。
遇到图片如何处理?
加载图片,不影响继续渲染,等图片加载完插进来。
5. 网页重绘和重排
重绘:重新绘制这个元素,当元素的样式发生改变浏览器会重新绘制这个元素。
重排:重新排列这个元素,当元素位置或者结构发生改变,浏览器重新计算渲染整个DOM 树。重排可能影响其他元素,消耗会大一些
- 定位属性及浮动
position 、top、bottom、left、right、float、clear
- 盒子模型相关属性
width、height、padding、margin、display、border
- 文本相关属性
font-size、font-weight、font-family、text-align、line-height、vertival-align、white-space
优化手段:
- 集中修改样式,使用class
- 使用createDocumentFragment批量操作DOM
- 使用BFC特性,不影响其他元素位置
- 元素位置移动变换使用 CSS3 的 transform 来代替对 top left 等的操作
- 使用CSS中的动画
6. Window.onload和DOMContentLoaded区别
Window.onload
资源全部加载完成才执行,包括图片DomContentLoaded
DOM渲染完成即可,图片可能尚未下载
7. 前端安全问题 如何防范
XSS
Cross Site Script 跨站脚本攻击(跨站请求攻击)
手段:攻击者将JS脚本插入到网页中,渲染时执行JS
比如获取到你的cookies,获取你的通过img方式传给自己的服务器,因为img规避跨域的。
预防:替换特殊字符,可以把<变成&It; >变成>直接显示,而不会作为脚本执行。
<script src="https://rawgit.com/leizongmin/js-xss/master/dist/xss.js"></script>
<script>
// apply function filterXSS in the same way
var html = filterXSS('<script>alert("xss");</scr' + "ipt>");
alert(html);
</script>
CSRF
- Cross Site Request Forgery 跨站请求伪造
- 手段:攻击者诱导用户去访问另一个网站的接口
登录邮箱,内容下面有个广告,点了广告,跳转了新的页面,页面什么都没有,然后你就关了。
因为你已经登录了有了cookies,当你打开了页面,访问gamail接口,自动把你的邮件转发到另一个邮箱中。(网站的api发现有cookie,认为使用户自己操作的)。诱导转发邮件,类似的还有诱导购买,诱导关注。
- 预防:严格控制跨域+验证码限制
- 服务端严格跨域请求限制,判断接口来源referrer(http请求头 请求来源)
- 禁止跨域传递cookies,为cookies设置SameSite
- 关键接口加短信验证码
DDoS
- Distribute denial-of-service分布式拒绝服务
- 手段:分布式、大规模流量访问,使浏览器瘫痪 (一般是通过一些其他手段,散布一些病毒木马到一些手机上,控制病毒木马统一的时间去访问网站)
- 预防:需要硬件防护(如阿里云WAF)
点击劫持 Click Jacking
- 手段:攻击者在界面上蒙一个透明的iframe,诱导用户点击
- 预防:让iframe不能跨域加载
- 设置请求x-Frame-Options:sameorigin frame页面的地址只能为同源域名下的页面
2. 判断两个域名是否一致
在顶层页面打开的url(跳出框架)和本页面打开url
SQL注入
- 现在前后端分离,这种方式比较少了
- 手段:攻击者提交内容写入sql语句,破坏数据库
- 预防:处理输入的内容,替换特殊字符
8. 进程和线程的概念
首先,计算机的核心是CPU,他承担了所有的计算任务,他就像一座工厂,时刻运行着。
假定工厂的电力有限,一次只能供给一个车间使用,也就是说,一个车间开工的时候,其他车间必须停工。背后的含义是:单个CPU一次只能运行一个任务。
进程好比工厂的车间,他代表CPU能够处理的单个任务,任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。
一个车间里,可以有很多工人,他们协同完成一个任务,线程好比车间的工人,一个进程可以包括多个线程。
车间的工人们是共享的,比如许多房间每个工人都可以进出。这象征着一个进程的内存空间是共享的,每个线程都可以使用这些共享内存。
用专业的语言解释就是:
进程: 一个进程就是一个程序运行的实例,详细解释就是:启动一个程序的时候,操作系统会为该程序创建一块内存,用来存放代码,运行中的数据和一个执行任务的主线程,我们把这样一个运行环境叫做进程。
线程: 是处理具体任务的,线程不能单独存在,它是由进程来启动和管理的。
- 单线程:一个进程只有一个主线程
- 多线程:一个进程中有多个线程
从图中我们可以看到:线程是依附进程的,而进程中使用多线程并行处理能提升运算效率。
特点:
- 进程中任意线程执行出错,都会导致整个进程崩溃
- 线程之间共享进程中的数据
- 当一个进程关闭后,操作系统会回收进程所占的内存
- 进程之间的内容相互隔离
9. 多进程浏览器
早期的浏览器是单进程的,但随之Chrome浏览器的发展,衍生了多进程架构:
浏览器主进程
- 负责管理各个标签页的创建和销毁
- 负责浏览器的页面显示和功能(前进,后退,收藏等)
- 负责资源的管理与下载
- 负责子进程的管理
渲染进程
- 浏览器内核,主要负责HTML,CSS,JS等文件的解析和执行
- 默认情况下,浏览器会为每一个标签页开启一个新的渲染进程,以保证不同标签页之间不会影响。
网络进程
主要负责页面的网络资源加载
GPU进程
负责3D绘制和硬件加速
插件进程
主要是负责插件的运行,每个插件使用时候都会创建一个对应的进程
10. 浏览器的渲染线程有哪些
浏览器的渲染线程总共有五种:
GUI渲染线程
负责渲染浏览器页面,解析html、css,构建dom树,CSSOM树,render树。
JS引擎线程
JS引擎线程也称为JS内核,负责处理JS脚本程序,解析脚本,运行代码。
定时器触发线程
- 计时器触发线程即setInterval和setTimeout所在线程
- W3C 在 HTML 标准中规定,定时器的定时时间不能小于 4ms,如 果是小于 4ms,则默认为 4ms。
事件触发线程
事件触发线程用来控制事件循环
异步HTTP请求线程
XMLHttpRequest连接后通过浏览器开一个线程请求
11. JS为什么是单线程
- JS作为浏览器脚本语言,主要用途是与用户互动,以及操作DOM。
- 这是JS语言特征决定,否则会带来很复杂的同步问题 比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
- HTML5 提出Web Worker标,允许JS脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM,这并没有改变JS单线程的本质。
12. 僵尸进程和孤儿进程是什么
僵尸进程:子进程比父进程先结束, 而父进程又没有释放子进程占用的资源,那么子进程的进程描述符仍然保存在系统中,这种进程称之为僵死进程。
孤儿进程:父进程退出了, 而它的一个或多个进程还在运行,那这些子进程都会成为孤儿进程。
13. Cookie与session区别
HTTP请求是无状态的,客户端无法识别两个请求是否来自同一客户端。cookie和session用来识别用户身份,实现对话控制。
cookie
- 服务端向客户端set-cookie,客户端每次请求带着cookie
- cookie存储着用户标识
- 默认有跨域限制:不跨域共享,不跨域传递
Session
- Cookie+session是常见的登录验证解决方案
- Session存储于服务端的一个对象,存储用户详细信息,和cookies一一对应
二者区别主要有以下四点:
-
存储位置:cookie存储客户端于临时文件夹中。session存储在服务器内存中。
-
安全性:Cookie 以明文的方式存储在客户端,安全性低,从可通过加密算法进行加密。session存储在服务器内存中,安全性较高。
-
大小:cookie数量大小有限制,数量不超过50 个,单个大小<4kb。session保存数据没有限制,内存有多大就能有多大。
-
生命周期:cookie的生命周期是累计的,创建时,开始计时,时间到,生命周期结束。 session的生命周期是间隔的,从创建时,就开始计时,期间无访问,销毁,有访问,重新计算,关机会造成session生命周期结束。
14. Cookie和token区别
- Cookie是HTTP规范,随着请求自动传递,token需要自己加上
- Cookie会默认被浏览器存储,token需要自己存储(storage)
- Token默认没有跨域限制
15. 常见的前端登录方案
Cookie和session
- 浏览器输入用户名密码,服务端校验,通过后把用户信息写入session
- 服务器通过set-cookie(用户标识)给浏览器
- 浏览器访问其他接口时候带着cookie,服务端根据cookie去session中去拿,定位用户信息。
JWT( JSON web token)
- 前端登录,登录成功后,后端返回加密token
- 前端自行存储token(其中包含了用户信息,加密了)
- 以后请求都带着token,作为用户信息
16. Session 和JWT哪个更好
Sesison优点
- 原理简单,易于学习
- 用户信息存储在服务端,可快速封禁某个用户
Session缺点
- 占用服务端内存,硬件成本高
- 多进程,多服务器时,不好同步,须使用第三方缓存,如reids
- 默认有跨域限制
JWT优点
- 不占用服务端内存
- 多进程、多服务不受影响
- 没有跨域限制
JWT缺点
- 用户信息存储在客户端,无法快速封禁用户
- 万一客户端密钥被泄露,用户信息会全部丢失
- Token体积一般会比cookie会增加数据请求量
适用场景
- 如果有严格的信息管理需求( 保密、快速封禁)推荐Session
- 如果没有特殊要求,建议使用JWT(如初创型网站)
17. localStorage和sessionStorage区别
localStorage和sessionStorage都是H5新增本地存储方式;本质是一个hash表。
目的解决cookie存储空间不足问题,每个只能最大只能存4kb,每个 domain限制20个cookie,而storage可以存储5M-10M。 主要区别如下:
生命周期
- localStorage的生命周期是永久的,除用户手动清除浏览器缓存。
- sessionStorage生命周期仅在当前会话有效,当标签页关闭会被清除。
作用域
- localStorage的作用域是在同一浏览器,可以共享localStorage数据。
- sessionStorage的作用域是在同一浏览器且同一窗口共享localStorage数据。
18. Cookies和localStorage能跨域吗
Cookies默认是不支持跨域 ,但是在二级域名是可以共享cookie的,实现单点登录。
Localstorage默认也是不支持跨域的,只有当一个页面通过iframe嵌入到另一个页面中,并且这两个页面具有相同的源时,它们可以共享localStorage。
19. 如何实现页面多标签通讯
Websocker
- 无跨域限制,需要服务端支持,成本高
- 客户端发消息给客户端,客户端再往下同步到其他页面
LocalStorage
- A页面设置localstorage,B页面监听localstorage值变化
<!--Eg:列表页,详情页 (点击修改标题按钮,同步到列表页)-->
<!--详情页-->
<body>
<button id="btn1">修改标题</button>
</body>
<script>
const btn1 = document.getElementById('btn1');
btn1.addEventListener('click',()=>{
const newInfo ={
id:100,
name:'标题'+Date.now()
}
localStorage.setItem('change',JSON.stringify(newInfo))
})
</script>
<!--列表页-->
<script>
// 当storage的值发生变化时候会触发
window.addEventListener('storage',()=>{
console.log(event.key) //change
console.log(JSON.parse(event.newValue)) // {id: 100, name: '标题1693969577645'}
})
</script>
SharedWork
- SharedWorker是webWorker的一种
- webWorker可开启子进程执行JS,但不能操作DOM
- SharedWorker可单独开启一个进程,用于同域页面通讯
- 调试不方便,不支持ie11
20. 如何实现页面和iframe间通讯
postMessage
<!--父元素:使用window.iframe1.contentWindow.postMessage进行发送信息-->
<body>
<button id="btn1">发送消息</button>
<iframe id="iframe1" src="./child.html"></iframe>
</body>
<script>
const btn1 =document.getElementById('btn1');
btn1.addEventListener('click',()=>{
// 发送消息 是否域名限制
window.iframe1.contentWindow.postMessage('hello','*')
})
// 接收信息
window.addEventListener('message',event=>{
// 来源域名信息
console.log(event.origin)
console.log(event.data)
})
</script>
<!--子元素:使用message事件接收信息-->
<body>
<button id="btn1">发送消息</button>
</body>
<script>
const btn1 =document.getElementById('btn1');
btn1.addEventListener('click',()=>{
window.parent.postMessage('world','*')
})
// 接收信息
window.addEventListener('message',event=>{
// 来源域名信息
console.log(event.origin)
console.log(event.data)
})
</script>
21. 对浏览器的理解
浏览器的主要功能是将用户选择的web资源呈现出来,它先是从服务器请求资源,然后将其展示在浏览器窗口中,资源的格式通常是HTML,也包括PDF、image及其他格式。用户用URL来指定所请求资源的位置。
HTML和CSS规范中规定了浏览器解释html文档的方式,由W3C这个组织对这些规范进行维护,W3C是指定web标准的组织。但是浏览器厂商会扩展,不严格遵循规范标准,这为web开发者带来了严重的兼容问题。
浏览器可以分为两部分,shell和内核。
- shell:是指浏览器的外壳,例如:菜单,工具栏等。主要是提供给用户界面操作,参数设置等。它是调用内核来实现各种功能。
- 内核:是浏览器的核心,内核是基于标记语言显示内容的程序和模块。
浏览器内核主要分为两部分:
- 渲染引擎:渲染引擎的职责就是渲染,默认情况下,渲染引擎可以显示html文档及图片,也可以借助插件展示其他的类型数据,比如:借助pdf阅读器,显示pdf格式。
- JS引擎:解析和执行JS来实现网页动态效果。
最开始渲染引擎和JS引擎没有区分很明确,后来JS引擎越来越独立,内核就倾向于指渲染引擎。
22. 常见浏览器内核比较
浏览器内核有四种,分别是:IE(trident)火狐(gecko)苹果和欧朋的(webkit)谷歌(blink)
- Trident
微软早起官方自带浏览器是IE,占据大量的市场份额,比较流行,后期微软长时间不更新,就导致了Trident和W3C标准脱节,还有本身存在很多bug和安全问题,现在使用的已经越来少少了。
- Gecko
火狐浏览器采用的内核,接口十分丰富,缺点也很明显,耗费资源和内存
- Webkit
Safari苹果采用的内核,优点就是页面渲染速度比较快,缺点就是兼容性不是很好,编写一些不标准的网页无法正确显示
- Blink
是 Webkit 的一个分支。是谷歌与欧朋共同研发,后面Opera 弃用了自己的 Presto 内核,加入 Google 阵营,跟随谷歌一起研发 Blink。
23. 浏览器同源策略
- ajax请求中,浏览器要求网页和服务端接口必须同源(安全)
- 同源:协议、域名、端口三者必须一致
- 加载图片、CSS、JS可无视同源策略
<img src="跨域的图片地址">
<link href ="跨域的css地址">
<script src="跨域的JS地址"></script>
- 所有的跨域,都必须经过服务端允许和配合
同源政策主要限制了三个方面:
- 当前域下的 js 脚本不能够访问其他域下的 cookie、localStorage 和 indexDB。
- 当前域下的 js 脚本不能够操作访问操作其他域下的 DOM。
- 当前域下 ajax 无法发送跨域请求。
24. 如何解决跨域问题
JSONP
- script标签天然具有跨域能力
- 服务端拼接信息
- 具有局限性, 仅支持get方法
CROSS
proty
通过代理,将前端发的浏览器请求转化为服务器向服务器的请求。
devServer:{
proxy:{
// 发送的路径 http://127.0.0.1/user/login
// 本地 http://localhost:8080/api/user/login
// 检查本地所有以/api开头的请求路径,将api前面的路径改为target路径
// 发送路径无api,替换成空串。
'/api':{
target:" http://127.0.0.1",
// 这一步完成后: http://127.0.0.1/api/users/info
pathRewrite:{'^/api': ""}, // 路径重写,如果路径以api开头,替换为空串
// http://localhost:4000/users/info
changeOrigin:true // 支持跨域
}
}
}
nginx代理跨域
#proxy服务器
server {
listen 81;
server_name www.domain1.com;
location / {
proxy_pass http://www.domain2.com:8080; #反向代理
proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
index index.html index.htm;
# 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
add_header Access-Control-Allow-Origin http://www.domain1.com; #当前端只跨域不带cookie时,可为*
add_header Access-Control-Allow-Credentials true;
}
}
- node中间件代理
var express = require('express');
var proxy = require('http-proxy-middleware');
var app = express();
app.use('/', proxy({
// 代理跨域目标接口
target: 'http://www.domain2.com:8080',
changeOrigin: true,
// 修改响应头信息,实现跨域并允许带cookie
onProxyRes: function(proxyRes, req, res) {
res.header('Access-Control-Allow-Origin', 'http://www.domain1.com');
res.header('Access-Control-Allow-Credentials', 'true');
},
// 修改响应信息中的cookie域名
cookieDomainRewrite: 'www.domain1.com' // 可以为false,表示不修改
}));
app.listen(3000);
console.log('Proxy server is listen at port 3000...');
前端直接调用
"login": [ function (cb, result) {
trainingcamp.login({phone,typecode,verify}, function (data) {
cb(null, data);
});
}],
login: function (d, cb) { // 登陆
console.log("---------这个是给后端接口的,再看看-------")
console.log(d)
api({
port: "CLASSWAPAPI",
path: "/passport/user/getphonelogincode",
method: 'post',
data: d
}, cb);
}
}
转载自:https://juejin.cn/post/7280436036948377634