浏览器常用面试题

146 阅读6分钟

defer和async

两者都是异步加载js,加载阶段不会阻塞HTML解析,执行时机不同

  • defer 加载完js会在HTML解析完之后执行,多个defer可以保证执行顺序
  • async 加载完js会立刻执行,多个async不能保证执行顺序

加载阶段:浏览器把资源下载到本地的过程
解析阶段:意思就是将一个元素转换成另一个形式的过程,比如HTML解析,文件下载到本地就是包含字符串的文件,浏览器将字符串读取到内存,对其编译,将字符串转换成一种易于表达的数据结构(DOM树)。

DOMContentLoaded和load

  • DOMContentLoaded当HTML加载并解析完后执行,无需等待样式表、图片等资源的加载
  • load需要等待页面的HTML、JS、CSS、图片等资源全部加载完之后执行

浏览器输入URL发生了什么

  1. 浏览器进程通过进程间通信(IPC)把URL发送给网络进程
  2. 网络进程判断该URL是否在缓冲当中,有缓存直接将结果返回给浏览器进程,
  3. 没有缓存进行DNS解析,建立TCP连接,发送请求,最后返回数据,如果数据是下载内容会提交给浏览器的下载管理器
  4. 准备渲染进程:默认一个标签一个渲染进程,如果是同一站点就复用同一个渲染进程
  5. 提交文档:网络进程接收完数据会通知浏览器进程,浏览器进程会通知渲染进程和网络进程进行数据传输,此时会建立管道进行数据通信,传输完之后渲染进程会通知浏览器进程(管道就是操作系统在内核中开辟的一段缓冲区,进程A可以将需要交互的数据拷贝到这段缓冲区,进程B就可以读取了)
  6. 渲染阶段:DOM树、CSS树、Layout布局树、Layer图层树
  7. 关闭TCP连接

图层:拥有层叠上下文元素(设置定位、opacity、will-change等)、需要裁剪的地方
图层树构建:渲染进程将每个图层的绘制顺序放置在图层列表中,列表交给合成线程来绘制,合成线程将图层分成图块(图层大视口小),并在光栅化线程池将图块转换成位图(GPU加速),最后将位图发送给合成线程

重排和重绘

参考之前文章
重排和重绘优化

XSS攻击

Cross Site Scripting 跨站脚本攻击,通过巧妙的方法注入恶意脚本使用户加载执行。可盗取用户信息(窃取Cookie),分为存储型、反射型和文档型。

  • 存储型:恶意脚本存储在服务端,客户端获取后执行
  • 反射型:恶意脚本是通过作为网络请求的参数,经过服务器,然后再反射到客户端执行
  • 文档型:数据传输过程劫持网络数据包,修改HTML文档。

预防XSS攻击

  • 对用户输入转码
  • 开启CSP,本质是开启白名单,告诉浏览器哪些资源可以加载。
    1.设置 HTTP 首部中的 Content-Security-Policy
    2.设置meta标签:<meta http-equiv="Content-Security-Policy">
  • Cookie设置HttpOnly:JavaScript 便无法读取 Cookie 的值

CSRF攻击

CSRF(Cross-site request forgery), 即跨站请求伪造,指的是攻击者诱导用户点击链接,打开第三方网站,然后利用用户目前的登录状态发起跨站请求。主要有三种攻击类型

  • GET类型:img的src属性是会进行请求的,黑客可以利用这点去进行攻击,同时Cookie又是紧跟域名的,发起请求的同时Cookie也会被带上
  • POST类型:构建一个表单,默认隐藏,用户进入页面自动提交表单
  • 链接类型:在a标签构建一个请求,诱导用户点击

预防CSRF攻击

  • 设置Cookie的SameSite属性为严格模式Strict,禁止第三方获取Cookie
  • CSRF Token验证,服务器向浏览器返回一个随机字符串作为token,后面请求每次请求都携带这个token给服务端验证

跨域常用解决方案

浏览器同源策略:协议、域名、端口都相同,否则就会跨域

JSONP

利用<script>标签没有跨域限制,src带有callback参数,服务端将结果数据拼接到回调函数中,返回给浏览器,浏览器执行。缺点就是只能发生GET请求。

跨域资源共享(CORS)

如果是普通跨域:服务端设置Access-Control-Allow-Origin为*Origin
如果需要传递Cookie:

  • 前端设置withCredentials:true
  • 服务端设置Access-Control-With-Credentials:true
  • 服务端设置Access-Control-Allow-Origin设置为Origin值(非*)

nginx代理跨域

通过Nginx配置一个代理服务器域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域访问。

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中间件实现跨域代理,原理大致与nginx相同,都是通过启一个代理服务器,实现数据的转发

存储

  • Cookie:用于记录用户状态的一种方式,最大容量4k,紧跟域名
  • LocalStorage:HTML5提供的本地存储方式,最大容量5M
  • SessionStorage:HTML5提供的本地存储方式,最大容量5M,窗口关闭就失效了
  • indexDB:一种本地的数据库存储机制,能存储大量数据(取决硬盘容量)

事件

事件机制

分为三阶段:捕获阶段、目标阶段、冒泡阶段

  • 捕获阶段(外到内):事件从window流向目标节点,在途径节点触发捕获事件,直到目标节点
  • 目标阶段:事件到目标节点就触发
  • 冒泡阶段(内到外):事件在目标节点触发后不会停止,往window冒泡

addEventListener

监听事件第三个参数默认是false,代表冒泡事件。设置true代表捕获事件。
也可以设置成一个对象,有三个属性:

  • capture:布尔值,同上
  • once:布尔值,为true表示监听函数执行一次就解绑
  • passive:布尔值,为true表示永远不会调用 preventDefault

事件委托(事件代理)

事件的委托是指将事件统一绑定给元素的共同的祖先元素,这样当后代元素上的事件触发时,会一直冒泡到祖先元素,从而通过祖先元素的响应函数来处理事件。事件委派是利用了冒泡,通过委派可以减少事件绑定的次数,提高程序的性能,节省内存。

例:给下述代码的p标签绑定点击事件

<div>
    <p>1</p>
    <p>2</p>
    <p>3</p>
</div>

给每个p绑定监听事件

[].map.call(document.querySelectorAll('div p'),function(item){
    item.addEventListener('click',function(e){
        console.log(e.target.innerText);
    })
})

使用事件委托

document.querySelector('div').addEventListener('click',function(e){
    console.log(e.target.innerText);
})