文章目录
  1. 1. 二叉树的三种遍历方法
  2. 2. 前端性能优化
  3. 3. flex布局
  4. 4. 移动端适配
    1. 4.1. rem方式
    2. 4.2. flex布局方式
    3. 4.3. 百分比布局
  5. 5. React组件中的ajax
  6. 6. React组件间的通信
    1. 6.1. 父子组件之间的通信
    2. 6.2. 子父组件之间的通信
    3. 6.3. 跨级组件之间通信
    4. 6.4. 兄弟组件间的通信
  7. 7. ES6中的异步操作
    1. 7.1. 回调函数
    2. 7.2. 事件监听
    3. 7.3. 发布/订阅
    4. 7.4. Promise对象
    5. 7.5. generator
    6. 7.6. async/await
  8. 8. ajax的error方法
    1. 8.1. beforSend
    2. 8.2. success
    3. 8.3. complete
    4. 8.4. error
    5. 8.5. 触发规则
  9. 9. div中垂直居中的方法
    1. 9.1. 单行文字
    2. 9.2. 多行文字
    3. 9.3. 子div居中-偏移
    4. 9.4. 子div居中-translate
    5. 9.5. 子div居中-absolute
    6. 9.6. 子div居中-flex
  10. 10. Bootstrap栅格系统原理
    1. 10.1. 实现原理
    2. 10.2. 工作原理
  11. 11. Echarts的常见配置

日期:2018-03-20日中午11点,于苏宁总部进行面试。

以下为面试大部分问题,可能还有一点点记不太清了。

最开始的问题就是二叉树的遍历

二叉树的三种遍历方法

先序遍历:遍历顺序规则为【根左右】

中序遍历:遍历顺序规则为【左根右】

后序遍历:遍历顺序规则为【左右根】

思想:递归。

前端性能优化

这里我给出自己的建议,面试的时候,问的太突然,一下子懵了,都没怎么答出来。。。

  • 减少HTTP请求数
  • 合理设置HTTP缓存
  • js、css、img等资源合并压缩
  • CSS Sprites来合并多张图片
  • LazyLoad 懒加载页面图片内容
  • 将css放在head标签。否则会导致,页面由无CSS状态跳转到CSS状态,用户体验比较糟糕。
  • 将js文件放在body最后面引入。
  • 避免资源重复请求
  • js层面,慎用with方法。使用with相当于增加了作用域链长度。而每次查找作用域链都是要消耗时间的,过长的作用域链会导致查找性能下降。
  • js层面,避免使用 eval和 Function。
  • 减少作用域链查找。这里举个例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    普通写法:
    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),简称”项目”。

借用下阮一峰的图:
flex容器图

容器默认存在两根轴:水平的主轴(main axis)和垂直的交叉轴(cross axis)。主轴的开始位置(与边框的交叉点)叫做main start,结束位置叫做main end;交叉轴的开始位置叫做cross start,结束位置叫做cross end。

项目默认沿主轴排列。单个项目占据的主轴空间叫做main size,占据的交叉轴空间叫做cross size。

移动端适配

rem方式

rem是根据网页的根元素来设置字体大小。

1
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">

解释:

  • 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进行适配。

1
2
3
4
@media screen and (-webkit-min-device-pixel-ratio: 2) {
-webkit-transform: scaleY(.5);
transform: scaleY(.5);
}

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 后进行相应的处理。
父组件:

1
2
3
4
5
6
7
render(){
return(
<div>
<Child name = "yk" />
</div>
)
}

子组件:

1
2
3
4
5
6
7
8
9
10
11
import React, { Component } from 'react';
export default class Child extends Component {
render(){
return (
<div>
<div>my name is: {this.props.name}</div>
</div>
)
}
}

子父组件之间的通信

父组件将一个函数作为 props 传递给子组件,子组件调用该回调函数,便可以向父组件通信。

子组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
render(){
const Subneed = (info) => {
return () => {
this.props.CelClick(info)
};
};
return (
<div>
<div >my name is: {this.props.name}</div>
<Button onClick = { Subneed('bbb') }>点击我一下</Button>
</div>
)
}

父组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class TestPhp extends Component{
state = {
str : 'aaa'
}
handleClick = (info) => {
this.setState({
str : info
})
}
render(){
return(
<div>
<Child name = "yk" CelClick={ this.handleClick } />
<div>{this.state.str}</div>
</div>
)
}
}

跨级组件之间通信

父组件向子组件的子组件通信,向更深层的子组件通信。跨级组件通信可以采用下面两种方式:

  • 中间组件层层传递 props
  • 使用 context 对象

当组件层次较浅,在三层以内时,可以使用props来进行传值。

使用context对象是另一种方式,context相当于一个全局变量,把要通信的内容放在这个容器中,这样一来,不管嵌套有多深,都可以随意取用。

使用context,需要满足两个条件:

  • 上级组件要声明自己支持 context,并提供一个函数来返回相应的 context 对象
  • 子组件要声明自己需要使用 context
1
2
3
constructor(props,context){
super(props,context);
}

兄弟组件间的通信

兄弟组件之间的通信,可以采用下面两种方式:

  • 利用二者共同父组件的 context 对象进行通信
  • 使用自定义事件的方式

这里说一下自定义事件的方式:

需要使用一个 events 包。

1
npm install events --save

新建一个 ev.js,引入 events 包,并向外提供一个事件对象,供通信时使用:

1
2
import { EventEmitter } from "events";
export default new EventEmitter();

给A组件添加事件监听:

1
2
3
4
5
6
7
8
9
10
11
12
13
componentDidMount(){
// 声明一个自定义事件
// 在组件装载完成以后
this.eventEmitter = emitter.addListener("callMe",(msg)=>{
this.setState({
msg
})
});
}
// 组件销毁前移除事件监听
componentWillUnmount(){
emitter.removeListener(this.eventEmitter);
}

给B组件添加事件注册:

1
2
3
4
5
6
7
8
9
10
11
12
13
render(){
const cb = (msg) => {
return () => {
// 触发自定义事件
emitter.emit("callMe","Hello")
}
}
return(
<div>
<button onClick = { cb("blue") }>点击我</button>
</div>
);
}

自定义事件是典型的发布/订阅模式,通过向事件对象上添加监听器和触发事件来实现组件间通信。

更详细的可以看简书《React 中组件间通信的几种方式》

ES6中的异步操作

ES6以前,常用的有4中异步操作的方式:

  • 回调函数
  • 事件监听
  • 发布/订阅
  • Promise对象

(阮一峰的博客)有更详细的介绍,我这里参考该博客,只简单介绍一下相关实现:

回调函数

1
2
3
4
5
6
function f1(callback){
setTimeout(function () {
// f1的任务代码
callback();
}, 1000);
}

回调函数的优点是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合,流程会很混乱,而且每个任务只能指定一个回调函数。

事件监听

1
2
3
4
5
6
7
8
f1.on('done', f2);
function f1(){
setTimeout(function () {
// f1的任务代码
f1.trigger('done');
}, 1000);
}

事件监听的优点是比较容易理解,可以绑定多个事件,每个事件可以指定多个回调函数,而且可以”去耦合”,有利于实现模块化。缺点是整个程序都要变成事件驱动型,运行流程会变得很不清晰。

发布/订阅

1
2
3
4
5
6
7
8
9
10
function f1(){
setTimeout(function () {
// f1的任务代码
jQuery.publish("done");
}, 1000);
}
// 订阅
jQuery.subscribe("done", f2);
// 取消订阅
jQuery.unsubscribe("done", f2);

这种方法的性质与”事件监听”类似,但是明显优于后者。因为我们可以通过查看”消息中心”,了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。

Promise对象

1
2
3
4
5
6
7
8
9
function f1(){
var dfd = $.Deferred();
setTimeout(function () {
// f1的任务代码
dfd.resolve();
}, 500);
return dfd.promise;
}
f1().then(f2).then(f3).fail(f4);;

它还有一个前面三种方法都没有的好处:如果一个任务已经完成,再添加回调函数,该回调函数会立即执行。所以,你不用担心是否错过了某个事件或信号。这种方法的缺点就是编写和理解,都相对比较难。

ES6新增的几个异步操作

generator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function fn1 () {
console.log('Function 1')
}
function fn2 () {
setTimeout(() => {
console.log('Function 2')
af.next()
}, 500)
}
function fn3 () {
console.log('Function 3')
}
function* asyncFunArr (...fn) {
fn[0]()
yield fn[1]()
fn[2]()
}
const af = asyncFunArr(fn1, fn2, fn3)
af.next()
// output =>
// Function 1
// Function 2
// Function 3

generator函数asyncFunArr()接受一个待执行函数列表fn,异步函数将会通过yield来执行。在异步函数内,通过af.next()激活generator函数的下一步操作。

async/await

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function fn1 () {
console.log('Function 1')
}
function fn2 () {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('Function 2')
resolve()
}, 500)
})
}
function fn3 () {
console.log('Function 3')
}
async function asyncFunArr () {
fn1()
await fn2()
fn3()
}
asyncFunArr()
// output =>
// Function 1
// Function 2
// Function 3

异步的操作都返回Promise,需要顺序执行时只需要await相应的函数即可,这种方式在语义化方面非常友好,对于代码的维护也很简单——只需要返回Promise并await它就好,无需像generator那般需要自己去维护内部yield的执行。

ajax的error方法

问题是:ajax的error方法是怎样触发的?

先看一看jQuery的ajax方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$.ajax({
url:'',
type:"post",
data:"",
dataType:"json",
beforeSend:function(XMLHttpRequest, settings){
},
success:function(data, textStatus, XMLHttpRequest){
},
complete:function(XMLHttpRequest, textStatus){
},
error:function(XMLHttpRequest, textStatus, errorThrown){
}
});

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中垂直居中的方法

单行文字

1
2
3
4
5
6
7
.div{
width:300px;
height:200px;
text-align:center;
line-height:200px;
overflow: hidden;
}

多行文字

1
2
3
4
5
6
.div{
width: 300px;
margin: 50px auto;
text-align: center;
padding: 50px 20px;
}

子div居中-偏移

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.father{
width: 300px;
height: 200px;
position: relative;
}
.child{
width: 100px;
height: 100px;
margin: auto;
position: absolute;
left: 50%;
top: 50%;
margin-left: -50px;
margin-top: -50px;
}

子div居中-translate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.father{
width: 300px;
height: 200px;
position: relative;
}
.child{
width: 100px;
height: 100px;
margin: auto;
position: absolute;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%);
-webkit-transform: translateX(-50%) translateY(-50%);
}

子div居中-absolute

1
2
3
4
5
6
7
8
9
10
11
12
.father{
width: 300px;
height: 200px;
position: relative;
}
.child{
width: 100px;
height: 100px;
margin: auto;
position: absolute;
top: 0;left: 0;right: 0;bottom: 0;
}

子div居中-flex

1
2
3
4
5
6
7
8
9
10
11
.father{
width: 300px;
height: 200px;
display: flex;
justify-content: center;/*水平居中*/
align-items:center; /*垂直居中*/
}
.child{
width: 100px;
height: 100px;
}

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: 全局的字体样式
文章目录
  1. 1. 二叉树的三种遍历方法
  2. 2. 前端性能优化
  3. 3. flex布局
  4. 4. 移动端适配
    1. 4.1. rem方式
    2. 4.2. flex布局方式
    3. 4.3. 百分比布局
  5. 5. React组件中的ajax
  6. 6. React组件间的通信
    1. 6.1. 父子组件之间的通信
    2. 6.2. 子父组件之间的通信
    3. 6.3. 跨级组件之间通信
    4. 6.4. 兄弟组件间的通信
  7. 7. ES6中的异步操作
    1. 7.1. 回调函数
    2. 7.2. 事件监听
    3. 7.3. 发布/订阅
    4. 7.4. Promise对象
    5. 7.5. generator
    6. 7.6. async/await
  8. 8. ajax的error方法
    1. 8.1. beforSend
    2. 8.2. success
    3. 8.3. complete
    4. 8.4. error
    5. 8.5. 触发规则
  9. 9. div中垂直居中的方法
    1. 9.1. 单行文字
    2. 9.2. 多行文字
    3. 9.3. 子div居中-偏移
    4. 9.4. 子div居中-translate
    5. 9.5. 子div居中-absolute
    6. 9.6. 子div居中-flex
  10. 10. Bootstrap栅格系统原理
    1. 10.1. 实现原理
    2. 10.2. 工作原理
  11. 11. Echarts的常见配置