微前端描述下

概括来讲,微前端就是项目级别的模块化。即将庞大的项目拆分成N个独立的完全具备独立的开发、运行能力小工程。整个系统就将由这些小工程协同合作,实现所有页面的展示与交互。通常用于toB的前端工程。

  • 怎么协同
  • 注意事项

微前端首先解决的,是如何拆分巨大且迭代时间线冗长的应用,从而解决巨大体量应用随着技术更迭、产品升级、人员流动带来的工程上的问题。解构之后还需要再重组,重组的过程中我们就会碰到各种隔离性、依赖去重、通信、应用编排等问题。
隔离性问题:
首先是样式隔离问题,我们将工程拆分为多个项目,再统一集成到工程内部进行部署,如果没有考虑样式隔离问题,出现className重复,会造成一系列样式问题。
目前解决微前端样式隔离的方案有几种:

  • iframe(微前端最简单、暴力的解决方案),每个iframe即为一个沙盒,使项目间解耦
  • CSS模块化即使用webpack配置时使用className命名规则在规则前后添加hash使className值唯一
  • style-component即在webpack打包时将css样式打入dom内联样式中,使用行内样式

react 生命周期

李明轩面试整理 - 图1

react通信

父组件向子组件通信

React是单向数据流,父组件通过props向子组件传递需要的数据

子组件向父组件通信

父组件创建一个回调函数,子组件通过props访问函数并更新父组件

跨级组件通信

层层组件传递props

使用context

context是一个全局变量,像是一个大容器,在任何地方都可以访问到,我们可以把要通信的信息放在context上,然后在其他组件中可以随意取到。但是React官方不建议使用大量context,尽管他可以减少逐层传递,但是当组件结构复杂的时候,我们并不知道context是从哪里传过来的

没有嵌套关系的组件通信

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

pureComponent

跨域通信有哪些

JSONP

JSONP的原理:通过<script>标签的异步加载来实现的。比如说,实际开发中,我们发现,head标签里,可以通过<script>标签的src,里面放url,加载很多在线的插件。这就是用到了JSONP。

WebSocket

http://www.ruanyifeng.com/blog/2017/05/websocket.html

CORS

Hash

url的#后面的内容就叫Hash。Hash的改变,页面不会刷新。这就是用 Hash 做跨域通信的基本原理。

postMessage

H5中新增的postMessage()方法,可以用来做跨域通信。
场景:窗口 A (http:A.com)向跨域的窗口 B (http:B.com)发送信息。步骤如下。
(1)在A窗口中操作如下:向B窗口发送数据:

  1. // 窗口A(http:A.com)向跨域的窗口B(http:B.com)发送信息
  2. Bwindow.postMessage('data', 'http://B.com'); //这里强调的是B窗口里的window对象

(2)在B窗口中操作如下:

  1. // 在窗口B中监听 message 事件
  2. Awindow.addEventListener('message', function (event) { //这里强调的是A窗口里的window对象
  3. console.log(event.origin); //获取 :url。这里指:http://A.com
  4. console.log(event.source); //获取:A window对象
  5. console.log(event.data); //获取传过来的数据
  6. }, false);

http缓存

浏览器缓存分为强缓存和协商缓存,浏览器加载一个页面的简单流程如下:
浏览器先根据这个资源的http头信息中的资源过期时间来判断是否命中强缓存。如果命中则直接加载缓存中的资源,并不会将请求发送到服务器。
如果未命中强缓存,则浏览器会将资源加载请求发送到服务器。服务器来判断浏览器本地缓存是否失效。若可以使用,则服务器并不会返回资源信息,浏览器继续从缓存加载资源。
如果未命中协商缓存,则服务器会将完整的资源返回给浏览器,浏览器加载新资源,并更新缓存。

强缓存:

强缓存是利用http的返回头中的Expires或者Cache-Control两个字段来控制的,用来表示资源的缓存时间。
Expires
缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点。也就是说,Expires=max-age + 请求时间,需要和Last-modified结合使用。但在上面我们提到过,cache-control的优先级更高。 Expires是Web服务器响应消息头字段,在响应http请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求。
李明轩面试整理 - 图2
该字段会返回一个时间,比如Expires:Thu,31 Dec 2037 23:59:59 GMT。这个时间代表着这个资源的失效时间,也就是说在2037年12月31日23点59分59秒之前都是有效的,即命中缓存。这种方式有一个明显的缺点,由于失效时间是一个绝对时间,所以当客户端本地时间被修改以后,服务器与客户端时间偏差变大以后,就会导致缓存混乱。于是发展出了Cache-Control。
Cache-Control
Cache-Control是一个相对时间,例如Cache-Control:3600,代表着资源的有效期是3600秒。由于是相对时间,并且都是与客户端时间比较,所以服务器与客户端时间偏差也不会导致问题。
Cache-Control与Expires可以在服务端配置同时启用或者启用任意一个,同时启用的时候Cache-Control优先级高。

协商缓存

若未命中强缓存,则浏览器会将请求发送至服务器。服务器根据http头信息中的Last-Modify/If-Modify-Since或Etag/If-None-Match来判断是否命中协商缓存。如果命中,则http返回码为304,浏览器从缓存中加载资源。

Last-Modify/If-Modify-Since
浏览器第一次请求一个资源的时候,服务器返回的header中会加上Last-Modify,Last-modify是一个时间标识该资源的最后修改时间,例如Last-Modify: Thu,31 Dec 2037 23:59:59 GMT。
李明轩面试整理 - 图3
当浏览器再次请求该资源时,发送的请求头中会包含If-Modify-Since,该值为缓存之前返回的Last-Modify。服务器收到If-Modify-Since后,根据资源的最后修改时间判断是否命中缓存。
李明轩面试整理 - 图4
如果命中缓存,则返回http304,并且不会返回资源内容,并且不会返回Last-Modify。由于对比的服务端时间,所以客户端与服务端时间差距不会导致问题。但是有时候通过最后修改时间来判断资源是否修改还是不太准确(资源变化了最后修改时间也可以一致)。于是出现了ETag/If-None-Match。

ETag/If-None-Match
与Last-Modify/If-Modify-Since不同的是,Etag/If-None-Match返回的是一个校验码(ETag: entity tag)。ETag可以保证每一个资源是唯一的,资源变化都会导致ETag变化*。ETag值的变更则说明资源状态已经被修改。服务器根据浏览器上发送的If-None-Match值来判断是否命中缓存。
李明轩面试整理 - 图5

总结:
浏览器第一次请求:
李明轩面试整理 - 图6

浏览器再次请求时:
李明轩面试整理 - 图7

ES5实现继承

一. 原型链继承

原型链继承的原理很简单,直接让子类的原型对象指向父类实例,当子类实例找不到对应的属性和方法时,就会往它的原型对象,也就是父类实例上找,从而实现对父类的属性和方法的继承
原型继承的缺点:
由于所有Child实例原型都指向同一个Parent实例, 因此对某个Child实例的父类引用类型变量修改会影响所有的Child实例
在创建子类实例时无法向父类构造传参, 即没有实现super()的功能

二. 构造函数继承

构造函数继承,即在子类的构造函数中执行父类的构造函数,并为其绑定子类的this,让父类的构造函数把成员属性和方法都挂到子类的this上去,这样既能避免实例之间共享一个原型实例,又能向父类构造方法传参
构造函数继承的缺点:
继承不到父类原型上的属性和方法

三. 组合式继承

既然原型链继承和构造函数继承各有互补的优缺点, 那么我们为什么不组合起来使用呢, 所以就有了综合二者的组合式继承
Child.prototype = new Parent()
组合式继承的缺点:
每次创建子类实例都执行了两次构造函数(Parent.call()和new Parent()),虽然这并不影响对父类的继承,但子类创建实例时,原型中会存在两份相同的属性和方法,这并不优雅

四. 寄生式组合继承

为了解决构造函数被执行两次的问题, 我们将指向父类实例改为指向父类原型, 减去一次构造函数的执行
但这种方式存在一个问题,由于子类原型和父类原型指向同一个对象,我们对子类原型的操作会影响到父类原型,例如给Child.prototype增加一个getName()方法,那么会导致Parent.prototype也增加或被覆盖一个getName()方法,为了解决这个问题,我们给Parent.prototype做一个浅拷贝

  1. function Parent(name) {
  2. this.name = [name]
  3. }
  4. Parent.prototype.getName = function() {
  5. return this.name
  6. }
  7. function Child() {
  8. // 构造函数继承
  9. Parent.call(this, 'zhangsan')
  10. }
  11. //原型链继承
  12. // Child.prototype = new Parent()
  13. Child.prototype = Object.create(Parent.prototype) //将`指向父类实例`改为`指向父类原型`
  14. Child.prototype.constructor = Child
  15. //测试
  16. const child = new Child()
  17. const parent = new Parent()
  18. child.getName() // ['zhangsan']
  19. parent.getName() // 报错, 找不到getName()

react懒加载

当我们一次性大批量的加载资源的时候,会占用大量的内存,尤其是在一些低内存的设备上会造成卡顿的现象,所以我们就需要在必要的时候再进行资源的加载。
懒加载就是在真正需要资源才加载资源,这样就可以节省内存,尽可能的减少卡顿现象的出现。
李明轩面试整理 - 图8
代码分割
(1)为什么要进行代码分割?
现在前端项目基本都采用打包技术,比如 Webpack,JS逻辑代码打包后会产生一个 bundle.js 文件,而随着我们引用的第三方库越来越多或业务逻辑代码越来越复杂,相应打包好的 bundle.js 文件体积就会越来越大,因为需要先请求加载资源之后,才会渲染页面,这就会严重影响到页面的首屏加载。
而为了解决这样的问题,避免大体积的代码包,我们则可以通过技术手段对代码包进行分割,能够创建多个包并在运行时动态地加载。现在像 Webpack、 Browserify等打包器都支持代码分割技术。
(2)什么时候应该考虑进行代码分割?
这里举一个平时开发中可能会遇到的场景,比如某个体积相对比较大的第三方库或插件(比如JS版的PDF预览库)只在单页应用(SPA)的某一个不是首页的页面使用了,这种情况就可以考虑代码分割,增加首屏的加载速度。
React的懒加载
示例代码:

  1. import React, { Suspense } from 'react';
  2. const OtherComponent = React.lazy(() => import('./OtherComponent'));
  3. function MyComponent() {
  4. return (
  5. <div>
  6. <Suspense fallback={<div>Loading...</div>}>
  7. <OtherComponent />
  8. </Suspense>
  9. </div>
  10. );
  11. }

如上代码中,通过 import()React.lazySuspense 共同一起实现了 React 的懒加载,也就是我们常说了运行时动态加载,即 OtherComponent 组件文件被拆分打包为一个新的包(bundle)文件,并且只会在 OtherComponent 组件渲染时,才会被下载到本地。

React利用 React.lazyimport()实现了渲染时的动态加载 ,并利用Suspense来处理异步加载资源时页面应该如何显示的问题。

commonjs 和 es module区别

CommonJS

  1. 对于基本数据类型,属于复制。即会被模块缓存。同时,在另一个模块可以对该模块输出的变量重新赋值。
  2. 对于复杂数据类型,属于浅拷贝。由于两个模块引用的对象指向同一个内存空间,因此对该模块的值做修改时会影响另一个模块。
  3. 当使用require命令加载某个模块时,就会运行整个模块的代码。
  4. 当使用require命令加载同一个模块时,不会再执行该模块,而是取到缓存之中的值。也就是说,CommonJS模块无论加载多少次,都只会在第一次加载时运行一次,以后再加载,就返回第一次运行的结果,除非手动清除系统缓存。
  5. 循环加载时,属于加载时执行。即脚本代码在require的时候,就会全部执行。一旦出现某个模块被”循环加载”,就只输出已经执行的部分,还未执行的部分不会输出。

ES6模块

  1. ES6模块中的值属于【动态只读引用】。
  2. 对于只读来说,即不允许修改引入变量的值,import的变量是只读的,不论是基本数据类型还是复杂数据类型。当模块遇到import命令时,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。
  3. 对于动态来说,原始值发生变化,import加载的值也会发生变化。不论是基本数据类型还是复杂数据类型。
  4. 循环加载时,ES6模块是动态引用。只要两个模块之间存在某个引用,代码就能够执行。

    JS事件循环机制

    由于JS是单线程的,JS引擎会将同步事件放入主线程,将异步事件放入异步任务队列中,JS执行栈轮询主线程是否为空,当主线程为空,会将异步任务队列中的事件放入主线程执行。
    当当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件,然后再去宏任务队列中取出一个事件。同一次事件循环中,微任务永远在宏任务之前执行
  • 宏任务按顺序执行,且浏览器在每个宏任务之间渲染页面
  • 所有微任务也按顺序执行,且在以下场景会立即执行所有微任务
    • 每个回调之后且js执行栈中为空。
    • 每个宏任务结束后。

event Loop机制

宏任务微任务、哪些

由JS引擎独立完成的就是微任务,反之,不是由JS引擎独立完成的就是宏任务
宏任务:setTimeout
微任务:Promise

flex:1代表什么

flex:1 1 auto

  • 第一个参数表示: flex-grow 定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大
  • 第二个参数表示: flex-shrink 定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小
  • 第三个参数表示: flex-basis给上面两个属性分配多余空间之前, 计算项目是否有多余空间, 默认值为 auto, 即项目本身的大小

    函数组件和类组件:

    书写形式不同,类组件需要声明class
    函数组件渲染return的结果,类组件渲染render的结果
    函数组件没有this指向,类组件有this指向
    函数组件没有state,类组件有state
    类组件需要extends,函数组件不需要继承(轻量)
    函数组建没有生命周期钩子

CSS居中的方法

flex:
justify-content:center;
align-items:center;

inlineBlock:
给外部盒子设置属性
inline-height:(外部盒子高度);
text-align:center;
给需要居中的盒子设置属性
display:inline-block;
vertical-align:middle;

绝对定位:
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;

setState是同步还是异步,什么情况下是异步的

有时表现出异步,有时表现出同步
1.setState只在合成事件和钩子函数中是“异步”的,在原生事件和setTimeout 中都是同步的。
2.setState 的“异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形成了所谓的“异步”,当然可以通过第二个参数 setState(partialState, callback) 中的callback拿到更新后的结果。
3.setState 的批量更新优化也是建立在“异步”(合成事件、钩子函数)之上的,在原生事件和setTimeout 中不会批量更新,在“异步”中如果对同一个值进行多次setState,setState的批量更新策略会对其进行覆盖,取最后一次的执行,如果是同时setState多个不同的值,在更新时会对其进行合并批量更新。
在React中,如果是由React引发的事件处理(比如通过onClick引发的事件处理),调用setState不会同步更新this.state,除此之外的setState调用会同步执行this.state。所谓“除此之外”,指的是绕过React通过addEventListener直接添加的事件处理函数,还有通过setTimeout/setInterval产生的异步调用。
原因:在React的setState函数实现中,会根据一个变量isBatchingUpdates判断是直接更新this.state还是放到队列中回头再说,而isBatchingUpdates默认是false,也就表示setState会同步更新this.state,但是,有一个函数batchedUpdates,这个函数会把isBatchingUpdates修改为true,而当React在调用事件处理函数之前就会调用这个batchedUpdates,造成的后果,就是由React控制的事件处理过程setState不会同步更新this.state。

盒模型

标准盒模型、怪异盒模型、flex弹性盒
标准盒模型:
一个块的总宽度 = width + margin(左右) + padding(左右) + border(左右)
一个块的总高度 = height + margin(上下) + padding(上下) + border(上下)
怪异盒模型:
一个块的总宽度 = width + margin(左右)
一个块的总高度 = height + margin(上下)

react性能优化

shouldComponentUpdate避免重复渲染
我们可以通过根据自己的业务特性,重载shouldComponentUpdate,只在确认真实DOM需要改变时,再返回true。一般的做法是比较组件的props和state是否真的发生变化,如果发生变化则返回true,否则返回false。
class CounterButton extends React.Component {
constructor(props) {
super(props);
this.state = {count: 1};
}
shouldComponentUpdate(nextProps, nextState) {
if (this.props.color !== nextProps.color) {
return true;
}
if (this.state.count !== nextState.count) {
return true;
}
return false;
}
render() {
return (
color={this.props.color}
onClick={() => this.setState(state => ({count: state.count + 1}))}>
Count: {this.state.count}

);
}
}
React.lazy
React利用 React.lazyimport()实现了渲染时的动态加载 ,并利用Suspense来处理异步加载资源时页面应该如何显示的问题。
组件尽可能的进行拆分、解耦
组件尽可能的细分,比如一个input+list组件,可以将list分成一个PureComponent,只在list数据变化时更新。否则在input值变化页面重新渲染的时候,list也需要进行不必要的DOM diff。
使用函数式组件及Hooks
函数式组件相对于需要继承React.Component的类组件更加轻量 活用useEffect,useMemo等API可以减少不必要的render

深拷贝和浅拷贝

深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的
基本数据类型的特点:直接存储在栈(stack)中的数据
引用数据类型的特点:存储的是该对象在栈中引用,真实的数据存放在堆内存里
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。李明轩面试整理 - 图9

拷贝与赋值的区别↓
李明轩面试整理 - 图10

浅拷贝的方法:

  1. // 浅copy 只copy一层
  2. const obj = {
  3. a:1,
  4. b:2
  5. c: {
  6. d:1,
  7. e: null
  8. },
  9. }
  10. function copy(targetObj){
  11. const obj = {};
  12. for(let i in targetObj){
  13. obj[i] = targetObj[i];
  14. }
  15. return obj;
  16. }
  17. //深copy - 递归 最准确有效的方案, 像一些lodash库中都是基于递归封装的
  18. function deepClone(targetObj){
  19. const obj = {};
  20. for(let i in targetObj){
  21. // 判断值类型,如果是复杂类型,递归
  22. if(xxxx){
  23. //deepClonexxxxxx
  24. }else{
  25. obj[i] = targetObj[i];
  26. }
  27. }
  28. return obj;
  29. }
  30. // 深copy - JSON.stringify 和 JSON.parse 缺点: 如果属性值为undefined,这个属性不会被copy上
  31. function deepClone2(targetObj){
  32. return JSON.parse(JSON.stringify(targetObj));
  33. }

1. Object.assign()
Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
2. Array.prototype.concat()

  1. let arr = [1, 3, {
  2. username: 'kobe'
  3. }];
  4. let arr2=arr.concat();
  5. arr2[2].username = 'wade';
  6. console.log(arr);

深拷贝的方法:
1.JSON.parse(JSON.stringify())
这个方法不能处理函数
2.递归
递归方法实现深度拷贝原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝。
3.使用函数库lodash
该函数库也有提供 _.cloneDeep 用来做 Deep Copy。

闭包、及应用场景

函数内部沿着作用域链访问外部变量的特性叫做闭包
应用场景:
采用函数引用方式的setTimeout调用
私有变量
闭包通常用来创建内部变量,使得这些变量不能被外部随意修改,同时又可以通过指定的函数接口来操作。

tree sharking原理

tree-shaking的消除原理是依赖于ES6的模块化特性。
ES6模块依赖关系是确定的,和运行时的状态无关,可以进行可靠的静态分析,这就是tree-shaking的基础。所谓静态分析就是不执行代码,从字面量上对代码进行分析,ES6之前的模块化,比如我们可以动态require一个模块,只有执行后才知道引用的什么模块,这个就不能通过静态分析去做优化。
webpack插件优化了tree sharking,原理是根据AST进行作用域的判别,进而判断代码是否有用。

loader 和 plugins区别

前提:webpack是一个模块打包工具,默认只能处理JS文件,不能处理其他文件。

loader:
在企业开发中,我们不光要对JS文件进行打包,也会对图片、CSS进行打包,为了使webpack对其他文件进行打包,在打包前,就需要将其他文件处理为webpack可以识别处理的模块。loader相当于变相的扩展了webpack ,但是它只专注于转化文件这一个领域。
注意:loader都是用NodeJS编写的

loader特点:
单一原则, 一个loader只做一件事情
多个loader会按照从右至左, 从下至上的顺序执行

plugin:
插件(plugin)是 webpack 的支柱功能。用于扩展webpack的功能。相比loader,plugin的功能更加的丰富,而不仅局限于资源的加载。一个插件就是一个类,可以在打包过程中的特定阶段执行。

从作用角度简单来讲
loader帮助我们加载文件资源,而plugins则是loader的延伸,并不限制于加载文件资源。丰富了loader的功能。

场景题

推荐在线编程环境 https://codepen.io/pen/
技术:无限制(React、jQuery、HTML等均可)
交付内容:动态表格截图_2,失败截图_1, 源码截图*2(JS、HTML)
时间:40分钟

需求内容:动态表格

  1. 动态行 首行为列名信息 从接口返回值信息中获取
  2. 动态列 第二行开始为数据内容 从返回信息中获取
  3. 接口地址固定
  4. 请求参数不同,获取的资源数据不同
  5. 同一请求参数,响应数据的行数不同
  6. code为0时表示请求成功,需要渲染出表格内容
  7. code非0时表示发生异常,需要在页面上渲染出具体的错误内容(接口返回值中包含错误原因)(加分项)

请求示例:

示例1:
POST:http://localhost:8080/getTableData?type=student
响应内容:
{
“code”: 0,
“msg”: “成功”,
“msgLog”: “Success”,
“timestamp”: “2020-12-09 20:28:30”,
“data”: {
“totalCountRow”: 2,
“totalCountCol”: 2,
“columnDetails”: [
{
“index”: 0,
“name”: “id”,
“label”: “序号”,
“length”: 10,
“scale”: 0,
“type”: 3,
“typeName”: “INT”
},
{
“index”: 1,
“name”: “name”,
“label”: “姓名”,
“length”: 10,
“scale”: 0,
“type”: 4,
“typeName”: “String”
}
],
“data”: [
[
1,
“学生1”
],
[
2,
“学生2”
]
]
}
}
示例2:
POST:http://localhost:8080/getTableData?type=student
响应内容:
{
“code”: 0,
“msg”: “成功”,
“msgLog”: “Success”,
“timestamp”: “2020-12-09 20:28:30”,
“data”: {
“totalCountRow”: 4,
“totalCountCol”: 2,
“columnDetails”: [
{
“index”: 0,
“name”: “id”,
“label”: “序号”,
“length”: 10,
“scale”: 0,
“type”: 3,
“typeName”: “INT”
},
{
“index”: 1,
“name”: “name”,
“label”: “姓名”,
“length”: 10,
“scale”: 0,
“type”: 4,
“typeName”: “String”
}
],
“data”: [
[
2,
“学生2”
],
[
3,
“学生3”
],
[
6,
“学生6”
],
[
10,
“学生10”
]
]
}
}

示例3:
POST:http://localhost:8080/getTableData?type=teacher
响应内容:
{
“code”: 0,
“msg”: “成功”,
“msgLog”: “Success”,
“timestamp”: “2020-12-09 20:28:30”,
“data”: {
“totalCountRow”: 4,
“totalCountCol”: 3,
“columnDetails”: [
{
“index”: 0,
“name”: “teacherName”,
“label”: “教师姓名”,
“length”: 10,
“scale”: 0,
“type”: 4,
“typeName”: “String”
},
{
“index”: 1,
“name”: “teacherPhone”,
“label”: “教师手机号”,
“length”: 10,
“scale”: 0,
“type”: 4,
“typeName”: “String”
},
{
“index”: 2,
“name”: “teacherAddr”,
“label”: “教师住址”,
“length”: 50,
“scale”: 0,
“type”: 4,
“typeName”: “String”
}
],
“data”: [
[
“教师1”,
“15130000000”,
“北京”
],
[
“教师21”,
“15130000000”,
“未知”
],
[
“教师11”,
“15130000000”,
“杭州”
],
[
“教师011”,
“15130000000”,
“上海”
]
]
}
}

示例4
POST:http://localhost:8080/getTableData?type=teacher
响应内容:
{
“code”: 12,
“msg”: “参数有误,请检查”,
“msgLog”: “”,
“timestamp”: “2020-12-09 20:28:30”,
“data”: {}
}

示例5
POST:http://localhost:8080/getTableData?type=teacher
响应内容:
{
“code”: 15,
“msg”: “缺少权限,请确认”,
“msgLog”: “”,
“timestamp”: “2020-12-09 20:28:30”,
“data”: {}
}
1DD041E5-E72B-4743-8676-65955A6A5C63.png

防抖和节流区别,大概实现

了解哪些新技术

说一下npm包管理机制

场景题

A插件依赖D插件版本是1.0.1,B插件依赖D插件版本是1.0.2,C插件依赖D插件1.1.0,那么npm i 之后,下载了几个版本的D插件

HTTP常见的状态码 ,401 403分别是什么, 常见的请求头响应头有哪些

说一下webpack配置,常用的loader、plugin

场景题目

  1. if(a == 1) {
  2. console.log(a);
  3. }
  4. // 控制台会报错么
  5. // 如果报错是什么类型的错误
  6. a is not defind

手写一个单例模式

  1. const fo = (function(){
  2. let count = 0;
  3. return function(){
  4. count ++;
  5. if(count === 1){
  6. console.log(0)
  7. }else {
  8. console.log(1)
  9. }
  10. }
  11. })()
  12. fo();
  13. fo();

手写一个发布订阅模式

手写一个redux的compose函数

手写一个组合继承

垃圾回收机制了解么,介绍一下

自定义hooks和函数有什么区别

事件循环输出顺序问题

实现函数异步请求成功后就返回,失败后重试max次

前端怎么埋点监控

hooks为什么不能写在if 语句里面

因为hooks的本质是一个单链表结构,如果在if语句中使用的话,会造成后续的hook丢失的风险。
所有条件语句中都不可使用hooks。

useCallback的实现原理

怎么画1px像素线,逻辑像素,物理像素的概念

自己写的mock服务是怎么实现的,为什么不在webpack里用相关插件

写一个Promise.all函数

一道setTimeout事件循环的题目

手写题实现电话号码隔位显示(3 4 4)

算法题[0, 2, 3, 0, 5, 0, 0]将0全部移动到后面去

说一下redux如何使用

redux源码介绍下

解释下https

介绍几个git常见的操作命令

react-redux中connect怎么连接组件的

写一个自定义hooks,useDiff

数组去重方法越多越好

写一个匹配邮箱的正则

实现函数

  1. function repeat(s, count) {}
  2. repeat('s', 3) // 输出 ‘sss’
  • 函数实现

    1. // 正则匹配标签名 输出div p span
    2. <div>
    3. <p>
    4. <span></span>
    5. </p>
    6. <span>
    7. </span>
    8. </div>
  • 实现一个深拷贝

  • 实现函数统计字符串里面出现次数最多的字符
  • hooks常用的api有哪些
  • useState,useEffect,useRef,useMemo在存储数据方面有什么区别
  • hooks组件怎么向外传递方法
  • 写一个三列等距布局,越多越好
  • 写一个公共组件需要注意哪些
  • 写一个表单生成组件

    • 生成一个完整的组件
    • 表单填入的值,可以返回出去
      1. const config = [
      2. {
      3. type: 'input',
      4. name: 'name',
      5. key: 'name'
      6. },
      7. {
      8. type: 'select',
      9. name: 'work',
      10. key: 'work',
      11. options: []
      12. }
      13. ]
      14. <FormC config={config} />
  • 你ts用的多么,说几个高级用法

  • 介绍下interface和type的区别
  • 详细说一下机器学习做的事情
  • 你觉得AI智能给前端带来的变化1:

    1. // a.js
    2. module.exports = {};
    3. exports = {
    4. name: 'json'
    5. };
    6. // b.js
    7. const a = require('./a.js'); // 输出什么
  • 2:

    1. // a.js
    2. module.exports = function a() {}
    3. // b.js
    4. // 在b中用es6 module语法怎么引入
  • webpack打包原理是怎么样的

  • webpack 插件写过没,介绍下原理
  • webpack5介绍下
  • 看你用过react介绍下fiber架构
  • esbuild知道么介绍下
  • vue现在出了一个打包工具vite,介绍下为什么会比其他的打包工具快

    介绍下https加密过程

    redux原理

    第三方登录,如果让你去设计,你会怎么考虑

    介绍下浏览器和node的事件循环

    做了一道原型链输出问题

    做了一道setTimeout输出问题

    做了一道this.setState输出问题(异步和合并)

    实现一下promise.race

    实现一下task().eat().sleep(2000).eat().sleep(2000)函数

    判断链表有环但是空间复杂度是O(1)

    redux用的熟么,介绍一下源码

    connect怎么处理组件的

    context会有什么性能问题么

    实现防抖节流

    实现sum(1)(2, 3)(4)柯里化

    实现一个非树状结构转树状结构函数

    一个查找最长子字符串算法

    乾坤框架如何做到隔离

    实现一个String.prototype._trim函数

    实现一个reduce

    实现一个多个请求,并行和串行的函数