苏宁易购面试总结
更新日期:
日期:2018-03-20日中午11点,于苏宁总部进行面试。
以下为面试大部分问题,可能还有一点点记不太清了。
最开始的问题就是二叉树的遍历
二叉树的三种遍历方法
先序遍历:遍历顺序规则为【根左右】
中序遍历:遍历顺序规则为【左根右】
后序遍历:遍历顺序规则为【左右根】
思想:递归。
前端性能优化
这里我给出自己的建议,面试的时候,问的太突然,一下子懵了,都没怎么答出来。。。
- 减少HTTP请求数
- 合理设置HTTP缓存
- js、css、img等资源合并压缩
- CSS Sprites来合并多张图片
- LazyLoad 懒加载页面图片内容
- 将css放在head标签。否则会导致,页面由无CSS状态跳转到CSS状态,用户体验比较糟糕。
- 将js文件放在body最后面引入。
- 避免资源重复请求
- js层面,慎用with方法。使用with相当于增加了作用域链长度。而每次查找作用域链都是要消耗时间的,过长的作用域链会导致查找性能下降。
- js层面,避免使用 eval和 Function。
减少作用域链查找。这里举个例子:
123456789101112131415161718普通写法:var globalVal = 1; //全局变量function myFun(info){for(var i = 100000;i--;){// 每次访问 globalVar 都需要查找到作用域链最顶端,本例中需要访问 100000 次globalVal += 1;}}高效写法:var globalVal = 1; //全局变量function myFun(info){var localVal = globalVal; // 局部变量缓存全局变量for(var i = 100000;i--;){localVal += 1; // 访问当前局部变量是最快的}globalVal = localVal;// 本例中只需要访问2次全局变量}图片使用base64或其他编码优化
- 公共js或css库可以使用CDN引用,例如,bootcdn等。
- 图片压缩,可以使用Caesium_boxed进行图片压缩。
- 使用nginx的反向代理,发送静态资源,减少服务器压力。
flex布局
这个比较常用,就说了布局的一些方法,但是面试官想问的是布局原理,这里我就不写布局方法了,之前的博客里有写到flex布局。写一下布局的原理。
采用 Flex 布局的元素,称为 Flex 容器(flex container),简称”容器”。它的所有子元素自动成为容器成员,称为 Flex 项目(flex item),简称”项目”。
借用下阮一峰的图:
容器默认存在两根轴:水平的主轴(main axis)和垂直的交叉轴(cross axis)。主轴的开始位置(与边框的交叉点)叫做main start,结束位置叫做main end;交叉轴的开始位置叫做cross start,结束位置叫做cross end。
项目默认沿主轴排列。单个项目占据的主轴空间叫做main size,占据的交叉轴空间叫做cross size。
移动端适配
rem方式
rem是根据网页的根元素来设置字体大小。
|
|
解释:
- width=device-width 这句代码可以把布局视口设置成为浏览器(屏幕)的宽度。
- initial-scale=1 的意思是初始缩放的比例是1,使用它的时候,同时也会将布局视口的尺寸设置为缩放后的尺寸。而缩放的尺寸就是基于屏幕的宽度来的,也就起到了和width=device-width同样的效果。
- user-scalable: 用户是否可以手动缩放
- maximum-scale: 允许用户缩放到的最大比例
通过document.clientWidth 来计算rem值
布局和文字都使用rem
flex布局方式
手淘团队的一套flexible
百分比布局
问了几个做Android开发的朋友,他们用的是百分比布局,具体细节不清楚。。。。
Retina屏幕
可以使用CSS的媒体查询@midia,和scaleY进行适配。
|
|
React组件中的ajax
问题是:ajax发生在React中的哪个生命周期中?
React组件的生命周期分为三个状态:
- Mounting:已插入真实 DOM
- Updating:正在被重新渲染
- Unmounting:已移出真实 DOM
React为每个状态都提供了两种处理函数,will函数在进入状态之前调用,did函数在进入状态之后调用,三种状态共计五种处理函数。
- componentWillMount(): 组件被渲染到页面上之前执行,在组件的整个生命周期内只执行一次。
- componentDidMount(): 组件被渲染到页面上后立马执行,在组件的整个生命周期内只执行一次。
- componentWillUpdate(object nextProps, object nextState): 当组件shouldComponentUpdate返回true或者调用forceUpdate时将触发此函数。该函数在函数第一次渲染的时候不会执行。
- componentDidUpdate(object prevProps, object prevState): 在组件重新渲染过程中,重新执行render方法并更新组件视图后立即执行该函数。类似组件第一次渲染过程中的 componentDidMount,该函数在第一次渲染时不会执行。
- componentWillUnmount(): 当组件被从页面中移除之前调用,此时是清理战场的好时机,如清理定时器、终止网络请求等。
组件的数据来源,通常是通过Ajax请求从服务器获取,可以使用componentDidMount方法的生命周期中调用Ajax请求,等到请求成功,再用this.setState方法重新渲染UI页面。
React组件间的通信
父子组件之间的通信
父组件通过向子组件传递 props,子组件得到 props 后进行相应的处理。
父组件:
子组件:
子父组件之间的通信
父组件将一个函数作为 props 传递给子组件,子组件调用该回调函数,便可以向父组件通信。
子组件:
父组件:
跨级组件之间通信
父组件向子组件的子组件通信,向更深层的子组件通信。跨级组件通信可以采用下面两种方式:
- 中间组件层层传递 props
- 使用 context 对象
当组件层次较浅,在三层以内时,可以使用props来进行传值。
使用context对象是另一种方式,context相当于一个全局变量,把要通信的内容放在这个容器中,这样一来,不管嵌套有多深,都可以随意取用。
使用context,需要满足两个条件:
- 上级组件要声明自己支持 context,并提供一个函数来返回相应的 context 对象
- 子组件要声明自己需要使用 context
|
|
兄弟组件间的通信
兄弟组件之间的通信,可以采用下面两种方式:
- 利用二者共同父组件的 context 对象进行通信
- 使用自定义事件的方式
这里说一下自定义事件的方式:
需要使用一个 events 包。
|
|
新建一个 ev.js,引入 events 包,并向外提供一个事件对象,供通信时使用:
给A组件添加事件监听:
给B组件添加事件注册:
自定义事件是典型的发布/订阅模式,通过向事件对象上添加监听器和触发事件来实现组件间通信。
更详细的可以看简书《React 中组件间通信的几种方式》
ES6中的异步操作
ES6以前,常用的有4中异步操作的方式:
- 回调函数
- 事件监听
- 发布/订阅
- Promise对象
(阮一峰的博客)有更详细的介绍,我这里参考该博客,只简单介绍一下相关实现:
回调函数
|
|
回调函数的优点是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合,流程会很混乱,而且每个任务只能指定一个回调函数。
事件监听
|
|
事件监听的优点是比较容易理解,可以绑定多个事件,每个事件可以指定多个回调函数,而且可以”去耦合”,有利于实现模块化。缺点是整个程序都要变成事件驱动型,运行流程会变得很不清晰。
发布/订阅
|
|
这种方法的性质与”事件监听”类似,但是明显优于后者。因为我们可以通过查看”消息中心”,了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。
Promise对象
|
|
它还有一个前面三种方法都没有的好处:如果一个任务已经完成,再添加回调函数,该回调函数会立即执行。所以,你不用担心是否错过了某个事件或信号。这种方法的缺点就是编写和理解,都相对比较难。
ES6新增的几个异步操作
generator
|
|
generator函数asyncFunArr()接受一个待执行函数列表fn,异步函数将会通过yield来执行。在异步函数内,通过af.next()激活generator函数的下一步操作。
async/await
|
|
异步的操作都返回Promise,需要顺序执行时只需要await相应的函数即可,这种方式在语义化方面非常友好,对于代码的维护也很简单——只需要返回Promise并await它就好,无需像generator那般需要自己去维护内部yield的执行。
ajax的error方法
问题是:ajax的error方法是怎样触发的?
先看一看jQuery的ajax方法。
|
|
beforSend
请求发送前的回调函数,用来修改请求发送前jqXHR对象,此功能用来设置自定义HTTP头信息,等等。该XMLHttpRequest和设置对象作为参数传递。
success
请求成功后的回调函数。这个函数传递3个参数:从服务器返回的数据,并根据dataType参数进行处理后的数据,一个描述状态的字符串;还有XMLHttpRequest对象 。
complete
请求完成后回调函数(请求success和error之后均调用)。这个回调函数得到2个参数: XMLHttpRequest对象和一个描述请求状态的字符串(“success”, “notmodified”, “error”, “timeout”, “abort”, 或者”parsererror”)。
error
请求失败时调用此函数。有以下三个参数:XMLHttpRequest对象、描述发生错误类型的一个字符串和捕获的异常对象。如果发生了错误,错误信息(第二个参数)除了得到null之外,还可能是”timeout”, “error”, “abort” ,和 “parsererror”。当一个HTTP错误发生时,errorThrown 接收HTTP状态的文本部分,比如: “Not Found”(没有找到) 或者 “Internal Server Error.”(服务器内部错误)。
触发规则
Ajax请求通过XMLHttpRequest对象发送请求,该对象有四个状态(readyState):
0-未初始化、1-正在初始化、2-发送数据、3-正在发送数据、4-完成。
当XMLHttpRequest.readyState为4时,表示ajax请求已经完成可以得到响应结果。
ajax的success和error方法根据响应状态码来触发:
- 当XMLHttpRequest.status为200的时候,表示响应成功,此时触发success().
- 其他状态码则触发error()。
除了根据响应状态码外,ajax还会在下列情况下走error方法:
- 返回数据类型不是JSON
- 网络中断
- 后台响应中断
div中垂直居中的方法
单行文字
|
|
多行文字
|
|
子div居中-偏移
|
|
子div居中-translate
|
|
子div居中-absolute
|
|
子div居中-flex
|
|
Bootstrap栅格系统原理
实现原理
- 把网页总宽度平分为12份,开发人员可以自由按分组合,以便开发出简洁方便的程序
- 通过定义容器大小、平分为12份,再调整内外边距,最后结合媒体查询,就制作出强大的响应式栅格系统
- 栅格系统用于通过一系列的行(row)与列(column)的组合来创建页面布局,你的内容就可以放入这些创建好的布局中。
工作原理
- “行(row)”必须包含在 .container (固定宽度)或 .container-fluid(100%宽度)中,以便为其赋予合适的排列(aligment)和内补(padding)。
- 通过“行(row)”在水平方向创建一组“列(column)”。
- 你的内容应当放置于“列(column)”内,并且,只有“列(column)”可以作为行(row)”的直接子元素。
- 类似 .row 和 .col-xs-4 这种预定义的类,可以用来快速创建栅格布局。Bootstrap 源码中定义的 mixin 也可以用来创建语义化的布局。
- 通过为“列(column)”设置 padding 属性,从而创建列与列之间的间隔(gutter)。通过为 .row 元素设置负值 margin 从而抵消掉为 .container 元素设置的 padding,也就间接为“行(row)”所包含的“列(column)”抵消掉了padding。
- 负值的 margin就是下面的示例为什么是向外突出的原因。在栅格列中的内容排成一行。
- 栅格系统中的列是通过指定1到12的值来表示其跨越的范围。例如,三个等宽的列可以使用三个 .col-xs-4 来创建。
- 如果一“行(row)”中包含了的“列(column)”大于 12,多余的“列(column)”所在的元素将被作为一个整体另起一行排列。
- 栅格类适用于与屏幕宽度大于或等于分界点大小的设备 , 并且针对小屏幕设备覆盖栅格类。 因此,在元素上应用任何 .col-md- 栅格类适用于与屏幕宽度大于或等于分界点大小的设备 , 并且针对小屏幕设备覆盖栅格类。 因此,在元素上应用任何 .col-lg- 不存在, 也影响大屏幕设备。
Echarts的常见配置
这里主要以Echarts3为例,简述常用的setOption配置项:
- title: 标题和副标题
- legend: 可以通过点击图例控制哪些系列不显示
- xAxis: 直角坐标系中X轴的样式设置
- yAxis: 直角坐标系中Y轴的样式设置
- tooltip: 提示框组件
- series: 系列列表。每个系列通过 type 决定自己的图表类型
- color: 调色盘颜色列表
- backgroundColor: 背景色,默认无背景
- textStyle: 全局的字体样式