# 浏览器

# 1.BOM属性有哪些方法?(BOM是浏览器对象模型)DOM(文档对象模型)

常用document.getElementById(根据id找到元素) document.getElementsByName(返回指定名称的元素的集合)

# 1.1location对象

location.href返回当前网页的URL
location.search返回URL中参数字符串即?号后的内容
location.hash返回#后面的内容
location.host返回域名 www.baidu.com
location.hostname 返回主域名部分 baidu.com
location.reload() 重载当前页面
1
2
3
4
5
6

# 1.2history对象

history.go()前进或后退指定的页面数
history.back()后退一页
history.forward()前进一页
1
2
3

# 1.3navigator对象

navigator.userAgent 返回用户代理头 内容包含浏览器版本手机系统等等

(localStorage.setItem(key, value) localStorage.getItem(key) sessionStorage设置和取出用法相同) 都是将数据保存在浏览器端 cookie中的数据可以传给服务端 document.cookie="key=value"; 删除只需把过期时间设置为现在以前即可,修改可以用同一个key不同的value来覆盖原来的value。

存储时间:

  • cookie在过期时间之前一直有效(cookie不设置过期时间,默认就是关闭浏览器失效)
  • sessionStorage仅在当前浏览器窗口关闭前有效
  • localStorage可以持久保存数据,需要手动清除 (对于同一个网站,不同标签页共享cookie和localStorage,不共享sessionStorage) 存储大小:
  • cookie只有4K
  • sessionStorage和localStorage大概可以保存5M信息 服务器交互: cookie会自动在请求头中携带,在服务器和浏览器之间相互传递 sessionStorage和localStorage不会自动把数据发送给服务器

cookie经常用来保存用户登陆状态(http无状态,需要用cookie存用户登陆状态),设置过期时间在浏览器保存用户id session可以用来存储用户的登陆信息

cookie存在跨域问题,现在常用localStorage存token

# sessionStorage多窗口不能共享状态吗?

不一定。 正常来说,每个tab会创建自己的sessionStorage,关闭时会清除对应的sessionStorage 但是在本页面打开新同源页面会临时共享之前页面的sessionStorage(比如通过window.open打开同源的页面) =》等于打开时带过去,但是之后再在原页面修改新页面不会获取到新的值=》MDN上称为复制

# 3.cookie和session区别(单个cookie保存的数据不超过4K)?

  • cookie的数据保存在客户端,session的数据保存在服务端
  • cookie最大只能保存4K,session的大小没有限制,但是过大会消耗服务器性能
  • cookie不安全,攻击者可以通过分析本地的cookie进行cookie欺骗。如果数据考虑到安全需要使用session

# Session工作原理?

浏览器第一次访问服务器时,服务器会创建一个session对象,把产生的session_id放在响应头返回给浏览器, 浏览器收到后会保存到cookie中,下次再访问时,就会把session_id放到请求头发送给服务器, 服务器通过session_id找到对应的session. (用户数据都保存在session中,session机制决定当前客户端只会获取到自己的session)

cookie具有不可跨域名性。 设置setMaxAge为0会删除cookie,修改cookie通过新增同名cookie覆盖用来的cookie,只能修改value和maxAge,否则视为新增。 如果不希望Cookie在HTTP等非安全协议中传输,可以设置Cookie的secure属性为true。这样浏览器只会在HTTPS和SSL等安全协议中传输此类Cookie

# 4.在地址栏输入一个url到页面渲染完毕,中间发生了什么?=>(网络阶段=>解析阶段=>渲染阶段)

DNS解析->TCP连接->发送http请求->服务器处理请求->返回响应->浏览器渲染页面

# DNS解析过程

先去浏览器找是否有该域名对应的ip地址,没有会去系统缓存中查找 也没有就会去本地域名服务器查找,如果没找到会去根域名服务器请求解析 根域名会返回该域名所属的主域名服务器地址 本地服务器再去主域名服务器查询,主域名服务器没找到会递归向下一级的子域名服务器查找,直到找到返回给本地域名服务器 本地域名服务器将结果进行缓存,并返回给客户机

# TCP连接+HTTP请求

DNS解析得到ip地址和端口后=》 浏览器会构造一个http请求,请求报文包括请求行(method=》get,post等,requestUrl,http version)请求头和请求体 然后把http请求封装在tcp包中,tcp包一次经过(传输层,网络层,数据链路层,物理层)到达服务器

# 浏览器如何渲染页面|浏览器的运行机制

  • 解析html文件构建dom树
  • 解析css文件构成CSS规则树
  • 根据dom树和css规则树构建渲染树
  • 布局(layout,计算出每个节点在屏幕的位置)和绘制渲染树(遍历render树,绘制每个节点)

# css是否阻塞html解析,渲染?

css不会阻塞dom树的解析(css解析和dom树的解析是并行操作),但会阻塞dom树的渲染 js会阻塞dom树的解析和渲染 =>如果css不阻塞dom的渲染,那么会在解析css的过程中渲染一次,而css解析完又会重新渲染一次,浪费性能

# 6.重绘和重排(回流)?

(浏览器需要重新渲染树,就是重排(大),只重新绘制受影响的部分是重绘(小))

  • 重绘:浏览器重新绘制,改color,background-color等外观属性都会触发
  • 重排:隐藏或修改尺寸大小/添加或删除dom元素等都会触发。每个页面第一次加载的时候都会触发一次重排。
  • 重排一定触发重绘,重绘不一定触发重排

# 6.1减少重绘重排的方法?

  • 少进行dom操作
  • 对于多次触发重排的元素可以使用绝对定位脱离文档流
  • 统一改变样式,不要一个个元素改

# 7.跨域|同源策略?

同源指的是协议,域名,端口都相同。三者有任一不同,就构成了跨域。同源目的是为了浏览器安全。 后端不存在跨域,跨域是因为浏览器的同源政策。

# 如何解决跨域?

  • jsonp(将声明的回调函数名称拼接到请求的url后面,通过在script元素的src属性进行发送。服务器接收到请求后, 把函数名和需要返回给客户端的数据拼接成字符串进行返回,客户端调用之前声明的回调函数对返回的数据进行操作。要求必须是get请求。一定要服务端配合) 浏览器对于script,iframe等标签的src等属性,是没有同源策略限制的
function jsonp ({ url, onData, params }) {
  const script = document.createElement('script')
  // 一、为了避免全局污染,使用一个随机函数名
  const cbFnName = `JSONP_PADDING_${Math.random().toString().slice(2)}`
  // 二、默认 callback 函数为 cbFnName
  script.src = `${url}?${stringify({ callback: cbFnName, ...params })}`
  // 三、使用 onData 作为 cbFnName 回调函数,接收数据
  window[cbFnName] = onData;
  document.body.appendChild(script)
}
// 发送 JSONP 请求
jsonp({
  url: 'http://localhost:10010',
  params: { id: 10000 },
  onData (data) {
    console.log('Data:', data)
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  • cors(需要服务器响应头设置允许跨域Access-Control-Allow-Origin: *) =》安全问题 并且不能携带cookie =》cors实际上浏览器发出了请求,服务端也正常返回,只是浏览器不接收。需要设置后才可以接收
  • nginx反向代理(正向代理是客户端代理,反向代理是服务端代理)=>反向代理为同一域名就不存在跨域 location /api {

    # 或者是 http://localhost:8080

    proxy_pass http://api.shanyue.tech; } 常用的是将/api开头的都代理到后端的服务器上
  • proxy(本地 webpack-dev-server)

# 8.前端如何反向代理proxy(用来本地测试)?

vue.config.js可以配置会自动代理到后端。相当于vue开启了一个服务器,通过服务器转发你的请求到跨域的后端服务器。(服务器之间没有跨域)

devServer: {
    proxy: {
        '/api': { // 需要代理的接口
            target: '后端url',
            changeOrigin: true,//是否跨域
            pathRewrite: { // 重定向地址
                '^/api': ''
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11

# 9.浏览器请求头的connection:keep-alive作用?

说明TCP连接保持打开状态,浏览器可以继续发送请求

# 10.多个页面之间通讯问题?

  • localstorage存储,监听storage事件,就可以获得存储的值

# 12.虚拟dom解决的问题?

  • 用来解决浏览器性能问题。
  • 操作dom时,浏览器会从构建dom树开始整个流程执行一遍,频繁操作dom会引起不必要的节点位置计算,影响浏览器性能。

# 13.虚拟dom实现原理?

用js对象模拟dom树,根据diff算法比较新老dom树的差异,再用patch算法将差异应用到真正dom树上

# 虚拟dom和真实dom的区别?

  • 虚拟dom是以对象的形式模拟真实dom树
  • 操作虚拟dom消耗性能小,并且不会进行重排和重绘的操作
  • 操作真实dom,会触发频繁的重绘和重排,性能消耗比较大
  • 当HTML解析到script标签时,会暂停构建DOM,直到解析完成才会从暂停的地方重新开始构建

# 14.cookie常用字段?

  • path:在该路径下的页面才能访问到cookie
  • domain:子域,在该子域下才可以访问cookie,一般设置/=》表示同站点下的所有页面都可以访问这个cookie =》如domain设置为 .a.com,则b.a.com和c.a.com均可使用该Cookie
  • name:cookie名称
  • value:cookie值
  • httponly:设置不允许通过js来访问cookie(dom api的方式,防xss)
  • expires:设置过期时间
  • *same:限制第三方cookie

# 15.http缓存机制了解吗?

  • 会先判断是否有强缓存,(根据header判断)=》状态码200

  • 如果没有,会发请求到服务器验证是否命中协商缓存,如果也没有,才去服务器直接取数据。

  • 强缓存(Expires或Cache-control,同时存在Cache-control优先级更高,状态码返回200) cache-control: no-cache会缓存,开启后每次会像服务端发请求验证才可以使用缓存 no-store不缓存 强缓存可以通过设置cache-control:max-age或者expires: Sun, 06 Mar 2022 09:28:39 GMT

  • 协商缓存(Last-Modified/If-Modified-Since/If-UnModified-Since,Etag/If-None-Match/If-Match, ETag优先级更高,状态码返回304) 协商缓存可以通过last-modify或者etag来进行判断

  • Last-Modified/If-Modified-Since 浏览器发送请求,返回的响应头会有last-modify。 当再次请求last-modify会做为If-Modify-Since放到请求头里给服务端校验

  • Etag/If-None-Match 浏览器发送请求,返回的响应头会有ETag 当再次请求Etag会作为If-None-Match放到请求头里给服务端校验。(服务端校验是否和原来的Etag值相等)

# 强缓存和协商缓存各自存在的问题

  • expire可能存在时间不同步的问题,expires用的是绝对时间,cache-control用的是相对时间。
  • last-modify有个精度问题,到秒。e-tag没有精度问题,只要文件改变,etag值就发生改变=》etag是服务器随机生成的字符串

# 16.DNS解析过程(DNS是域名系统缩写,DNS解析就是将域名转换成对应的ip地址)?

浏览器缓存->系统缓存->本地域名服务器->根域名服务器->主域名服务器去找->去子服务器去找->继续往下查找直到知道->返回给本地域名服务器->返回给客户机。

  1. 先检查浏览器缓存是否有域名对应的ip地址
  2. 没有会去系统缓存中查询
  3. 都没有采取请求本地域名服务器解析
  4. 本地服务器没有会去根域名服务器查询
  5. 根域名服务器返回所查询域的主域名服务器地址
  6. 本地服务器去主域名服务器查询
  7. 主域名服务器如果没有找到,会重复向下一级的子域名服务器查询,直到找到
  8. 本地域名服务器把结果进行缓存,并返回给客户机
  • 先去浏览器找是否有该域名对应的ip地址,没有会去系统缓存中查找
  • 也没有就会去本地域名服务器查找,如果没找到会去根域名服务器请求解析
  • 根域名会返回该域名所属的主域名服务器地址
  • 本地服务器再去主域名服务器查询,主域名服务器没找到会递归向下一级的子域名服务器查找,直到找到返回给本地域名服务器
  • 本地域名服务器将结果进行缓存,并返回给客户机

拆分就是:客户机(浏览器=>系统) 服务器(本地域名=>根域名=>本地域名 本地域名=>主域名) 递归(主域名的子域名递归查找=》本地域名服务器) 客户机

# 17.a.com的cookie在b.com能访问到吗?

能。在a.com域名下的网站引入b.com域名下的js,这个js需要获取b.com的cookie。SSO单点登录通过统一的验证中心。

# *18.常见的浏览器内核?

  • Safari,Chrome都是webkit内核=》chrome现在调整为blink内核
  • 浏览器引擎分成:渲染引擎和JS引擎
  • 渲染引擎负责将html和css等渲染到页面上,不同内核渲染效果不同,我们平时说的浏览器兼容就是兼容不同内核。
  • JS引擎负责解析js来实现网页动态效果
  • 因为http协议无状态,所以登录需要辨别客户端的身份。
  • cookie-session在负载均衡时有问题,分发的机器可能没有保存用户信息的session。
  • token的没有这种问题。

token登陆流程:

  1. 发送请求到服务端,服务端返回一个带签名的token给客户端
  2. 客户端存储token,每次请求都携带这个token
  3. 服务端验证token,成功才返回数据

# *20.service worker?

常用来做缓存文件,提高首屏速度

# 21.performance API中哪个指标用来衡量首屏时间?

window.performance.timing

# 22.load和domContentLoaded事件的先后顺序?

  • 初始HTML文档被完全加载和解析后,domContentLoaded事件被触发(无需等待样式,图像的完全加载)
  • 当整个页面及所有依赖资源(样式表和图片)都加载完成时,将触发load事件 =》domContentLoad触发,说明dom结构加载完成,而load触发说明页面所有资源都加载完成

# *23.Data Url是什么?

  • 平时主要用来把图片转成base64格式嵌入网页,是前缀为data:的URL。
  • 缺点:base64格式的图片会比原来的体积大三分之一左右,不会缓存每次都要下载(可以写入到css中,随css被缓存下来)

# 24.gzip如何开启?

通过nginx来开启gzip=》nginx的http配置里加上gzip on|off

# 原理

主要是对重复数据做处理,所以重复度越高的文件可压缩的空间越大

# 可以对图片进行gzip压缩吗

可以,但是基本都不开启,因为可能会使它们变得更大。

# *25.web worker?

  • 百万条数据计算,会造成页面假死(GUI渲染线程和JS引擎线程互斥,js有大量计算时,会造成UI阻塞,严重就是页面卡死)
  • web worker主要还是通过多线程解决问题。web worker线程中可以执行ajax请求

# *26.前端大文件存储?

可以用indexedDB,按key/value形式存储,更接近于非关系型数据库

# *27.页面级数据暂存?

比如浏览到第几页=》通过把整个组件的data缓存到localStorage来实现。因为$data是只读的,所以要通过Object.assign()把拷贝的对象返回给data

# 28.浏览器架构?

  • 浏览器主要由用户界面(包括地址栏,书签等),浏览器引擎(提供了开始加载URL,前进后退等方法),渲染引擎(负责显示请求的内容),网络(HTTP请求),
  • 显示后端(包括窗口,组合框等),JS解释器器,XML解析器(将XML文档解析成DOM),存储这八个部分组成。

# 29.内存泄漏(memory leak)?

  • 内存泄漏就是指内存得不到释放,最后会导致内存溢出
  • 原因:垃圾回收无法回收这块内存(这样这块内存就被浪费了,就是内存泄漏)

# 常见的有=>循环引用及不规范使用闭包导致的内存泄漏(事件重复监听未移除也会导致)

  1. 闭包导致的内存泄漏无法解决,只能尽量避免使用闭包
  2. 全局变量
  3. 定时器

# 优化手段

  • 堆内存:参数指针指向null即可
  • 堆内存:取消上下文引用即可

# *30.前端性能优化-RAIL了解过吗?

  • RAIL是个性能模型,RAIL分别对应Response,Animation,Idle,Load
  • Response响应速度
  • Animation动画的渲染速度
  • Idle最大空闲空间=》可以在空闲空间执行一些延后的任务,但是必须以用户的交互为最高优先级
  • Load:页面加载时间

# 分析RAIL工具

  • Chrome DevTools
  • LightHouse

# *31.浏览器有哪些进程了解吗?

  • 浏览器包含了browser进程(浏览器主进程),第三方插件进程,GPU进程(浏览器渲染进程)。
  • GPU进程包括:GUI渲染线程,JS引擎线程,事件触发线程,定时器线程,HTTP请求线程。GUI渲染线程和JS引擎线程互斥,一个执行另一个会被挂起
  • 浏览器页面初次渲染后,JS引擎线程和事件触发线程工作流程:
  1. 同步任务在JS引擎线程上执行,形成执行栈
  2. 主线程外事件触发线程管理一个任务队列,只要异步任务有了结果就在任务队列中放置一个事件
  3. 执行栈中的同步任务执行完毕,系统就会读取任务队列,如果有异步任务要执行,将其加到主线程的执行栈中并执行其异步任务

# 32.如何统计当前页面出现的所有标签?

# 获取所有标签的API

  • document.querySelectorAll('*')
  • document.getELementsByTagName('*')
  • $$('*')=>浏览器控制台使用

# 实现

// 通过reduce去比较大小,返回最多的标签
const maxBy = (list, keyBy) => list.reduce((x, y) => (keyBy(x) > keyBy(y) ? x : y));
function getFrequentTag() {
  // 获取到所有标签
  let tags = [...document.querySelectorAll("*")]
    .map((x) => x.tagName)
    // 通过reduce去计算每个标签出现的次数
    tags = tags.reduce((o, tag) => {
      o[tag] = o[tag] ? o[tag] + 1 : 1;
      return o;
    }, {});
  return maxBy(Object.entries(tags), (tag) => tag[1]);
}
// 最后返回的是个数组,key是最多的标签,value是出现的次数['LI', 1120]
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 33.localhost:3000和localhost: 5000的cookie共享吗?

共享。对浏览器来说,cookie区分域,不区分端口,所以共享。(https和http协议不同也共享)