一年半前端工作经验试水杭州:我是如何拿下网易、阿里和滴滴 offer 的
前言
笔者毕业于东北大学,大学毕业社招进入环球网,前端开发工程师一职。技术栈:React+node,Github 地址
成果
来到杭州的目标非常的明确,大厂。其实就是网易、阿里和滴滴。好在基本三家都拿到了offer。最终决定选择阿里p6。
面试题
大厂流程比较长,比如阿里就面试了将近三周。所以期间也面试了很多别的公司,创业公司or上市公司。这里我把我所被问到的面试题总结梳理一下。简单深入的都有。笔者个人工作经验不丰富,如若回答不好的地方欢迎指正。
HTML && CSS 部分
CSS常用布局
CSS常用布局为盒模型div+css、其中需要注意IE的怪异盒模型,我们通常通过box-sizing解决。传统盒模型布局方式中我们可以细分为文档流布局、浮动布局、定位布局。在ie10+中我们可以使用flex布局,其中我们需要理解最为核心的容器和轴的概念。二维布局中,我们可以使用Grid布局。对于三栏布局,除了浮动实现方式,还有双飞翼布局和圣杯布局。其实双飞翼布局就是对圣杯布局的bug修复,一种改造升级。了解BFC么?
BFC即为块级格式化上下文。在普通流的Box属于一种formatting box,类型可以为block或者为inline。但是不能同时为这两者。并且Block boxes在block formatting context里格式化,inline boxes在inline formatting context中格式化,任何被渲染的元素都属于一个box,不是block就是inline。其一般表现规则分为以下几种情况:1、在创建了BFC的元素中,其子元素按照文档流一个接着一个放置。垂直方向上他们的起点是一个包含块的顶部,两者相邻元素的垂直距离取决margin特性。2、在BFC中,每一个元素的左外边与包含块的左边相接触。及时存在浮动也是如此。除非这个元素也创建了一个新的BFC。3、BFC就是页面的一个独立的行政区域。所有浏览器都会将BFC放到浮动元素所在行的剩余空间内。当HTML满足一下任意一个条件即可产生BFC:float不为none、overflow不为hidden、display为table-cell,table-caption,inline-block中的任何一个。Position值不为relative或static。通常我们使用BFC为了不和浮动元素重叠。清除内部浮动元素。解决上下元素相邻时候重叠。居中方面问题
分别从水平居中垂直居中两方面回答。水平、垂直居中分为单个元素、多个元素、已知宽高和未知宽高回答session、cookie、sessionStorage、localStorage区别
从client和Server中区分回答session是什么以及一般session如何使用注意事项以及安全策略,cookie、sessionStorage和localStorage等分别从概念和异同处回答。最后补充项目中的使用情况px/em/rem的区别
px顾名思义就是我们通常说的像素大小。em和rem都是相对大小,不过em是继承父级元素的字体大小,rem是相对于根元素的大小,这个单位可谓是集相对大小和绝对大小为一身。通过它可以做到只修改根元素即可修改所有字体的大小,又可以避免字体大小逐层复合的连锁反应。当rem相对于浏览器进行缩放,1rem默认为16px。animation和transition使用过么
写法:animation:name duration timing-function delay iteration-function direction .transition为过渡动画,这种效果可以在事件中触发,并且圆滑的以动画效果改变css的属性值。不同于transform,transform为2D转换(问题非常开放,尽可能多回答你所知道的)css编写注意事项
这个考验个人平时编码的总结和约束。问题较为开放,可以结合个人开发体验和团队约束来回答。比如0后面不带单位、尽量使用简写、使用子选择器、合理使用id等。
JavaScript部分
JavaScript闭包了解么
闭包即为函数,但是我们通常所说的闭包是指有权访问并操作别的函数作用域中变量的函数。一般表现形式为函数中返回函数。通常我们开发中就会有很多的闭包比如定时器、事件监听、IIFE等。此题是一个借题发挥涨分的题目,我们可以谈谈闭包的高级用法,比如单例模式中的使用、js的节流和防抖甚至可以谈一谈js的运行机制,gc机制。前端跨域都有哪些解决方案
js的前端跨域很多,通常我们给出方案并且应该简述优缺点,比如方式有,jsonp、document.domain+iframe、window.name+ifram、location.hash+iframe、postMessage跨域、CORS跨域、websocket跨域、node代理跨域以及NGINX代理等方式。其中对于我们常用的可以张开说明,比如CORS跨域中简单请求和非简单请求的header字段,access-control-allow-origin、access-control-allow-headers、access-control-allow-method等常规head字段说明。JavaScript中的继承
1、Es6中我们直接可以使用extends关键字去继承,通过重写super完成继承,但是在es5中,才是应该去主要细说的。2.原型继承的核心为父类的实例作为子类的原型。优点:非常纯粹的继承关系、简单易用、父类新增的原型方法原型属性子类都可以访问到。缺点:要为子类新增属性和方法,必须要放到new Animal()之后,不能放到构造器中,来自原型对象的引用类型被所有实力共享,创建子类,无法向构造函数传参。3.构造函数继承核心:使用父类构造函数来增强子类的实例优点:解决了共享引用类型的问题,可以在构造函数里面传参,可以实现多继承。缺点:实例不是父类实例,只能继承父类的属性和方法,不能继承父类原型的方法和属性、无法实现函数的复用。4、组合继承的核心:通过调用父类的构造,继承父类的属性并保留参数的有点,通过父类的实例作为子类原型,实现函数复用。优点:可以继承属性和方法以及原型上的属性和方法、即是子类实例也是父类实例、不存在属性共享的问题、函数可复用。缺点:调用了两次构造函数,生成了两份实例。5、寄生组合继承核心:砍掉父类的实例属性,这样,在调用两次父类的构造时候,就不会初始化两次实例方法和属性,避免组合继承的缺点
es6 extends继承,ES5中的继承,实质上是先创建子类上的this,然后再将父类方法添加到this上,es6则是先创建父类的实例方法(必须调用super),然后在用子类构造函数修改this。没有调用super,是没有this的。JavaScript中的节流和防抖
防抖的原理为触发事件的n时间后才执行,如果n时间内事件再次被触发,则以新触发的时间为标准,然后n事件后再执行
节流的原理为在持续触发事件时候,每隔一段时间执行一次。我们可以使用time line或者定时器来实现,或者二者结合实现。JavaScript中的事件你了解哪些
这种题目一般为开放性的题目,首先我们可以从事件的阶段来回答,事件冒泡、事件捕获、以及目标阶段。同时我们可以说明下事件委托的使用方式以及浏览器兼容性的问题。setTimeout设置为0发生了什么
这主要考察js的执行机制,可以从event loop来回答,包括js的单线程以及task queue microtask queue等。原生ajax请求了解么
js判断数据类型的几种方法
1、最常见的typeof,返回的String格式。能够判断function,但是判断Object比较烦。2、判断已知数据类型可以用instanceof,注意instanceof后面一定是对象类型,并且大小写不能错。3.constructor方式。但是在实现继承的时候回出现错误,需要手动修改。4.通用但是很繁琐的方法为prototype:Object.prototype.toString.call(a)this指向问题
1、常规为题,从默认绑定、隐含绑定、明确绑定以及new绑定来回答。2、扩展es6中的箭头函数,以及call、apply和bind的区别。你如何看待JavaScript这门语言
开放新问题,可以从参考《JavaScript语言精粹》,谈谈自己开发中遇到的各种问题,最重要的不是吐槽而是对鸡肋的解决办法,比如继承的实现、块级作用域、变量提升等。es6用过么?说说promise的实现
关于es6的知识点这里不再赘述。Promise的实现主要是pub-sub模式。状态和行为相分离的难点。
React部分
react组件的生命周期相关
react组件生命周期最好的提现是使用es5 的编码方式,其中生命线大概分为两条路线,分别为getDefaultProps、getInitialState、componentWillMount、render、componentDidMount、(Running),然后分为两路,componentWillReceiveProps、shouldComponentUpdate、componentWillUpdate、render、componentDidUpdate以及componentWillUumount。我们一般在getInitialState中初始化组件的状态数据,在componentDidMount或者componentWillMount中进行获取API请求操作等,在shouldComponentUpdate中进行组件的优化。其中可以展开如何优化以及官方推荐的PureRenderMixin等实现方式。以及别的方面扩展结合自己的开发经验。setState是同步的还是异步的
通常我们的写法都是异步的,但是真正想要我们回答的是当给setState传入函数的时候,其实表现的为同步的。子组件的componentDidMount和父组件的componentDidMount哪一个先执行?
必然是子组件的componentDidMount限制性,可以从react组件的生命周期进行分析扩展。react的DIFF算法和virtual dom了解多少?
React的render函数返回的是一个DOM描述,结果仅仅是轻量级的js对象,reactjs在调用setState的时候会更新DOM,而且是先更新virtual dom,然后和实际dom比较,最后更新dom。React厉害的地方不是说他比真实的dom速度快,而是你不敢数据怎么变化,我都以最小的代价来更新视图。方法就是我在内存当中使用新的数据来构建一个virtual dom,然后和旧DOM进行比较,找出差异,然后更新到DOM节点上。当我们修改dom上的一个节点对应的state,react会立即将他标记为“脏状态”,在事件循环的最后才重新渲染所有的脏节点。在实际的代码中,会对新旧两棵树进行一次深度优先遍历,这样每一个节点都会有一个唯一的标记,没遍历到一个节点,就把该节点和新的树进行比较,如果有差异就记录到一个对象中,最后把差异应用到真正的DOM树上。算法实现步骤为:用js对象模拟DOM树,比较两颗虚拟DOM的差异,把差异应用到真正的DOM树上,DOM DIFF采用的是增量更新的方式,类似于打补丁。React需要为节点添加key来保证算法的效率。Key属性可以帮助react定位到正确的节点进行比较。从而大幅度减少DOM操作,提高性能。
MVC和MVVM了解么?可以大致说一下双向绑定的实现方式么?
Modal层代表数据模型,可以再modal层定义修改和操作数据的逻辑,view代表UI层,负责将数据转换成UI展现出来,viewModal是同步view和modal的对象。用户操作view层,view数据变化会同步到modal上,modal数据变化会立即反应到view中,viewModal通过实现双向绑定来将view和modal连接到一起。而双向绑定,我们可以从脏检查到标记更新来回答。
前端路由的实现方式
从react当中我们可以说说react router的使用和原理,以及h5中的historyAPI,pushState和replaceState来回答。a组件是b组件的父组件,b组件是c组件的父组件,如何让渲染后的b和c在同级
通过react16中不返回容器组件可以实现,也可以通过“曲线救国”的方式来实现。react SSR了解么
react ssr有很多种实现方式,但是原理不变,目的就是为了减少首屏白屏时间以及有好的SEO。对于实现方式我们可以从next.js以及webpack-isomorphic-tools来说实现。
浏览器相关
http三次握手后拿到HTML是如何进行加载的?
考核的主要是浏览器加载页面的机制。大概可以从浏览器拿到HTML,自上而下开始解析,大致分为解析DOM,解析CSSOM,构建渲染树,布局阶段以及绘制阶段来说明。其实尽可能的详细说明,比如构建DOM的时候分别通过Bytes、characters、tokens、Nodes最终到DOM等。回答也可以扩展repaint和reflow等浏览器优化。
简述浏览器优化
200 From cache和200 OK有什么区别?
顾名思义是form cache是强缓存,不会和服务器通信,而200OK即为服务器处理结果正确。以此可以从浏览器缓存、输入url回车、刷新页面以及强制刷新等方面展开缓存方面的讲解。
http2.0新特性了解吗
1、2采用二进制而非文本格式,二进制协议解析起来更高效。
2、采用多路复用,即为同一个tcp连接上可以建立多个http连接,那样的话,我们雪碧图就没有必要了。
3、使用报文头压缩,降低了开销。
4.可以让服务器主动向浏览器推送消息,支持服务端推送,也就是服务端可以对客户端有多个响应。
post和get有什么区别?
1 、GET把参数包含在URL中,POST通过request body传递参数。
2、GET在浏览器回退时是无害的,而POST会再次提交请求。GET产生的URL地址可以被Bookmark,而POST不可以。GET请求会被浏览器主动cache,而POST不会,除非手动设置。GET请求只能进行url编码,而POST支持多种编码方式。GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。GET请求在URL中传送的参数是有长度限制的,而POST么有。对参数的数据类型,GET只接受ASCII字符,而POST没有限制。GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。GET参数通过URL传递,POST放在Request body中。
3、GET和POST是什么?HTTP协议中的两种发送请求的方法。
4、HTTP是什么?HTTP是基于TCP/IP的关于数据如何在万维网中如何通信的协议。5、HTTP的底层是TCP/IP。所以GET和POST的底层也是TCP/IP,也就是说,GET/POST都是TCP链接。GET和POST能做的事情是一样一样的。你要给GET加上request body,给POST带上url参数,技术上是完全行的通的。
6、在我大万维网世界中,还有另一个重要的角色:运输公司。不同的浏览器(发起http请求)和服务器(接受http请求)就是不同的运输公司。虽然理论上,你可以在车顶上无限的堆货物(url中无限加参数)。但是运输公司可不傻,装货和卸货也是有很大成本的,他们会限制单次运输量来控制风险,数据量太大对浏览器和服务器都是很大负担。业界不成文的规定是,(大多数)浏览器通常都会限制url长度在2K个字节,而(大多数)服务器最多处理64K大小的url。超过的部分,恕不处理。如果你用GET服务,在request body偷偷藏了数据,不同服务器的处理方式也是不同的,有些服务器会帮你卸货,读出数据,有些服务器直接忽略,所以,虽然GET可以带request body,也不能保证一定能被接收到哦。
7、GET产生一个TCP数据包;POST产生两个TCP数据包。
8、对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。也就是说,GET只需要汽车跑一趟就把货送到了,而POST得跑两趟,第一趟,先去和服务器打个招呼“嗨,我等下要送一批货来,你们打开门迎接我”,然后再回头把货送过去。因为POST需要两步,时间上消耗的要多一点,看起来GET比POST更有效。因此Yahoo团队有推荐用GET替换POST来优化网站性能。但这是一个坑!跳入需谨慎。为什么?1. GET与POST都有自己的语义,不能随便混用。2. 据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。3. 并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。
构建工具
了解过webpack编译过程吗?
Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程,1、初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;2、开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译;3、确定入口:根据配置中的 entry 找出所有的入口文件;4、编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;5、完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;6、输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;7、输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。
编写过webpack插件吗
1、Webpack 通过 Plugin 机制让其更加灵活,以适应各种应用场景。在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。2、Webpack 启动后,在读取配置的过程中会先执行 new BasicPlugin(options) 初始化一个 BasicPlugin 获得其实例。在初始化 compiler 对象后,再调用 basicPlugin.apply(compiler) 给插件实例传入 compiler 对象。插件实例在获取到 compiler 对象后,就可以通过 compiler.plugin(事件名称, 回调函数) 监听到 Webpack 广播出来的事件。并且可以通过 compiler 对象去操作 Webpack。3、在开发 Plugin 时最常用的两个对象就是 Compiler 和 Compilation,它们是 Plugin 和 Webpack 之间的桥梁。Compiler 和 Compilation 的含义如下:Compiler 对象包含了 Webpack 环境所有的的配置信息,包含 options,loaders,plugins 这些信息,这个对象在 Webpack 启动时候被实例化,它是全局唯一的,可以简单地把它理解为 Webpack 实例;Compilation 对象包含了当前的模块资源、编译生成资源、变化的文件等。当 Webpack 以开发模式运行时,每当检测到一个文件变化,一次新的 Compilation 将被创建。Compilation 对象也提供了很多事件回调供插件做扩展。通过 Compilation 也能读取到 Compiler 对象。4、Compiler 和 Compilation 的区别在于:Compiler 代表了整个 Webpack 从启动到关闭的生命周期,而 Compilation 只是代表了一次新的编译。5、开发插件时需要注意:只要能拿到 Compiler 或 Compilation 对象,就能广播出新的事件,所以在新开发的插件中也能广播出事件,给其它插件监听使用、传给每个插件的 Compiler 和 Compilation 对象都是同一个引用。也就是说在一个插件中修改了 Compiler 或 Compilation 对象上的属性,会影响到后面的插件、有些事件是异步的,这些异步的事件会附带两个参数,第二个参数为回调函数,在插件处理完任务时需要调用回调函数通知 Webpack,才会进入下一处理流程。开发过webpack loader么
1、一个 Loader 的职责是单一的,只需要完成一种转换。如果一个源文件需要经历多步转换才能正常使用,就通过多个 Loader 去转换。在调用多个 Loader 去转换一个文件时,每个 Loader 会链式的顺序执行, 第一个 Loader 将会拿到需处理的原内容,上一个 Loader 处理后的结果会传给下一个接着处理,最后的 Loader 将处理后的最终结果返回给 Webpack。2、所以,在你开发一个 Loader 时,请保持其职责的单一性,你只需关心输入和输出。
感悟
以上问题包括但不全面对于这次杭州的求职。总的来说,你的简历就是你给面试官的考纲,所以简历一定要真实,及时面试过程中遇到不会的题目,也要沉着冷静思考,不会也要主动承认,然后最好能够提出自己的思考和猜测。千万别不懂装懂!千万别不懂装懂!千万别不懂装懂!
前端,个人还是觉得基础很重要,从基础到框架,从框架就到原理,从原理到源码,一步一脚印。一定要自信,直面面试官,表现出自己最好的状态。同事别太咄咄逼人,一定要尊敬面试官,礼貌。
最后,还是希望每一个求职者,都能够进入自己如愿以偿的公司拿到心仪的offer~
ps:如若文章有不当之处,欢迎大家指正。谢谢~
完
往期精彩回顾Nealyang 全栈前端Flutter 从入门到寄几玩儿
Decorator 从原理到实战