Vue 的使用主要考虑以下几点:

体积小,复杂度低
业务上移动端项目占比 70% 以上,Vue 的体积小,网络性能角度相比 React 更适合移动端
移动端一般巨型项目很少,从代码结构上来讲,使用 Vue 实现更符合我们的场景复杂度,React 更适合大型相对更复杂的 SPA
上手成本和迁移成本低
Vue 的学习和上手成本相对更低,团队成员对于 Vue 的认可度和热情也比较高
组件内双向绑定、数据依赖收集
组件内支持双向绑定,更方便的去进行组件内的数据响应与交互
独有的数据依赖收集模式使其默认的数据响应和渲染效率都要比 React 高一些
React 的使用主要考虑以下原因:

有一部分现有后台项目采用 React 技术栈,迭代和维护较少,老的项目如果没有足够的迁移价值则不额外投入资源
保留很小的一部分 React 技术生态也可以一定程度上保持一些技术多样性

一面
先完成笔试题

1 实现一个函数,判断输入是不是回文字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function isPalindrome(str){
let flag
if(typeof str!=='string'|| str.constructor !== String){
return
}
for(var i=0,j=str.length-1;i<j;i++,j--){
console.log(i,j)
if(str.charAt(i) !== str.charAt(j)){
return false;
}
}
return true;

}
console.log(isPalindrome('adddddda'));//true
console.log(isPalindrome('addddda'));//true
console.log(isPalindrome('adddasd'));//false

2 居中

transform:translate(-50%,-50%); (ie9以上兼容)
flex;align-items: center;justify-content: center ( 移动端首选)

3 实现效果,点击容器内的图标,图标边框变成border 1px solid red,点击空白处重置。

4 请简单实现双向数据绑定mvvm。

Object.defineProperty 来实现一个简单的数据双向绑定

1
2
<input type="text" id="input" />
<div id="div"></div>

1
2
3
4
5
6
7
8
9
10
11
12
13
var obj = {};
var input = document.getElementById("input");
var div = document.getElementById("div");

Object.defineProperty(obj, "name", {
set: function(newVal) {
input.value = newVal;
div.innerHTML = newVal;
}
});
input.addEventListener('input', function(event){
obj.name = event.target.value;
});

5 实现Storage,使得该对象为单例,并对localStorage进行封装设置值setItem(key,value)和getItem(key)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let instance =null;

class Storage {
constructor(props) {
this.state={

}
}
//实例化
static getInstance(name) {
if(!this.instance) {
this.instance = new Storage(name);
}
return this.instance;
}
setItem = (key, value) => localStorage.setItem(key, JSON.stringify(value)),
getItem = key => localStorage.getItem(key)
}

Q1 你的技术栈主要是react,那你说说你用react有什么坑点?

1.在JSX语法种,需要将 class改写成 className
2.react 打包后,项目部署完毕,刷新页面报错(404) 配置nginx 把目录指向index.html就可以解决
3.this.setState()会调用render方法,但并不会立即改变state的值,state是在render方法中赋值的。所以执行this.setState()后立即获取state的值是不变的。同样的直接赋值state并不会出发更新,因为没有调用render函数
4.监听事件和定时器需要在组件卸载的时候清除,否则切换路由的话这些事件还会一直执行下去
5.react列表渲染时为什么尽量不要把索引设置为key值
用state和jsx模板生成虚拟DOM,然后用虚拟DOM生成真实的 DOM,当我们state发生变化时,render函数执行,生成新的 虚拟DOM,然后比较新旧虚拟DOM的区别,找到区别,然后直接操作DOM,改变有区别的内容,这样比传统的操作DOM,极大的提升了性能。
再说虚拟DOM,虚拟DOM其实就是一个JS对象([‘div’,{class:’app’},’item’]),虚拟DOM核心之一是diff算法,diff算法的核心之一是同层对比,

因为react中渲染dom是通过render方式,也就是通过虚拟的dom与真实的存在的dom树比较之后发现哪不一样,再进行渲染,这样的渲染对于性能的提升很有帮助,所以键值在保证稳定性,唯一性的时候,在遍历寻找需要改变的地方时候就能很块的找见并对其进行操作,如果键值不是稳定的而是变化的就会使渲染更改dom的效率大大的打折。

我们如果不用索引为key , 程序能快速的对比出差异,反之也能对出差异,但是必须对比整个虚拟DOM,
这样,程序仍然能正常执行,只不过大大消耗了新旧虚拟DOM的对比的性能,并可能导致组件状态问题。

componentDidMount处理http请求,shouldComponentUpdate允许我们手动地判断是否要进行组件更新,根据组件的应用场景设置函数的合理返回值能够帮我们避免不必要的更新。
调用setState之后发生了什么
调用setState函数之后,react会将传入的参数对象与组件当前的状态合并,然后触发调和过程(Reconciliation)。以高效方式根据新的状态构建React元素树并且着手重新渲染整个UI界面。在React得到元素树之后,React会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染。(按需渲染,不是全部渲染)
React中keys的作用是什么
Keys是React用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。在React Diff算法中React会借助元素的Key值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染。

Q2 我现在有一个button,要用react在上面绑定点击事件,我要怎么做?

第一种是在构造函数中绑定this,第二种是在render()函数里面绑定this,第三种就是使用箭头函数,都能实现上述方法;
但是哪一种方法的性能最好,是我们要考虑的问题。应该大家都知道答案:第一种的性能最好。
因为第一种,构造函数每一次渲染的时候只会执行一遍;
而第二种方法,在每次render()的时候都会重新执行一遍函数;
第三种方法的话,每一次render()的时候,都会生成一个新的箭头函数,即使两个箭头函数的内容是一样的。
第三种方法我们可以举一个例子,因为react判断是否需要进行render是浅层比较,简单来说就是通过===来判断的,如果state或者prop的类型是字符串或者数字,只要值相同,那么浅层比较就会认为其相同;
但是如果前者的类型是复杂的对象的时候,我们知道对象是引用类型,浅层比较只会认为这两个prop是不是同一个引用,如果不是,哪怕这两个对象中的内容完全一样,也会被认为是两个不同的prop。

Q4 你说说event loop吧

对象放在heap(堆)里,常见的基础类型和函数放在stack(栈)里,函数执行的时候在栈里执行。栈里函数执行的时候可能会调一些Dom操作,ajax操作和setTimeout定时器,这时候要等stack(栈)里面的所有程序先走 (注意:栈里的代码是先进后出), 走完后再走WebAPIs,WebAPIs执行后的结果放在callback queue(回调的队列里,注意:队列里的代码先放进去的先执行),也就是当栈里面的程序走完之后,再从任务队列中读取事件,将队列中的事件放到执行栈中依次执行,这个过程是循环不断的。

1.所有同步任务都在主线程上执行,形成一个执行栈
2.主线程之外,还存在一个任务队列。只要异步任务有了运行结果,就在任务队列之中放置一个事件。
3.一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,将队列中的事件放到执行栈中依次执行
4.主线程从任务队列中读取事件,这个过程是循环不断的

Q5 说说事件流吧

1、一个完整的JS事件流是从window开始,最后回到window的一个过程
2、事件流被分为三个阶段(1~5)捕获过程、(5~6)目标过程、(6~10)冒泡过程

Q6 我现在有一个进度条,进度条中间有一串文字,当我的进度条覆盖了文字之后,文字要去进度条反色,怎么实现?

css反色属性 mix-blend-mode
CSS 文字反色

Q1 你为什么要离开上一家公司?

Q2 你觉得理想的前端地位是什么?

Q3 那你意识到问题所在,你又尝试过解决问题吗

Q1 说一下你上一家公司的一个整体开发流程吧

react 的虚拟dom是怎么实现的

在Web开发中,需要将数据的变化实时反映到UI上,这时就需要对DOM进行操作,也就是既状态改变了就要操作相应的DOM元素。
解决这个问题有一个非常直观的方法,可以大大降低视图更新的操作,那就是:
一旦状态发生了变化,就用模版引擎重新渲染整个视图,然后用新的视图更换掉旧的视图。
我们一定会想到:这样的做法会导致很多的问题。最大的问题就是这样做效率太低。
因为即使一个小小的状态变更都要重新构造整棵DOM,性价比太低;而且这样做的话,input和textarea的会失去原有的焦点。
最后的结论会是:对于局部的小视图的更新,没有问题;但是对于大型视图,如全局应用状态变更的时候,需要更新页面较多局部视图的时候,这样的做法不可取。
但是我们会发现,其实React就是这么做的,只是增加了Virtual DOM(虚拟DOM)来避免了整棵DOM 树变更。

DOM树的结构是非常庞大的,因此操作它们的时候要小心,轻微的变更就可能就会导致整个页面重排,这可是杀死性能的罪魁祸首。
相对于DOM 对象,原生的JavaScript 对象处理起来更快,而且更简单。并且我们可以很容易地用JavaScript 来构造DOM。
既然这样,是不是可以将上面的“状态变更->重新渲染整个视图”的方式稍微修改一下:
用JavaScript 对象表示DOM 信息和结构,当状态变更的时候,重新渲染这个JavaScript 的对象结构。
当然这样做其实没什么用,因为真正的页面其实没有改变。
但是可以用新渲染的对象树去和旧的树进行对比,记录这两棵树差异。记录下来的不同就是我们需要对页面真正的DOM 操作,
然后把它们应用在真正的DOM 树上,页面就变更了。
这样就可以做到:视图的结构确实是整个全新渲染了,但是最后操作DOM的时候确实只变更有不同的地方。这就是所谓的Diff算法。
为什么这样做就快了呢?因为:javaScript很快,DOM很慢。所以要尽量使用快的,减少使用慢的。

Diff算法包括以下三个步骤:
1、用JavaScript 对象结构表示DOM 树的结构;然后用这个树构建一个真正的DOM 树,插到文档当中。
2、当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异。
3、把2所记录的差异应用到步骤1所构建的真正的DOM 树上,视图就更新了。
这种做法在React中叫做Virtual DOM(虚拟DOM)。由虚拟DOM来确保只对界面上真正变化的部分进行实际的DOM操作。

虚拟DOM本质上就是在JS 和DOM 之间做了一个缓存。可以类比CPU 和硬盘,既然硬盘这么慢,我们就在它们之间加个缓存:既然DOM 这么慢,我们就在它们JS 和DOM 之间加个缓存。CPU(JS)只操作内存(Virtual DOM),最后的时候再把变更写入硬盘(DOM)。

最后要特别说明一下:
Virtual DOM并没有完全实现DOM,Virtual DOM最主要的还是保留了Element之间的层次关系和一些基本属性。
因为DOM实在是太复杂,一个空的Element都复杂得能让你崩溃,并且几乎所有内容我们根本不需要关心。
所以Virtual DOM里每一个Element实际上只有几个属性,并且没有那么多乱七八糟的引用。

Q3 react 的渲染过程中,兄弟节点之间是怎么处理的?也就是key值不一样的时候。

Q4 我现在有一个数组[1,2,3,4],请实现算法,得到这个数组的全排列的数组,如[2,1,3,4],[2,1,4,3]。。。。你这个算法的时间复杂度是多少

数组全排列:

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
// ES6
class Permutation {
constructor(arr) {
this.arr = Array.from(arr); // 传入的数组
this.result = [];// 存储全排列结果
this.len = 0;
this.run(0); // 首次创建对象时初始化方法
}

run(index) {
// 单遍历到数组末端时,将结果储存在result数组中,全排列次数加1
if (index == this.arr.length - 1) {
this.result.push(Array.from(this.arr));
this.len++;
return;
}
for(let i = index; i < this.arr.length; i++) {
[this.arr[index], this.arr[i]] = [this.arr[i], this.arr[index]]; // 与下标为i的元素交换位置
this.run(index + 1); // 剩下元素全排列
[this.arr[index], this.arr[i]] = [this.arr[i], this.arr[index]]; // 复原数组
}
}
}

let p = new Permutation(["A", "B", "C"]);
console.log(p.result);
console.log(p.len);

Q5 我现在有一个背包,容量为m,然后有n个货物,重量分别为w1,w2,w3…wn,每个货物的价值是v1,v2,v3…vn,w和v没有任何关系,请求背包能装下的最大价值。

四面

Q1 请说一下你的上一家公司的研发发布流程。

1.需求会议,完成最终功能需求确认,
2.并且选取合适的sprint master,sprint master完成排期预估,并且主导每天的晨会,
3.UI原型图和需求会上传到tfs,
4.在git的master上新建分支,分支名字前端是

Q2 你说一下webpack的一些plugin,怎么使用webpack对项目进行优化。

webpack-dev-server
CommonsChunkPlugin
ExtractTextWebpackPlugin 分离 CSS
UglifyJsPlugin
DefinePlugin

  代码热替换, HotModuleReplacementPlugin

  生成html文件,HtmlWebpackPlugin

  将css成生文件,而非内联,ExtractTextPlugin

  报错但不退出webpack进程,NoErrorsPlugin

  代码丑化,UglifyJsPlugin,开发过程中不建议打开

  多个 html共用一个js文件(chunk),可用CommonsChunkPlugin

  清理文件夹,Clean

  调用模块的别名ProvidePlugin,例如想在js中用[Math Processing Error]与jQuery对应起来

webpack DllPlugin、webpack DllReferencePlugin 预编译第三方库文件
使用 HappyPack 来加速代码构建
Q4 es6 class 的new实例和es5的new实例有什么区别

Q5 请手写实现一个promise

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
function myPromise(callback) {

let self = this;
self.status = 'pending';
self.value = undefined;
self.reason = undefined;
self.fullFiledArr = [];
self.rejectedArr = [];
function resolve(value) {
if (self.status == 'pending') {
self.value = value;
self.status = 'fufilled';
self.fullFiledArr.forEach(function (f) {
f.call(this, self.value)
});
}
}

function reject(reason) {
if (self.status == 'pending') {
self.reason = reason;
self.status = 'rejected';
self.rejectedArr.forEach(function (f) {
f(self.reason)
});
}
}
try {
callback(resolve, reject);
} catch (e) {
reject(e);
}
}

myPromise.prototype.then = function (onFufilled, onRejected) {
let self = this;
let myPromise2;
switch (self.status) {
case 'pending':
myPromise2 = new myPromise(function (resolve, reject) {
self.fullFiledArr.push(function () {
try {
let res = onFufilled(self.value);
if (res instanceof myPromise) {
res.then(resolve, reject);
}
// resolvePromise(myPromise,res);
} catch (e) {
reject(e);
}
})
self.rejectedArr.push(function () {
try {
let res = onRejected(self.reason);
if (res instanceof myPromise) {
res.then(resolve, reject);
}
} catch (e) {
reject(e);
}
})
});
break;
case 'fufilled':
myPromise2 = new myPromise(function (resolve, reject) {
try {
let res = onFufilled(self.value);
if (res instanceof myPromise) {
res.then(resolve, reject);
}
} catch (e) {
reject(e);
}
});
break;
case 'rejected':
myPromise2 = new myPromise(function (resolve, reject) {
try {
let res = onRejected(self.reason);
if (res instanceof myPromise) {
res.then(resolve, reject);
}
} catch (e) {
reject(e);
}
})
}
return myPromise2;
}

 

function resolvePromise(promise, x, resolve, reject) {
if (promise === x) {
throw new TypeError("type error")
}
let isUsed;
if (x !== null && (typeof x === "object" || typeof x === "function")) {
try {
let then = x.then;
if (typeof then === "function") {
//是一个promise的情况
then.call(x, function (y) {
if (isUsed) return;
isUsed = true;
resolvePromise(promise, y, resolve, reject);
}, function (e) {
if (isUsed) return;
isUsed = true;
reject(e);
})
} else {
//仅仅是一个函数或者是对象
resolve(x)
}
} catch (e) {
if (isUsed) return;
isUsed = true;
reject(e);
}
} else {
//返回的基本类型,直接resolve
resolve(x)
}
}

const async1 = () => {
var p = new myPromise((resolve, reject) => {
console.log('1');
setTimeout(() => {
resolve('链式调用1');
}, 1000)
})
return p;
}

const async2 = () => {
var p = new myPromise((resolve, reject) => {
console.log('2');
setTimeout(() => {
resolve('链式调用2');
}, 1000)
})
return p;
}

 

async1().then((data) => {
console.log(data);
return async2();
}).then((data) => {
console.log(data);
})

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
function Promise(fn){
var state = 'pending';
var doneList = [];
var failList= [];
this.then = function(done ,fail){
switch(state){
case "pending":
doneList.push(done);
failList.push(fail);
return this;
break;
case 'fulfilled':
done();
return this;
break;
case 'rejected':
fail();
return this;
break;
}
}
function resolve(newValue){
state = "fulfilled";
setTimeout(function(){
var value = newValue;
for (var i = 0;i<doneList.length;i++){
var temp = doneList[i](value);
if(temp instanceof Promise){
var newP = temp;
for(i++;i<doneList.length;i++){
newP.then(doneList[i],failList[i]);
}
}else{
value = temp;
}
}
},0);
}
function reject(newValue){
state = "rejected";
setTimeout(function(){
var value = newValue;
var tempRe = failList[0](value);
//如果reject里面传入了一个promise,那么执行完此次的fail之后,将剩余的done和fail传入新的promise中
if(tempRe instanceof Promise){
var newP = tempRe;
for(i=1;i<doneList.length;i++){
newP.then(doneList[i],failList[i]);
}
}else{
//如果不是promise,执行完当前的fail之后,继续执行doneList
value = tempRe;
doneList.shift();
failList.shift();
resolve(value);
}
},0);
}
fn(resolve,reject);
}
var p = function (){
return new Promise(function(resolve,reject){
setTimeout(function(){
reject('p 的结果');
}, 100);
});
}
var p2 = function (input){
return new Promise(function(resolve){
setTimeout(function(){
console.log('p2拿到前面传入的值:' + input)
resolve('p2的结果');
}, 100);
});
}
p()
.then(function(res){console.log('p的结果:' + res); return 'p then方法第一次返回'},function(value){console.log(value);return 'p then方法第一次错误的返回'})
.then(function(res){console.log('p第一次then方法的返回:'+res); return 'p then方法第二次返回'})
.then(p2)
.then(function(res){console.log('p2的结果:' + res)});

五面
Q1 你说一下你的技术有什么特点

Q2 说一下你觉得你最得意的一个项目?你这个项目有什么缺陷,弊端吗?

Q3 现在有那么一个团队,假如让你来做技术架构,你会怎么做?

Q4 说一下你上一家公司的主要业务流程,你参与到其中了吗?

Q10 说一下浏览器的缓存机制

http://note.youdao.com/noteshare?id=74c654bea765bb13b618c30ee90025f4

转载请保持原始链接

原始链接: https://ru23.com/note/acc871bc.html