前端常见八股文---浏览器

目录

1、什么是浏览器的同源策略?为什么要有同源策略?

2、怎么解决跨域问题?

2.1、JSONP

2.2、WebSocket

2.3、Cors

2.4、反向代理(node接口代理) 

2.5、Nginx

2.6、postMessage

3、浏览器的本地存储方式有哪些,有什么区别,分别有哪些应用场景?

3.1、webStorage

3.2、Cookie

3.3、IndexedDB

3.4、Web SQL

4、回流与重绘


1、什么是浏览器的同源策略?为什么要有同源策略?

官方解析:同源策略(Same-Origin Policy)是浏览器安全策略的一项重要规则,它限制了浏览器只允许当前网页的脚本与来自同一站点(协议、主机、端口号相同)的窗口进行交互,而限制了与不同源(协议、主机、端口号任一不同)的窗口进行交互。同源策略的目的在于有效保护用户的信息安全和隐私。

同源策略主要分为三种:

  • DOM同源策略: 限制了一个文档中的脚本只能访问同一来源的文档对象模型。
  • XMLHttpRequest请求同源策略: 限制了通过XMLHttpRequest发起的请求只能访问同一来源的资源。
  • Cookie、LocalStorage存储同源策略: 限制了对Cookie和LocalStorage的访问只能来自同一来源的页面。

如果不存在同源策略的限制,将会导致一系列Web攻击,如跨站脚本攻击(XSS)和跨站请求伪造(CSRF)。例如,通过iframe引入其他页面,用户在页面中输入了用户名和密码,恶意攻击者就可以获取不同源的DOM结构,进而获取用户的敏感信息。又如,用户向网站A发送的请求携带了对应的Cookie,当用户访问恶意网站B时,恶意脚本可能导致向网站A发送携带Cookie的Ajax请求,从而获取用户信息。同源策略的存在有助于防范这些安全风险。

2、怎么解决跨域问题?

官方解析:跨域问题(Cross-Origin Resource Sharing,CORS)是由于浏览器的同源策略(Same-Origin Policy)导致的。同源策略要求两个 URL 的协议、主机名和端口号都相同,否则就被认为是跨域的。浏览器在跨域请求时会受到同源策略的限制。

为了解决跨域问题,可以采用以下几种方法:

2.1、JSONP

因为浏览器同源策略的存在,导致存在跨域问题,以下这三个标签加载资源路径是不受束缚的:

1. script标签:<script src="加载资源路径"></script>
2. link标签:<link herf="加载资源路径"></link>
3. img标签:<img src="加载资源路径"></img>

JSONP利用了script标签的src加载不受同源策略限制的特性。通过在前端创建一个script标签,将回调函数名传递给后端,后端在接收到请求后,将所需数据拼接成一个字符串,形如callback(所需数据),并作为脚本返回给前端。前端接收到这个字符串后,会自动执行回调函数,从而获取数据。JSONP需要前后端协作,而且仅支持GET请求方法。

前端代码示例:

// index.html http://127.0.0.1:8080/index.html
 const jsonp = (url, params, cbName) => {
    return new Promise((resolve, reject) => {
        const script = document.createElement('script');
        window[cbName] = (data) => {
            resolve(data);
            document.body.removeChild(script);
        };
        params = { ...params, callback: cbName };
        const str = Object.keys(params).map(key => `${key}=${params[key]}`);
        script.src = `${url}?${str.join('&')}`;
        document.body.appendChild(script);
    });
};

jsonp('http://example.com/api', { name: 'john', age: 25 }, 'callback')
    .then(data => {
        console.log(data); // 处理获取的数据
    });

后端代码示例:

// index.js http://127.0.0.1:8000
const http = require('http');
const urllib = require('url');
const port = 8000;

http.createServer(function(req, res) {
    const { query } = urllib.parse(req.url, true);
    if (query && query.callback) {
        const { name, age, callback } = query;
        const person = `${name}今年${age}岁了!!!`;
        const str = `${callback}(${JSON.stringify(person)})`;
        res.end(str);
    } else {
        res.end(JSON.stringify('没有数据'));
    }
}).listen(port, function() {
    console.log('server is listening on port ' + port);
});

JSONP的缺点是需要前后端协作,且仅支持GET请求方法。

2.2、WebSocket

WebSocket在设计时就考虑了可以跨域使用,与同源策略不附属。与传统的跨域检测方式不同,WebSocket的跨域检测是由服务端来处理的。尽管浏览器仍会携带Origin跨域请求头,但服务端负责判断这次跨域WebSocket请求是否合法。这种设计机制为WebSocket提供了更灵活的跨域使用方式。

前端代码示例:

// index.html http://127.0.0.1:8080/index.html
 function myWebsocket(url,params){
 return new Promise((resolve,reject)=>{
 const socket = new WebSocket(url)
 socket.onopen = () => {
 socket.send(JSON.stringify(params))
 }
 socket.onmessage = (e) => {
 resolve(e.data)
 }
 })
 }
 myWebsocket('ws://127.0.0.1:8000',{ name:'mos', age:18 }).then(data=>){
 console.log(data) // mos今年18岁了!!!
 }

后端代码示例:

// index.js http://127.0.0.1:8000
const Websocket = require('ws');
const port = 8000;
const ws = new Websocket.Server({ port })
ws.on('connection',(obj) =>{
 obj.on('message',(data) =>{
 data = JSON.parse(data.toString())
 const { name, age } = data
 obj.send(`${name}今年${age}岁了!!!`)
 })
})
2.3、Cors

CORS,全称为Cross-Origin Resource Sharing,意为跨域资源共享。通常由后端来进行配置开启,一旦开启,前端就可以跨域访问后端。在前端跨域访问到后端时,后端开启CORS,发送Access-Control-Allow-Origin字段到前端,如果域名匹配,浏览器就不会执行跨域拦截,从而解决跨域问题。

前端代码示例:

// index.html http://127.0.0.1:8080/index.html
// 步骤一:创建异步对象
var ajax = new XMLHttpRequest();
// 步骤二:设置请求的url参数,参数一是请求的类型,参数二是请求的url,可以带参数
ajax.open('get', 'http://127.0.0.1:8000?name=梁理论&age=23');
// 步骤三:发送请求
ajax.send();
// 步骤四:注册事件 onreadystatechange 状态改变就会调用
ajax.onreadystatechange = function () {
    if (ajax.readyState == 4 && ajax.status == 200) {
        // 步骤五 如果能够进到这个判断 说明数据完美的回来了,并且请求的页面是存在的
        console.log(ajax.responseText);
    }
}

后端代码示例:

// index.js http://127.0.0.1:8000
const http = require('http');
const urllib = require('url');
const port = 8000;
http.createServer(function(req,res){
 // 开启Cors
 res.writeHead(200,{
 // 设置允许跨域的域名,也可设置*允许所有域名
 'Access-Control-Allow-Origin': 'http://127.0.0.1.8080',
 // 跨域允许的请求⽅法,也可以设置*允许所有⽅法
 "Access-Control-Allow-Methods": 'DELETE,PUT,POST,GET,OPIIONS',
 // 允许的header类型
 'Access-Contorl-Allow-Headers': 'Content-Type'
 })
 const { query : { name, age } } = urllib.parse(req.url, true);
 res.end(`${name}今年${age}岁啦!!!`);
}).listen(port,function(){
 console.log('server is listening on port ' + port);
})
2.4、反向代理(node接口代理) 

同源策略它只是浏览器的⼀个策略而已,它是不限制后端的,也就是前端-后端会被同源策略限制,但是后端-后端 则不会被限制,所以可以通过Node接口代理,先访问已设置Cors的后端1,再让后端1去访问后端2获取数据到后端1,后端1再把数据传到前端 

前端代码:

// index.html http://127.0.0.1:8080
//步骤⼀:创建异步对象
 var ajax = new XMLHttpRequest();
 //步骤⼆:设置请求的url参数,参数⼀是请求的类型,参数⼆是请求的url,可以带参数,动态的传递参数
starName到服务端
 ajax.open('get', 'http://127.0.0.1:8888?name=mos&age=18');
 //步骤三:发送请求
 ajax.send();
 //步骤四:注册事件 onreadystatechange 状态改变就会调⽤
 ajax.onreadystatechange = function () {
 if (ajax.readyState == 4 && ajax.status == 200) {
 //步骤五 如果能够进到这个判断 说明 数据 完美的回来了,并且请求的⻚⾯是存在的
 console.log(ajax.responseText);//输⼊相应的内容
 }
 }

后端1代码:

// index2.js http://127.0.0.1:8888
const http = require('http');
const urllib = require('url');
const querystring = require('querystring');
const port = 8888;
http.createServer(function (req, res) {
 // 开启Cors
 res.writeHead(200, {
 //设置允许跨域的域名,也可设置*允许所有域名
 'Access-Control-Allow-Origin': 'http://127.0.0.1:5500',
 //跨域允许的请求⽅法,也可设置*允许所有⽅法
 "Access-Control-Allow-Methods": "DELETE,PUT,POST,GET,OPTIONS",
 //允许的header类型
 'Access-Control-Allow-Headers': 'Content-Type'
 })
 const { query } = urllib.parse(req.url, true);
 const { methods = 'GET', headers } = req
 const proxyReq = http.request({
 host: '127.0.0.1',
 port: '8000',
 path: `/?${querystring.stringify(query)}`,
 methods,
headers
 }, proxyRes => {
 proxyRes.on('data', chunk => {
 console.log(chunk.toString())
 res.end(chunk.toString())
 })
 }).end()
}).listen(port, function () {
 console.log('server is listening on port ' + port);
})

后端2代码:
 

// index.js http://127.0.0.1:8000
const http = require('http');
const urllib = require('url');
const port = 8000;
http.createServer(function (req, res) {
 console.log(888)
 const { query: { name, age } } = urllib.parse(req.url, true);
 res.end(`${name}今年${age}岁啦!!!`)
}).listen(port, function () {
 console.log('server is listening on port ' + port);
})
2.5、Nginx

其实Nginx跟node接口代理是同一个道理,只不过Nginx不需要我们去搭建一个中间服务

server{
 listen 8888;
 server_name 127.0.0.1;
 
 location /{
 proxy_pass 127.0.0.1:8000;
 }
 }
2.6、postMessage

场景: 在 http://127.0.0.1:5500/index.html 页面中使用了 <iframe> 标签内嵌了 http://127.0.0.1:5555/index.html 的页面。

尽管这两个页面存在于同一个页面中,但需要使用 <iframe> 标签进行嵌套。这两个页面之间无法直接进行通信,因为它们的端口号不同,根据同源策略,存在跨域问题。

那应该怎么办呢?使用postMessage可以使这两个页面进行通信

// http:127.0.0.1:5500/index.html
<body>
 <iframe src="http://127.0.0.1:5555/index.html" id="frame"></iframe>
</body>
<script>
 document.getElementById('frame').onload = function () {
 this.contentWindow.postMessage({ name:'mos', age:18 }, 'http://127.0.0.1:5555')
 window.onmessage = function (e) {
 console.log(e.data) // mos今年18岁啦!!!
 }
 }
</script>
// http://127.0.0.1:5555/index.html
<script>
 window.onmessage = function (e) {
 const { data: { name, age }, origin } = e
 e.source.postMessage(`${name}今年${age}岁啦!!!`, origin)
 }
</script>

3、浏览器的本地存储方式有哪些,有什么区别,分别有哪些应用场景?

常见的浏览器的本地存储方式:webSrorage( localStorage 和 sessionStorage )、indexedDB、Cookies、Web SQL

3.1、webStorage

localStorage:
用于存储持久数据,除非用户手动将其从浏览器中删除,否则数据经终身存储,即使用户关闭窗口或选项卡,它也不会过期。

sessionStorage:
用户存储临时会话数据,页面重新加载后数据仍然存在,但当关闭浏览器或选项卡时数据就会丢失。

方法和属性:

  • setItem(): 用于存储数据,接受两个参数,即 key 和 value。使用形式:localStorage.setItem(key, value);
  • getItem(): 用于获取数据,接受一个参数 key,即需要访问其值的键。使用形式:localStorage.getItem(key);
  • removeItem(): 用于删除数据,接受一个参数 key,即需要删除其值的键。使用形式:localStorage.removeItem(key);
  • clear(): 用于清除其中存储的所有数据。使用形式:localStorage.clear();
  • key(): 该方法用于获取 localStorage 中数据的所有 key,接受一个数字作为参数,该数字可以是 localStorage 项的索引位置。

localStorage 和 sessionStorage 都非常适合缓存非敏感应用数据,可以在需要存储少量简单值的情况下使用。它们本质上是同步的,并且会阻塞主 UI 线程,因此需要谨慎使用。

3.2、Cookie

Cookie主要用于身份验证和用户数据持久性。Cookies与请求一起发送到服务器,并在响应时发送到客户端;因此,cookie数据在每次请求时都会与服务器交换。服务器可以使用 cookie 数据向用户发送个性化内容。严格来说,cookie 并不是客户端存储方式,因为服务器和浏览器都可以修改数据。它是唯一可以在一段时间后自动使数据过期的方式。

每个 HTTP 请求和响应都会发送 cookie 数据。存储过多的数据会使 HTTP 请求更加冗长,从而使应用比预期更慢:

  • 浏览器限制 cookie 的大小最大为 4kb,特定域允许的 cookie 数量为 20 个,并且只能包含字符串;
  • Cookie 的操作是同步的;
  • 不能通过 web workers 来访问,但可以通过全局 window 对象访问。

Cookie 通常用于会话管理、个性化以及跨网站跟踪用户行为。我们可以通过服务端和客户端设置和访问 cookie。Cookie 还具有各种属性,这些属性决定了在何处以及如何访问和修改它们。

Cookie 分为两种类型:

  • 会话 Cookie:没有指定 Expires 或 Max-Age 等属性,因此在关闭浏览器时会被删除;
  • 持久性 Cookie:指定 Expires 或 Max-Age 属性。这些 cookie 在关闭浏览器时不会过期,但会在特定日期(Expires) 或时间长度 (Max-Age) 后过期。
3.3、IndexedDB

IndexedDB 提供了一个类似 NoSQL 的 key/value 数据库,它可以存储大量结构化数据,甚至是文件和 blob。每个域至少有 1GB 的可用空间,并且最多可以达到剩余磁盘空间的 60%。

IndexedDB 特点如下:

  • 可以将任何 JavaScript 类型的数据存储为键值对,例如对象(blob、文件)或数组等。
  • IndexedDB API 是异步的,不会在数据加载时停止页面的渲染。
  • 可以存储结构化数据,例如 Date、视频、图像对象等。
  • 支持数据库事务和版本控制。
  • 可以存储大量数据。
  • 可以在大量数据中快速定位/搜索数据。
  • 数据库是域专用的,因此任何其他站点都无法访问其他网站的 IndexedDB 存储,这也称为同源策略。

IndexedDB 使用场景:

  • 存储用户生成的内容:例如表单,在填写表单的过程中,用户可以离开并稍后再回来完成表单,存储之后就不会丢失初始输入的数据。
  • 存储应用状态:当用户首次加载网站或应用时,可以使用 IndexedDB 存储这些初始状态。可以是登录身份验证、API 请求或呈现 UI 之前所需的任何其他状态。因此,当用户下次访问该站点时,加载速度会增加,因为应用已经存储了状态,这意味着它可以更快地呈现 UI。
  • 对于离线工作的应用:用户可以在应用离线时编辑和添加数据。当应用程序重新连接时,IndexedDB 将处理并清空同步队列中的这些操作。
3.4、Web SQL

Web SQL 是 HTML5 中的另一种本地数据库存储方案,采用 SQL 语句进行数据存储和查询。然而,目前已经被弃用,不建议使用。

应用场景:
Web SQL 类似于 IndexedDB,适用于需要离线存储数据和进行本地数据库操作等场景。

4、回流与重绘

回流一定会伴随着重绘,但是重绘可以单独发生。

页面渲染过程包括:

  • 解析 HTML 生成 DOM 树
  • 处理 CSS 生成 CSSOM 树,
  • 将 DOM 树和 CSSOM 树合并生成 Render Tree(渲染树,不包括 display:none 的节点和 head 节点,但包括 visibility:hidden 的节点),
  • 然后根据 Render Tree 得到每个节点的几何信息,
  • 最终将这些信息传送到 GPU 进行绘制。

回流(reflow): Render Tree 中节点的大小、边距等发生改变时需要重新计算几何信息的过程,称为回流。

重绘(repaint): 改变元素的字体颜色、背景颜色等不会影响页面布局变化的操作,称为重绘。

简而言之,影响页面布局的需要回流,不会改变页面布局的只需要重绘。回流的消耗较大(涉及到重新布局,一定会伴随着重绘),而重绘的消耗相对较小。

引起回流的操作

  • 页面初始化渲染
  • DOM 结构改变:删除、插入元素
  • 改变节点的几何信息:margin/padding/width/height/display:none/border/fontSize
  • 改变窗口大小(resize)
  • 获取某些属性的值:offsetTop, offsetLeft, offsetWidth, offsetHeight, scrollTop/Left/Width/Height, clientTop/Left/Width/Height, width, height
  • 调用了 getComputedStyle() 或者 IE 的 currentStyle

减少回流的方法

  • 避免逐条样式的改变,最好一次性改变多条,或者通过 class 实现,浏览器会对回流做优化,等到足够数量的变化发生时一次性进行批处理回流。
  • 避免循环操作 DOM 元素,可以先创建一个元素(文档片段),在这个元素上操作完后再插入页面。
  • 读取部分引起回流的属性时可以通过缓存的方式将结果保存,尽量不要频繁调用获取。
  • 将回流的元素设置成绝对定位、固定定位,使得元素脱离文本流,以减少代价。
  • 30
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值