前端面试题

前端面试题(持续更新中…)

文章目录

react篇

1.说说你对fiber架构的理解?解决了什么问题?

React Fiber 是 Facebook 花费两年余时间对 React 做出的一个重大改变与优化,是对 React 核心算法的一次重新实现 。增加了优先级,优先级高的任务可以中断优先级低的任务,再重新执行优先级低的任务,增加了异步任务,dom diff树变成了链表
解决了页面长时间不更新导致的页面响应度差,用户感觉到卡顿的问题

2.说说你对react的理解?有哪些特性?

React,用于构建用户界面的 JavaScript 库,只提供了 UI 层面的解决方案
遵循组件设计模式、声明式编程范式和函数式编程概念,以使前端应用程序更高效 使用虚拟 DOM 来有效地操作
DOM,遵循从高阶组件到低阶组件的单向数据流 帮助我们将界面成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,构成整体页面
react 类组件使用一个名为 render() 的方法或者函数组件return,接收输入的数据并返回需要展示 的内容

特性:JSX 语法
单向数据绑定
虚拟 DOM
声明式编程
Component

3.Redux的实现原理,写出其核心实现代码?

React的组件需要获取或者修改页面的数据,通过dispatch方法调用actions进入到Reducer函数中修改state的数据内容,state更新后,通知组件更新页面即可。
使用步骤:

创建一个store文件夹,新建一个index.js文件
文件中导入redux的createStore方法,用于创建公共数据区域
创建一个reducer纯函数,接受两个参数state,actions分别表示数据和操作state的方法,返回state数据给组件页面
把reducer作为createStore的参数抛出
在需要使用的页面导入store文件,通过store.getState获取数据,通过store.dispatch触发action修改state数据
store.subscrible 方法监听 store 的改变,避免数据不更新

const redux = require('redux');

const initialState = {
  counter: 0
}

// 创建reducer
const reducer = (state = initialState, action) => {
  switch (action.type) {
    case "INCREMENT":
      return {...state, counter: state.counter + 1};
    case "DECREMENT":
      return {...state, counter: state.counter - 1};
    case "ADD_NUMBER":
      return {...state, counter: state.counter + action.number}
    default: 
      return state;
  }
}

// 根据reducer创建store
const store = redux.createStore(reducer);

store.subscribe(() => {
  console.log(store.getState());
})

// 修改store中的state
store.dispatch({
  type: "INCREMENT"
})
// console.log(store.getState());

store.dispatch({
  type: "DECREMENT"
})
// console.log(store.getState());

store.dispatch({
  type: "ADD_NUMBER",
  number: 5
})
// console.log(store.getState());

4.useEffect的依赖为引用类型如何处理?

useEffect为引用类型的时候,可能会导致监听发不出,原因就是监听的统一个地址的时候,对象本身地址没变,所以监听的结果就是认为数据并没有改变从而不直径调用
1.如果数据是对象的话,可以监听对象的里面的值,值是基本类型,如果值改变了,那么可以监听执行
2.在去修改对象和数据的时候,使用深拷贝或者浅拷贝,这样地址发生改变可以监听执行
3.可以转成字符串,通过json.stringify(),监听字符串这样的,这样改变也会执行

5.说说React生命周期有哪些不同的阶段?每个阶段对应的方法是?

Mounting(挂载阶段):已插入真实 DOM
Updating(更新阶段):正在被重新渲染
Unmounting(卸载阶段):已移出真实 DOM
挂载阶段:

  • constructor() 在 React 组件挂载之前,会调用它的构造函数。
  • componentWillMount: 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。
  • componentDidMount(): 在组件挂载后(插入 DOM 树中)立即调用

更新运行阶段:

  • componentWillReceiveProps: 在接受父组件改变后的props需要重新渲染组件时用到的比较多,外部组件传递频繁的时候会导致效率比较低
  • shouldComponentUpdate():用于控制组件重新渲染的生命周期,state发生变化,组件会进入重新渲染的流程,在这里return false可以阻止组件的更新
  • render(): render() 方法是 class 组件中唯一必须实现的方法。
  • componentWillUpdate(): shouldComponentUpdate返回true以后,组件进入重新渲染完成之前进入这个函数。
  • componentDidUpdate(): 每次state改变并重新渲染页面后都会进入这个生命周期
    卸载或销毁阶段
    componentWillUnmount (): 在此处完成组件的卸载和数据的销毁。

6.说说你对useEffect的理解,可以模拟哪些生命周期?

使用钩子函数useEffect可以实现组件的副作用
useEffect(希望执行的动作,[组件状态的列表])
第二个参数是用来处理useEffect的调用的时机,是一个数组,数组内是组件状态的列表
1、useEffect模拟componentDidMount
2、useEffect模拟componentDidUpdate
3、useEffect模拟componentWillUnmount

7.在使用redux过程中,如何防止定义的action-type的常量重复?

Es6引入了一种新的原始数据类型Symbol,表示独一无二的值
Symbol是一个原始类型的值,不是对象symbol函数可以接受一个字符串作为参数,表示对symbol实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分

8.调和阶段setState干了什么?

代码中调用 setState函数之后,React会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。
经过调和过程,React会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个 UI 界面;
在 React 得到元素树之后,React会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染;
在差异计算算法中,React能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。

9.shouldComponentUpdate有什么作用?

shouldComponentUpdate函数是react中的一个生命周期函数,用于在更新组件前检查数据是否变化,从而决定是否重新渲染组件
作用:
(1)提升组件性能:当组件在接收到新的props和state时,可以通过 shouldComponentUpdate判断是否需要重新渲染,减少不必要的渲染
(2)允许控制:react通过此函数允许开发者控制组件是否重新渲染,能够提高开发效率,实施预期的操作

10.react新出来两个钩子函数是什么?和删掉的will系列有什么区别?

React类组件中新出来的两个生命周期函数是getDerivedStateFromProps和getSnapshotBeforeUpdate。

区别在于:

在组件更新阶段,getDerivedStateFromProps是在render方法之前调用,用于更新组件的state状态。而getSnapshotBeforeUpdate是在组件的DOM更新之前调用,可以获取到组件更新前的状态。
getDerivedStateFromProps钩子函数是一个静态方法,不可以访问组件实例的this对象,只能通过传入的参数获取当前组件的状态和属性。而getSnapshotBeforeUpdate方法可以访问组件实例的this对象。
will系列的生命周期函数已经被标记为过时,不建议再使用,而getDerivedStateFromProps和getSnapshotBeforeUpdate是React16.3以后新增的生命周期函数,可以在类组件中使用。

总的来说,getDerivedStateFromProps和getSnapshotBeforeUpdate的使用场景比will系列更加明确和安全,但需要注意使用时的细节。

11.React的props.children使用map函数来遍历会收到异常显示,为什么?应该 如何遍历?

在react 中 props.children 不一定是数组 有三种可能 :
1.当前组件没有子节点数据类型就是undefined,
2.有一个子节点数据类型就是object,
3 .有多个子节点的时候才会是array ,只有在多个节点的时候才可以直接调用map方法,react提供了一个react.children.map()方法,可以安全遍历子节点对象。

12.说说你对react hook的理解?

React hooks:就是用函数的形式代替换来的继承类的形式,并且使用预函数的形式管理state,有hooks可以不再使用类的形式定义组件了
Hooks优点:告别难以理解的class(this和生命周期的痛点)
解决业务逻辑难以拆分的问题
使状态逻辑复用变得简单可行
函数组件从设计思想上来看更加契合react的理念
React hooks常用的四个钩子UseState(),useContext(),useEffect(),useReducer()
1,内部状态useState: useState是官方提供的一个保存组件内部状态的函数
2,useEffect函数: 函数组件没有生命周期钩子函数,使用useEffect来模拟常用的一些钩子函数
3,useMemo函数: 这个hook函数作用就是实现计算属性。
4,React.memo:这个函数是16.6提出的一个包装函数,也是一种高阶组件。实现的代码优化效果和pureComponent一样,当你子组件无需更新的时候,不会频繁调用
5,useRef函数:获取节点数据
6,useHistory函数(第三方): 可以获取到一个history对象,通过这个对象可以进行路由调准 ( 在react-router-dom这个路由包里提供了常用的一些hook )

13.说说React中setState和replaceState的区别?

setState 是修改其中的部分状态,相当于 Object. assign,只是覆盖,不会减少原来的状态;
replaceState 是完全替换原来的状态,相当于赋值,将原来的 state 替换为另一个对象,如果新状态属性减少,那么 state 中就没有这个状态了。

14.说说react中onClick绑定后的工作原理?

React 中的 onClick 事件绑定是一种用于处理用户交互的常见方法。它的工作原理如下:
首先,在 React 组件中,开发人员定义一个 onClick 事件处理函数,该函数将在用户单击元素时被调用。
然后,使用 JSX 语法将该函数与 DOM 元素绑定。
例如,可以在一个按钮上添加 onClick 属性并将其设置为处理函数的名称。当用户单击该按钮时,浏览器会触发一个 click 事件。
React 将该事件传递给 onClick 处理函数,并调用该函数。
处理函数可以执行一些操作,例如更新组件的状态或调用其他函数。
总的来说,onClick 事件绑定的工作原理是通过将事件处理函数绑定到 DOM 元素上来实现的。当用户与该元素交互时,浏览器会触发事件并将其传递给 React,最终调用处理函数并执行相关操作。

15.说说react diff的原理是什么?

react中diff算法主要遵循三个层级的策略:
tree层级:dom节点跨层级的操作不做优化,只会对相同层级的节点进行比较

component层级:如果是同一个类的组件,则会继续往下diff运算,如果不是一个类的组件,那么直接删除这个组件下的所有子节点,创建新的

element层级:对于比较同一层级的节点们,每个节点在对应的层级用唯一的key作为标识提供了 3 种节点操作,分别为 INSERT_MARKUP(插入)、MOVE_EXISTING (移动)和 REMOVE_NODE (删除)

16.说说React中setState执行机制?

setState是用于更新组件状态使用的一个方法,在组件中定义一个状态,要想使得更新状态引起页面得重新渲染,就必须要使用setState来更新状态。
setState默认是同步的,但是react中会有一个变量来控制setState时直接更新(同步)还是放入到队列后续执行(异步),这个变量是根据一个方法Batced来判断setState方法是在react中执行得还是绕过react执行的。

17.React render方法的原理,在什么时候会触发?

一、原理
在类组件和函数组件中,render函数的形式是不同的。
在类组件中render函数指的就是render方法;而在函数组件中,指的就是整个函数组件。
二、触发时机
render的执行时机主要分成了两部分:
类组件调用 setState 修改状态
函数组件通过useState hook修改状态

18.React性能优化的手段有哪些?

避免使用内联函数
使用immutable
懒加载组件
事件绑定方法
服务器渲染

19.说说react的事件机制?

React的事件机制是基于合成事件(SyntheticEvent)的。合成事件是React封装的一种事件对象,它是对浏览器原生事件的封装,提供了跨浏览器的一致性,同时也有更好的性能和可靠性。

20.说说React jsx转换成真实DOM的过程?

使用React.createElement或JSX编写React组件 ReactDOM.render将生成好的虚拟DOM渲染到指定容器上,
react中的jsx语法会通过babel转化为 js代码,以React.createElement函数形式存在,createElement函数返回一个ReactElement函数,ReactElement函数返回一个的虚拟节点,虚拟节点中嵌套虚拟节点,就形成了虚拟DOM,最后通过ReactDOM.render方法转化为真实DOM

21.说说你对@reduxjs/toolkit的理解?和react-redux有什么区别?

React-redux是官方react UI绑定层,允许React组件从redux存储中读取数据,并将操作分派到存储以更新的状态。提供了connect,Provider等API,帮助我们连接react和redux,实现的逻辑会更加的严谨高效。
@reduxjs/tookit是对Redux的二次封装,开箱即用的一个高效的Redux开发工具,使得创建store,更新store

22.什么是强缓存和协商缓存?

强缓存:是根据返回头中的 Expires 或者 Cache-Control 两个字段来控制的,都是表示资源的缓存有效时间
协商缓存:是由服务器来确定缓存资源是否可用。 主要涉及到两对属性字段,都是成对出现的,即第一次请求的响应头带上某个字, Last-Modified 或者 Etag,则后续请求则会带上对应的请求字段 If-Modified-Since或者 If-None-Match,若响应头没有 Last-Modified 或者 Etag 字段,则请求头也不会有对应的字段。

23.知道react里面的createPortal么,说说其使用场景?

react.createPortal 来制作弹窗组件,它在Modal 组件位置进行 fixed 定位,可以任意的挂载到某个dom元素上,使用后的管理更方便,但是注意需要预留html的挂载元素

24.Provider和connect的底层原理实现,写出其核心代码?

connect方法接受两个参数:mapStateToProps和mapDispatchToProps。它们定义了 UI 组件的业务逻辑。前者负责输入逻辑,即将state映射到 UI 组件的参数(props),后者负责输出逻辑,即将用户对 UI 组件的操作映射成 Action

connect方法生成容器组件以后,需要让容器组件拿到state对象,才能生成 UI 组件的参数。
一种解决方法是将state对象作为参数,传入容器组件。但是,这样做比较麻烦,尤其是容器组件可能在很深的层级,一级级将state传下去就很麻烦。
React-Redux 提供Provider组件,可以让容器组件拿到state。

25.说说Connect组件的原理是什么?

Connect本质上就是一个高阶函数,他是真正和store进行数据交互的组件,首先传入mapStateToProps,mapDispatchToProps,然后返回一个生产component的函数,在将真正的component作为参数传入到wrapWithconnect,这样就生产出了一个经过包裹的connect组件

26.说说react 中jsx语法糖的本质?

React 使用 JSX 来替代常规的JavaScript。
JSX 是按照 XML 语法规范 的 JavaScript 语法扩展。
JSX 语法的本质:并不是直接把 JSX 渲染到页面上,而是内部先转换成了 createElement 形式,再渲染的。

27.说说你对redux中间件的理解?常用的中间件有哪些?

中间件是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务,衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享,功能共享的目的

常用的中间件:

Redux-thunk:用于异步操作

Redux-logger:用于日志记录

上述的中间件都需要通过applyMiddlewares进行注册
实现原理:redux中间件被放入到一个数组中,嵌套执行,然后执行store.dispatch方法,这一执行返回getState和dispatch这两个方法,然后根据dispatch进行判断,最后根据判断来决定执行

28.react新出来两个钩子函数是什么?和删掉的will系列有什么区别?

新生命周期中新增了两个钩子,分别为getDerivedStateFromProps(从props中得到衍生的state)和getSnapshotBeforeUpdate。

区别
1、componentWillMount中可能需要做的事(一些数据初始化的操作就应该放在这个钩子中处理),constructor与componentDidMount也能做,甚至做的更好,此方法被废弃。
2、componentWillReceiveProps实际行为与命名并不相符,由于不稳定性已由getDerivedStateFromProps代替;
3、而componentWillUpdate同等理由被getSnapshotBeforeUpdate代替

29.redux本来是同步的,为什么它能执行异步代码?实现原理是什么?中间件的实现原理是什么?

因为它使用了redux-thunk或者redux-thunk这两个异步操作的中间件,
原理是在派发action时,要执行dispatch进行拦截,先不更新store中的数据,等返回数据???
将所有的中间件放入到一个数组中,进行嵌套,最后将执行store.dispatch方法,这一执行操作,会返回两个方法,一个是getState方法,另一个是dispatch方法,然后根据返回的这两个方法,进行if判断,再根据这个判断,执行相对于的操作

30.redux中同步action与异步action最大的区别是什么?

同步action:执行了dispatch函数之后,对应的reducer纯函数立即得到执行,reducer执行完了之后,state立即就改变了,此时用store.getState函数,取到的是最新的state值;

异步action:原则上redux并没有提供异步action的处理方案,异步的action需要依赖第三方的中间件解决(如redux-thunk),dispatch了一个异步action(本质上是dispatch的一个函数)之后,目标state并不会立即响应,而是要看异步函数内部的逻辑,来决定state什么时候响应.

31.redux-saga和redux-thunk的区别与使用场景?

redux-saga:
redux-saga在store的index文件中创建saga中间件连接到store,saga中间件可以监控派发action,如果有action.type值与监控的变量一致,则执行该函数的内容,在这个函数中也可以再派发一个新的action

redux-thunk:
redux-thunk相当于是基于store的升级,一般情况,我们传给store的action是一个对象,但是通过redux-thunk中间件,我们可以把部分的业务逻辑(异步请求)等放在action中进行处理。当store接收到函数类型的action,redux-thunk会执行该函数,并传入参数dispatch,当函数内部的逻辑执行完成后,会再次派发一个action继续向下执行。

区别:
redux-thunk和redux-saga中间件相当于对store的升级,thunk通过执行action中的函数实现业务逻辑,没有扩展API;saga通过定义saga函数进行监控,同时提供一些常用的API

应用场景: redux-thunk:
dispatch一个action之后,到达reducer之前,进行一些额外的操作,就需要用到middleware。你可以利用 Redux
middleware 来进行日志记录、创建崩溃报告、调用异步接口或者路由等等。
  换言之,中间件都是对store.dispatch()的增强。redux-thunk就是用来异步操作,比如接口请求等。

redux-saga:
redux-saga是一个用于管理redux应用异步操作的中间件,redux-saga通过创建sagas将所有异步操作逻辑收集在一个地方集中处理,可以用来代替redux-thunk中间件

32.说说React生命周期中有哪些坑?如何避免?

getDerivedStateFromProps 容易编写反模式代码,使受控组件和非受控组件区分模糊

  • componentWillMountReact 中已被标记弃用,不推荐使用,主要的原因是因为新的异步架构会导致它被多次调用,所以网络请求以及事件绑定应该放到 componentDidMount
  • componentWillReceiveProps 同样也被标记弃用,被 getDerivedStateFromProps 所取代,主要原因是性能问题。
  • shouldComponentUpdate 通过返回 true 或者 false 来确定是否需要触发新的渲染。主要用于性能优化。
  • componentWillUpdate 同样是由于新的异步渲染机制,而被标记废弃,不推荐使用,原先的逻辑可结合 getSnapshotBeforeUpdatecomponentDidUpdate 改造使用。
    如果在 componentWillUnmount 函数中忘记解除事件绑定,取消定时器等清理操作,容易引发 bug
    如果没有添加错误边界处理,当渲染发生异常时,用户将会看到一个无法操作的白屏,所以一定要添加。

33.React合成事件的原理?

React并不是将click事件绑在该div的真实DOM上,而是在document处监听所有支持的事件,当事件发生并冒泡至document处时,React将事件内容封装并交由真正的处理函数运行。
收集的事件放在dispatchQueue数组中,而冒泡和捕获的区别在于执行时机和顺序,那么我们只需要对数组按照不同顺序循环执行即可
首先会在fiber节点进入render阶段的complete阶段时,将事件监听绑定在root上。然后调用ensureListeningTo进行事件绑定,生成事件合成对象、收集事件、触发真正的事件。
React 合成事件(SyntheticEvent)是 React 模拟原生 DOM 事件所有能力的一个事件对象,即浏览器原生事件的跨浏览器包装器。它根据 W3C 规范 来定义合成事件,兼容所有浏览器,拥有与浏览器原生事件相同的接口。
将事件绑定在document - v16/容器元素 - v17统一管理,防止很多事件直接绑定在原生的dom元素上。造成一些不可控的情况
React 想实现一个全浏览器的框架, 为了实现这种目标就需要提供全浏览器一致性的事件系统,以此抹平不同浏览器的差异。

34.React render方法的原理,在什么时候会触发?

Render方法在类组件和函数组件中表示的是不一样的
Render方法在类组件中表示的是一个方法,而render方法在函数组件中表示的是整个函数组件
类组件是在调用setState中触发,函数组件是在调用useState hook时触发

35.React的路由的原理是什么,写出其实现的核心代码?

react路由是react应用程序中的一种导航系统,通过react router 来实现,它通过react的组件化机制来实现路由的配置,react路由的实现原理主要是通过浏览器的History API来实现URL的变化,并通过react组件来实现路由的映射和渲染

import { Route, Link } from 'react-router-dom';
import City from './Routers/City';
import Home from './Routers/Home';

export default function App() {
  return (
    <div>
      <Route path='/city' component={City}></Route>
      <Route path='/home' component={Home}></Route>
      <Link to='/home'> 跳转home</Link>
    </div>
  )
}

36.说说reduce方法的作用?自己手动封装一个reduce,写出其核心代码?

  1. reduce遍历数组的方法,类似map,forEatch,reduce的可以设置初始值

2.reduce其中 init有值时 pre为init,item为数组中的值,index为当前索引,arr是数组本身
init没值时,pre为数组中的值,item类推,index为item值的索引, 有return时,pre为 return 出的值

var a = [4,5,6]
undefined
a.reduce((pre,item,index,arr)=>{
    console.log(pre,item,index,arr)
    
})
// 打印结果
   4         5 1 [4,5,6]
   undefined 6 2 [4,5,6]
 
 
a.reduce((pre,item,index,arr)=>{
    console.log(pre,item,index,arr)
    
},9)
//打打印结果
    9         4 0 (3) [4, 5, 6]
    undefined 5 1 (3) [4, 5, 6]
    undefined 6 2 (3) [4, 5, 6]

reduce封装

// myReduce
Array.prototype.myReduce = function (fn,...args) {
     if(!arguments.length){
        throw new error;
     }
     let pre = args.length>0? args[0] : this[0];
     let startIndex = args.length>0?0:1;
     for(let i = startIndex;i<this.length; i++) {
         pre = fn(pre,this[i],i,this)
     }
     return pre
 }

37.ReactDOM.render是如何串联渲染链路的?

ReactDOM.render 调用栈大致可以拆分成如下三个阶段:
初始化阶段、render阶段、commit阶段

在初始化阶段,整个初始化的工作过程都是在为render 阶段做准备。在render阶段由 ReactDOM.render 发起的首屏渲染的场景下,它触发的就是 performSyncWorkOnRoot。它开启的就是 render 阶段;finishSyncRoot标志着render方法的结束,在这个过程中,穿插了大量了beginWork、completeWork调用(这两个方法串联起来就是一个模拟递归的过程),这个两个方法就是render的工作内容。commit阶段完成了对 ReactDOM.render 调用栈的分析。表面上剖析的是首次渲染的渲染链路,实际上将包括同步模式下的挂载、更新链路都串联了一遍

38.React中”栈调和”Stack Reconciler过程是怎样的?

在React中,“栈调和”(Stack Reconciler)是指React的虚拟DOM(Virtual DOM)和渲染引擎之间的协作过程,用于更新DOM树。它的过程如下:

React通过调用组件的render方法,生成一个虚拟DOM树。
React将新的虚拟DOM树和旧的虚拟DOM树进行比较,找出需要更新的部分。
React将需要更新的部分转换成DOM操作,然后将它们添加到一个更新队列中。
React通过调用requestIdleCallback等待浏览器空闲时间,然后开始执行更新队列中的DOM操作。
React将更新队列中的DOM操作按照顺序依次执行,更新DOM树。
在这个过程中,React使用了一种叫做“协调”(Reconciliation)的算法,用于比较新旧虚拟DOM树的差异。这个算法的核心思想是,将虚拟DOM树转换成一个树形结构,然后递归地比较每个节点的属性和子节点,找出需要更新的部分。这个算法的时间复杂度是O(n),其中n是虚拟DOM树的节点数。

总的来说,“栈调和”是React的核心机制之一,它保证了React的高效性和可靠性,使得React能够快速地更新DOM树,同时保持应用程序的正确性。

39.什么是发布订阅模式,写出其核心实现代码?

发布订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知。订阅者把自己想订阅的事件注册到调度中心,当发布者发布该事件到调度中心,也就是该事件触发时,由调度中心统一调度订阅者注册到调度中心的处理代码。

核心实现代码

// 声明EventEmitter事件发生器构造函数
function EventEmitter() {
    this._events = Object.create(null);
}
//on 订阅方法实现  因为在实例上调用,所以写在原型上
EventEmitter.prototype.on = function(type,callback){
    // 如果实例不存在则创建一个空对象,Object.create(null)没有链
    if(!this._events) {
        this._events = Object.create(null);
    }
    if(this._events[type]){ //如果失恋有对应的值,直接往数组push
        this._events[type].push(callback)
    }else { //第一次订阅,没有失恋,就声明{失恋:[cry]}
        this._events[type] = [callback];
    }
};
// emit方法实现
EventEmitter.prototype.emit = function(type){
    if(this._events[type]){ //{失恋:[cry,eat]} 如果失恋对应有值,依次执行里面的方法
        this._events[type].forEach(fn=>fn())
    }
};
module.exports = EventEmitter

40.react中getDerivedStateFromProps使用方式?

getDerivedStateFromProps 会在调用 render 方法之前调用,即在渲染 DOM 元素之前会调用,并且在初始挂载及后续更新时都会被调用。
state 的值在任何时候都取决于 props。
getDerivedStawteFromProps 的存在只有一个目的:让组件在 props 变化时更新 state。
该方法返回一个对象用于更新 state,如果返回 null 则不更新任何内容。

41.react如何做性能优化?

1.避免使用内联函数 ,每次render渲染时,都会创建一个新的函数实例,应该在组件内部创建一个函数,将事件绑定到函数,这样每次调用render时,就不会创建单独的函数实例

2.懒加载组件:在react中使用suspense lazy组件

3.事件绑定方式:从性能考虑,在render方法中使用bind和箭头函数,都会生成新的方法实例,在constructer中使用bind和箭头函数,性能提高

4.服务器渲染,可以使用户更快的看到显示成功的页面,服务器渲染可以起一个node服务,可以使用express,调用react的renderToString方法,将跟组件渲染成字符串,在输出到相应中

5.组件拆分,合理使用hooks
6.SSR服务器渲染
7.pureComponent组件继承
8.shouldComponentUpdate
9.react Fragments 避免额外标记

42.说说对react refs的理解?应用场景?

创建ref的形式有三种:

  • 传入字符串,使用时通过 this.refs.传入的字符串的格式获取对应的元素
  • 传入对象,对象是通过 React.createRef() 方式创建出来,使用时获取到创建的对象中存在 current 属性就是对应的元素
  • 传入hook,hook是通过 useRef() 方式创建,使用时通过生成hook对象的 current 属性就是对应的元素

在某些情况下,我们会通过使用refs来更新组件,但这种方式并不推荐,更多情况我们是通过props与state的方式进行去重新渲染子元素

但下面的场景使用refs非常有用:

  • 对Dom元素的焦点控制、内容选择、控制
  • 对Dom元素的内容设置及媒体播放
  • 对Dom元素的操作和对组件实例的操作
  • 集成第三方 DOM 库。

43.React和vue的区别

React 的思路是 HTML in JavaScript 也可以说是 All in JavaScript,通过 JavaScript 来生成HTML,所以设计了JSX语法,还有通过JS来操作CSS
而Vue 是把 HTML,CSS,JavaScript 组合到一起,用各自的处理方式,vue有单文件组件,可以把html,css,js写到一个文件中,html提供了模板引擎来处理

44.react中state、props、ref三大核心属性的特点和使用方式?

state让你可以通过数据驱动视图,让视图自动根据数据的改变而改变。
props可以提高组件的灵活性,通过外界传值让组件根据用户特性而呈现不同的视图。
refs能够减少用户对DOM的直接操作,而可以通过获取代理节点而提高开发效率和页面性能。

45.React中如何使用防抖节流?

安装:npm i lodash

引入:import _ from ‘lodash’

46.React实现触底加载实现,需要哪些相关属性?

我们想要判断一个div是否已经触底了,我们首先要给这个div绑定一个onScroll属性,在这我们获取滚动条的高度,页面可视高度和滚动轴到顶部的距离,scroll函数有一个event参数,打印event,我们可以看到event下面的所有属性,在页面样式中,我们想要实现滚动轴,那么就需要使用overflow,scroll出现滚动轴

47.React在哪个生命周期请求接口,哪个清除定时器?

48.React中路由单页面应用的优缺点都有哪些?

优点:
良好的交互体验

良好的前后端工作分离模式

减轻服务器压力
缺点:
首屏加载慢

不利于SEO

不适合开发大型项目

49.如何解决react页面不刷新?

找到package.json文件所在的目录,

在package.json同级根目录下创建.env文件

在.env文件中写入”FAST_REFRESH=false”并保存即可

50.React的事件和普通的html事件有什么不同?

区别:
对于事件名称命名方式,原生事件为全小写,react 事件采用小驼峰
对于事件函数处理语法,原生事件为字符串,react 事件为函数
react 事件不能采用 return false 的方式来阻止浏览器的默认行为

51.redux的工作原理?

作用:集中式管理react应用中多个组件共享状态

1)首先我们要确定我们要做什么

2)让Action Creators创建action(也就是你要做的事情)

3)通过dispatch将action分发出去

4)store对要使用的reducer进行绑定,然后将action分发到对应的reducer上

5)在reducer上进行相应的action操作并返回结果给store

6)组件就可以通过store的API向store进行操作获取返回的结果

52.Redux如何进行异步请求?

使用redux Thunk中间件

安装redux Thunk

在创建redux store的时候,使用applyMiddleware ,函数将redux Thunk中间件传递给createStore函数

接下来,就可以在action creator中使用redux Thunk中间件的函数dispatch和getState来发送异步请求了

53.Redux中store常用的api都有?

(createStore【创建 store 】)

(store.dispatch【派发action传递给 store】)

(store.getState【可以获取到 store 里面所有的数据】)

(store.subscribe【订阅store的改变,只有store进行改变,这个方法就执行】)

54.applyMiddleware的作用是什么?

它是redux的原生方法,作用是将所有中间件组成一个数组

55.combineReducers的作用是什么?

把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore 方法。

合并后的 reducer 可以调用各个子 reducer,并把它们返回的结果合并成一个 state 对象。 由 combineReducers() 返回的state对象,会将闯入的每个reducer返回的state按其传递给combineReducers(),时对应的key进行命名

56.React-redux和redux的区别?

(1)redux和组件进行对接的时候是直接在组件中进行创建,react-redux是运用provider将组件和store对接,使在provider里的所有组件都能共享store里的数据,还要用connect将组件和react连接
(2)获取state的方式不一样:
redux获取state是直接通过store.getState()。
React-redux获取state是通过mapStateToProps函数,只 要state数据变化就能获取最新数据
(3)触发action的方式不一样
Redux是使用dispatch直接触发,来操作store的数据
React-redux是使用mapStateToProps函数然后在调用 dispatch进行触发

57.Redux和Vuex有什么区别,它们有什么共同思想吗?

相同点:
state共享数据

流程一致:定义全局state,触发修改方法,修改state

全局注入store

不同点:

redux使用的是不可变数据,而Vuex是可变的。

redux每次都是用新的state替换旧的state,vuex是直接修改。

redux在检测数据变化时是通过diff算法比较差异的;vuex是通过getter/setter来比较的

vuex定义了state,getter,mutation,action;redux定义了state,reducer,action

vuex中state统一存放,方便理解;react中state依赖reducer初始值

vuex同步使用mutation,异步使用action;redux同步异步都使用reducer

相同思想:单一数据源 变化可预测 MVVM思想

58.Connect方法的作用?

Connect方法使得ui组件变成了容器组件(包含了业务逻辑和数据状态管理)

Connect使得组件和store的数据同步关联,connect:store的state数据一变,组件的 state会跟着变

59.redux的三大原则

单一数据源
整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。
State 是只读的
唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。
使用纯函数来执行修改
为了描述 action 如何改变 state tree ,你需要编写 reducers。
React-redux

60.react-redux的工作原理?

React-redux是一个轻量级的封装库,核心方法只有两个:
Provider :
Provider模块的功能很简单,从最外部封装了整个应用,并向connect模块传递store。而最核心的功能在connect模块中。
Connect:
1.connect通过context获取Provider中的store,通过store.getState()获取整个store tree 上所有state。
2、connect模块的返回值wrapWithConnect为function。
3、wrapWithConnect返回一个ReactComponent对象Connect,Connect重新render外部传入的原组件WrappedComponent,并把connect中传入的mapStateToProps, mapDispatchToProps与组件上原有的props合并后,通过属性的方式传给WrappedComponent。
react-redux的作用:
将组件分为了容器组件和ui组件
取代redux中的‘store.subscribe’监听组件的状态变化,用于渲染组件
配合redux使用,使组件轻松的拿到全局状态,方便组件间的通信

61.Provider和connect的底层原理实现,写出其核心代码?

connect方法接受两个参数:mapStateToProps和mapDispatchToProps。它们定义了 UI 组件的业务逻辑。前者负责输入逻辑,即将state映射到 UI 组件的参数(props),后者负责输出逻辑,即将用户对 UI 组件的操作映射成 Action

connect方法生成容器组件以后,需要让容器组件拿到state对象,才能生成 UI 组件的参数。
一种解决方法是将state对象作为参数,传入容器组件。但是,这样做比较麻烦,尤其是容器组件可能在很深的层级,一级级将state传下去就很麻烦。
React-Redux 提供Provider组件,可以让容器组件拿到state。

62.什么是JSX,详细说明jsx语法?

JSX 是 JavaScript 语法的一种语法扩展,并拥有 JavaScript 的全部功能
定义虚拟Dom不用写引号

1.定义虚拟dom不需要写引号

2.标签种混入js表达式要有{}

3.样式类名指定使用className

4.内联样式,要用style={{key:value}}的形式去写

5.必须只有一个根标签

6.标签需要闭合

7.标签首字母:

1)若小写字母开头,则将标签改为html中同名元素

2)若大写字母开头,react则去渲染对应的组件,若组件没有定义,则报错

63.说说你对高阶组件的理解?应用场景有那些?

高阶组件:接受一个或多个函数作为输入,输出一个函数,在react中,高阶组件接收一个或多个组件作为参数并且返回一个组件,本质也就是一个函数,并不是一个组件
应用场景:高阶组件能够提高代码的复用性和灵活性,在实际应用中,常常用于与核心业务无关但又在多个模块使用的功能,如权限控制,日志记录,数据校验,异常处理,统计上报等

vue篇

1.Vue中自定义指令的理解,应用场景有哪些?

指令系统是计算机硬件的语言系统,也叫机器语言,它是系统程序员看到的计算机的主要属性。因此指令系统表征了计算机的基本功能决定了机器所要求的能力
注册一个自定义指令有全局注册与局部注册
全局注册主要是通过Vue.directive方法进行注册
Vue.directive第一个参数是指令的名字(不需要写上v-前缀),第二个参数可以是对象数据,也可以是一个指令函数
局注册通过在组件options选项中设置directive属性
然后你可以在模板中任何元素上使用新的 v-focus property,如下:

  • 表单防止重复提交
  • 图片懒加载
  • 一键 Copy的功能
  • 拖拽指令、
  • 页面水印、
  • 权限校验

2.说说你对vue中mixin的理解?

mixin是一种类,在vue中就是js文件,主要的作用是作为功能模块引用。因为在项目中,可能不同组件会有相同的功能,比如控制元素的显示和隐藏,如果他们的变量和规则也完全相同的话,就可以把这个功能单独提取出来,放在mixin.js中,再引入,就可以实现一样的功能了。引入的方法也分为全局混入和局部混入,局部混入就是在每个组件中引入,全局混入就是在main.js中通过Vue.mixin()引入。

3.Vue组件之间通信的方式有哪些,能够说出七种及其以上的得满分,必须写出 具体的实现方式才可以?

通过 props 传递
通过 $emit 触发自定义事件
使用 ref
EventBus
p a r e n t 或 parent 或 parentroot
attrs 与 listeners
Provide 与 Inject
Vuex

4.Vuex的实现原理是什么,写出其实现的核心代码?

vuex是利用vue的mixin混入机制,在beforeCreate钩子前混入vuexInit方法,vuexInit方法实现了store注入vue组件实例,并注册了vuex store的引用属性$store。vuex的state是借助vue的响应式data实现的。getter是借助vue的计算属性computed特性实现的。
1、在vue项目中先安装vuex

import Vuex from 'vuex';
Vue.use(vuex);// vue的插件机制

2、利用vue的 插件机制,使用Vue.use(vuex)时,会调用vuex的install方法,装载vuex,install方法的代码如下:

export function install (_Vue) {
  if (Vue && _Vue === Vue) {
    if (process.env.NODE_ENV !== 'production') {
      console.error(
        '[vuex] already installed. Vue.use(Vuex) should be called only once.'
      )
    }
    return
  }
  Vue = _Vue
  applyMixin(Vue)
}

3、applyMixin方法使用vue 混入机制,vue的生命周期beforeCreate钩子函数前混入vuexInit方法,核心代码如下:

Vue.mixin({ beforeCreate: vuexInit });
function vuexInit () {
    const options = this.$options
    // store injection
    if (options.store) {
      this.$store = typeof options.store === 'function'
        ? options.store()
        : options.store
    } else if (options.parent && options.parent.$store) {
      this.$store = options.parent.$store
    }
}

5.SPA首屏加载速度慢的怎么解决?

一 加载慢的原因
在页面渲染的过程, 导致加载速度慢的因素:
网络延时问题
资源文件体积过大
资源是否重复发送请求去加载
加载脚本的时候,渲染内容堵塞了
二 解决方案
常见的几种SPA 首屏优化方式

减小入口文件体积
静态资源本地缓存
UI框架按需加载
图片资源的压缩
组件重复打包
开启GZip压缩
使用SSR

6.为什么data属性是一个函数而不是一个对象?

7. Vue2的响应式的原理是什么?

原理:通过数据劫持 defineProperty + 发布订阅者模式,当 vue 实例初始化后 observer 会针对实例中的 data 中的每一个属性进行劫持并通过 defineProperty() 设置值后在 get() 中向发布者添加该属性的订阅者,这里在编译模板时就会初始化每一属性的 watcher,在数据发生更新后调用 set 时会通知发布者 notify 通知对应的订阅者做出数据更新,同时将新的数据根性到视图上显示。
缺陷:只能够监听初始化实例中的 data 数据,动态添加值不能响应,要使用对应的 Vue.set()。

8. Vue中数据更新视图不更新的原因是什么?

Vue是一个响应式框架,当我们改变数据时,Vue会自动更新视图,但是有时候会发现数据更新了,但是视图却没有更新,这是因为Vue的响应式机制的原因。由于Vue的响应式机制是依赖于Object.defineProperty()方法实现的,只有当对象的属性存在于data对象中,才能被Vue监听到,而如果对象的属性不存在于data对象中,Vue就无法监听到,也就无法更新视图。
解决方法:
1、使用Vue.set()方法:Vue提供了一个Vue.set()方法,它可以用来添加一个属性到Vue实例上,并触发视图的更新,用法如下:

Vue.set(vm.userInfo, 'age', 28)

2、使用Vue.delete()方法:Vue还提供了一个Vue.delete()方法,它可以用来删除Vue实例上的属性,并触发视图的更新,用法如下:

Vue.delete(vm.userInfo, 'age')

9. Vue中nextTick的作用是什么?

1: $nextTick 用法于原理:

在Vue 中更新DOM 是异步的, nextTick的主要应用的场景及原因。

1.1: 在Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中

在 Vue生命周期函数中 created钩子函数中: DOM 并没有进行任何渲染, 而此时进行DOM 操作无异于是徒劳的, 所以此处一定将DOM 操作的js 代码放进Vue.nextTick 回调函数中, 与之对应的就是mounted()

钩子函数, 因为该钩子函数执行时所有的DOM 挂载和渲染都已经完成, 此时在该钩子函数中进行任何操作都不会有问题。

1.2: 在数据变化后要执行某个回调函数,而这个操作需要使用随数据改变而改变的DOM结构的时候, 这个操作都应该放进Vue.nextTick () 回调函数中。

10. Vue中组件缓存如何实现? A页面到B页面。A页面缓存,A页面到C页面,A页面缓存取消?如何实现?

总结:主要运用了keep-alive的 max和生命周期activated函数;
1.当从A 跳转到B 列表时,因为只能缓存一个组件,所以B页面缓存,A页面取消缓存,当B页面再到A页面时,A页面因没有缓存,从而从新加载
2.当跳转到A页面,从A页面到跳转到 编辑/新增 页面时,A页面有缓存, 编辑/新增页面不缓存,当前缓存的还是A页面,返回到A页面时,A页面不刷新,但是keep-alive的activated生命周期函数会被调用,在里面可以从新请求当前搜素条件的列表
注:第一次进入A页面时,activated函数也会被调用,在munted之后,所以activated里的请求在munted中就不要请求,要不就会出现两次相同的请求;

11. 说说你对Vue中虚拟DOM和diff算法的理解?

diff 算法是一种通过同层的树节点进行比较的高效算法
其有两个特点:

比较只会在同层级进行, 不会跨层级比较
在diff比较的过程中,循环从两边向中间比较
diff 算法在很多场景下都有应用,在 vue 中,作用于虚拟 dom 渲染成真实 dom 的新旧 VNode 节点比较

js篇

1.大文件如何做断点续传?

所谓断点续传就是将一个大文件分成几部分任务,每一个任务采用一个线程,在遇到网络卡顿,上传失败的时候,就不需要重新下载、上传,可以接着上次下载部分,来完成后面的为下载,未完成部分继续执行。

2.原生js如何实现上拉加载下拉刷新?

上拉加载:需要计算出当前父元素的scrollHeight在计算出当前元素的scrollTop和clientHeight通过这三个属性就可以判断出用户滑动的位置是否在最后,定义一个事件来实时判断,当用户滑动的位置到达底层后显示加载更多,后台请求服务器返回数据,与当前列表拼接则完成上拉加载操作。一般需要配合节流使用避免重复请求
下拉刷新:可以通过用户滑动当前元素的顶部位置是否达到需要刷新的位置,如果达到则进行释放操作,将元素与顶部的距离设置为0,进行刷新列表,
通过touchstart、touchmove、touchend三个事件,来判断用户得操作
当用户滑动距离大于顶部得话就设置transformy值实时修改
这两种操作都可以使用第三方库来完成。

3.如何通过原生js实现一个节流函数和防抖函数?

防抖的实现:定义一个闭包函数,在函数内部定义个timer变量用于存储定时器,在返回的函数中判断该变量是否存在(定时器是否开始),如果存在则表示上一次的点击事件还没执行完成表示不是最后一次执行,则不进行操作;若定时器不存在则表示可以执行点击在规定的时间内不重复点击就可以实现一个防抖函数了

节流的实现:跟上面几乎差不多,只不过它需要定义一个开始点击的时间,事件执行的时间,进行计算,在规定时间内依次执行。

4.节流防抖的区别是什么,具体解释一下?

防抖和节流本质是不一样的,防抖是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行
防抖:
防抖触发频率事件时n秒后只会执行一次,如果n秒内再次触发,则会重新计算
概述:每次触发时都会取消之前的延时调用
节流
高频事件触发,每次触发事件时设置一个延迟调用方法,并且取消之前的延时调用方法,
概述:每次触发事件时都会判断是否等待执行的延时函数
区别:降低回调执行频率,节省计算资源
函数防抖一定时间连续触发的时间,只在最后执行一次,而函数节流一段时间内只执行一次

5.为什么for循环比forEach性能高?

一、for()循环? for循环就是通过下标,对循环中的代码反复执行,功能强大,可以通过index取得元素。处理比较复杂的处理的时候比较方便
二、forEach()循环
forEach()循环方法用于调用数组的每个元素,并将元素传递给回调函数。foreach有的也叫做增强for循环,forEach其实是for循环的一个特殊简化版。forEach循环对于空的数组是不会执行回调函数的

如何进行选择? foreach相对于for循环,代码减少了,但是foreach依赖于IEnumerable。
在运行的时候效率比for循环低。当然,在处理不确定循环次数的循环,或者循环次数需要计算的情况下。使用foreach比较方便,而且foreach的代码经过编译系统的代码优化以后,for循环使用更加灵活。

6.Js的数据类型和内存存储区别

js中的数据类型分为:基本数据类型和引用数据类型
基本数据类型:Number,String,Boolean,Undefined,null,symbol
引用数据类型 :Object,Array,Function
存储区别:
基本数据类型和引用数据类型存储在内存中的位置不同:
基本数据类型存储在栈中
引用类型的对象存储于堆中
当我们把变量赋值给一个变量时,解析器首先要确认的就是这个值是基本类型值还是引用类型值

声明变量时不同的内存地址分配:
简单类型的值存放在栈中,在栈中存放的是对应的值
引用类型对应的值存储在堆中,在栈中存放的是指向堆内存的地址
不同的类型数据导致赋值变量时的不同:
简单类型赋值,是生成相同的值,两个对象对应不同的地址
复杂类型赋值,是将保存对象的内存地址赋值给另一个变量。也就是两个变量指向堆内存中同一个对象

7.for…in循环和for…of循环的区别?

for in 是ES5的语法,for of 是ES6的语法
for in 是无序遍历数组或对象的,也就是随机遍历,不按照顺序来; for of 是按照顺序遍历的
for in 是对key值遍历的,对于对象来说,遍历的是对象的key值,对于数组来说,遍历的是数组的下标; for of是对数值遍历的,不能遍历对象,可以遍历数组,是对数组的每一个元素数值遍历
for in会把数组或对象的原型上的属性或方法也遍历出来 ,对于对象来说,可以使用对象的hasOwnProperty()这个方法判断是否是自身实例属性,可以只对自身属性遍历,或者使用Object.keys()方法获取对象键值组成的数组,这个数组不包含原型的属性和方法;而for of 只会把当前数组自己本身的元素值遍历出来
普通的for循环能使用break、continue、 return跳出循环,forEach中可以使用return跳出循环,无法使用break和continue;在for in和for of是可以使用break和return和continue

8.Js数据类型判断都有哪几种方式?至少说出5种?它们的区别是什么?

1. typeof判断
typeof返回的类型都是字符串形式
2. Constructor
实例constructor属性指向构造函数本身
constructor 判断方法跟instanceof相似,但是constructor检测Objectinstanceof不一样,constructor还可以处理基本数据类型的检测,不仅仅是对象类型
3. Instanceof
instanceof可以判类型是否是实例的构造函数
instanceof 后面一定要是对象类型,并且大小写不能错,该方法适合一些条件选择或分支。
4. Object.prototype.toString.call()
判断类型的原型对象是否在某个对象的原型链上
5. 通过object原型上的方法判断
比如array.isArray()来判断是不是一个数组
6. ===(严格运算符)
通常出现在我们的条件判断中,用来判断数据类型的话就会非常的有局限性,比如判断一个变量是否为空,变量是否为数据等

9.说说你对Object.defineProperty()的理解?

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
该方法接受三个参数

  • 第一个参数是 obj:要定义属性的对象,
  • 第二个参数是 prop:要定义或修改的属性的名称或 Symbol
  • 第三个参数是 descriptor:要定义或修改的属性描述符
    函数的第三个参数 descriptor 所表示的属性描述符有两种形式:数据描述符存取描述符
  • 数据描述符是一个具有值的属性,该值可以是可写的,也可以是不可写的。
  • 存取描述符是由 getter 函数和 setter 函数所描述的属性。
    一个描述符只能是这两者其中之一;不能同时是两者。
    这两种同时拥有下列两种键值:
    configurable 是否可以删除目标属性或是否可以再次修改属性的特性(writable, configurable, enumerable)。设置为true可以被删除或可以重新设置特性;设置为false,不能被可以被删除或不可以重新设置特性。默认为false
    enumerable 当且仅当该属性的 enumerable 键值为 true 时,该属性才会出现在对象的枚举属性中。默认为 false

10.说说你对事件循环event loop的理解?

任务在主线程不断进栈出栈的一个循环过程。任务会在将要执行时进入主线程,在执行完毕后会退出主线程。
JavaScript是一门单线程的语言,意味着同一时间内只能做一件事,
但是这并不意味着单线程就是阻塞,而实现单线程非阻塞的方法就是事件循环
在JavaScript中,所有的任务都可以分为
同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行
异步任务:异步执行的任务,比如ajax网络请求,setTimeout定时函数等
异步任务还可以细分为微任务与宏任务
微任务一个需要异步执行的函数,执行时机是在主函数执行结束之后、当前宏任务结束之前
宏任务的时间粒度比较大,执行的时间间隔是不能精确控制的,对一些高实时性的需求就不太符合

11.什么是闭包,应用场景是什么?

参考回答: 闭包是指有权访问另外一个函数作用域中的变量的函数。
闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会继续存在。闭包就是函数的“堆栈”在函数返回后并不释放,我们也可以理解为这些函数并不在栈上分配,而是在堆上分配。当在一个函数内定义另外一个函数就会产生闭包。
应用场景:例如私有变量,使用定时器,手动写入防抖函数和节流函数

12.来讲讲js中的闭包

参考回答: 闭包是指有权访问另外一个函数作用域中的变量的函数。 闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会继续存在。闭包就是函数的“堆栈”在函数返回后并不释放,我们也可以理解为这些函数并不在栈上分配,而是在堆上分配。当在一个函数内定义另外一个函数就会产生闭包。
(2)为什么要用: 匿名自执行函数:我们知道所有的变量,如果不加上 var 关键字,则默认的会添加到全 局对象的属性上去,这样的临时变量加入全局对象有很多坏处,比如:别的函数可能误 用这些变量;造成全局对象过于庞大,影响访问速度(因为变量的取值是需要从原型链 上遍历的)。除了每次使用变量都是用
var 关键字外,我们在实际情况下经常遇到这样一 种情况,即有的函数只需要执行一次,其内部变量无需维护,可以用闭包。 结果缓存:我们开发中会碰到很多情况,设想我们有一个处理过程很耗时的函数对象, 每次调用都会花费很长时间,那么我们就需要将计算出来的值存储起来,当调用这个函 数的时候,首先在缓存中查找,如果找不到,则进行计算,然后更新缓存并返回值,如 果找到了,直接返回查找到的值即可,闭包正是可以做到这一点,因为它不会释放外部 的引用,从而函数内部的值可以得以保留

13.call,apply,bind的区别

call apply bind都可以改变函数调用的this指向。
函数.call(对象,arg1,arg2…)
函数.apply(对象,[arg1,arg2,…])
var ss=函数.bind(对象,arg1,arg2,…)
1.第一个参数都是指定函数内部中this的指向(函数执行时所在的作用域),然后根据指定的作用域,调用该函数。
2.都可以在函数调用时传递参数。call,bind方法需要直接传入,而apply方法需要以数组的方式传入
3.call,apply方法是在调用之后立即执行函数,而bind方法没有立即执行,需要将函数再执行一遍。
4.改变this对象的指向问题不仅有call,apply,bind方法,也可以使用that变量来固定this的指向。

14.箭头函数与普通函数的区别

写法不同:
(1)箭头函数使用箭头定义,普通函数中没有。
(2)箭头函数全都是匿名函数:普通函数可以有匿名函数,也可以有具名函数
(3)箭头函数不能用于构造函数:普通函数可以用于构造函数,以此创建对象实例。
(4)箭头函数中 this 的指向不同:在普通函数中,this 总是指向调用它的对象,如果用作构 造函数,它指向创建的对象实例。
(5)箭头函数不具有 arguments 对象:每一个普通函数调用后都具有一个arguments 对 象,用来存储实际传递的参数。但是箭头函数并没有此对象。
其他区别:
箭头函数不具有 prototype 原型对象。箭头函数不具有 super。箭头函数不具有 new.target

15.js继承的几种方式,分别说明优缺点?

(1)原型链继承
优点:写法简单,容易理解,
缺点:引用类型的值会被所有实例共享,在子类实例对象创建时,不能向父类传参

(2)借用构造函数继承
优点:避免了引用类型的值会被所有实例共享
缺点:方法在构造函数中,每次创建实例对象时都会重新创建一遍方法

(3)组合继承
优点:融合原型链和借用构造函数的优点,是js中最常用的继承方式
缺点:无论什么情况下,父类构造函数都会被调用两次,一是创建子类原型对象时,二是类构造函数的内部

(4)原型式继承
优点:不需要单独创建构造函数
缺点:引用类型的值会被所有实例共享

(5)寄生式继承
优点:不需要单独创建构造函数
缺点:方法在构造函数中,每次创建实例对象时都会重新创建一遍

(6)寄生组合继承
优点:高效率只调用一次父类构造函数,并且避免了子类原型对象上不必要,多余的属性,同时将原型链 保持不变,因此能使用instanceof和isprototypeof
缺点:代码复杂

16.JS判断数组中是否包含某个值,提供四种方法?

Array.indexOf():判断数组中是否存在某个值,如果存在返回数组元素的下标,否则返回-1

Array.includes():判断一个数组是否包含一个指定的值,如果存在返回true,否则返回false

Array.find():返回数组中满足条件的第一个元素的值,如果没有,返回undefined

$.inArray():使用jquery的inArray方法,该方法返回元素在数组中的下标,如果不存在数组中,返回-1

Array.findIndex():返回数组中满足条件的第一个元素的索引,如果没有找到,返回-1

17.Js中深拷贝和浅拷贝的定义以及具体实现都有哪些方法?

深拷贝:拷贝对象各个层级的属性,就是复制出来的每个对象都有属于自己的内存空间,不会相互影响
浅拷贝:子对象复制父对象,父子对象发生关联,两者属性值指向同一内存空间,就是改变其中一个对象,另一个对象也会跟着改变

18.合成事件和原生事件的区别?

1.事件名称命名方式不同
2.事件处理函数写法不同
3.阻止默认行为方式不同

19.js内存泄漏的几种情况?

内存泄露
是当一块内存不再被应用程序使用的时候,由于某种原因,这块内存没有返回给操作系统或空闲内存池的现象
1.意外的全局变量
2.闭包引起的内存泄露
3.DOM之外的引用
怎样避免内存泄露

1)减少不必要的全局变量,或者生命周期较长的对象,及时对无用的数据进行垃圾回收;
2)注意程序逻辑,避免“死循环”之类的 ;
3)避免创建过多的对象 原则:不用了的东西要及时归还。

20.说说new操作符具体干了什么?

创建一个空对象,并且this变量引用该对象,同时还继承了该函数的原型
属性和方法被加入到this引用的对象中
新创建的对象由this所引用,并且最后隐式返回this

21.Class类的静态属性和静态方法的理解?

静态属性、静态方法:
就是类自身的属性和方法,只能在类自身调用,实例对象是无法调用到静态属性和方法的,只能类自身调用,当然子类也可以调用父类的静态属性和方法,
实例属性和实例方法的理解

实例属性、实例方法:.
就是实例可以调用的属性和方法,记住实例是无法调用类的静态属性和方法的,但是类可以调用实例属性和实例方法

22.什么是“事件代理”,事件委托的优点是什么?

简单理解就是将一个响应事件委托到另一个元素
当节点被点击时,click事件向上冒泡,父节点捕获到事件后,我们判断是否为所需的节点,然后进行处理

23. Math.ceil()、Math.round()、Math.floor()三者的区别是什么?

math.ceil():执行向上舍入,总是将值向上舍入最为接近的整数
math.floor():执行向下舍入,总是将值向下舍入最为接近的整数
math.round():执行标准舍入,将数值四舍五入为最接近的整数

24.js中数组是如何在内存中存储的?

数组不是以一组连续的区域存储在内存中,而是一种哈希映射的形式。它可以通过多种数据结构来实现,其中一种是链表

js中的数据类型分为:基本数据类型和引用数据类型
基本数据类型:Number,String,Boolean,Undefined,null,symbol
引用数据类型 :Object,Array,Function
存储区别:
基本数据类型和引用数据类型存储在内存中的位置不同:
基本数据类型存储在栈中
引用类型的对象存储于堆中
当我们把变量赋值给一个变量时,解析器首先要确认的就是这个值是基本类型值还是引用类型值

声明变量时不同的内存地址分配:
简单类型的值存放在栈中,在栈中存放的是对应的值
引用类型对应的值存储在堆中,在栈中存放的是指向堆内存的地址
不同的类型数据导致赋值变量时的不同:
简单类型赋值,是生成相同的值,两个对象对应不同的地址
复杂类型赋值,是将保存对象的内存地址赋值给另一个变量。也就是两个变量指向堆内存中同一个对象

25.This分别指向什么?

this总是指向函数的直接调用者(而非间接调用者)
如果有new关键字,this指向new出来的那个对象
在事件中,this指向触发这个事件的对象,特殊的是,IE中的attachEvent中的this总是指向全局对象Window

26. 原型和原型链的理解?

原型:
JavaScript的所有对象中都包含了一个 [proto] 内部属性,这个属性所对应的就是该对象的原型
JavaScript的函数对象,除了原型 [proto] 之外,还预置了 prototype 属性
当函数对象作为构造函数创建实例时,该 prototype 属性值将被作为实例对象的原型 [proto]。
原型链
当一个对象调用的属性/方法自身不存在时,就会去自己 [proto] 关联的前辈 prototype 对象上去找
如果没找到,就会去该 prototype 原型 [proto] 关联的前辈 prototype 去找。依次类推,直到找到属性/方法或 undefined 为止。从而形成了所谓的“原型链

27.js数组的常用方法

arr.push():将一个或多个元素添加到数组的末尾
arr.pop():从数组中删除最后一个元素
unshift():将一个或多个元素添加到数组的开头
shift():从数组中删除第一个元素
concat():合并两个数组
splice():通过删除或替现有元素或者原地添加新的元素来修改数组
slice():返回一个新的数组对象
join():将一个数组的所有元素连接成一个字符串并返回这个字符串
sort():对数组进行排序
reverse():将数组中元素的位置颠倒

28.如何避免ajax数据请求重新获取?

在前端开发中,由于网络不稳定或者用户频繁操作等原因,可能会导致 AJAX 请求被重复发送,这可能会对系统造成不良的影响。为了避免重复请求,可以采用以下几种方法:
防抖(debounce):在连续触发事件的过程中,只执行最后一次操作。例如,当用户在搜索框输入关键字时,可以在用户输入停止一段时间后才发送 AJAX 请求。
节流(throttle):在一段时间内只执行一次操作。例如,当用户滚动页面时,可以每隔一段时间才执行一次 AJAX 请求,以减少重复请求的次数。
**取消请求:**在发送 AJAX 请求前,判断是否有相同的请求正在进行,如果有则取消旧请求。可以使用 XMLHttprequest 对象的 abort() 方法取消 AJAX 请求。
缓存请求结果:在获取到 AJAX 请求的结果后,可以将结果缓存到本地,并在后续的请求中优先使用缓存结果,避免重复请求。可以使用浏览器的本地存储(localStorage 或 sessionStorage)来实现数据的缓存。

需要根据具体情况选择适合的方法,以避免重复请求对系统造成不必要的负担。同时,还应该注意请求的合理频率和缓存策略,以提高系统的性能和用户体验。

29.说说你对BOM对象的理解?常见的BOM对象有哪些?

BOM (Browser Object Model),浏览器对象模型,提供了独立于内容与浏览器窗口进行交互的对象
其作用就是跟浏览器做一些交互效果,比如如何进行页面的后退,前进,刷新,浏览器的窗口发生变化,滚动条的滚动,以及获取客户的一些信息如:浏览器品牌版本,屏幕分辨率
浏览器的全部内容可以看成DOM,整个浏览器可以看成BOM
常见的BOM:
window:BOM的核心对象是window,它表示浏览器的一个实例
location
navigator:对象主要用来获取浏览器的属性,区分浏览器类型,属性较多,且兼容性比较复杂
screen:保存的纯粹是客户端能力信息,也就是浏览器窗口外面的客户端显示器的信息,比如像素宽度和像素高度
history:对象主要用来操作浏览器url的历史记录,可以通过参数向前,向后,或者向指定url跳转

30.window.onload和$(document).ready?

window.onload即为所有的都加载完毕,即除了DOM树之外,还包括所有的文章,图片,数据等所有元素都加载完毕之后才运行,而**$(document).ready**仅仅是在DOM树生成和加载完毕之后就开始运行。
不同点:
1.window.onload不能同时编写几个,如果同时包含几个,则只能运行一个,而另外一个可以同时编写多个,而且都可以执行。
2.运行时间不同,window.onload只有在所有的元素全部加载完成之后才运行,而另一个DOM树加载完成之后就可以了。
3.简写不同:window.onload不能简写,(document).ready(function(){})可以简写成(function(){});

31. 说说原生Ajax的实现原理是什么,实现步骤?

Ajax的工作原理相当于在用户和服务器之间加了—个中间层(AJAX引擎),使用户操作与服务器响应异步化。并不是所有的用户请求都提交给服务器。像—些数据验证和数据处理等都交给Ajax引擎自己来做,,只有确定需要从服务器读取新数据时再由Ajax引擎代为向服务器提交请求。

步骤

  1. 创建XMLHttpRequest对象。
  2. 设置请求方式。
  3. 调用回调函数。
  4. 发送请求。

32. 为什么要有跨域?JS跨域怎么解决?

因为同源策略,所谓同源就是一种安全限制,它要求浏览器发出的请求的协议,域名,端口号必须要和ajax请求路径的协议,域名,端口号相同才能成功发起请求即成功访问到服务器;协议、域名、端口号都相同才会满足同源策略,三者中只要有一个不一样就是存在跨域问题。

解决:

Jsonp

JSONP 是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,兼容性好(兼容低版本IE),缺点是只支持get请求,不支持post请求。

CORS 是跨域资源分享(Cross-Origin Resource Sharing)的缩写。它是 W3C 标准,属于跨源 AJAX 请求的根本解决方法。

通过ngnix解决跨域问题

设置反向代理服务器
也就是在前端开启一个服务器,前端页面代码逻辑先跟代理服务器交互,然后再代理服务器与服务端的服务器进行交互,这样是两台服务器进行交互就不会存在跨域问题,因为跨域问题是浏览器向服务器发送请求时才会发生的问题

33.如何监听div的尺寸变化?

方法一:利用ResizeObserver监听
ResizeObserver是现代浏览器提供的一种监听元素尺寸变化的API,支持监听多个dom元素的尺寸变化,你只实例化一个ResizeObserver,然后调用observe()方法传入需要监听的dom元素,当元素的尺寸发生变化时,就会触发回调函数
方法二:利用MutationObserver监听
MutationObserver是另一种常用的监听元素变化的API,相比ResizeObserver更为通用,也可以监听元素的尺寸变化,使用该API,我们可以监听dom元素的子节点,属性,文本内容等变化,并在回调函数中进行处理,对于div元素的尺寸变化,我们可以通过监听其子节点的变化来判断

34.什么是面向对象编程及面向过程编程,它们的异同和优缺点?

面向过程和面向对象的区别及优缺点
面向过程:
面向过程是一种自顶向下的编程。
面向过程优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、 Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
缺点:没有面向对象易维护、易复用、易扩展。
面向对象:
面向对象是将事物高度抽象化。面向对象必须先建立抽象模型,之后直接使用模型就行了。
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更加灵活、更加易于维护。面向对象技术具有程序结构清晰,自动生成程序框架,实现简单,可有效地减少程序的维护工作量,代码重用率高,软件开发效率高等优点。
缺点:因为类调用时需要实例化,开销比较大,比较消耗资源,性能比面向过程低。

dom篇

1.说说 Real DOM 和 Virtual DOM 的区别?优缺点?两者的区别?

虚拟 DOM 不会进行排版与重绘操作,而真实 DOM 会频繁重排与重绘
虚拟 DOM 的总损耗是“虚拟 DOM 增删改+真实 DOM 差异增删改+排版与重绘”
真实 DOM 的总损耗是“真实 DOM 完全增删改+排版与重绘”
真实 DOM 的优势:易用
缺点:效率低,解析速度慢,内存占用量过高
性能差:频繁操作真实 DOM,易于导致重绘与回流
使用虚拟 DOM 的优势如下:
简单方便:如果使用手动操作真实 DOM 来完成页面,繁琐又容易出错,在大规模应用下维护起来也很困难
性能方面:使用 Virtual DOM,能够有效避免真实 DOM 数频繁更新,减少多次引起重绘与回流,提高性能
跨平台:React 借助虚拟 DOM,带来了跨平台的能力,一套代码多端运行
缺点:
在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化
首次渲染大量 DOM 时,由于多了一层虚拟 DOM 的计算,速度比正常稍慢

2.说说React中的虚拟dom?在虚拟dom计算的时候diff和key之间有什么关系?

虚拟dom: 原生的JS DOM操作非常消耗性能,而React把真实原生JS DOM转换成了JavaScript对象。这就是虚拟Dom(Virtual Dom)
每次数据更新后,重新计算虚拟Dom,并和上一次生成的虚拟dom进行对比,对发生变化的部分作批量更新。在此其中,React提供了ShouldcomponentUpdate生命周期来让开发者手动控制减少数据变化后不必要的虚拟dom对比,提升性能和渲染效率。

diff和key之间关系: 当同一层级的某个节点添加了对于其他同级节点唯一的key属性,当它在当前层级的位置发生了变化后。react
diff算法通过新旧节点比较后,如果发现了key值相同的新旧节点,就会执行移动操作(然后依然按原策略深入节点内部的差异对比更新),而不会执行原策略的删除旧节点,创建新节点的操作。这无疑大大提高了React性能和渲染效率

3.说说你对React中虚拟dom的理解?

Virtual DOM是一种编程概念。通俗点理解,虚拟DOM是一棵虚拟的JavaScript对象树,画重点,”虚拟的“、”JS对象“,指的是它把真实的网页文档节点,虚拟成一个个的js对象,并以树型结构,保存在内存中。

webpack篇

1.说说webpack中常见的loader?解决了什么问题?

1.Babel-loader:将ES6、TypeScript等高级语言转换为浏览器可以识别的JavaScript代码,解决了不同浏览器对JavaScript语言支持不同的问题。

2.CSS-loader:将CSS文件中的样式转换为JavaScript模块,方便在JavaScript代码中引用,并解决了浏览器对不同CSS特性的兼容性问题。

3.Style-loader:将CSS模块的样式插入到HTML页面的标签中,使样式生效。

4.File-loader/url-loader:将图片、字体等文件打包成单个文件,方便在浏览器中加载和使用。

5.Vue-loader:将Vue单文件组件转换为JavaScript模块,方便在应用程序中使用Vue框架。

这些Loader主要解决了前端开发中代码转换、资源管理等问题。通过使用Loader,我们可以将不同类型的文件转换为可执行的JavaScript、CSS等代码,并将所有资源打包成单个文件,方便在浏览器中加载和使用。这样可以大大提高前端开发的效率和代码质

2.说说webpack中代码分割如何实现?

Webpack 实现代码分割的主要方法是使用动态导入语法(Dynamic Import Syntax)或者配置 optimization.splitChunks。

动态导入语法
Webpack 2 提供了 import() 函数来实现动态导入模块。这个函数会返回一个 Promise 对象,可以用于异步加载模块。在 Webpack 中,可以将动态导入语法用于实现代码分割。
optimization.splitChunks 配置
webpack4 及以上版本可以使用 optimization.splitChunks 配置项,自动地将公共的代码块提取到单独的文件中。这样可以避免重复打包相同的代码块,从而减小文件大小和加载时间。

3.说说如何借助webpack来优化前端性能?

1.JS代码压缩
2.CSS代码压缩
3.Html文件代码压缩
4.文件大小压缩
5.图片压缩
6.Tree Shaking
7.代码分离
8.内联 chunk

4.前端跨域的解决方案?

一、什么是跨域?
在前端领域中,跨域是指浏览器允许向服务器发送跨域请求,从而克服Ajax只能同源使用的限制。
二、什么是同源策略?
同源策略是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。
同源策略限制以下几种行为:
Cookie、LocalStorage 和 IndexDB 无法读取
DOM和JS对象无法获得
AJAX 请求不能发送
三、9种跨域解决方案
1、JSONP跨域
2、跨域资源共享(CORS)
3、nginx代理跨域
4、nodejs中间件代理跨域
5、document.domain+ iframe跨域
6、location.hash + iframe跨域
7、window.name + iframe跨域
8、postMessage跨域
9、WebSocket协议跨域
小结
  以上就是9种常见的跨域解决方案,jsonp(只支持get请求,支持老的IE浏览器)适合加载不同域名的js、css,img等静态资源;CORS(支持所有类型的HTTP请求,但浏览器IE10以下不支持)适合做ajax各种跨域请求;Nginx代理跨域和nodejs中间件跨域原理都相似,都是搭建一个服务器,直接在服务器端请求HTTP接口,这适合前后端分离的前端项目调后端接口。document.domain+iframe适合主域名相同,子域名不同的跨域请求。postMessage、websocket都是HTML5新特性,兼容性不是很好,只适用于主流浏览器和IE10+。

5.说说webpack proxy的工作原理?为什么能够解决跨域?

proxy工作原理实质上是利用http-proxy-middleware 这个http代理中间件,实现请求转发给其他服务器

举个例子:

在开发阶段,本地地址为http://localhost:3000,该浏览器发送一个前缀带有/api标识的请求到服务端获取数据,但响应这个请求的服务器只是将请求转发到另一台服务器中

const express = require('express');
const proxy = require('http-proxy-middleware');
 
const app = express();
 
app.use('/api', proxy({target: 'http://www.example.org', changeOrigin: true}));
app.listen(3000);

跨域:
在开发阶段, webpack-dev-server 会启动一个本地开发服务器,所以我们的应用在开发阶段是独立运行在 localhost的一个端口上,而后端服务又是运行在另外一个地址上

所以在开发阶段中,由于浏览器同源策略的原因,当本地访问后端就会出现跨域请求的问题

通过设置webpack proxy实现代理请求后,相当于浏览器与服务端中添加一个代理者

当本地发送请求的时候,代理服务器响应该请求,并将请求转发到目标服务器,目标服务器响应数据后再将数据返回给代理服务器,最终再由代理服务器将数据响应给本地

6.介绍一下 Tree Shaking?

Tree Shaking 指的就是当我引入一个模块的时候,我不引入这个模块的所有代码,我只引入我需要的代码,这就需要借助 webpack 里面自带的 Tree Shaking 这个功能来帮我们实现。
官方有标准的说法:Tree-shaking的本质是消除无用的js代码。无用代码消除在广泛存在于传统的编程语言编译器中,编译器可以判断出某些代码根本不影响输出,然后消除这些代码,这个称之为DCE(dead code elimination)

在 webpack 项目中,有一个入口文件,相当于一棵树的主干,入口文件有很多依赖的模块,相当于树枝。实际情况中,虽然依赖了某个模块,但其实只使用其中的某些功能。通过 Tree-Shaking,将没有使用的模块摇掉,这样来达到删除无用代码的目的。

ES6系列

1.promise的理解

Promise是异步编程的一种解决方案,他是一个对象,可以获取异步操作的消息,避免了回调地狱。
从语法上说,Promise 是一个对象,从它可以获取异步操作的消息

Promise的实例有三个状态:pending进行中,resolved已完成,rejected 已拒绝
状态一旦改变,就不会再变
Promise 的状态只能从“等待”转到“完成”或者“拒绝”,不能逆向转换,同时“完成”和“拒绝”也不能相互转换.

promise 必须提供一个 then 方法以访问其当前值、终值和据因。
promise.then(resolve, reject),resolve 和 reject 都是可选参数。
如果 resolve 或 reject 不是函数,其必须被忽略
. then 方法必须返回一个 promise 对象

2.ES6都有哪些新增,最少说出七个?

1.新增了let,const关键字
2.symbol
3.promise
4.解构赋值
5.箭头函数
6.扩展运算符
7.es6模块化模块化 module
8.class类关键字
9.模板字符串
10.对象方面:map(),set()
11.数组方面:array.from(),includes(),map(),filter(),reduce,foreach(),find(),findIndex,include,some(),every()
12.Proxy属性代理

3.说说AMD、CMD、commonJS模块化规范的区别?

AMD(异步模块定义):
AMD作为一种模块化规范,有其对应的第三方库作为具体实现,最常用的就是RequireJS。也就是说,AMD规范的应用,需要借助RequireJS第三方库才能实现。
AMD规范是非同步加载模块,允许指定回调函数。

CMD(通用模块定义)
CMD是SeaJS 在推广过程中对模块定义的规范化产出。

CommonJS
根据CommonJS规范,一个单独的文件就是一个模块。加载模块使用require方法,该方法读取一个文件并执行,最后返回文件内部的exports对象。所以,定义一个模块就是写一个新的js文件,但是最后要将文件的内容exports出来。
CommonJS 加载模块是同步的,所以只有加载完成才能执行后面的操作

AMD和CMD区别:
对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible(尽可能的懒加载,也称为延迟加载,即在需要的时候才加载)
CMD 推崇依赖就近,AMD 推崇依赖前置
AMD 的 API 默认是一个当多个用,CMD 的 API 严格区分,推崇职责单
最明显的区别就是在模块定义时对依赖的处理不同:
AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块
CMD推崇就近依赖,只有在用到某个模块的时候再去require

4.什么是回调地狱

回调地狱:在回调函数中再嵌套回调函数的情况称为回调地狱(是实现代码顺序执行的一种操作方式)

(1)代码的可读性差、可维护性差
(2)代码的扩展性差解决回调地狱问题回调地狱的问题
嵌套层次很深,可读性差,难以维护.无法正常使用return 和throw.多个回调之间难以建立联系
回调地狱的解决办法:
(1)、Promise
(2)、async/await

5.说说你对同步和异步的理解?

同步,是所有的操作都做完,才返回给用户结果。即写完数据库之后,在相应用户,用户体验不好。

异步,不用等所有操作等做完,就相应用户请求。即先相应用户请求,然后慢慢去写数据库,用户体验较好。

同步:
同步的思想是:所有的操作都做完,才返回给用户。这样用户在线等待的时间太长,给用户一种卡死了的感觉(就是系统迁移中,点击了迁移,界面就不动了,但是程序还在执行,卡死了的感觉)。这种情况下,用户不能关闭界面,如果关闭了,即迁移程序就中断了。

异步:
将用户请求放入消息队列,并反馈给用户,系统迁移程序已经启动,你可以关闭浏览器了。然后程序再慢慢地去写入数据库去。这就是异步。但是用户没有卡死的感觉,会告诉你,你的请求系统已经响应了。你可以关闭界面了。

版本控制系列

1.说说package.json中版本号的规则?

第一部分主版本号,表示有一个不兼容上个版本的比较大的更改
第二部分次版本号,增加了新的功能
第三部分修订版本号,修复bug
第四部分日期版本号+希腊字母版本号

2.说说你对git rebase 和git merge的理解?区别?

rebase会将整个分支移动到另一个分支上,有效地整合了所有分支上的提交,主要的好处是历史记录更加清晰,是在原有提交的基础上将差异内容反映进去,消除了 git merge所需的不必要的合并提交

merge和rebasea都是合并历史记录,但是各自特性不同:merge通过merge合并分支会新增一个merge commit,然后将两个分支的历史联系起来其实是一种非破坏性的操作,对现有分支不会以任何方式被更改,但是会导致历史记录相对复杂rebaserebase会将整个分支移动到另一个分支上,有效地整合了所有分支上的提交主要的好处是历史记录更加清晰,是在原有提交的基础上将差异内容反映进去,消除了 git merge所需的不必要的合并提交

3.Git分支出现冲突,如何解决,提供正确思路?

查看本地分支 创建本地分支 切换分支 合并分支 删除分支 解决冲突

html篇 css篇

1.什么是垂直外边距合并?说说合并后的几种情况?

垂直外边距合并:外边距合并指的是,当两个垂直外边距相遇时,它们将形成一个外边距。
合并后的外边距的高度等于两个发生合并的外边距的高度中的较大者。
实际工作中,垂直外边距合并问题常见于第一个子元素的margin-top会顶开父元素与父元素相邻元素的间距,而且只在标准浏览器下(FirfFox、Chrome、Opera、Sarfi)产生问题,IE下反而表现良好。

合并后的几种情况:相邻块元素垂直外边距的合并
当上下相邻的两个块元素相遇时,如果上面的元素有下外边距margin-bottom,下面的元素有上外边距margin-top,则他们之间的垂直间距不是margin-bottom与margin-top之和,而是两者中的较大者。
这种现象被称为相邻块元素垂直外边距的合并(也称外边距塌陷)。

解决方案:
尽量只给一个盒子添加margin值。
嵌套块元素垂直外边距的合并
对于两个嵌套关系的块元素,如果父元素没有上内边距及边框,则父元素的上外边距会与子元素的上外边距发生合并,合并后的外边距为两者中的较大者,即使父元素的上外边距为0,也会发生合并。

解决方法:
父盒子可以加个边框
用overflow(加了这句话在浏览器中可以看到也是自动加了1px的边框)。
可以为父元素定义上内边距。

2.谈谈你是如何做移动端适配的?

1.px + viewport适配
2.rem布局 。CSS3媒体查询适配 。基于设计图的rem布局 。基于屏幕百分比的rem布局 。小程序的rpx布局
3.vw布局 。通过媒体查询的方式即CSS3的meida queries 。以天猫首页为代表的flex弹性布局 。以淘宝首页为代表的rem+viewport缩放 。rem方式

3.移动端1像素的解决方案?

1.媒体查询利用设备像素比缩放,设置小小数像素
2.设置border-image方案
3.background-image渐变实现
4.box-shadow
5.viewport+rem
6.transform: scale(0.5) 方案 - 推荐: 很灵活 7、媒体查询 + transfrom 对方案1的优化

4.弹性盒中的缩放机制是怎样的?

弹性盒中的项目设置flex-grow属性定义项目的放大比例,默认值为0,值越大,放大越厉害,且不支持负值;
而flex-shrink属性定义项目的缩小比例,默认值为1,数值越大,缩小越厉害,同样不支持负值;

5.说说设备像素、css像素、设备独立像素、dpr、ppi之间的区别?

无缩放情况下,1个CSS像素等于1个设备独立像素
设备像素由屏幕生产之后就不发生改变,而设备独立像素是一个虚拟单位会发生改变
PC端中,1个设备独立像素 = 1个设备像素 (在100%,未缩放的情况下)
在移动端中,标准屏幕(160ppi)下 1个设备独立像素 = 1个设备像素
设备像素比(dpr) = 设备像素 / 设备独立像素
每英寸像素(ppi),值越大,图像越清晰

6.如何使用css实现一个三角形?

1.使用border绘制三角形:利用了高度为零的容器以及透明的border实现
2.使用linear-gradient绘制
3.使用conic-gradient绘制
4.transform:rotate配合overflow:hidden绘制三角形
5.使用clip-path绘制

7.清除浮动的几种方式,各自的优缺点,推荐使用哪种?

方法一:额外标签法:
给谁清除浮动,就在其后额外添加一个空白标签,给其设置clear:both
优点:通俗易懂,书写方便
缺点:添加许多无意义的标签,结构化比较差
方法二:父元素添加overflow:hidden
通过触发BFC方式,实现清除浮动
优点:代码简洁
缺点:内容增多的时候容易造成不会自动换行导致内容被隐藏掉,无法显示要溢出的元素
方法三:使用after伪元素清除浮动
优点:符合闭合浮动思想,结构语义化正确
缺点:ie6-7不支持伪元素:after,使用zoom:1触发hasLayout
方法四:使用before和after双伪元素清除浮动
优点:代码更简洁
缺点:用zoom:1触发hasLayout
方法五:为父元素设置高度
优点:简单粗暴直接有效,清除了浮动带来的影响
缺点:需要手动添加高度,如果后面的高度发生变化,还要再次修改高度,给后期的维护带来麻烦

8.Css实现子盒子在父盒子上下左右居中,写出三种方式

利用position与margin结合

利用position与transform结合

利用flex布局

9.CSS3的新特性都有哪些?

1、圆角效果;
2、图形化边界;
3、块阴影与文字阴影;
4、使用RGBA实现透明效果;
5、渐变效果;
6、使用“@Font-Face”实现定制字体;
7、多背景图;
8、文字或图像的变形处理;
9、多栏布局;
10、媒体查询等。

10.Sass、LESS区别是什么?大家为什么要使用他们?

主要区别:sass和less主要区别在于实现方式:
less是基于JavaScript的在客户端处理 所以安装的时候用npm,sass是基于ruby所以在服务器处理。
less:Less 是一门 CSS 预处理语言,它扩展了 CSS 语言,增加了变量、Mixin、函数等特性,使 CSS 更易维护和扩展。
Less 可以运行在 Node 或浏览器端。

1、sass与less的安装
sass基于Ruby语言开发而成,因此安装sass前需要安装Ruby。
less 在服务器端最容易的安装方式就是通过 npm(node.js 的包管理器)。less 在客户端使用【.less】(LESS源文件),只需要在官网载一个javascript脚本文件主【less.js】,然后在HTML中引入即可。
2、变量
sass 是以 开头定义的变量,如: 开头定义的变量,如: 开头定义的变量,如:mainColor: #963;
less 是以@开头定义的变量,如 @mainColor: #963;
3、作用域
sass 没有全局变量,满足就近原则,但是实际中可以将需要定义的全局属性放在base.scss 文件中。注意变量名重复; less 中的作用域和其他程序语言中的作用域非常的相同,他首先会查找局部定义的变量,如果没有找到,会像冒泡一样,一级一级往下查找,直到根为止。

11.什么是重绘与回流?

重绘:当各种盒子的位置,大小以及其他属性,例如颜色,字体大小等都确定下来后,浏览器于是便把这些元素按照各自的特性绘制一遍,于是页面的内容出现了,这个过程称之为repaint

回流:对于dom结构中的各个元素都有自己的盒子模型,这些都需要浏览器根据各种样式来计算根据计算结果将元素放到它该出现的位置,这个过程称之为reflow

12.Css3中如何使用媒体查询?

1.用@media开头 注意@符号
2.mediatype 媒体类型 screen手机类型
3.关键字 and并且 not非 only特定(可以省略)
4.media feature 媒体特性 必须由小括号包裹

13.说说你对BFC的理解?

BFC(即块级格式化上下文),它是指一个独立的块级渲染区域,该区域拥有一套渲染规则来约束块级盒子的布局,且与区域外部无关。
注意:只有块级盒子参与。

14.说说你对盒子模型的理解?

当对一个文档进行布局(layout)的时候,浏览器的渲染引擎会根据标准之一的 CSS 基础框盒模型(CSS basic box model),将所有元素表示为一个个矩形的盒子(box)

一个盒子由四个部分组成:content、padding、border、margin
content,即实际内容,显示文本和图像

boreder,即边框,围绕元素内容的内边距的一条或多条线,由粗细、样式、颜色三部分组成

padding,即内边距,清除内容周围的区域,内边距是透明的,取值不能为负,受盒子的background属性影响

margin,即外边距,在元素外创建额外的空白,空白通常指不能放其他元素的区域

15说说你对弹性盒子布局的理解?

16.元素水平垂直居中的方法有哪些?

1.行内元素水平垂直居中的方法
1.1.水平居中:text-align:center
1.2.垂直居中
1.2.1单行文本的垂直居中:只需要让元素高度和行高相等
1.2.2多行文本的垂直居中:不固定高度的垂直居中:设置上下的padding值
2.块级元素水平垂直居中的方法
2.1.使用flex布局实现水平垂直居中
2.2.绝对定位配合margin的方式实现水平垂直居中
2.3.绝对定位配合translate的方式实现水平垂直居中

17.如果需要手动写动画,你认为最小时间间隔是多少,为什么?

建议最小时间间隔为0.1-0.3s

多数显示器默认频率是60Hz,即1秒刷新60次,所以理论上最小间隔为1/60*1000ms = 16.7ms

为了浏览器的渲染和用户体验的话,最小设置0.1-0.3s最佳

18.iframe有哪些缺点?

1.iframe会阻塞主页面的Onload事件;

⒉iframe和主页面共享链接池,而浏览器对相同城的链接有限制,所以会影响页面的并行加载;

⒊使用iframe之前需要考虑这两个缺点,如果需要使用iframe,最好是通过JavaScript;

⒋动态给iframe添加src属性值,这样可以可以绕开以上两个问题

⒌不利于seo

⒍代码复杂,无法一下被搜索引擎索引到

⒎iframe框架页面会增加服务器的http请求,对于大型网站不可取

⒏很多的移动设备无法完全显示框架,设备兼容性差

19.重绘和回流(重排)是什么,如何避免?

css
避免设置多层内联样式。
如果需要设置动画效果,最好将元素脱离正常的文档流。
避免使用CSS表达式(例如:calc())。
JavaScript
避免频繁操作样式,最好将样式列表定义为class并一次性更改class属性。
避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。
可以先为元素设置为不可见:display: none,操作结束后再把它显示出来。

20. 说说你对弹性盒子的理解?使用场景有哪些?常用属性?

  • 弹性盒子是 CSS3 的一种新的布局模式。

  • CSS3 弹性盒( Flexible Box 或 flexbox),是一种当页面需要适应不同的屏幕大小以及设备类型时确保元素拥有恰当的行为的布局方式。

    主轴(main axis)是沿着 flex 元素放置的方向延伸的轴(比如页面上的横向的行、纵向的列)。该轴的开始和结束被称为 main start 和 main end。
    交叉轴(cross axis)是垂直于 flex 元素放置方向的轴。该轴的开始和结束被称为 cross start 和 cross end。

属性

以下6个属性设置在容器上。
flex-direction //主轴的方向
flex-wrap //轴线,如何换行
flex-flow //flex-direction和flex-wrap属性的简写形式
justify-content //水平方向主轴上的对齐方式
align-items //垂直方向主轴上的对齐方式
align-content //多根(水平)轴线的对齐方式

21. 说说你对css预编译语言的理解?使用场景有哪些?

Css 作为一门标记性语言,语法相对简单,对使用者的要求较低,但同时也带来一些问题

需要书写大量看似没有逻辑的代码,不方便维护及扩展,不利于复用,尤其对于非前端开发工程师来讲,往往会因为缺少 Css 编写经验而很难写出组织良好且易于维护的 Css 代码

Css预处理器便是针对上述问题的解决方案

ts篇

1.ts中的类修饰符?

修饰成员的可访问性
public修饰符---- 公共的:类中成员的默认修饰符,代表是公共的,任何位置都可以访问类中的成员
private修饰符—私有的:类中的成员如果使用private来修饰,那么外部是无法访问这个成员数据的,当然子类中也是无法访问该成员数据的
protected修饰符—受保护的:类中的成员如果使用protected来修饰,那么外部是无法访问这个成员数据的,但是子类中是可以访问该成员数据的
修饰成员的可修改性 ----- readonly
readonly修饰符:对类中的属性成员进行修饰,修饰后,
1、该属性成员,就不能在外部被随意修改了
2、类中的普通方法中,无法修改readonly修饰的成员属性值
3、类中constructor中,可以在初始化时修改readonly修饰的成员属性值

2.TS中的泛型是什么?有什么作用

当我们定义一个变量不确定类型的时候有两种解决方式:
使用any:使用any定义时存在的问题:虽然 以 知道传入值的类型但是无法获取函数返回值的类型;另外也失去了ts类型保护的优势
使用泛型:泛型指的是在定义函数/接口/类型时,不预先指定具体的类型,而是在使用的时候在指定类型限制的一种特性。
泛型的用法
1.在函数中使用泛型:

function test <T> (arg:T):T{
  console.log(arg);
  return arg;
}
test<number>(111);// 返回值是number类型的 111
test<string | boolean>('hahaha')//返回值是string类型的 hahaha
test<string | boolean>(true);//返回值是布尔类型的 true

使用方式类似于函数传参,传什么数据类型,T就表示什么数据类型, 使用表示,T也可以换成任意字符串。
2.在接口中使用泛型

// 注意,这里写法是定义的方法哦
interface Search {
  <T,Y>(name:T,age:Y):T
}

let fn:Search = function <T, Y>(name: T, id:Y):T {
  console.log(name, id)
  return name;
}
fn('li',11);//编译器会自动识别传入的参数,将传入的参数的类型认为是泛型指定的类型

3.在类中使用泛型

class Animal<T> {
 name:T;
 constructor(name: T){
  this.name = name;
 }
 action<T>(say:T) {
   console.log(say)
 }
}
let cat = new Animal('cat');
cat.action('mimi')

3.说说你对TypeScript中泛型的理解及其应用场景?

一、是什么?
泛型程序设计是程序设计语言的一种风格或范式。
泛型允许我们在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型在typescript中,定义函数、接口或者类的时候,不预先定义好具体的类型,而在使用的时候在指定类型的一种特性。

二、使用方式
泛型通过<> 的形式进行表述,可以声明:

函数
接口

三、应用场景
通过上面初步的了解,后述在编写 typescript 的时候,定义函数,接口或者类的时候,不预先定义好具体的类型,而在使用的时候在指定类型的一种特性的时候,这种情况下就可以使用泛型。

4.说说你对TypeScript装饰器的理解及其应用场景?

装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上
是一种在不改变原类和使用继承的情况下,动态地扩展对象功能
同样的,本质也不是什么高大上的结构,就是一个普通的函数,@expression 的形式其实是Object.defineProperty的语法糖
expression 求值后必须也是一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入
三、应用场景
可以看到,使用装饰器存在两个显著的优点:
代码可读性变强了,装饰器命名相当于一个注释
在不改变原有代码情况下,对原来功能进行扩展
后面的使用场景中,借助装饰器的特性,除了提高可读性之后,针对已经存在的类,可以通过装饰器的特性,在不改变原有代码情况下,对原来功能进行扩展

5.type和interface的区别?

相同点:都可以用来定义对象或者函数的结构,而严谨的来说,type 是引用,而 interface是定义。
不同点:type 在声明类型别名之后实际上是一个赋值操作,它需要将别名与类型关联起来。也就是说类型别名不会创建出一种新的类型,它只是给已有类型命名并直接进行引用。interface是定义了一个接口类型。
type 能够表示非对象类型, 而 interface 则只能表示对象类型。
interface可以继承其他的接口、类等对象类型, type 不支持继承。

组件篇

shouldComponentUpdate如何做子组件性能优化?

为什么不能用数组下标来作为react组件中的key?

React中受控组件,如何实现受控组件双向数据绑定,具体实现?

React中,能否直接将 props 的值复制给 state,为什么

Context状态树的运行流程

React如何实现双向数据绑定?

为什么react元素有一个$$type属性?

React元素有一个type属性,这是React内部用来标识组件类型的属性,它与type属性不同。
元素的type属性是一个字符串,用于表示元素的类型,可以是原生DOM元素的标签名,也可以是自定义组件的类型,例如“div”、“span”、“MyComponent”等。
而type属性是React内部用来区分不同类型组件的属性,它是一个内部属性,用户通常不需要使用它。type属性的值是一个Symbol类型的值,它可以确保在不同的JavaScript环境中具有唯一性。
React使用type属性来进行组件类型比较和优化,例如在shouldComponentUpdate生命周期方法中,React会比较新旧组件的type属性,来确定是否需要进行更新操作。在一些高级应用场景下,用户也可以使用type属性来自定义组件的类型,以实现更高级的功能。
需要注意的是,由于$$type属性是React内部使用的属性,用户不应该直接使用它来判断组件类型或进行其他操作,而应该使用React提供的API来进行操作。

为什么不能直接使用this.state改变数据?

React中的Props类型校验和默认值的理解?

React中如何加点击事件,事件方法中this指向undefind如何解决?

React如何循环列表渲染,里面可以使用if吗?

React中getDerivedStateFromProps使用方式,怎么理解?

39.React组件之间如何通信,写出其具体实现?

父传子通信流程
在父组件中的子组件标签上绑定自定义属性,挂载传递的数据,子组件中props接受传递的数据,直接使用即可
子传父通信的流程
父组件中子组件标签上绑定一个属性,传递一个方法给子组件,子组件中通过props接受这个方法,直接调用,传递相应的参数即可
20.非父子组件通信
状态提升(中间人模式)
React中的状态提升概括来说,就是将多个组件需要共享的状态提升到它们最近的父组件,在父组件上改变这个状态然后通过props分发给子组件
context状态树传参

9.props和state相同点和不同点?render方法在哪些情况下会执行?

props:
props是一个从外部传进组件的参数,由于React具有单向数据流的特性,所以他的主要作用是从父组件向子组件中传递数据,它是不可改变的,如果想要改变它,只能通过外部组件传入新的props来重新渲染子组件,否则子组件的props和展示形式不会改变,props除了可以传字符串,数字,还可以传递对象,数组甚至是回调函数
state:
state主要作用是用于组件保存,控制及修改自己的状态,它只能在constructor中初始化,state是可以被改变的,state放改动的一些属性,比如点击选中,再点击取消,类似这种属性就放入带state中,注意:没有state的叫做无状态组件,多用props少用state,多写无状态组件,注意:修改state的值时,必须通过调用setState方法,当我们调用this.setState方法时,React会更新组件的数据状态,并且重新调用render方法

不同点:
1.props不可以在组件内部修改,但state可以在组件内部修改
2.可以从父组件修改自组件的props,而不能从父组件修改自组件的state

相同点:
1.props和state都是导出HTML的原始数据。
2.props和state都是确定性的,如果我们写的组件为同一props和state的组合生成了不同的输出,那木我们肯定在哪里做错了
3.props和state都会触发渲染更新
4.props和state都是纯JS对象(用typeof来判断,结果都是object)
5.可以从父组件得到初始值props和state的初始值

触发时机: 类组件调用用setState修改状态,从而执行render
函数组件通过useState的hook修改状态,函数通过useState这种形式更新数据,当数组的值不在发生变化就不会触发render

说说你对受控组件和非受控组件的理解?应用场景?

受控组件: 简单来讲,就是受我们控制的组件,组件的状态全程响应外部数据
非受控组件:非受控组件,简单来讲,就是不受我们控制的组件
一般情况是在初始化的时候接受外部数据,然后自己在内部存储其自身状态
当需要时,可以使用ref 查询 DOM
应用场景
大部分时候推荐使用受控组件来实现表单,因为在受控组件中,表单数据由React组件负责处理
如果选择非受控组件的话,控制能力较弱,表单数据就由DOM本身处理,但更加方便快捷,代码量少
1、受控组件使用场景:
一般用在需要动态设置其初始值的情况。例如:某些form表单信息编辑时,input表单元素需要初始显示服务器返回的某个值然后进行编辑。

2、非受控组件使用场景:
一般用于无任何动态初始值信息的情况。例如:form表单创建信息时,input表单元素都没有初始值,需要用户输入的情况。

React 路由组件和组件的区别?

写法不用:一般组件:

路由组件

存放位置不同:一般组件:components文件夹下

路由组件:pages文件夹下

接收到的props不同:

一般组件:写组件标签时传递了什么,组件中props就能接收到什么

路由组件:接收到三个固定的属性

现有一个父组件A,嵌套一个子组件B,请简述组件A,B组件生命周期的挂载阶段执行流程?

首先

父组件Constructor()创建实例的钩子函数

父组件GetDerivedStateFromProps()在props,state更新时执行

父组件Render()在挂载组件时执行

父组件的render执行到一般后发现还有子组件就会执行

子组件Constructor()创建实例的钩子函数

子组件GetDerivedStateFromProps()在props,state更新时执行

子组件Render()在挂载组件时执行

子组件ComponentDidMount()组件挂在完毕执行

当子组件的ComponentDidMount执行完毕后在执行父组件的ComponentDidMount()

渲染列表为什么要用key?

在 React 中,使用 key 属性来标识每一个列表项目是很重要的。当 React 渲染一个列表时,它会尝试重用已经渲染过的组件来提高性能。如果每个列表项目都没有唯一标识,React 就无法确定哪些组件是新的,哪些是已经存在的,从而导致错误。从而导致错误。使用 key 来给每一个列表项目赋予唯一标识,可以让 React 正确地重用和重新渲染组件。

react为什么不用数组下标来绑定key

key值虽然能够在每个时刻都唯一,但是变来变去,那么就会误导react做出错误判断,甚至导致错误的渲染结果

react中key值的作用:

在一般情况下key值是当我们在做子元素遍历的时候需要使用的。因为我们如果要进行数据的更新,就需要进行虚拟dom的对比,而key值就是每个元素节点对应的唯一值。这个时候就需要对比子元素的key值是否有匹配项,如果有的情况下则会进行数据的更新;如果key值没有匹配项,那么这个节点就需要进行删除和重新创建。
因此我们在遍历的时候千万不要用 index下标 或者 时间戳、随机数 等进行key值的赋值。这样会造成元素的移除重新创建浪费性能。

函数组件和class组件的区别

  1. 语法上的区别:
    函数式组件是一个纯函数,它是需要接受props参数并且返回一个React元素就可以了。类组件是需要继承React.Component的,而且class组件需要创建render并且返回React元素,语法上来讲更复杂
  2. 调用方式
    函数式组件可以直接调用,返回一个新的React元素;类组件在调用时是需要创建一个实例,然后通过调用实例里的render方法来返回一个React元素
  3. 状态管理
    函数式组件没有状态管理,类组件有状态管理。
  4. 使用场景
    类组件没有具体的要求。函数式组件一般是用在大型项目中来分割大组件(函数式组件不用创建实例,所以更高效),一般情况下能用函数式组件就不用类组件,提升效率

其他区别:
class组件有生命周期,函数组件无生命周期
class组件是由this对象,函数组件没有this对象

setSate是同步还是异步

setState是同步还是异步,不是绝对的,根据不同情况,是不一样的,如下:
1、同步的情况
1)setState在setTimeout中是同步的。
2)setState在原生事件中是同步的,即通过dom绑定事件的方式实现。
2、异步的情况
1)setstate在合成事件中是异步的,这里说的异步实际上是react的批量· 更新,达到了提升性能的目的
2)setstate在生命周期中是异步的

React中检测props数据类型,都有哪些prop类型

optionalArray: PropTypes.array, //数组
optionalBool: PropTypes.bool, //布尔
optionalFunc: PropTypes.func,//函数
optionalNumber: PropTypes.number,//数值
optionalObject: PropTypes.object, //对象
optionalString: PropTypes.string, //字符串
optionalSymbol: PropTypes.symbol,//symbol

React的组件的通信方式?

父传子
1.父组件提供要传递的state数据
2.给子组件标签添加属性,值为 state 中的数据
3.子组件中通过 props 接收父组件中传递的数据

子传父
1.父组件提供一个回调函数(用于接收数据)
2.将该函数作为属性的值,传递给子组件
3.子组件通过 props 调用回调函数
4.将子组件的数据作为参数传递给回调函数

react兄弟组件通信的流程?
将共享状态提升到最近的公共父组件中,由公共父组件管理这个状态
思想:状态提升
公共父组件职责:
提供共享状态
提供操作共享状态的方法
要通讯的子组件只需通过 props 接收状态或操作状态的方法

什么是react组件

react组件:我们平时所看到的完整页面,实际上他是由很多组件组合而成,比如我们看到的搜索框、点击按钮、历史记录等都是一个个的组件,正是因为有了这些组件我们才能够有了如此丰富多彩的页面功能。

react组件的特点

1)可复用:可重复使用在页面中可能出现多次。

2)独立性:可保证每个组件之间不会相互影响。

3)可组合性:可以通过组合多个组件去完成不同的功能。

组件创建方式

组件的创建方式主要分为两种:
1)使用js函数或箭头函数创建组件:函数组件又叫无状态组件:负责数据展示。
a)函数名必须以大写字母开头(主要是为了区分组件和普通元素)
b)必须有返回值

渲染函数组件:用函数名作为组件标签名。(具体使用ReactDOM.render(<函数名 />,渲染位置)的方式渲染组件)。

其中渲染位置一般可通过document.获取。
如:获取id标签,表示为document.ElementById(“”);

2)使用类方法创建组件(ES6的class创建的组件):类组件又叫有状态组件:负责更新UI,让数据动起来
a)类名必须以大写字母开头
b)类组件应该继承ReactComponent父类,可以使用父类中提供的方法或属性(其中继 承关键字为extends)
c)类组件必须提供render()方法
d)render()方法必须有返回值,表示该组件的结构

既然函数组件(无状态组件)、类组件(有状态组件),那么其中的状态到底代表什么呢?其实状态state就是我们所说的数据。

如何组织组件

把每个组件放到单独的js文件中,便于我们使用。
1)创建js文件
2)在文件中导入react(import React from ’react‘)
3)创建组件(通过函数或类方法)
4)导出该组件(export default 组件名)
5)在你要使用的js文件中导入组件(import 组件名 from ‘./js文件名’)
6)渲染导入的组件(依然依据上述渲染方式)

组件中的state和setState

state :状态就是我们所说的数据。
setState():顾名思义就是修改状态,即修改state的,所以当我们想要修改数据state时,我们就要使用this.setState函数修改,为什么我们不能通过state直接进行修改呢,原因就是啊state是私有数据,他不能直接修改要通过函数进行修改

作用:修改状态(state)、更新UI。
思想:数据驱动视图

说说React中setState和replaceState的区别?

setState 是修改其中的部分状态,相当于 Object. assign,只是覆盖,不会减少原来的状态;
replaceState 是完全替换原来的状态,相当于赋值,将原来的 state 替换为另一个对象,如果新状态属性减少,那么 state 中就没有这个状态了。

解决this指向的问题

1)使用箭头函数
箭头函数特点:自身不绑定this ,箭头函数this指向外部环境
2)通过ES5中的bind()方法,将事件处理程序中的this与组件实例绑定到一起
Function.prototype.bind()方法
constructor(){
super() ///语法要求
this.onIncrement = this.onIncrement.bind(this)
绑定this
}
render(){
return (
///此时的this指向就是事件绑定的this
<button onClick = {this.onIncrement}
)
}

3)class实例方法(推荐使用)

即箭头函数形式的class方法。

Centext状态树的执行流程?

第一步:先创建一个全局共享的js文件createContext.js
第二步:创建一个contextParent.jsx父组件
第三步:创建contextParent父组件的子组件ContextChild.jsx
第四步:创建contextParent父组件的子组件ContextChild2.jsx

在父组件中我们通过createContext() 创建一个空对象,在父组件的最外层我们使用Provider包裹数据,通过value绑定要传递的对象数据。
在嵌套的子组件中,我们有两种方式获取数据:
(1) 我们可以使用Customer标签,在标签中绑定一个箭头函数,函数的形参context就是value传递的数据
(2). class组件中我们可以定义static contextType=context对象,组件中直接使用this.context获取数据。

说说你对高阶组件的理解?应用场景有那些?

高阶组件:接受一个或多个函数作为输入,输出一个函数,在react中,高阶组件接收一个或多个组件作为参数并且返回一个组件,本质也就是一个函数,并不是一个组件

应用场景:高阶组件能够提高代码的复用性和灵活性,在实际应用中,常常用于与核心业务无关但又在多个模块使用的功能,如权限控制,日志记录,数据校验,异常处理,统计上报等

什么是单项数据流和状态提升?

单向数据流
数据主要从父节点传到子节点,如果父级的某个props改变了,react会重新渲染所有子节点,若子组件想改改变父组件中的值,用event事件,子组件是不能够在自己的组件中去修改父组件的值,所以子组件中可以通过调用父组件的方法在父组件中方法中修改父组件的值
状态提升
通常,state都是首先添加到需要渲染数据的组件中去,然后,如果其他组件也需要这个state,那么你可以将它提升至这些组件的最近的公共父组件中

生命周期

说说你对useEffect的理解,可以模拟哪些生命周期?

使用钩子函数useEffect可以实现组件的副作用
useEffect(希望执行的动作,[组件状态的列表])
第二个参数是用来处理useEffect的调用的时机,是一个数组,数组内是组件状态的列表
1、useEffect模拟componentDidMount
2、useEffect模拟componentDidUpdate
3、useEffect模拟componentWillUnmount

React 生命周期中 getDerivedStateFromProps怎么使用,怎么理解?

函数组件如何使用副作用,挂载,更新,卸载的钩子对应生命周期?

React在哪个生命周期请求接口,哪个清除定时器?

请叙述react生命周期函数及含义?

React网络请求到底该放在哪个生命周期中?

总的来说,对于异步请求,最好放在componentDidMount中去操作,对于同步的状态改变,可以放在componentWillMount中,开发实际应用中用的比较少。

如果认为在componentWillMount里发起请求能提早获得结果,这种想法其实是错误的,通常componentWillMount比componentDidMount早不了多少微秒,网络上任何一点延迟,这一点差异都可忽略不计

看看react的生命周期:

constructor() ----> componentWillMount() ----> render() ----> componentDidMount()

上面这些方法的调用是有次序的,由上而下依次调用。

constructor被调用是在组件准备要挂载的最开始,此时组件尚未挂载到网页上。

componentWillMount方法的调用在constructor之后,在render之前,在这方法里的代码调用setState方法不会触发重新render,所以它一般不会用来做加载数据使用

componentDidMount方法中的代码,是在组件已经完全挂载到网页上才会调用被执行,所以可以保证数据的加载。此外,在这方法中调用setState方法,会触发重新渲染。所以,官方设计这个方法就是用来加载外部数据用的,或处理其他的副作用代码。与组件上的数据无关的加载,也可以在constructor里做,但constructor是做组件state初绐化工作,并不是做加载数据这工作的,constructor里也不能setState,还有加载的时间太长或者出错,页面就无法加载出来。所以有副作用的代码都会集中在componentDidMount方法里。

生命周期调用方法的顺序是什么,挂在和更新阶段都是什么时候触发?

组件的生命周期可分成三个状态:

Mounting(挂载):已插入真实 DOM
Updating(更新):正在被重新渲染
Unmounting(卸载):已移出真实 DOM
挂载
Constructor->render()->componentDidMount

当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:

constructor(): 在 React 组件挂载之前,会调用它的构造函数。

getDerivedStateFromProps(): 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。

render(): render() 方法是 class 组件中唯一必须实现的方法。

componentDidMount(): 在组件挂载后(插入 DOM 树中)立即调用。

render() 方法是 class 组件中唯一必须实现的方法,其他方法可以根据自己的需要来实现。

更新
执行时机:setState(), forceUpdate(), 组件接收到新的props

Render()->componentDidUpdate()

每当组件的 state 或 props 发生变化时或者forceUpdate(),组件就会更新。

当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:

getDerivedStateFromProps(): 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。根据 shouldComponentUpdate() 的返回值,判断 React 组件的输出是否受当前 state 或 props 更改的影响。

shouldComponentUpdate():当 props 或 state 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。

render(): render() 方法是 class 组件中唯一必须实现的方法。

getSnapshotBeforeUpdate(): 在最近一次渲染输出(提交到 DOM 节点)之前调用。

componentDidUpdate(): 在更新后会被立即调用。

render() 方法是 class 组件中唯一必须实现的方法,其他方法可以根据自己的需要来实现。

卸载

当组件从 DOM 中移除时会调用如下方法:

componentWillUnmount(): 在组件卸载及销毁之前直接调用。

React生命周期旧,父组件-子组件的生命周期装载阶段的执行顺序?

ComponentWillUpdate可以直接修改state的值吗?

不行,这样会导致无限循环报错
在react中直接修改state,render函数不会重新执行渲染,应使用setSate方法进行修改

虚拟DOM

为什么虚拟dom能够提升性能?

虚拟dom相当于在js和真实的dom中间加了一个缓存,利用dom的diff算法避免了没有必要的dom操作,从而提高了性能

React虚拟dom的两种创建方式?

第一种:使用js的方式创建
需要调用react,createElement()的方式进行创建
第二种:使用jsx的方式创建

什么是虚拟DOM,为什么虚拟DOM能够提升性能?

虚拟dom其实就是一个javascript对象,通过这个javascript对象来描述真实dom

说说react diff的原理是什么?

react中diff算法主要遵循三个层级的策略:
tree层级:dom节点跨层级的操作不做优化,只会对相同层级的节点进行比较

component层级:如果是同一个类的组件,则会继续往下diff运算,如果不是一个类的组件,那么直接删除这个组件下的所有子节点,创建新的

element层级:对于比较同一层级的节点们,每个节点在对应的层级用唯一的key作为标识提供了 3 种节点操作,分别为 INSERT_MARKUP(插入)、MOVE_EXISTING (移动)和 REMOVE_NODE (删除)

虚拟dom的好处

减少dom操作,虚拟dom可以将多次dom操作合并为一次操作,
研发效率的问题:虚拟dom的出现,为数据驱动视图这一思想提供了高度可用的载体,使得前端开发能够基于函数式ui的编程方式高效的声明式编程
跨平台的问题:虚拟dom是对真实渲染内容的一层抽象,同一套虚拟dom,可以对接不同平台的渲染逻辑,从而实现“一次编码,多端运行”

虚拟dom和真实dom的区别?

真实dom在浏览器通过dom.api操作的,复杂的对象

虚拟dom可以通过this.$slots.default查看

真实的dom是一个对象,它的属性非常多,在浏览器中做dom操作,会比较消耗性能

虚拟dom是一个对象,它的属性相比较于真实的dom就比较少,用少量的属性描述一个dom,无法在浏览器中直接显示

说说Real DOM和Virtual DOM的区别?优缺点?

两者的区别如下:

虚拟 DOM 不会进行排版与重绘操作,而真实 DOM 会频繁重排与重绘

虚拟 DOM 的总损耗是“虚拟 DOM 增删改+真实 DOM 差异增删改+排版与重绘”,真实 DOM 的总损耗是“真实 DOM 完全增删改+排版与重绘”

真实 DOM 的优势:
易用
缺点:

效率低,解析速度慢,内存占用量过高

性能差:频繁操作真实 DOM,易于导致重绘与回流

使用虚拟 DOM 的优势如下:

简单方便:如果使用手动操作真实 DOM 来完成页面,繁琐又容易出错,在大规模应用下维护起来也很困难

性能方面:使用 Virtual DOM,能够有效避免真实 DOM 数频繁更新,减少多次引起重绘与回流,提高性能

跨平台:React 借助虚拟 DOM,带来了跨平台的能力,一套代码多端运行

缺点:

在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化

首次渲染大量 DOM 时,由于多了一层虚拟 DOM 的计算,速度比正常稍慢

虚拟dom一定比真实dom快吗,为什么?

虚拟dom并不是比原生dom要快,更确切的说,虚拟dom是比操作不当的原生dom快

虚拟DOM不一定会比操作原生DOM更快。

虚拟DOM的优势在于节点进行改动的时候尽量减少开销

React从来没说过虚拟DOM会比原生更快。

框架的本质是提升开发效率,让我们的注意力更集中于数据

路由

React 路由组件和组件的区别?

Link和NavLink的区别?

说说你对React-router的理解?V5和V6的区别?

V5中Switch换成Routes标签,
V5中exact属性没有了,V6默认是精准匹配
V5中的component属性,V6用的element,element属性是组件标签
V6中重定向导航组件换成Navigate
V6中路由嵌套直接采用组件包裹的方式,可以不适用path绝对路径,
V6中的 相当于vue中router-view
获取参数和Hooks方法的变化
props获取V6中props值没有参数数据,必须采用Hooks的路由获取数据。
withRouter在V6版本中也被去掉了。

react-router的实现原理是什么?

React Router是一个基于React的路由库,它可以实现单页应用的路由功能,使用它可以实现页面的切换,而不需要重新加载整个页面,从而提高用户的体验。
React Router的原理很简单,它利用了浏览器的History API,在不刷新页面的情况下,改变url,从而达到在不同的页面之间切换的效果。当用户在浏览器中输入一个url,React Router会通过浏览器的HistoryAPI来改变url,从而跳转到新的页面
React Router还提供了一些特性,例如路由组件,可以将路由拆分成独立的组件,从而更加方便地管理路由,React Router还提供了参数,查询字符串,错误处理等功能,可以更方便地控制路由,从而提供更好的体验

React Router的实现也很简单,它使用BrowerRouter来实现路由功能,BrowerRouter会根据浏览器url的变化来触发路由,从而进行相应的操作

总的来说,React Router是一个非常有用的库,它可以让我们更轻松地实现单页应用的路由功能,而不需要刷新页面,从而提高用户体验,它的实现也很简单,只需要利用浏览器的History API,就可以实现页面切换的功能

react的路由模式有几种?

React路由有两种模式:分别是

1、hash模式,该模式会在路径前加入“#”号成为一个哈希值;

2、history模式,该模式允许操作浏览器的曾经在标签页或者框架里访问的会话历史记录。

react路由传递参数的方式?

动态路由传参:使用props.match.params.参数名
query传参:使用query:{参数名:参数值}接收,
this.props.location.query.参数名
state传参:state:{参数名:参数值}
this.props.location.state.参数名

react的路由跳转方式有哪些?

使用react-router中的link实现页面跳转

使用push进行页面跳转

使用routerComponentProps中的history进行页面的回退

react如何配置路由嵌套?

在路由配置里加上children属性来设置子路由,子路由的路径不需要加/,然后在主路由中协商outlet标签,用作二级路由的出口,必须设置,否则二级路由的页面无法展示

React中路由跳转传参方式,编程式跳转传参和组件跳转传参分别怎么传值?

编程式:通过path进行传递参数
传递一个对象通过state进行传递参数

withRouter的作用?

withRouter的作用:把不是通过路由切换过来的组件,将react-router的history、location和match三个对象传入到props对象上;
默认情况下必须是经过路由匹配渲染的组件才存在this.props,才拥有路由参数,才能使用编程式导航的写法,执行this.props.history.push(‘/detail’)跳转到对应路由的页面
然而不是所有组件都直接与路由相连(通过路由跳转到此组件)的,当这些组件需要路由参数时,使用withRouter就可以给此组件传入路由参数,此时就可以使用this.props

React中路由模式BrowserRoute和HashRouter的区别,最少说出三点不同?

(1)底层原理不一样:BrowserRoute使用的是H5的history API,HashRouter使用的是URL的哈希值

(2)path表现形式不一样:BrowserRoute的路径中没有#,HashRouter的路径包含#

(3)刷新后对路由state参数的影响:BrowserRoute没有任何影响,因为state保存在history对象中,HashRouter刷新后会导致路由state参数的丢失

懒加载

react中图片懒加载如何实现?

安装ReactLazyLoad Image组件

引用组件

声明图片

添加图片占位符

添加模糊图

懒加载节省数据和带宽

懒加载减少cdn花销

懒加载提升SEO

Hooks

常用的 React Hooks 有哪些,最少5个,简单介绍用法?

useState():状态钩子,纯函数组件没有状态,useState()用于设置和使用组件的状态属性

useEffect():副作用钩子,可以实现特定的功能,如异步请求

useCallback():记忆函数,可以防止因为组件重新渲染,导致方法被重新创建,起到缓存作用

useMemo():记忆组件,useCallback()的功能可以由useMemo所替代,返回一个记忆函数

useRef():保存引用值,useRef()等价于类组件中的react.createRef()

useContext():共享状态钩子,作用屎进行状态分发,避免了使用props进行数据的传递

useReducer():action钩子,在使用react的过程中,如果遇到状态管理,一般会用到redux,而react本身不提供状态管理的,而useReducer()提供了状态管理

React.memo() 和 useMemo() 的用法是什么,有哪些区别?

react.memo()是一个高阶组件,可以使用它来包装不想重新渲染的组件,除非其中的props发生变化

useMemo()是一个react hook,可以使用它在组件中包装函数,可以使用它来确保该函数中的值仅在其依赖项之一发生变化时才重新计算

useMemo,memo, useCallback如何提升性能呢?

react中hooks是什么作用?

React hooks:就是用函数的形式代替换来的继承类的形式,并且使用预函数的形式管理state,有hooks可以不再使用类的形式定义组件了

Hooks优点:
告别难以理解的class(this和生命周期的痛点)
解决业务逻辑难以拆分的问题
使状态逻辑复用变得简单可行
函数组件从设计思想上来看更加契合react的理念

React hooks常用的四个钩子

UseState(),useContext(),useEffect(),useReducer()

http篇

1.说说TCP为什么需要三次握手和四次握手?

在进行了三次握手后在服务器和客户端清晰的知道发送的对象是谁。
为了安全,在我们进行大量数据进行传输时会有恶意拦截的操作。
四次挥手是为了防止客户端是否还有未发送完的数据。
采用三次握手是为了防止失效的连接请求报文段突然又传送到主机B,因而产生错误
主要作用就是为了确认双方的接收能力和发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备
因为通信不可能100%可靠,而上面的三次握手已经做好了通信的准备工作,再增加握手,并不能显著提高可靠性,而且也没有必要。

2.说说你对webSocket的理解?

webSocket是一种网络传输协议,用于OSI模型的应用层,可以在单个ICP连接上进行全双工通信,能更好的节省服务器资源
客户端和服务器只需要一次握手,就可以创建持久性的连接,进行双向数据的传输

3.短轮询、长轮询和 WebSocket 间的区别?

短轮询:短轮询是一种客户端与服务器之间的通讯方式,客户端定期向服务器发送请求,以检查是否有新消息。如果没有新消息,服务器会返回一个空响应。这种方法的缺点是客户端发送的请求频率较高,这可能导致网络拥塞和服务器负载过高。

长轮询:长轮询是一种改进的轮询方式,其中客户端发送一个请求并保持连接打开,直到服务器有新消息可用或连接超时。这种方法减少了不必要的请求,但仍然需要发送大量的 HTTP 请求。

SSE:SSE(Server-Sent Events)是一种单向通信协议,其中服务器可以将消息推送到客户端。与轮询不同,客户端只需发送一个请求,服务器可以随时发送新消息。这种方法可以减少网络流量和服务器负载。

WebSocket:WebSocket 是一种双向通信协议,它允许服务器和客户端在连接打开的情况下实时通信。WebSocket 可以减少网络流量和服务器负载,因为它不需要客户端发送大量的 HTTP 请求来获取新消息。

4.CDN的特点及意义?

CDN的作用:实际上,内容分发布网络(CDN)是一种新型的网络构建方式,它是为能在传统的IP网发布宽带丰富媒体而特别优化的网络覆盖层;而从广义的角度,CDN代表了一种基于质量与秩序的网络服务模式

CDN 是构建在数据网络上的一种分布式的内容分发网。
CDN的作用:是采用流媒体服务器集群技术,克服单机系统输出带宽及并发能力不足的缺点,可极大提升系统支持的并发流数目,减少或避免单点失效带来的不良影响

2.说说你对koa中洋葱模型的理解?

Koa的洋葱模型是以next()函数为分割点,先由外到内执行Request的逻辑,然后再由内到外执行Response的逻辑,这里的request的逻辑,我们可以理解为是next之前的内容,response的逻辑是next函数之后的内容,也可以说每一个中间件都有两次处理时机。洋葱模型的核心原理主要是借助compose方法

5.谈谈你对immutable.js的理解?

immutable不可改变的,在计算机中,即指一旦创建,就不能再被更改的数据
对immutable对象的任何修改或添加删除操作都会返回一个新的immutable对象

immutable实现原理是Persistent Data Structure (持久化数据结构):用一种数据结构来保存数据
当数据被修改时,会返回一个对象,但是新的对象会尽可能的利用之前的数据结构而不会对内存造成浪费,也就是使用旧数据创建新数据时,要保证旧数据同时可用并且不变,同时为了避免deepCopy把所有节点都复制一遍带来的性能损耗,immutable使用
Structural Sharing(数据共享) 如果对象树中一个及诶大那发生变化,只修改这个节点和受它影响的父节点,其他节点则进行共享

nodejs

39.说说你对nodejs 中间件的理解,如何封装一个node 中间件?

中间件(Middleware)是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的

在NodeJS中,中间件主要是指封装所有Http请求细节处理的方法。一次Http请求通常包含很多工作,如记录日志、ip过滤、查询字符串、请求体解析、Cookie处理、权限验证、参数验证、异常处理等,但对于Web应用而言,并不希望接触到这么多细节性的处理,因此引入中间件来简化和隔离这些基础设施与业务逻辑之间的细节,让开发者能够关注在业务的开发上,以达到提升开发效率的目的。

中间件的行为比较类似Java中过滤器的工作原理,就是在进入具体的业务处理之前,先让过滤器处理。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值