西安腾讯前端一面试总结

事发突然很难受,刚睡醒,就接到腾讯面试官的电话,之前一直在想面试会问啥,其实这次面试并不能,出乎意料的简单,但是错失了机会。基础问题,被说安全基础差,踏实学习吧。之前一直浮躁,从没用认识思考基础知识,而且这是第一次面试,超级紧张。

现在回顾复盘一下问了一些什么,附带应该有的回答

1、你什么时候开始学习前端的,项目经历
2、你从输出一个url到看到页面经过的过程
  • 输入网址:浏览器在输入网址的时候,就可能只能的匹配url
  • DNS解析:详细见4问(回答的不具体,然后展开又问了一下)
  • 建立TCP连接
  • 向客户端发起HTTP请求
  • 服务器处理请求
  • 服务器响应
  • 浏览器展示HTML
3、页面渲染的过程
  • 根据html文件构建DOM树和CSSOM树。构建DOM树期间,如果遇到JS,阻塞DOM树及CSSOM树的构建,优先加载JS文件,加载完毕后,继续再构建DOM树及CSSOM树

  • 构建渲染树

  • 页面的重绘与重排。页面渲染完成后,若JS操作了DOM节点,根据JS对DOM操作动作的大小,浏览器会对页面进行重绘和重排

    重排:由于布局的改变引起的页面重新渲染 重绘:由于样式的改变引起的页面重新渲染)

4、DNS具体解析的过程
  1. 首先会在本机的hosts的文件里寻找,如果没用就向本地DNS服务器进行递归查询

  2. 本地服务器采用迭代查询。它向一个根域名服务器查询。

  3. 根域名服务器告诉本地服务器,下一次查询的顶级域名服务器的ip地址

  4. 本地域名服务器向顶级域名服务器查询

  5. 顶级域名服务器告诉本地域名服务器下一把的权限服务器的ip地址

  6. 本地域名服务器向权限服务器进行查询。

  7. 权限服务器告诉本地服务器所查的主机的ip地址

  8. 本地域名服务器把查询结果告诉主机

    整个过程共用到8个UDP报文

5、三次握手、四次挥手的细节
  • 三次握手
    1. 客户端发送syn包(seq=x)到服务器,并进入SYN_SEND状态,等待服务器确认
    2. 服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己页发送一个SYN包(seq=y),即SYN+ACK包,此时服务器进入SYN_RECV状态
    3. 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入Established状态
  • 四次挥手
    1. 主动关闭方发送一个FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不会再给你发送数据了(当然,再FIN包之前发送出去的数据,如果没用收到对应的ack确认报文,主动关闭方依然会重发这些数据),但是此时主动关闭方还可以接受数据
    2. 被动关闭方收到FIN包后,发送一个ACK给对方,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)
    3. 被动关闭方发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据页发送完了,不会再给你发送数据了。
    4. 主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+!,至此,完成四次挥手

既然都到这里了,我们把传输过程中的问题页一并复习了吧

  • 超时重传:超时重传机制用来保障TCP传输的可靠性。每次发送数据包时,发送的数据宝都有seq号,接受端收到数据后,会回复ack进行确认,表示某一seq号数据已经收到。发送方在发送了某个seq包后,等待一段时间,如果没有收到对应的ack回复,就会认为报文丢失,会重传这个数据包。
  • 快速重传:接受数据一方发现有数据包丢失掉了,就会发送ack报文告诉发送方重传丢失的报文。如果发送端连续收到标号相同的ack包,则会触发发送端的快速重传。(超时重传就类似于发送端傻等超时,而快速重传就类似接受端主动告诉发送端,没收到,而触发超时重传)
  • 流量控制:这里主要说TCP的滑动窗口流量控制。TCP头有个字段叫WIndow,由叫Advertised-Window,这个字段是接受端告诉发送端自己还有多少缓冲区可以接受数据。于是发送端就可以根据这个接受端的处理能力来发送数据,而不会导致接收端处理不过来。滑动窗口可以提高TCP传输效率的一种机制。
  • 拥塞控制:滑动窗口用来做流量控制。流量控制只会关注发送端和接受端自身的状况,而没有考虑整个网络的通信情况。拥塞控制,则是基于整个网络来考虑的。考虑一下这样的场景:某个时刻网络上的延时突然增加,那么,TCP对这个事做出的应对只有重传数据,但是,重传会导致网络负担更重,于是会导致更大的延迟以及更多丢包,于是,这种情况会进入恶心循环被不断地放大。试想一下,如果一个网络内有成千上万的TCP连接都这样行事,会马上形成“网络风暴”,TCP这个协议就会拖垮整个网络。为此TCP就引入了拥塞控制策略。拥塞策略算法主要包括:慢启动,拥塞避免,快重传,快速恢复。
    • 慢开始:刚开始发送方维护一个拥塞窗口,大小等于发送窗口,通过出现了超时来判断网络出现拥塞。慢开始的思路是一开始发送方发送一个字节,在收到接受方的确认,然后发送的字节数量增大一倍(就是以指数增长的速率),逐步增大拥塞窗口,当拥塞窗口到达满开始门限,停止慢开始算法,开始拥塞避免算法。
    • 拥塞避免:拥塞避免的增长速率变成线性增长,也就是每经过一个往返时间RTT就把发送方的拥塞窗口加1
    • 快重传:上述两个算法使得网络传输速率一直增大,直至出现超时,这时候需要将拥塞窗口重新调整到1字节开始,使用慢开始算法,同时需要将慢开始门限调整为超时时的拥塞窗口大小的一半,继续执行慢开始算法、拥塞避免算法。
    • 快恢复:如果收到了连续3个对同一报文的重复确认,此时可能发生了报文缺失,发送方不执行慢开始算法,直接使用快重传算法,立即发送缺失的报文段。同时执行快恢复算法,将门限值调整为此时拥塞窗口的一半,并执行拥塞避免算法
6、前端安全的问题有哪些,具体防范措施
  • 跨站脚本攻击(XSS攻击):跨站脚本攻击,指的是恶意攻击者往Web页面里插入恶意html代码,当用户浏览该页之时,嵌入其中Web里面的html代码会被执行,从而达到用户的特殊目的。

    预防措施:输入过滤,对用户提交的数据进行有效性验证。

    • 过滤一些些常见的敏感字符,例如:< > ‘ “ & # \ javascript expression ;

      1
      .replace(/src="javascript:/g, 'src="').replace(/src='javascript:/g, 'src=\'')
    • 过滤或移除特殊的Html标签, 例如: <script>, <iframe>

    • 过滤JavaScript 事件的标签,例如 “onclick=”, “onfocus”

    • 通过http-only Cooke禁止读取某些敏感Cookies

    • 在关键业务部分增加验证码,防止脚本冒充用户提交危险操作

    输出编码:当需要将一个字符串输出到Web网页时,同时又不确定这个字符串中是否包括XSS特殊字符(如< > &‘”等),为了确保输出内容的完整性和正确性,可以使用编码(HTMLEncode)进行处理。

  • SQL语句注入

  • 跨站请求伪造(CSRF攻击)

    防范措施:

    Token,就是令牌,最大的特点就是随机性,不可预测。一般黑客或软件无法猜测出来。

    那么,Token有什么作用?又是什么原理呢?

    Token一般用在两个地方:

    • 1)防止表单重复提交、
    • 2)anti csrf攻击(跨站点请求伪造)。

    两者在原理上都是通过session token来实现的。当客户端请求页面时,服务器会生成一个随机数Token,并且将Token放置到session当中,然后将Token发给客户端(一般通过构造hidden表单)。下次客户端提交请求时,Token会随着表单一起提交到服务器端。
    然后,如果应用于“anti csrf攻击”,则服务器端会对Token值进行验证,判断是否和session中的Token值相等,若相等,则可以证明请求有效,不是伪造的。
    不过,如果应用于“防止表单重复提交”,服务器端第一次验证相同过后,会将session中的Token值更新下,若用户重复提交,第二次的验证判断将失败,因为用户提交的表单中的Token没变,但服务器端session中Token已经改变了。

    SameSite 属性

    Cookie 的SameSite属性用来限制第三方 Cookie,从而减少安全风险。

    它可以设置三个值。

    • Strict
    • Lax
    • None

    2.1 Strict

    Strict最为严格,完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 Cookie。

    1
    Set-Cookie: CookieName=CookieValue; SameSite=Strict;

    这个规则过于严格,可能造成非常不好的用户体验。比如,当前网页有一个 GitHub 链接,用户点击跳转就不会带有 GitHub 的 Cookie,跳转过去总是未登陆状态。

    2.2 Lax

    Lax规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。

    1
    Set-Cookie: CookieName=CookieValue; SameSite=Lax;

    导航到目标网址的 GET 请求,只包括三种情况:链接,预加载请求,GET 表单。详见下表。

    请求类型 示例 正常情况 Lax
    链接 <a href="..."></a> 发送 Cookie 发送 Cookie
    预加载 <link rel="prerender" href="..."/> 发送 Cookie 发送 Cookie
    GET 表单 <form method="GET" action="..."> 发送 Cookie 发送 Cookie
    POST 表单 <form method="POST" action="..."> 发送 Cookie 不发送
    iframe <iframe src="..."></iframe> 发送 Cookie 不发送
    AJAX $.get("...") 发送 Cookie 不发送
    Image <img src="..."> 发送 Cookie 不发送

    设置了StrictLax以后,基本就杜绝了 CSRF 攻击。当然,前提是用户浏览器支持 SameSite 属性。

    2.3 None

    Chrome 计划将Lax变为默认设置。这时,网站可以选择显式关闭SameSite属性,将其设为None。不过,前提是必须同时设置Secure属性(Cookie 只能通过 HTTPS 协议发送),否则无效。

    下面的设置无效。

    1
    Set-Cookie: widget_session=abc123; SameSite=None

    下面的设置有效。

    1
    Set-Cookie: widget_session=abc123; SameSite=None; Secure
  • 文件上传

    xss参考链接:https://www.cnblogs.com/for-easy-fast/articles/12956062.html

7、XSS具体是如何发动攻击的
  • 分类:XSS分为反射型、存储型、及DOM-based型,其中反射型和DOM-based型可以归为非持久XSS攻击类型。存储型可以归类为持久性XSS攻击

    • 反射型XSS:一般指攻击者通过特定的方式来诱惑受害者去访问一个包含恶意代码的URL。当受害者点击恶意链接url的时候,恶意代码会直接在受害者的主机上的浏览器执行。

      那么为什么要叫反射型呢?因为这种攻击方式的注入代码是从目标服务器通过错误信息,搜索结果等方式反射回来的,而为什么又叫非持久性XSS呢?因为这种攻击方式只有一次性。

      攻击步骤如下

      1. 攻击者在url后面的参数中加入恶意攻击代码
      2. 当用户打开带有恶意代码的URL的时候,网址服务器将恶意代码从URL中取出,拼接在html中并返回给浏览器端
      3. 用户浏览器接受到响应后执行解析,其中的恶意代码也会被执行到。
      4. 攻击者通过恶意代码来窃取到用户数据并发送到攻击者的网址。攻击者会获取到比如cookie等信息,然后使用该信息来冒充合法用户的行为,调用目标网站接口执行攻击等操作。
    • 存储型XSS:如果有一个博客网站,攻击者在上面发布一篇文章,如果不对文章进行任何处理就存入数据库,那么下次,其他用户访问该文章的时候,服务器就会读取然后响应给客户端,那么浏览器就会执行该段脚本,然后攻击者就会获取用户的cookie,然后会把cookie发送到攻击者的服务器上了。

      攻击步骤如下:

      1. 攻击者将恶意代码提交到目标网站数据库中
      2. 用户打开目标网站时,网站服务器将恶意代码从数据库中取出,然后拼接成html中返回给浏览器。
      3. 用户浏览器接受到响应后解析执行,其中恶意代码也会被执行。
      4. 那么恶意执行代码后,就能获取到用户数据,比如上面的cookie等信息,那么把该cookie发送到攻击者网站中,那么攻击者拿到该cookie然后会冒充该用户的行为,调用目标网站接口等违法操作。

      防范措施:后端需要对提交的数据进行过滤

      前端页可以做一下处理方式,比如对script标签,将特殊字符替换成HTML编码等。

    • DOM-based型xss:我们客户端的js可以对页面dom节点进行动态的操作,比如插入、修改页面的内容。比如说客户端从URL中提取数据并且在本地执行、如果用户在客户端输入的数据包含了恶意的js脚本的话,但是这些脚本又没有做任何过滤处理的话,那么我们的应用程序就又可能收到DOM-based XSS的攻击。

      攻击步骤如下:

      1. 攻击者构造出特殊的URL、在其中可能包含恶意代码。
      2. 用户打开带有恶意的代码的URL
      3. 用户浏览器收到响应后解析执行。前端使用js取出url的恶意代码并执行。
      4. 执行时,,恶意代码窃取用户数据并发送到攻击者的网站中,那么攻击者网站拿着这些数据去冒充用户的行为操作。调用目标网站接口执行攻击者的一些操作。

      一般又如下的DOM操作

      • document.write

      • innerHTML

      • location、location.href、location.replace、iframe.src、document.referer、window.name

        因此我们需要对HTML进行编码,对JS进行编码来防止这些问题产生。

      参考链接:https://www.cnblogs.com/tugenhua0707/p/10909284.html#_labelTop

      https://www.cnblogs.com/mmy67/p/9923422.html

8、有哪些页面优化的手段,从框架啊、代码的角度说
  • 资源压缩,减少http请求:合并css、合并javascript、合并图片(精灵图片的使用:例如微博上有很多的小图标,浏览器上的图片很多都是从服务器上获取的,如果分别存放在服务器上,就会增加很多的请求),同时使用Gzip压缩可以达到比较好的效果,但是如果服务器的资源不足的时候,但是通信带宽良好需要权衡考虑。

  • 使用CDN:在浏览器第一次打开页面时,缓存是起不了作用的。这时候,CDN就很有用(CDN的本质仍然是一个缓存,而且将数据缓存在离用户最近的地方,使用户以最快的速度获取数据,就是所谓的网络访问第一跳,由于CDN部署在网络运营商的机房,这些运营商又是终端用户的网络服务提供商,因此用户请求路由的第一跳就到达了CDN服务器)

  • 图片较多的页面可以使用lazyLoad等技术优化

  • 非核心代码异步加载:不用立即使用的js代码可以采用异步加载

  • 利用浏览器缓存:Css、javascript、logo、图标这些静态资源文件更新的频率都比较低,而且这些文件几乎每次http请求都需要的,如果将这些文件缓存在浏览器中,可以极好的改善性能。通过设置http头中的cache-controlexpires属性,可设定浏览器缓存,缓存时间可以是几天,甚至是几个月。

    但是某些时候,静态资源文件变化需要及时应用到客户端浏览器,这种情况,可以通过改变文件名实现,即更新javascript文件并不是更新javascript文件内容,而是生成一个新的JS文件并更新HTML文件中的引用。

    使用浏览器缓存策略的网站在更新静态资源时,应该采用逐量更新的方法,比如需要更新1-个图标文件,不应当把10个文件一次全部更新,而是应该一个文件一个文件逐步更新,并有一定的间隔时间,以避免用户浏览器突然大量缓存失效,集中更新缓存,造成服务器负载骤增、网络堵塞的情况。

  • DNS预解析

  • Css尽量放在上方,javascript尽量放在下方:浏览器在下载完全部Css之后,才会对整个页面进行渲染,因此最好的做法是将Css放在页面的最上方(<head>)如果将Css放在Body中,则浏览器可能还未下载和解析到Css就已经开始渲染页面了,这就导致页面可能会从无Css状态跳转到有Css状态,用户体验会比较差。相反,JavaScript在浏览器加载javascript之后立即执行,有可能会阻塞整个页面,造成页面显示缓慢,因此javascript最好放在页面最下面。如果页面解析的时候就需要用到javascript,这时放在下面就不是很合适。

  • 减少cookie传输:太大的cookie会严重影响数据传输,因此哪些数据需要写入cookie需要慎重考虑,尽量减少cookie中 传输量。另一方面。对于某些静态资源的访问,发送cookie没有意义,可以考虑静态资源使用独立域名去访问,避免请求静态资源时发送cookie,减少cookie传输次数

  • Javascript代码优化

    • Dom:

      1. HTMl Collection(HTMl收集器,返回的是一个数组内容信息)在脚本中document.images、document.forms、getElementsByTagName()返回的都是HTMLCollection类型的集合,在平时使用的时候大多将它作为数组来使用,因为它有length属性,也可以使用索引来访问每个元素。不过在访问性能上比数组差很多,原因是这个集合并不是一个静态的结果,它表示的仅仅是一个特定的查询,每次访问该集合时都会重新执行这个查询从而更新查询的结果。所谓的访问集合包括读取集合的length属性、访问集合里的元素。 因此,当你需要遍历HEML Collection的时候,尽量将它转为数组再去访问,以提高性能。即使不转变成为数组,也尽可能的少访问它,例如在遍历的时候可以将length属性、成员保存到局部变量后再使用局部变量。
      2. DOM操作还需要考虑浏览器的回流和重绘,应位哪都需要消耗资源的。
    • 慎用with

      1
      2
      3
      with(obj){
      p = 1
      };

      代码块的行为实际上的修改了代码块中的执行环境,将obj放在了其作用域链的最前端,在with代码块中访问非局部变量是都是先从obj上开始查找,如果没有再依次按作用链域向上查找,因此使用with相当于增加了作用域链长度。而每次查找作用域链都是要消耗时间的,过长的作用域链会导致查找性能下降。因此,除非你能肯定在with代码中只访问obj中的属性,否则慎用with,替代的可以使用局部变量来缓存需要访问的属性。

    • 避免使用eval和Function

      每次eval或Function构造函数作用于字符串表示的源代码时,脚本引擎都需要将源代码转换成可执行代码。这是很消耗资源的操——通常比简单的函数调用慢100倍以上。

      eval函数效率特别低,由于事先无法知晓上下文,因此只能有浏览器在运行时解释代码。这对性能影响很大。Function构造函数也比eval略好,因为使用此代码不会影响周围代码,但是速度仍然很慢,此外,使用eval和Function也不利于Javascript压缩攻击执行压缩。

    • 减少作用域查找

      在循环的时候尤其需要注意的问题。如果在循环中需要访问非本作用域下的变量时请在遍历之前用局部变量缓存该变量,并在遍历结束后再重写那个变量,这一点对全局变量尤其重要,因为全局变量处于作用域链的最顶端,访问时的查找次数是最多的。此外,要减少作用域链查找还应该减少闭包的使用。

    • 字符串拼接,使用+号来拼接字符串的效率比较低,因此相比较之下可以使用数组的join方法,将需要拼接的字符串放在数组中最后调用join方法得到结果。但是数组也有一定的开销,因此需要拼接的字符串较多的时候可以考虑用此方法。

  • 反向代理服务器:传统代理服务器位于浏览器以测,代理浏览器将http请求发送到互联网上,而反向代理服务器位于网站机房以测,代理网站web服务器接收http请求。和传统代理服务器可以保护浏览器安全一样,反向代理服务器也具有保护网站安全的作用,来自互联网的请求必须经过代理服务器,相当于web服务器和可能的网络攻击之间建立了一个屏障。除了安全功能代理服务器也可以通过配置缓存功能加速web请求。当用户第一次访问静态内容的时候,静态内容就被缓存在反向代理服务器上,这样当用户访问该静态能让的时候,就可以直接从反向代理服务器返回,加速web请求速度,减轻web服务器负载压力。事实上,有些网站会把动态内容也缓存在代理服务器上,比如维基百科和一些博客论坛网站,把热门词条、帖子、博客缓存在反向代理服务器上加速用户访问速度,当这些动态内容有变化的时,通过内部通知反向代理缓存失效,反向代理会重新加载最新的动态内容再次缓存起来。

    此外,反向代理也可以实现负载均衡的功能,而通过负载均衡构建的应用集群可以提高系统总体处理能力,进而改善网站高并发情况下的性能。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!