关于react.js:京东前端react面试题及答案

38次阅读

共计 10409 个字符,预计需要花费 27 分钟才能阅读完成。

useEffect 与 useLayoutEffect 的区别

(1)共同点

  • 使用成果: useEffect 与 useLayoutEffect 两者都是用于解决副作用,这些副作用包含扭转 DOM、设置订阅、操作定时器等。在函数组件外部操作副作用是不被容许的,所以须要应用这两个函数去解决。
  • 应用形式: useEffect 与 useLayoutEffect 两者底层的函数签名是完全一致的,都是调用的 mountEffectImpl 办法,在应用上也没什么差别,根本能够间接替换。

(2)不同点

  • 应用场景: useEffect 在 React 的渲染过程中是被异步调用的,用于绝大多数场景;而 useLayoutEffect 会在所有的 DOM 变更之后同步调用,次要用于解决 DOM 操作、调整款式、防止页面闪动等问题。也正因为是同步解决,所以须要防止在 useLayoutEffect 做计算量较大的耗时工作从而造成阻塞。
  • 应用成果: useEffect 是依照程序执行代码的,扭转屏幕像素之后执行(先渲染,后扭转 DOM),当扭转屏幕内容时可能会产生闪动;useLayoutEffect 是扭转屏幕像素之前就执行了(会推延页面显示的事件,先扭转 DOM 后渲染),不会产生闪动。useLayoutEffect 总是比 useEffect 先执行。

在将来的趋势上,两个 API 是会长期共存的,临时没有删减合并的打算,须要开发者依据场景去自行抉择。React 团队的倡议十分实用,如果切实分不清,先用 useEffect,个别问题不大;如果页面有异样,再间接替换为 useLayoutEffect 即可。

react 版本差别

react16.8 hooks

React 16 之后有三个生命周期被废除(但并未删除)

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

官网打算在 17 版本齐全删除这三个函数,只保留 UNSAVE_前缀的三个函数,目标是为了向下兼容,

react 16.4 新增

getSnapshotBeforeUpdate

getDerivedStateFromProps

对于废除的生命周期函数,官网会采纳逐渐迁徙的形式来实现版本的迁徙:

16.3:为不平安的生命周期引入别名,UNSAFE_componentWillMount、UNSAFE_componentWillReceiveProps 和 UNSAFE_componentWillUpdate。(旧的生命周期名称和新的别名都能够在此版本中应用。)

将来 16.x 版本:为 componentWillMount、componentWillReceiveProps 和 componentWillUpdate 启用废除告警。(旧的生命周期名称和新的别名都将在这个版本中工作,然而旧的名称在开发模式下会产生一个正告。)

17.0:删除 componentWillMount、componentWillReceiveProps 和 componentWillUpdate。(在此版本之后,只有新的“UNSAFE_”生命周期名称能够应用。)。

虚构 DOM 的引入与间接操作原生 DOM 相比,哪一个效率更高,为什么

虚构 DOM 绝对原生的 DOM 不肯定是效率更高,如果只批改一个按钮的文案,那么虚构 DOM 的操作无论如何都不可能比实在的 DOM 操作更快。在首次渲染大量 DOM 时,因为多了一层虚构 DOM 的计算,虚构 DOM 也会比 innerHTML 插入慢。它能保障性能上限,在实在 DOM 操作的时候进行针对性的优化时,还是更快的。所以要依据具体的场景进行探讨。

在整个 DOM 操作的演化过程中,其实主要矛盾并不在于性能,而在于开发者写得爽不爽,在于研发体验 / 研发效率。虚构 DOM 不是别的,正是前端开发们为了谋求更好的研发体验和研发效率而发明进去的高阶产物。虚构 DOM 并不一定会带来更好的性能,React 官网也素来没有把虚构 DOM 作为性能层面的卖点对外输入过。** 虚构 DOM 的优越之处在于,它可能在提供更爽、更高效的研发模式(也就是函数式的 UI 编程形式)的同时,依然放弃一个还不错的性能。

React Hooks 在平时开发中须要留神的问题和起因

(1)不要在循环,条件或嵌套函数中调用 Hook,必须始终在 React 函数的顶层应用 Hook

这是因为 React 须要利用调用程序来正确更新相应的状态,以及调用相应的钩子函数。一旦在循环或条件分支语句中调用 Hook,就容易导致调用程序的不一致性,从而产生难以预料到的结果。

(2)应用 useState 时候,应用 push,pop,splice 等间接更改数组对象的坑

应用 push 间接更改数组无奈获取到新值,应该采纳析构形式,然而在 class 外面不会有这个问题。代码示例:

function Indicatorfilter() {let [num,setNums] = useState([0,1,2,3])
  const test = () => {
    // 这里坑是间接采纳 push 去更新 num
    // setNums(num)是无奈更新 num 的
    // 必须应用 num = [...num ,1]
    num.push(1)
    // num = [...num ,1]
    setNums(num)
  }
return (
    <div className='filter'>
      <div onClick={test}> 测试 </div>
        <div>
          {num.map((item,index) => (<div key={index}>{item}</div>
          ))}      </div>
    </div>
  )
}

class Indicatorfilter extends React.Component<any,any>{constructor(props:any){super(props)
      this.state = {nums:[1,2,3]
      }
      this.test = this.test.bind(this)
  }

  test(){
      // class 采纳同样的形式是没有问题的
      this.state.nums.push(1)
      this.setState({nums: this.state.nums})
  }

  render(){let {nums} = this.state
      return(
          <div>
              <div onClick={this.test}> 测试 </div>
                  <div>
                      {nums.map((item:any,index:number) => (<div key={index}>{item}</div>
                      ))}                  </div>
          </div>

      )
  }
}

(3)useState 设置状态的时候,只有第一次失效,前期须要更新状态,必须通过 useEffect

TableDeail 是一个公共组件,在调用它的父组件外面,咱们通过 set 扭转 columns 的值,认为传递给 TableDeail 的 columns 是最新的值,所以 tabColumn 每次也是最新的值,然而理论 tabColumn 是最开始的值,不会随着 columns 的更新而更新:

const TableDeail = ({columns,}:TableData) => {const [tabColumn, setTabColumn] = useState(columns) 
}

// 正确的做法是通过 useEffect 扭转这个值
const TableDeail = ({columns,}:TableData) => {const [tabColumn, setTabColumn] = useState(columns) 
    useEffect(() =>{setTabColumn(columns)},[columns])
}

(4)善用 useCallback

父组件传递给子组件事件句柄时,如果咱们没有任何参数变动可能会选用 useMemo。然而每一次父组件渲染子组件即便没变动也会跟着渲染一次。

(5)不要滥用 useContext

能够应用基于 useContext 封装的状态管理工具。

setState 之后 产生了什么?

  • (1)代码中调用 setState 函数之后,React 会将传入的参数对象与组件以后的状态合并,而后触发所谓的和谐过程(Reconciliation)。
  • (2)通过和谐过程,React 会以绝对高效的形式依据新的状态构建 React 元素树并且着手从新渲染整个 UI 界面;
  • (3)在 React 失去元素树之后,React 会主动计算出新的树与老树的节点差别,而后依据差别对界面进行最小化重渲染;
  • (4)在差别计算算法中,React 可能绝对准确地晓得哪些地位产生了扭转以及应该如何扭转,这就保障了按需更新,而不是全副从新渲染。

setState 的调用会引起 React 的更新生命周期的 4 个函数执行。

shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate

说说你用 react 有什么坑点?

1. JSX 做表达式判断时候,须要强转为 boolean 类型

如果不应用 !!b 进行强转数据类型,会在页面外面输入 0

render() {
  const b = 0;
  return <div>
    {!!b && <div> 这是一段文本 </div>}
  </div>
}

2. 尽量不要在 componentWillReviceProps 里应用 setState,如果肯定要应用,那么须要判断完结条件,不然会呈现有限重渲染,导致页面解体

3. 给组件增加 ref 时候,尽量不要应用匿名函数,因为当组件更新的时候,匿名函数会被当做新的 prop 解决,让 ref 属性承受到新函数的时候,react 外部会先清空 ref,也就是会以 null 为回调参数先执行一次 ref 这个 props,而后在以该组件的实例执行一次 ref,所以用匿名函数做 ref 的时候,有的时候去 ref 赋值后的属性会取到 null

4. 遍历子节点的时候,不要用 index 作为组件的 key 进行传入

参考 前端进阶面试题具体解答

React 高阶组件、Render props、hooks 有什么区别,为什么要一直迭代

这三者是目前 react 解决代码复用的次要形式:

  • 高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 本身不是 React API 的一部分,它是一种基于 React 的组合个性而造成的设计模式。具体而言,高阶组件是参数为组件,返回值为新组件的函数。
  • render props 是指一种在 React 组件之间应用一个值为函数的 prop 共享代码的简略技术,更具体的说,render prop 是一个用于告知组件须要渲染什么内容的函数 prop。
  • 通常,render props 和高阶组件只渲染一个子节点。让 Hook 来服务这个应用场景更加简略。这两种模式仍有用武之地,(例如,一个虚构滚动条组件或者会有一个 renderltem 属性,或是一个可见的容器组件或者会有它本人的 DOM 构造)。但在大部分场景下,Hook 足够了,并且可能帮忙缩小嵌套。

(1)HOC 官网解释∶

高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 本身不是 React API 的一部分,它是一种基于 React 的组合个性而造成的设计模式。

简言之,HOC 是一种组件的设计模式,HOC 承受一个组件和额定的参数(如果须要),返回一个新的组件。HOC 是纯函数,没有副作用。

// hoc 的定义
function withSubscription(WrappedComponent, selectData) {
  return class extends React.Component {constructor(props) {super(props);
      this.state = {data: selectData(DataSource, props)
      };
    }
    // 一些通用的逻辑解决
    render() {
      // ... 并应用新数据渲染被包装的组件!
      return <WrappedComponent data={this.state.data} {...this.props} />;
    }
  };

// 应用
const BlogPostWithSubscription = withSubscription(BlogPost,
  (DataSource, props) => DataSource.getBlogPost(props.id));

HOC 的优缺点∶

  • 长处∶ 逻辑服用、不影响被包裹组件的外部逻辑。
  • 毛病∶ hoc 传递给被包裹组件的 props 容易和被包裹后的组件重名,进而被笼罩

(2)Render props 官网解释∶

“render prop” 是指一种在 React 组件之间应用一个值为函数的 prop 共享代码的简略技术

具备 render prop 的组件承受一个返回 React 元素的函数,将 render 的渲染逻辑注入到组件外部。在这里,”render” 的命名能够是任何其余无效的标识符。

// DataProvider 组件外部的渲染逻辑如下
class DataProvider extends React.Components {
     state = {name: 'Tom'}

    render() {
    return (
        <div>
          <p> 共享数据组件本人外部的渲染逻辑 </p>
          {this.props.render(this.state) }      </div>
    );
  }
}

// 调用形式
<DataProvider render={data => (<h1>Hello {data.name}</h1>
)}/>

由此能够看到,render props 的优缺点也很显著∶

  • 长处:数据共享、代码复用,将组件内的 state 作为 props 传递给调用者,将渲染逻辑交给调用者。
  • 毛病:无奈在 return 语句外拜访数据、嵌套写法不够优雅

(3)Hooks 官网解释∶

Hook 是 React 16.8 的新增个性。它能够让你在不编写 class 的状况下应用 state 以及其余的 React 个性。通过自定义 hook,能够复用代码逻辑。

// 自定义一个获取订阅数据的 hook
function useSubscription() {const data = DataSource.getComments();
  return [data];
}
// 
function CommentList(props) {const {data} = props;
  const [subData] = useSubscription();
    ...
}
// 应用
<CommentList data='hello' />

以上能够看出,hook 解决了 hoc 的 prop 笼罩的问题,同时应用的形式解决了 render props 的嵌套天堂的问题。hook 的长处如下∶

  • 应用直观;
  • 解决 hoc 的 prop 重名问题;
  • 解决 render props 因共享数据 而呈现嵌套天堂的问题;
  • 能在 return 之外应用数据的问题。

须要留神的是:hook 只能在组件顶层应用,不可在分支语句中应用。、

React.Component 和 React.PureComponent 的区别

PureComponent 示意一个纯组件,能够用来优化 React 程序,缩小 render 函数执行的次数,从而进步组件的性能。

在 React 中,当 prop 或者 state 发生变化时,能够通过在 shouldComponentUpdate 生命周期函数中执行 return false 来阻止页面的更新,从而缩小不必要的 render 执行。React.PureComponent 会主动执行 shouldComponentUpdate。

不过,pureComponent 中的 shouldComponentUpdate() 进行的是 浅比拟,也就是说如果是援用数据类型的数据,只会比拟不是同一个地址,而不会比拟这个地址外面的数据是否统一。浅比拟会疏忽属性和或状态渐变状况,其实也就是数据援用指针没有变动,而数据产生扭转的时候 render 是不会执行的。如果须要从新渲染那么就须要从新开拓空间援用数据。PureComponent 个别会用在一些纯展现组件上。

应用 pureComponent 的 益处:当组件更新时,如果组件的 props 或者 state 都没有扭转,render 函数就不会触发。省去虚构 DOM 的生成和比照过程,达到晋升性能的目标。这是因为 react 主动做了一层浅比拟。

展现组件 (Presentational component) 和容器组件 (Container component) 之间有何不同

展现组件关怀组件看起来是什么。展现专门通过 props 承受数据和回调,并且简直不会有本身的状态,但当展现组件领有本身的状态时,通常也只关怀 UI 状态而不是数据的状态。

容器组件则更关怀组件是如何运作的。容器组件会为展现组件或者其它容器组件提供数据和行为 (behavior),它们会调用 Flux actions,并将其作为回调提供给展现组件。容器组件常常是有状态的,因为它们是(其它组件的) 数据源。

概述一下 React 中的事件处理逻辑。

为了解决跨浏览器兼容性问题,React 会将浏览器原生事件(Browser Native Event)封装为合成事件(Synthetic Event)并传入设置的事件处理程序中。
这里的合成事件提供了与原生事件雷同的接口,不过它们屏蔽了底层浏览器的细节差别,保障了行为的一致性。另外,React 并没有间接将事件附着到子元素上,而是以繁多事件监听器的形式将所有的事件发送到顶层进行解决(基于事件委托原理)。
这样 React 在更新 DOM 时就不须要思考如何解决附着在 DOM 上的事件监听器,最终达到优化性能的目标。

请说岀 React 从 EMAScript5 编程标准到 EMAScript6 编程标准过程中的几点扭转。

次要扭转如下。
(1)创立组件的办法不同。
EMAScript5 版本中,定义组件用 React.createClass。EMAScript6 版本中,定义组件要定义组件类,并继承 Component 类。
(2)定义默认属性的办法不同。
EMAScript5 版本中,用 getDefaultProps 定义默认属性。EMAScript6 版本中,为组件定义 defaultProps 动态属性,来定义默认属性。
(3)定义初始化状态的办法不同。EMAScript5 版本中,用 getInitialState 定义初始化状态。EMAScript6 版本中,在构造函数中,通过 this. state 定义初始化状态。
留神:构造函数的第一个参数是属性数据,肯定要用 super 继承。
(4)定义属性束缚的办法不同。
EMAScript5 版本中,用 propTypes 定义属性的束缚。
EMAScript6 版本中,为组件定义 propsTypes 动态属性,来对属性进行束缚。
(5)应用混合对象、混合类的办法不同。
EMAScript5 版本中,通过 mixins 继承混合对象的办法。
EMAScript6 版本中,定义混合类,让混合类继承 Component 类,而后让组件类继承混合类,实现对混合类办法的继承。
(6)绑定事件的办法不同。
EMAScript5 版本中,绑定的事件回调函数作用域是组件实例化对象。
EMAScript6 版本中,绑定的事件回调函数作用域是 null。
(7)父组件传递办法的作用域不同。
EMAScript5 版本中,作用域是父组件。EMAScript6 版本中,变成了 null。
(8)组件办法作用域的批改办法不同。
EMAScript5 版本中,无奈扭转作用域。
EMAScript6 版本中,作用域是能够扭转的。

当调用 setState 的时候,产生了什么操作?**

当调用 setState 时,React 做的第一件事是将传递给 setState 的对象合并到组件的以后状态,这将启动一个称为和解(reconciliation)的过程。
和解的最终目标是,依据这个新的状态以最无效的形式更新 DOM。
为此,React 将构建一个新的 React 虚构 DOM 树(能够将其视为页面 DOM 元素的对象示意形式)。
一旦有了这个 DOM 树,为了弄清 DOM 是如何响应新的状态而扭转的,React 会将这个新树与上一个虚构 DOM 树比拟。
这样做,React 会晓得产生的确切变动,并且通过理解产生的变动后,在相对必要的状况下进行更新 DOM,即可将因操作 DOM 而占用的空间最小化。

React 中的 setState 和 replaceState 的区别是什么?

(1)setState() setState()用于设置状态对象,其语法如下:

setState(object nextState[, function callback])
  • nextState,将要设置的新状态,该状态会和以后的 state 合并
  • callback,可选参数,回调函数。该函数会在 setState 设置胜利,且组件从新渲染后调用。

合并 nextState 和以后 state,并从新渲染组件。setState 是 React 事件处理函数中和申请回调函数中触发 UI 更新的次要办法。

(2)replaceState() replaceState()办法与 setState()相似,然而办法只会保留 nextState 中状态,原 state 不在 nextState 中的状态都会被删除。其语法如下:

replaceState(object nextState[, function callback])
  • nextState,将要设置的新状态,该状态会替换以后的 state。
  • callback,可选参数,回调函数。该函数会在 replaceState 设置胜利,且组件从新渲染后调用。

总结: setState 是批改其中的局部状态,相当于 Object.assign,只是笼罩,不会缩小原来的状态。而 replaceState 是齐全替换原来的状态,相当于赋值,将原来的 state 替换为另一个对象,如果新状态属性缩小,那么 state 中就没有这个状态了。

受控组件和非受控组件区别是啥?

  • 受控组件 是 React 管制中的组件,并且是表单数据实在的惟一起源。
  • 非受控组件是由 DOM 解决表单数据的中央,而不是在 React 组件中。
    只管非受控组件通常更易于实现,因为只需应用 refs 即可从 DOM 中获取值,但通常倡议优先选择受管制的组件,而不是非受管制的组件。
    这样做的次要起因是受控组件反对即时字段验证,容许有条件地禁用 / 启用按钮,强制输出格局。

React 中 refs 的作用是什么

  • RefsReact 提供给咱们的平安拜访 DOM元素或者某个组件实例的句柄
  • 能够为元素增加 ref 属性而后在回调函数中承受该元素在 DOM 树中的句柄,该值会作为回调函数的第一个参数返回

useEffect(fn, []) 和 componentDidMount 有什么差别

useEffect 会捕捉 props 和 state。所以即使在回调函数里,你拿到的还是初始的 props 和 state。如果想得到“最新”的值,能够应用 ref。

Redux 中间件是什么?承受几个参数?柯里化函数两端的参数具体是什么?

Redux 的中间件提供的是位于 action 被发动之后,达到 reducer 之前的扩大点,换而言之,本来 view -→> action -> reducer -> store 的数据流加上中间件后变成了 view -> action -> middleware -> reducer -> store,在这一环节能够做一些 ” 副作用 ” 的操作,如异步申请、打印日志等。

applyMiddleware 源码:

export default function applyMiddleware(...middlewares) {return createStore => (...args) => {
        // 利用传入的 createStore 和 reducer 和创立一个 store
        const store = createStore(...args)
        let dispatch = () => {throw new Error()
        }
        const middlewareAPI = {
            getState: store.getState,
            dispatch: (...args) => dispatch(...args)
        }
        // 让每个 middleware 带着 middlewareAPI 这个参数别离执行一遍
        const chain = middlewares.map(middleware => middleware(middlewareAPI))
        // 接着 compose 将 chain 中的所有匿名函数,组装成一个新的函数,即新的 dispatch
        dispatch = compose(...chain)(store.dispatch)
        return {
            ...store,
            dispatch
        }
    }
}

从 applyMiddleware 中能够看出∶

  • redux 中间件承受一个对象作为参数,对象的参数上有两个字段 dispatch 和 getState,别离代表着 Redux Store 上的两个同名函数。
  • 柯里化函数两端一个是 middewares,一个是 store.dispatch

react-redux 的实现原理?

通过 redux 和 react context 配合应用,并借助高阶函数,实现了 react-redux

diff 虚构 DOM 比拟的规定

  • 【旧虚构 DOM】与【新虚构 DOM】中雷同 key

    若虚构 DOM 中的内容没有产生扭转,间接应用旧的虚构 DOM

    若虚构 DOM 中的内容产生扭转了,则生成新实在的 DOM,随后替换页面中之前的实在 DOM

  • 【旧虚构 DOM】中未找到 与【新虚构 DOM】雷同的 key

    依据数据创立实在 DOM,随后渲染到页面

类组件和函数组件之间的区别是啥?

  • 类组件 能够应用其余个性,如状态 state 和生命周期钩子。
  • 当组件只是接管 props 渲染到页面时,就是无状态组件,就属于函数组件,也被称为哑组件或展现组件。
    函数组件和类组件当然是有区别的,而且函数组件的性能比类组件的性能要高,因为类组件应用的时候要实例化,而函数组件间接执行函数取返回后果即可。为了进步性能,尽量应用函数组件。

    区别 函数组件 类组件
    是否有 this 没有
    是否有生命周期 没有
    是否有状态 state 没有

正文完
 0