专题:浏览器工作原理面试相关

1.一次完整的http服务过程

当我们在web浏览器的地址栏中输入:www.baidu.com,具体发生了什么?

  1. DNS域名解析,把一个网址关联上IP地址。
  2. 发送TCP连接,根据ip找到对应的服务器,给浏览器和服务器建立联系。
  3. 发送http请求。
  4. 服务器响应HTTP请求,浏览器得到html代码
  5. 浏览器解析html代码,并请求html代码中的资源(如js、css、图片等)(先得到html代码,才能去找这些资源)。
  6. 浏览器对页面进行渲染呈现给用户。
  7. 服务器关闭关闭TCP连接。

了解即可:

1.DNS怎么找到域名的?

DNS域名解析采用的是递归查询的方式,过程是,先去找DNS缓存->缓存找不到就去找根域名服务器->根域名又会去找下一级,这样递归查找之后,找到了,给我们的web浏览器返回ip。

2.为什么HTTP协议要基于TCP来实现?

TCP是一个端到端的可靠的面相连接的协议,他是一个比较底层的协议;HTTP基于传输层TCP协议不用担心数据传输的各种问题(当发生错误时,会重传)

3.最后一步浏览器是如何对页面进行渲染的?

a)解析html文件构成 DOM树
b)解析CSS文件构成渲染树
c)边解析,边渲染
d)JS 单线程运行,JS有可能修改DOM结构,意味着JS执行完成前,后续所有资源的下载是没有必要的,所以JS是单线程,会阻塞后续资源下载

2.浏览器是如何对页面进行渲染的?

浏览器渲染页面.png

  • 第一步:向web服务器发送请求之后,会返回index.html页面中的源码
  • 第二步:浏览器分配一个主线程,自动‘从上而下,自左向右’依次解析和执行代码
  • 第三步:
    • 当浏览器遇到link请求后,会去开辟新的线程加载这些资源文件(不会阻塞主线程解析);
    • 若果遇到style样式,正常解析,解析完成后再解析dom结构
    • 如果遇到@import,会让主线程去拿资源,导入之后,并且解析完成,才会继续渲染dom,这样会阻塞主线程
  • 第四步:若果遇到script标签样式,主线程会从服务器获取资源并解析,然后继续渲染dom结构。把js放到最后,防止js操作dom娶不到值。
    • 可以给标签设置async或defer属性,变成异步,或者放在底部,不会阻塞dom渲染
  • 第五步:生成dom树和css渲染树
  • 第六步:生成html渲染树,通过重构和重绘展示页面

有两个函数:

DOMContentLoaded

MDN的解释:当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完成加载。(也就是dom树有了,js执行加载了,会触发这个事件).

load 事件的触发:

当页面 DOM 结构中的 js、css、图片,以及 js 异步加载的 js、css 、图片都加载完成之后,才会触发 load 事件。
有两个概念

重构(回流):元素大小或位置发生变化的时候,会导致重新布局,重新计算元素位置和大小的过程叫重构。

  • 触发重构:
  • 页面第一次渲染的时候
  • 元素尺寸位置改变
  • 添加会删除可见dom
  • 浏览器尺寸变化

重绘:元素样式改变(color、background等非布局改变)

重绘不一定导致重构(回流),但重构了一定重绘
优化:

  • 1.在真实项目开发中,如果css代码不是很多(或者移动端项目),可以使用内嵌样式减少http请求,提高页面渲染速度。
  • link放在顶部是为了更快加载回css
  • script放在底部是为了获取dom元素或者不阻碍dom渲染
  • 2.减少回流:
    • 放弃传统的dom操作,基于vue数据驱动视图。此时注意样式和动画改变就?了
    • 样式集中改变,多个样式写在一个类里面,通过类来改变样式,尽可把操作样式写在dom树最末端
    • 缓存获取的值,需要改变的时候直接从缓存中获取,这样可以使读写分离,把它们放到渲染队列一次渲染
    • 元素批量修改,如果需要添加多个dom元素,可以利用文档碎片或者模板字符串
//创建文档碎片,都添加到里面,然后再添加到我们想要的添加的dom里面
let frag=document.createDocumentFragment();
for(let i=0;i<10,i++){
    let span=document.createElement('span')
    frag.appendChild(span)
}
navBox.appendChild(frag)
复制代码
    • 离线操作 DOM:把元素脱离文档流,然后对元素进行修改,这样只会导致重绘,而不会造成回流。
    • 将动画效果应用到 position 属性为 absolute 或 fixed 的元素上,避免影响其他元素的布局,这样只是一个重绘,而不是回流;
    • css硬件加速:利用transform、opacity、filters会触发硬件加速器,不会触发回流和重构

3.http缓存机制

  • 浏览器发送第一次http请求的过程中,可以在响应的时候进行缓存协商;在之后的发送请求过程中可以从缓存中拿数据。

3.1浏览器缓存分类

浏览器缓存分为强缓存和协商缓存,浏览器加载一个页面的简单流程如下:

    1. 浏览器先根据这个资源的http头信息来判断是否命中强缓存。如果命中则直接加在缓存中的资源,并不会将请求发送到服务器。(强缓存)
    1. 如果未命中强缓存,则浏览器会将资源加载请求发送到服务器。服务器来判断浏览器本地缓存是否失效。若可以使用,则服务器并不会返回资源信息,浏览器继续从缓存加载资源。(协商缓存)
    1. 如果未命中协商缓存,则服务器会将完整的资源返回给浏览器,浏览器加载新资源,并更新缓存。(新的请求)

强缓存

  • 命中强缓存时,浏览器并不会将请求发送给服务器。在Chrome的开发者工具中看到http的返回码是200,但是在Size列会显示为(from cache)。
  • 强缓存是利用http的返回头中的Expires或者Cache-Control两个字段来控制的,用来表示资源的缓存时间。
    • Expires:缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点,绝对时间如:Expires:Thu,31 Dec 2037 23:59:59 GMT
    • Cache-Control是一个相对时间,例如Cache-Control:3600,代表着资源的有效期是3600秒。由于是相对时间,并且都是与客户端时间比较,所以服务器与客户端时间偏差也不会导致问题。 Cache-Control:max-age=31536000

协商缓存

协商啥玩意啊?发送请求问服务器我缓存的内容有没有更新,若果没有更新,就用缓存资源并返回304;如果更新了就返回新的资源,状态码为200

若未命中强缓存,则浏览器会将请求发送至服务器。服务器根据http头信息中的Last-Modify/If-Modify-Since或Etag/If-None-Match来判断是否命中协商缓存。如果命中,则http返回码为304,浏览器从缓存中加载资源。

  • Etag/If-None-Match返回的是一个校验码(ETag: entity tag)。ETag可以保证每一个资源是唯一的,资源变化都会导致ETag变化。ETag值的变更则说明资源状态已经被修改。服务器根据浏览器上发送的If-None-Match值来判断是否命中缓存。

第一次响应时会发一个etag过来,当我们第二次请求的时候会请求一个etag,如果过两个etag相等就执行协商缓存

答题思路

  • http缓存技术主要是用来提高服务器并发效率的,有些资源我们可以直接拿取缓存,不需要重新发送请求。
  • 浏览器发送第一次http请求的过程中,可以在响应的时候进行缓存协商;在之后的发送请求过程中可以从缓存中拿数据。
  • 强缓存是利用Cache-Control:max-age=31536000字段设置的;协商缓存是利用Etag/If-None-Match来设置的。
    1. 浏览器先根据这个资源的http头信息来判断是否命中强缓存。如果命中则直接加在缓存中的资源,并不会将请求发送到服务器。(强缓存)
    1. 如果未命中强缓存,则浏览器会将资源加载请求发送到服务器。服务器来判断浏览器本地缓存是否失效。若可以使用,则服务器并不会返回资源信息,浏览器继续从缓存加载资源。(协商缓存)
    1. 如果未命中协商缓存,则服务器会将完整的资源返回给浏览器,浏览器加载新资源,并更新缓存。(新的请求)

4.fetch和axios

Fetch 也是前后端通信的一种方式。它是 XMLHttpRequest对象的一种替代方案,它是基于 Promise 的,浏览器原生提供这个对象.

Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。它本质上是对xhr的封装。

fetch优缺点:

优点:

  • 浏览器提供的原生api,性能比较好。
  • fetch()采用模块化设计,API 分散在多个对象上(Response 对象、Request 对象、Headers 对象),更合理一些;
  • fetch()通过数据流(Stream 对象)处理数据,可以分块读取,有利于提高网站性能表现,减少内存占用,对于请求大文件或者网速慢的场景相当有用。

缺点:

  • fetch()发出请求以后,只有网络错误,或者无法连接时,fetch()才会报错,其他情况都不会报错,而是认为请求成功。
  • fetch不支持超时时间控制,不能阻止请求过程
  • fetch没有办法原生监听请求的进度,而在上传大文件的时候我们想要看到进度条。而xhr可以进度检测。

axios的优缺点:

优点:

从浏览器中创建 XMLHttpRequests,可以进行web端的通信;也可以从node.js 创建 http 请求进行服务端通信。

  • 有请求拦截器和响应拦截器,可以实时做一些数据处理和条件校验。
  • 可以设置超时时间,并随时取消请求。
  • 自动转换 JSON 数据
  • 客户端支持防御 XSRF
  • 还提供了一些并发的请求接口

5.浏览器内多个标签页之间的通讯(多页面)

  • 浏览器数据存储的方式主要用本地存储方式解决。即调用 localStorage、Cookie等本地存储方式,来实现同源下多页面的数据通信。

5.1 Local Storage

  • Local Storage用于存储数据,但由于存在 storage这个事件,所以也可以对存储状态进行监听,从而达到页面间通信的目标, Chrome、Edge等浏览器下的这个 storage事件必须由其他同源页面触发.
// A页面
window.onstorage = function(e) {
  console.log(e.newValue); // previous value at e.oldValue
};
// B页面
localStorage.setItem('key', 'value');
复制代码

5.2 WebSocket

WebSocket是HTML5新增的协议,它的目的是在浏览器和服务器之间建立一个不受限的双向通信的通道,比如说,服务器可以在任意时刻发送消息给浏览器。实现多页签即时通讯。

几个关键步骤

需要前后端配合,前后端都要写上这些步骤

function socketConnect(url) {
    // 第一步: 创建WebSocket实例对象,连接ws协议
    let ws = new WebSocket(url); 
    // 第二步:写上open方法,连接建立起就会触发,可以在之里面发数据
    ws.onopen = e => {
        console.log('连接成功', e)
        ws.send('我发送消息给服务端'); // 客户端与服务器端通信
    }
    // 第三步:message方法监听服务器端返回的信息
    ws.onmessage = e => {
        console.log('服务器端返回:', e.data)
        // do something
    }
   // 第四步:还可以协商error方法和close方法,监视错误链接或关闭
    return ws; // 返回websocket对象
}
let wsValue = socketConnect('ws://121.40.165.18:8800'); // websocket对象
复制代码

可以利用封装好的socket.io来做

安装在本地项目

//npm i socket.io -S
//1.引入js文件
<script src="/socket.io/socket.io.js"></script>
//2.调用io接口
    const socket = io();
//3.发数据到后台,服务端用 socket.on('message',callback);接受
    socket.emit('自定义事件名', name);
//4.接受数据,服务端用socket.emit('message', name);
    socket.on('message', (msg) => {
        console.log(msg);
    });
复制代码

第二种 html5浏览器的新特性SharedWorker

环信客服、七陌客服去买

6.XSS、CSRF 以及如何防范

xss全称脚本跨站攻击,可以描述为黑客攻击你的浏览器,篡改浏览器的正常展示,从而窃取用户信息。

6.1分为三种:

  • 1.反射型–浏览器发送请求的过程中,xss代码出现在url中,目的是篡改请求信息,让服务器返回额外信息,比如个人私密信息。
  • 2.存储型–用户保存一些信息,发送给服务端。在这个过程中,黑客通过插入一些恶意脚本,把用户信息保存到数据库中,提交评论时,其他用户都会看到,造成了非常大的影响。
  • 3.dom型–服务器向浏览器发送html时,黑客加点恶意脚本,恶意篡改网站样式。

6.2 防御措施:`

(1)输入过滤:将用户输入的内容进行过滤。对所有用户提交内容进行可靠的输入验证,包括对 URL、查询关键字、POST数据等,仅接受指定长度范围内、采用适当格式、采用所预期的字符的内容提交,对其他的一律过滤。(客户端和服务器都要)

(2)输出转义

​ 例如: 往 HTML 标签之间插入不可信数据的时候,首先要做的就是对不可信数据进行 HTML Entity 编码 HTML 字符实体

function htmlEncodeByRegExp  (str){  
         var s = "";
         if(str.length == 0) return "";
         s = str.replace(/&/g,"&amp;");
         s = s.replace(/</g,"&lt;");
         s = s.replace(/>/g,"&gt;");
         s = s.replace(/ /g,"&nbsp;");
         s = s.replace(/\'/g,"&#39;");
         s = s.replace(/\"/g,"&quot;");
         return s;  
 }
var tmpStr="<p>123</p>";   
var html=htmlEncodeByRegExp (tmpStr)
console.log(html) //&lt;p&gt;123&lt;/p&gt;
document.querySelector(".content").innerHTML=html; //<p>123</p>
复制代码

(3)使用 HttpOnly Cookie

将重要的cookie标记为httponly,这样的话当浏览器向Web服务器发起请求的时就会带上cookie字段,但是在js脚本中却不能访问这个cookie,这样就避免了XSS攻击利用JavaScript的document.cookie获取cookie。

现代web开发框架如vue.js、react.js等,在设计的时候就考虑了XSS攻击对html插值进行了更进一步的抽象、过滤和转义,我们只要熟练正确地使用他们,就可以在大部分情况下避免XSS攻击。

CSRF

CSRF即跨站请求伪造,用户本来是想访问a网站(钓鱼网站),在这过程中跳出访问b网站(如银行)的链接,用户点击之后黑客冒充用户访问b网站,拿到b网站的用户信息。
CSRF防范措施主要是服务端做的,服务端验证请求头refer字段,加token或者手机验证码等。

7.vuex

VueX是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间数据的共享。它相当于一个公共仓库,保存着所有组件都能共用的数据。

7.1 三个特色

  • 能够在vuex中集中管理共享数据,易于开发和维护。
  • 能够高效实现组件之间的数据共享,提高开发效率。
  • 存储在Vuex中的数据是响应式的,能够实时保持数据与页面的同步。

7.2 使用步骤

  • 1.安装
  • 2.导入并使用
  • 3.创建store对象并导出
  • 4.将store对象挂载到vue实例

7.3 选项

7.3.1 state为唯一数据源

state提供唯一公共数据源,所有共享的数据都要统一放到Store的state属性中进行存储
mutations同步更新数据
actions异步操作数据,但要提交给mutations同步更新

组件如何拿到数据?

//组件获取数据
this.$store.state.xxx

/*
组件修改数据:
    1.当前组件绑定的方法中用dispatch方法触发actions里面的函数(派发)
    2.从actions的函数里面解构出commit,用于触发mutations里面的函数(触发)
    3.在mutations里面的函数中,解构出state,然后修改数据(操作)
*/
//如果不需要异步操作,可以直接跳过actions,组件直接通过commit()触发mutations中的函数
复制代码

7.3.2 modules模块

  • store对象中还有一个modules选项,用来对数据进行模块化管理的。在里面可以内嵌更小的store仓库
  • 如果需要操作模块中的数据:
    • 获取的时候需要加模块名this.$store.state.home.name
    • 操作的时候方法名前需要加模块前缀如:home/
modules:{
    home:{
        namespaced:true//开启命名空间
        states:{
            name:'蔡徐坤'
        },
        mutations:{ },
        actions:{}
    },
    category:{},
    cart:{},
    profile:{}

}
复制代码

7.4 映射方法

vuex还提供了几个方法,可以将全局的数据和方法映射为自己的数据和方法

1.通过mapState映射为当前组建的计算属性

import { mapState } from "vuex";
//在组建的computed选项里面使用
 ...mapState(["count"]),
  ...mapState('home',["count"]),
复制代码

2.通过mapMutations把mutations中的方法映射为当前组件的方法

import {mapMutations} from 'vuex';

  methods: {
    ...mapMutations(['sub']),
    //将全局中的sub方法,映射当前组建的方法,然后就可以当作自己的方法使用了
  }
复制代码

3.通过mapActions把actions中的方法映射为当前组件的方法

//第一步:引入函数到组件
import { mapActions } from "vuex";
//第二步:将指定的action函数映射为当前组件的函数,当前组件就可以当成自己的函数使用了
export default {
  methods: {
    ...mapActions(["subAsync"]),
  },
};
复制代码

8.封装组件

9.跨域

  • 跨域就是有浏览器参与的不同域之间(协议、域名、端口号)进行数据通信。
  • 跨域的出现是因为浏览器的同源策略,出于安全考虑,阻止跨域请求。
  • 跨域的解决有三种方法: ① CORS 跨域资源共享 ② JSONP,③服务器代理
    • 优先使用 CORS 跨域资源共享,如果浏览器不支持 CORS 的话,再使用 JSONP,最后再使用代理

方案一:CORS跨域资源共享(后端要做的 IE10)

后端在响应头设置:允许跨域
 Access-Control-Allow-Origin: *   // 表明允许所有的域名来跨域请求它    
 Access-Control-Allow-Origin: http://127.0.0.1:5500  // 只允许指定域名的跨域请求

CORS 跨域的过程
      ① 浏览器发送请求
      ② 后端在响应头中添加 Access-Control-Allow-Origin 头信息
      ③ 浏览器接收到响应
      ④ 如果是同域下的请求,浏览器不会额外做什么,这次前后端通信就圆满完成了
      ⑤ 如果是跨域请求,浏览器会从响应头中查找是否允许跨域访问
      ⑥ 如果允许跨域,通信圆满完成
      ⑦ 如果没找到或不包含想要跨域的域名,就丢弃响应结果
复制代码

方案二: Jsonp

  • JSONP 的原理:script 标签跨域不会被浏览器阻止,JSONP 主要就是利用 script 标签,加载跨域文件。
  • 缺点是仅支持get方法具有局限性,不安全可能会遭受XSS攻击。

使用 JSONP 实现跨域:

  • 前端声明一个回调函数,其函数名(如show)当做参数值,要传递给跨域请求数据的服务器,函数形参为要获取目标数据(服务器返回的data)。
  • 创建一个<script>标签,把那个跨域的API数据接口地址,赋值给script的src,还要在这个地址中向服务器传递该函数名(可以通过问号传参:?callback=show)。
  • 服务器接收到请求后,以函数调用形式传递参数。
1.后端创建一个接口,注意后面的aaaa是前端随便定义的:`https://www.imooc.com/api/http/jsonp?callback=aaaa`
2.这个接口打开的数据是这样的,以函数调用形式传输数据:
      aaaa(
        { "code": 200,
         "data": [ 
           { "word": "jsp" }, 
           { "word": "js" }, 
           { "word": "json" }, 
           { "word": "js 入门" }, 
           { "word": "jstl" } ] 
          }
          );
3.前端只需要在script标签里面引入这个接口,然后根据我们在接口自己定义的callback=aaaa声明一个aaaa函数
4.当发送请求时就调用了这个函数,把数据通过函数调用传递过来
复制代码

服务器端准备好 JSONP 接口:
   https://www.imooc.com/api/http/jsonp?callback=handleResponse

/*
前端手动加载 JSONP 接口或动态加载 JSONP 接口
*/
  const script = document.createElement('script');
  script.src ='https://www.imooc.com/api/http/jsonp?callback=handleResponse';
  document.body.appendChild(script);

//声明函数
      const handleResponse = data => {
        console.log(data);//拿到数据了
      };
//相当于通过函数执行传递参数给函数,函数内部获取数据
复制代码

方案三:服务器代理

利用服务器之间通信不需要跨域的特性,我们在使用vue框架的时候,在vue.config.js配置文件里设置服务器代理,就可以解决开发环境中的跨域问题。

//vue.config.js中devServer的代理功能(proxy)进行配置
module.exports = {
  devServer: {
    proxy: {
    //如果你的地址以/api开头,他就会请求到target里面的地址
      '/api': {
        target: '<url>',
        ws: true,
        changeOrigin: true//是否开启代理
        //这个选项就是把/api变成空
         pathRewrite : {
                    '^/api' : ''
                }
      }
    }
  }
}
复制代码

10.## 前端鉴权一般思路

  • 1 有些axios请求 需要token , 我们在axios的 请求拦截器 里面 配置 token
  • 2 有些页面 需要登录才能看 我们也可以用路由导航守卫 router.beforeEach 判断 token
  • 3.后台系统的侧边栏,不同的职位拥有不同的权限,他们看到的侧边栏和能操作的模块是不同的。
    • 不同权限的人登录给他展示不同的侧边栏。
    • 侧边栏一般是路由相关的页面,是需要循环生成的,不同的人,路由数组不一样,那么,循环生成的侧边栏,就不一样了.具体怎么实现请往下看:

这样就可以控制侧边栏 显示

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享