2020.10.29腾讯QQ音乐社招前端电话一面总结

面试官晚上19:16打过来的,聊了44分钟,到八点准时结束。无论过没过,都记录一下面试过程吧,是问了几个大问题,在几个大问题的基础上根据你的回答,再去问一些小问题。


前言

【面试官】:先自我介绍下吧
【我】:我现在是在南京工作,工作一年多了,公司里用的是React框架,接下来我也打算以React为主技术栈。
【面试官】:嗯,没了吗
【我】:嗯…
【面试官】:你换工作的原因是什么
【我】:我是从大三开始就想进大厂了,一直也在准备着,毕业时投了一些简历但是没有机会。所以又边工作边准备了一年多,现在有人帮我内推我就赶紧试一试。
【面试官】:你是什么开始学习前端的
【我】:我大三的时候开始在国外的一个叫做FCC(FreeCodeCamp)的网站上学习的,在那上面学了HTML、CSS、JQuery、JS。我在毕业的时候还不会框架,在学校里的时候,做项目都是用PHP+JQuery做的。进入这家公司之后,公司用的是React框架,我就花了一段时间感觉学习了React框架,学完React后感觉自己提升了好多,又接着学了一些前端的其他东西,前端的知识简直越学越多,感觉都学不完
【面试官】:你是在一个网站上学习的,那你有没有看过一些书籍之类的
【我】:我不怎么看纸质的书,我一般都是看网上的文档,因为纸质的书代码都在书上面,一行一行照着写也很麻烦,而且不方便在我有疑问的时候得到反馈。我学习主要是以在网上买视频课为主,因为买视频课可以直接学习到这个技术怎么用,它的原理是什么,遇到不会的地方还可以去联系他问他,现在这些付费的课程都会有个交流群,群里的其他学员也都会跟你一起交流,我觉得这样学习效率高一点,比看书好一点。

(巴拉巴拉说了这么一大堆,面试官就直接进入正题开始问了)


有了解怎么给一个不存在的元素添加事件吗

【我】:不存在的元素啊?那要先创建这个元素吧
【面试官】:那在创建之前就没有办法绑定事件吗
【我】:创建之前啊,那在创建之前…我好像不知道…
【面试官】:有一个div,里面会有一些元素,在点击元素的时候会触发一个事件,但是它现在并不在div里面
【我】:那可以把事件挂在父组件上面吗,可以吧,由父组件触发,然后可以检测到里面的target是谁
【面试官】:那它利用的是什么原理
【我】:是事件的代理
【面试官】:事件的代理是什么原理
【我】:事件的冒泡
【面试官】:事件的三个阶段是怎样实现的
【我】:先是从父组件到子组件的捕获,然后触发事件,最后从子组件冒泡到父组件。
【面试官】:那用事件捕获可以实现事件代理吗
【我】:额…可以
【面试官】:那一般你是用什么方式实现的
【我】:我一般使用冒泡
【面试官】:那为什么不用捕获呢
【我】:因为…嗯…嗯…我没注意过这个

参考MDN:
对事件冒泡和捕捉的解释
当一个事件发生在具有父元素的元素上(例如,在我们的例子中是<video>元素)时,现代浏览器运行两个不同的阶段 - 捕获阶段和冒泡阶段。 在捕获阶段:

浏览器检查元素的最外层祖先<html>,是否在捕获阶段中注册了一个onclick事件处理程序,如果是,则运行它。

然后,它移动到<html>中单击元素的下一个祖先元素,并执行相同的操作,然后是单击元素再下一个祖先元素,依此类推,直到到达实际点击的元素。

在冒泡阶段,恰恰相反:

浏览器检查实际点击的元素是否在冒泡阶段中注册了一个onclick事件处理程序,如果是,则运行它

然后它移动到下一个直接的祖先元素,并做同样的事情,然后是下一个,等等,直到它到达<html>元素。

在现代浏览器中,默认情况下,所有事件处理程序都在冒泡阶段进行注册。
事件冒泡是令人讨厌的行为,但有一种方法来解决它。标准事件对象具有可用的名为 stopPropagation()的函数, 当在事件对象上调用该函数时,它只会让当前事件处理程序运行,但事件不会在冒泡链上进一步扩大,因此将不会有更多事件处理器被运行(不会向上冒泡)。

事件分三个阶段:捕获、目标、冒泡。
捕获:由外向内,由不具体到最具体,由document到元素
目标:就是事件触发的元素
冒泡:由内向外,由最具体到最不具体,由元素到document

事件委托
冒泡还允许我们利用事件委托——这个概念依赖于这样一个事实,如果你想要在大量子元素中单击任何一个都可以运行一段代码,您可以将事件监听器设置在其父节点上,并让子节点上发生的事件冒泡到父节点上,而不是每个子节点单独设置事件监听器。


有了解过DOMContentLoaded这个事件吗

【我】:知道,这个是在网页图片、CSS全部加载完之后触发
【面试官】:window.onload事件呢?
【我】:啊,那你刚才问的是哪个,不是load吗(原谅我耳背了…)
【面试官】:DOMContentLoaded,我刚才问的是DOMContentLoaded
【我】:哦抱歉那我刚才听错了,我刚才讲的就是load事件,DOMContentLoaded事件就网页的骨架加载完就触发,不会等待图片、CSS样式加载完
【面试官】:那如果外链的JS没有加载完,DOMContentLoaded事件会不会触发?
【我】:不会,哦不,外链…它会触发吧…我不太确定了
【面试官】:如果有个外链标签在网页的头部,那网页会展示内容吗
【我】:是指script标签吗,还是指link啊?
【面试官】:外链的JS
【我】:那就渲染不出来了,JS会阻塞渲染
【面试官】:那DOMContentLoaded触发以后,页面上会展示内容吗
【我】:会展示
【面试官】:那外连接没加载完,DOMContentLoaded会触发吗
【我】:哦那不会,JS会阻止这个渲染进程
【面试官】:那在中间呢
【我】:在中间的话,那也不会
【面试官】那在尾部呢
【我】:不会,要等JS走完
【面试官】:那要想让它不要阻塞的话,有什么办法
【我】:可以给script标签加一个defer属性,还有async,让它延迟加载
【面试官】:如果不用这两个的话,还有什么其他方案
【我】:嗯…还有type=“module”
【面试官】:对于浏览器不支持的情况,比如这三个都不支持的情况,怎么实现呢
【我】:那可以把script标签的加载写在DOMContentLoaded事件的回调中,执行的时候再去生成这个script标签
【面试官】:嗯,那你这样的话不就导致加载事件被延后了吗,为什么要在这个事件中写呢
【我】:因为这个时候页面应该渲染好了吧,没渲染好的话,会不会造成页面的时间很长呢(此时我崩溃的内心:抱歉这一块我不熟,我都是在乱说,我也不知道遇到外链加载是怎样的过程,我好困,我只想睡觉,不要再引导我了,这个问题我答不上来的😭😭)
【面试官】:如果你用标签加载的话,也会导致白屏吗
【我】:会,会
【面试官】:你说的动态创建script标签也会吗
【我】:这个时候页面已经大致加载完成了,只有图片之类的没加载完,创建script标签用户已经感知不到了,不会有白屏
【面试官】:我的意思是,不在这个事件里去回调,直接通过动态创建script标签的方式去加载,会不会阻塞
【我】:额…不会,直接写在最尾部是吧
【面试官】:写在头部
【我】:啊,写在头部啊,写在头部的话,会
【面试官】:用动态创建标签的方式也会是吗
【我】:动态创建,就是创建到头部去是吗,…, …, 应该不会(我已麻木的内心:为什么一直揪着这个问题不放😭,我什么都不知道😭,我好困😭我已经听不懂你在问什么了😭我的脑子已经没办法解析你的问题了😭)

面试官莫非是听到了我的心声,终于结束了这个死亡话题

MDN
当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完全加载。
同步 JavaScript 会暂停 DOM 的解析。
如果您希望 DOM 在用户请求页面后尽可能快地解析,你可以做的一些事情是把你的 JavaScript 异步化 以及 优化样式表的加载, 由于被并行加载而减慢页面加载,从主 html 文档“窃取”流量。

看到了一个讲解详细的链接: http://www.mamicode/info-detail-2839422.html


有了解过浏览器的缓存吗,怎么设置浏览器的缓存

【我】:就是在请求的时候,如果服务器端返回的响应头中的cache-control是max-age的话,那就是强制缓存,此时max-age的值就是缓存时间,下一次客户端向服务器请求的话,就会先去本地检查一下本地是不是会有这个文件,如果没有过期的话,就从本地缓存中读取,如果过期的话,就继续去服务器上获取。如果服务器的cache-control的值是no-cache的话,就是协商缓存,协商缓存服务器还会同时返回一个Last-Modify和ETag,Last-Modify是最后一次修改时间,ETag是根据这个文件内容生成的唯一性标志,下一次客户端去请求的时候,会携带上这个Last-Modify和ETag,给服务端做验证,服务端优先使用ETag来判断,如果服务端文件修改了,那ETag就不一样了,则匹配不上。客户端如果没有携带ETag的话,就用Last-Modify来判断,判断修改时间是不是与服务端一致。如果服务端判断出客户端资源文件未修改,就返回一个304状态码,让客户端去读本地缓存,否则就返回200状态码和新的资源文件。
【面试官】:如果强缓存没有过期的话,文件发生了修改,那怎么更新这个缓存呢
【我】:可以使用浏览器的刷新功能
【面试官】:你怎么控制用户的浏览器呢
【我】:哦,就是开发人员弄这个操作是吗,那还可以清除缓存(此时我内心很慌😒,又开始乱说了)
【面试官】:那你怎么清除用户浏览器上的缓存呢
【我】:…(停顿10几秒) 通过手动清除缓存不算么,要通过状态码控制吗(我已经控制不止自己在说啥了)
【面试官】:强缓存没有过期的时候,浏览器会发起请求吗
【我】:不会,除非是刷新它
【面试官】:那你有一亿个用户,你要怎么给每个用户刷新他的浏览器
【我】:…(又停顿了十几秒),哦,是哦,…, 我没想到…
【面试官】:那你之前做过的项目有做过强缓存吗,一般都不处理这个…
【面试官】:为什么呢
【我】:因为这个是浏览器自带的机制吧,后端设置的响应头(我脑子里想讲的应该是服务端,脑子已经昏掉了)
【面试官】:后端了解浏览器、了解前端的知识吗(虽然我已经晕乎乎,但是面试官还很清醒😭)
【我】:可是这个不是后端返回的吗
【面试官】:那接口也是后端返回的,内容不是前端定的吗
【我】:前后端一起商量的…

如何更新强缓存:文件名根据内容哈希。


有了解过性能优化吗

【我】:有了解过
【面试官】:性能优化有哪些措施和手段
【我】:从网络层面,减少请求次数,提高请求速度,这一块可以通过缓存和打包的数目来控制。还有代码层面,开发人员写的代码要…(额,被打断)
【面试官】:你刚才说通过缓存,指的是什么
【我】:就是文件缓存,文件内容不变,我们现在打包的文件名不都是根据文件内容生成的一个哈希名嘛,如果文件内容不变,哈希值不变,浏览器就会用缓存机制优先从缓存中读取。
【面试官】:刚才你不是说没有设置这个缓存吗,那怎么会利用呢,你前面不是说没有设置强缓存吗
【我】:额…那…还有协商缓存
【面试官】:你觉得是强缓存好还是协商缓存好?
【我】:我觉得是协商缓存好,因为协商缓存每次会去校验一下,如果本地缓存可用的话,就可以读本地缓存了
【面试官】:那你不是相当于多了一次网络请求吗,那这次要是卡住了,那页面打开的速度就会变慢
【我】:但是能保证文件新鲜及时
【面试官】:那用强缓存就不能保证文件新鲜及时了吗
【我】:我没有解决上面一个问题…(饶了我吧,肿么又绕到了上一题🥺🥺😭☹️)
【面试官】:嗯?
【我】:我说我没有解决上面一个问题,就是说文件修改了,那它就不知道了,哦,哎,我想起来了,就是你刚才说的上一个问题,就是强缓存的文件修改了,那再次发起请求的话,那文件名变化了不就不能用本地的了吗,我不就发起了新的一轮请求了吗,这就破解了上一个问题了,对吧?
【面试官】:嗯,本来就应该是这个答案 (本可怜终于智商在线了┭┮﹏┭┮)
【我】:嗯,又绕回来了
【面试官】:嗯,就是说,你知道这个原理,但你之前没有用过,所以没有想到,对吧
【我】:嗯是的,刚才没有想到,不过是说到了文件变化了,就会导致文件名变化了,那就会发起新的请求了,才想起来的
【面试官】:嗯,行


Ajax跨域都有哪几种方式

【我】:JSONP、Nginx反向代理、Node中间件、CORS
【面试官】:你用过哪种
【我】:我用过Nginx,因为我们公司项目里用的就是Nginx,打包在docker里运行的
【面试官】:那相当于没有跨域是吧
【我】:我们开发模式下用的是webpack中的Proxy代理


CORS是怎么实现的

【我】:前端要设置请求头的origin为后端地址,后端也要设置allow响应头,允许访问的请求方式和IP地址
【面试官】:IP地址?那如果是CDN有很多个IP访问呢?
【我】:那就设置为星号
【面试官】星号?那黑客不就可以非法访问了吗
【我】:CDN一般不是以script标签、link标签,这不是不会有跨域问题吗
【面试官】:那用户呢,每个用户的IP都不一样啊,你这个Ajax是用户发起的啊
【我】:嗯,对
【面试官】:你没有去练习过这个CORS的使用是吗?
【我】:我之前学node的时候用过,但是只是练习的时候用的
【面试官】:那怎么设置响应头呢
【我】:在node里面可以设置四五个响应头,但名字很长,我可能记得不太全了
【面试官】:node使用什么方法设置的相应头
【我】:express里面带的设置响应头
【面试官】:它的方法名叫什么
【我】:嗯…你是说设置方法头的方法名吗
【面试官】:嗯
【我】:是setHeader吗
【面试官】:setHeader是吗
【我】应该是的(其实我也不记得了💔node好久没碰了)

在node中通过cors跨域。
cors : 全称 cross origin resource share 跨资源共享
在nodejs 中可以通过在服务器端设置代码如下实现cors跨域
res.setHeader(‘Access-Control-Allow-Origin’, “*”); //针对哪个域名可以访问,*表示所有
res.setHeader(‘Access-Control-Allow-Credentials’, true); //是否可以携带cookie
res.setHeader(‘Access-Control-Allow-Methods’, ‘POST, GET, PUT, DELETE, OPTIONS’);

// 在nodejs中,一般情况下是需要服务器代码在我们控制范围之内可以这样做
app.use('*',(req,res,next) => {
    res.setHeader('Access-Control-Allow-Origin','*');
    res.setHeader('Access-Control-Allow-Credential','true');
    res.setHeader('Access-Control-Allow-Methods','GET,POST,PUT,DELETE,OPTIONS');
    next();
});

有了解怎么捕获用户浏览的网页下的报错吗

【我】:可以通过try-catch捕获,还可以通过监听全局的事件error
【面试官】:error是在哪个对象上面?
【我】:在window上面
【面试官】:那你有用过吗
【我】:我今天还碰巧试了一下,今天有别人问到我到了
【面试官】:你是今天有面试吗
【我】:不是,是我一个朋友,他在他项目中用到了,他说在设置一个全局的监听error不起作用,他就来问问我,我就帮他试了一下,我就在我项目里试了一下,我发现也没起作用
【面试官】:那找到原因了吗
【我】:没有,我不知道是不是框架的原因,他用React的时候没有捕获到,我在我的React项目里试了一下,也没有起作用,但是我写在本地测试是可以的
【面试官】:你这个本地测试指什么,本地的React?
【我】:不是,就是普通的JS
【面试官】:React不普通吗
【我】:就是一个HTML文件里的script
【面试官】:原生是吧
【我】:对对对
【面试官】:那你在业务中并没有使用到这个方法
【我】:恩是的,但我之前也学到过它, 要不然人家问起来我也不会知道它是啥了


有了解XSS攻击吗

【我】:点击别人链接的时候,就带着自己的信息访问了这个URL,自己的cookie信息就泄漏了
【面试官】:那别人给你发一个百度的网站你点了会有问题吗
【我】:额这个是没问题,这个网址对你没有破坏性。不过有的网址比如说支付类的,拿别人发一个链接给你,你点一下就带着自己本地的cookie信息请求了这个地址了

MDN
XSS :Cross-site scripting(跨站脚本攻击)
跨站脚本攻击(Cross-site scripting,XSS)是一种安全漏洞,攻击者可以利用这种漏洞在网站上注入恶意的客户端代码。当被攻击者登陆网站时就会自动运行这些恶意代码,从而,攻击者可以突破网站的访问权限,冒充受害者。根据开放式 Web 应用安全项目(OWASP),XSS 在 2017 年被认为 7 种最常见的 Web 应用程序漏洞之一。


如果 Web 应用程序没有部署足够的安全验证,那么,这些攻击很容易成功。浏览器无法探测到这些恶意脚本是不可信的,所以,这些脚本可以任意读取 cookie,session tokens,或者其它敏感的网站信息,或者让恶意脚本重写HTML内容。


在以下2种情况下,容易发生 XSS 攻击:

  1. 数据从一个不可靠的链接进入到一个 Web 应用程序。
  2. 没有过滤掉恶意代码的动态内容被发送给 Web 用户。

恶意内容一般包括 JavaScript,但是,有时候也会包括 HTML,FLASH 或是其他浏览器可执行的代码。XSS 攻击的形式千差万别,但他们通常都会:将 cookies 或其他隐私信息发送给攻击者,将受害者重定向到由攻击者控制的网页,或是经由恶意网站在受害者的机器上进行其他恶意操作。


XSS 攻击可以分为3类:存储型(持久型)、反射型(非持久型)、DOM 型。

存储型 XSS:
注入型脚本永久存储在目标服务器上。当浏览器请求数据时,脚本从服务器上传回并执行。
反射型 XSS:
当用户点击一个恶意链接,或者提交一个表单,或者进入一个恶意网站时,注入脚本进入被攻击者的网站。Web服务器将注入脚本,比如一个错误信息,搜索结果等 返回到用户的浏览器上。由于浏览器认为这个响应来自"可信任"的服务器,所以会执行这段脚本。
基于 DOM 的 XSS:
通过修改原始的客户端代码,受害者浏览器的 DOM 环境改变,导致有效载荷的执行。也就是说,页面本身并没有变化,但由于DOM环境被恶意修改,有客户端代码被包含进了页面,并且意外执行。


那CSRF攻击有了解吗

【我】:就是用户在填写文本的时候,可能填的是一段脚本,就是含有script标签的JS代码,提交后如果前后端都不对这个文本做转义替换标签处理的话,可能前端在渲染时,就会直接让这段脚本在前端执行了
【面试官】:CSRF的全称是什么
【我】:全称…跨域攻击?(我怀疑我把XSS和CSRF讲反了!!)
【面试官】:跨域攻击?那XSS的全称是什么? (我大概是真的讲反了,凉!!)
【我】:我不会记反了吧?呵呵··(尴尬一笑····) 额,XSS的,我不记得了… 我是不是讲反了
【面试官】:那XSS攻击的具体原理是怎样的
【我】:原理就是本地的cookie去请求一个地址
【面试官】:请求什么地址,谁拿着本地的cookie
【我】:点击别人发给你的一个连接,带着本地信息请求了,get请求就发过去了
【面试官】:那为什么就会造成攻击呢
【我】:他请求了这个get请求,对这个用户产生了影响,比如说支付类、确定类,一个URL就是一个get接口
【面试官】:那为什么这个支付类的会产生攻击呢,它具体是怎么实现的呢,那比如说我现在给你发一个支付链接,你点了就会被攻击吗
【我】:现在不会了,现在我点了的话,肯定会有验证的,不会直接支付的,如果在有些网站,它没有做验证的话,只是用你的cookie判断你的身份,那这个会造成攻击的
【面试官】:为什么会拿着你的cookie呢
【我】:cookie会自动随着http请求发送到服务器的
【面试官】:那有什么办法让它不发送吗
【我】:可以禁掉cookie
【面试官】:那你自己的网站会受影响吗
【我】:会, 额… 那开发这个网站的人可以换个请求方式
【面试官】:那Post就不会有这个问题吗
【我】:我们点击事件是Get请求过去的
【面试官】:我知道那Post会不会有这个漏洞
【我】:Post的话,没有

参考网上这位大佬的文章:计算机网络 网络安全------XSS与CSRF的原理与防范
CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。


尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装成受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。
简单的说,CSRF是攻击者盗用你的名义来发送恶意消息,如:以你的名义发邮件,购买物品,转账等。
如在一个转账的网站,当我们保持着登录状态没有退出,然后这时候被诱导访问了另一个网站,该网站内有张不可显示的图片

这是攻击者利用图片资源嵌入了恶意的转账操作,所以可以在我们不知情的情况下转走我们的钱钱。
但上述例子是一个 GET 请求下的操作,那如果是 POST 请求呢?
这时候只需要在诱导用户访问的页面内嵌一个表单,通过表单自动提交也可以做到恶意转账。


CSRF的防范
CSRF 的本质原因是由于服务端的身份验证不够,仅仅通过校验 session 来判断,但这并不能保证所有的操作都是用户发起的,所以要防范 CSRF 得加强在服务端的校验处理。

  1. 尽量使用 POST
    因为 GET 请求实在是太容易被利用了,如上述的例子,只需要一个 img 的标签,而 img 又是不能过滤的数据,所以接口最好限制为 POST 请求。

  2. 加入验证码
    因为 CSRF 都是偷偷盗用用户名义发送的,我们只需要加入验证码校验,就可以确定该操作是用户自己的行为而不是盗用用户名义的行为。

  3. 验证Referer
    HTTP 头上有一个 Referer 字段,记录了该请求的源地址,黑客要发起 CSRF 的攻击只能从他们自己的网站构建请求,源地址不会是用户的地址。但道高一尺魔高一丈,虽然 Referer 的值是由浏览器提供的,但在某些浏览器上我们可以篡改 Referer 的值,所以有时候也并不安全。

  4. Token
    我们知道黑客只能冒用了我们的身份发送请求,所以我们只每次发送的请求带上一个黑客所不能伪造的验证信息,并且信息不能存在与 cookie 中。所以我们可以在 HTTP 的请求头中加入一个随机产生的 token(分布式环境下的 token 生成有点不同),客户端返回消息时验证该 token 是否一致,若不一致则认为这是一次 CSRF 攻击。


平时有遇到过安全方面的问题或事件吗

【我】:我以前在学校里面写代码的时候遇到过,可能那时候我对前端懂得也不是那么多,当时做学校的网站,有个支付功能的,我把金额、购买的东西这些信息放在前端提交过去,然后就有人拦截了请求,修改了数据再提交,导致订单不对了。之后我就是在用户确定下单的时候,生成一条待支付订单,然后用户提交的时候,带着订单编号过去,这样他就没法修改订单里的信息了
【面试官】:…
【我】:我觉得这种重要信息都不能放在前端,应该存在后端,前端就拿着一个编号就好了

这个问题第二次被问到了,不知道怎么回答比较好


你现在的项目中有难度或者有挑战的地方吗

【我】:我现在在我们公司就是做集团的管理系统的,每天的工作内容都差不多,所以我才想去更好的公司的,才有可能遇到更多的有挑战性的问题
【面试官】:一直都是做管理系统,没有做过别的是吗
【我】:我就做了一年,因为我来这个公司也就一年,也就做了两个嘛,不过我工作中肯定要做这个啊,有时候还做做组件,因为有些页面会有工作的组件,需要抽出来做。还有我们管理系统还有移动端,移动端是一个学习平台,不过移动端也是用React做的,我现在这段时间做移动端的这个学习APP更多一点,要不然一直做管理系统很无聊,所以我才想去跳槽去好公司,否则这个项目结束了又要进入下一个项目做管理系统,太无聊了
【面试官】:那管理系统有没有考虑做一个通用的管理中台这个东西呢,可以自动去搭建管理系统的
【我】:整个管理系统我倒是没有,但是我做了模块的自动生成,就是用Plop那个工具,可以自动化生成页面
【面试官】:怎么自动化生成
【我】:那个Plop不是前端的一个自动化工具嘛,先在项目中安装plop,然后在它的配置文件plopfile中写它的交互命令,输入你的模块名、接口名、字段名,它就根据模板生成文件,你所填入的信息都会替换进去,替换到模板的占位符中去,就能很快的生成一个一个页面。因为管理系统页面都很相似,我通过plop批量生成了一些模块之后,每个模块只需要再稍微改动一下就行了,这个是我前几个月的时候学到的plop这个工具,我就在我们项目里试了试


你的页面上一个按钮点击会发送一个Ajax请求到后台,后台返回成功或失败给个提示,然后有一天你收到一个反馈,按钮点击之后没有反应,但你不知道是谁反馈的,也找不到这个人,然后你在你的电脑上试了下,并没有问题,那你会怎么分析定位这个原因呢

【我】:在我电脑上没问题,那可能跟他电脑有关,那我先让其他同事也都试一下,看看有没有问题
【面试官】:那其他同事都没有呢
【我】:那可能是他电脑有问题吧… (emmmm…知识盲区)
【面试官】:你是说是他的问题不是你代码的问题?
【我】:如果我让同事们试了都没问题,那就可能跟他那边的环境有关系了
【面试官】:那你分析这个问题的原因有没有办法,你分析的思路是怎样的
【我】:得想办法定位到这个人,那还好搞一点
【面试官】:找不到这个人 (面试官难道是被这种事虐过么…)
【我】:找不到这个人的话,那就很难办了,那我怎么办… (我的脑子真的转不过来了…)
【面试官】:就没有办法了是吗
【我】:我、我不知道,因为我们现在做的系统都是集团的,如果遇到问题会有人一轮一轮报上来,都是能找到报错人的,还能定位一下问清楚,如果不知道是谁报出的错误,你也不知道他的环境,就光靠他说一句他报错了,那可能还是他操作有问题…

这个没想到,得问问我的群友们


知道怎么检测页面的性能数据并上报吗

【我】:没有
【面试官】:知道怎么去获取性能数据吗
【我】:chrome浏览器好像就支持,它底部有个performance面板
【面试官】:用JS怎么获取呢
【我】:哦,JS啊,JS我记得它好像有个方法,但我记得不太清楚了,它有个方法可以获取到网页每一阶段的时间,加载时间、响应的时间、渲染的时间,但那个方法名我不记得了,因为好像那个名字还有点长…
【面试官】:嗯

我当时想的是performance,面试官想问的应该是它吧
参考这位大佬的文章:Performance:前端页面性能监控
Performance是HTML5提供的,用于获取当前页面中与性能相关的信息(如:页面每个处理阶段精确时间)的API。可以通过调用只读属性 window.performance来获得。
Performance.timing属性分析:
navigationStart:当前浏览器窗口的前一个网页关闭发生unload事件时的Unix毫秒时间戳。如果没有前一个网页,则等于fetchStart属性。
unloadEventStart:如果有前一个网页,且与当前网页同源,则返回前一个网页unload事件发生时的Unix毫秒时间戳。如果没有前一个网页,或前一个网页与当前网页不同源,则返回值为0。
unloadEventEnd:如果有前一个网页,且与当前网页同源,则返回前一个网页unload事件的回调函数结束时的Unix毫秒时间戳。如果没有前一个网页,或前一个网页与当前网页不同源,则返回值为0。
redirectStart:返回第一个HTTP跳转开始时的Unix毫秒时间戳。如果没有跳转,或者不同源,则返回值为0。
redirectEnd:返回最后一个HTTP跳转结束时的Unix毫秒时间戳。如果没有跳转,或者不同源,则返回值为0。
fetchStart:返回浏览器准备使用HTTP请求读取文档时的Unix毫秒时间戳。该事件在网页查询本地缓存之前发生。
domainLookupStart:返回域名查询开始时的Unix毫秒时间戳。如果使用持久连接,或者信息是从本地缓存获取,则返回值等同于fetchStart属性的值。
domainLookupEnd:返回域名查询结束时的Unix毫秒时间戳。如果使用持久连接,或者信息是从本地缓存获取,则返回值等同于fetchStart属性的值。
connectStart:返回HTTP请求开始向服务器发送时的Unix毫秒时间戳。如果使用持久连接,则返回值等同于fetchStart属性的值。
connectEnd:返回浏览器与服务器之间的连接建立(连接建立指所有的握手和认证过程全部结束)时的Unix毫秒时间戳。如果使用持久连接,则返回值等同于fetchStart属性的值。
secureConnectionStart:返回浏览器与服务器开始安全连接的握手时的Unix毫秒时间戳。如果当前网页不要求安全连接,则返回0。
requestStart:返回浏览器向服务器发出HTTP请求时(或开始读取本地缓存时)的Unix毫秒时间戳。
responseStart:返回浏览器从服务器收到(或从本地缓存读取)第一个字节时的Unix毫秒时间戳。
responseEnd:返回浏览器从服务器收到(或从本地缓存读取)最后一个字节时(如果在此之前HTTP连接已经关闭,则返回关闭时)的Unix毫秒时间戳。
domLoading:返回当前网页DOM结构开始解析时(即Document.readyState属性变为loading,相应的readystatechange事件触发时)的Unix毫秒时间戳。
domInteractive:返回当前网页DOM结构解析结束、开始加载内嵌资源时(即Document.readyState属性变为interactive,相应的readystatechange事件触发时)的Unix毫秒时间戳。
domContentLoadedEventStart:返回当前网页DOMContentLoaded事件发生时(即DOM结构解析完毕、所有脚本开始运行时)的Unix毫秒时间戳。
domContentLoadedEventEnd:返回当前网页DOMContentLoaded事件结束时的Unix毫秒时间戳。
domComplete:返回当前网页DOM结构生成时(即Document.reayState属性变为complete,以及相应的readystatechange事件发生时)的Unix毫秒时间戳。
loadEventStart:返回当前网页load事件的回调函数开始时的Unix毫秒时间戳。如果该事件还没发生,返回0。
loadEventEnd:返回当前网页load事件的回调函数结束时的Unix毫秒时间戳。如果该事件还没发生,返回0。


通过以上属性,可以计算出网页加载各个阶段的耗时,比如:
DNS查询:domainLookupEnd - domainLookupStart
TCP连接:connectEnd - connectStart
TTI(页面可交互时间):domInteractive - navigationStart
domready时间:domContentLoadedEventEnd - navigationStart
onload:loadEventEnd - navigationStart


【面试官】:那我这边就没有其他问题了,今天的面试就先到这里,我们这边考虑之后有消息的话再联系你
【我】:嗯,好,谢谢
【面试官】:拜拜


花了几个小时复盘了一遍,感觉自己答得不好,很凌乱,不流畅,尤其是XSS和CSRF那一块混淆了,不清晰,讲错了。

继续加油吧!

更多推荐

2020.10.29腾讯QQ音乐社招前端电话一面总结