精华内容
下载资源
问答
  • React 面试题

    2021-01-12 16:24:29
    React 面试题 1、setState 是异步还是同步? 合成事件中是异步 钩子函数中的是异步 原生事件中是同步 setTimeout中是同步 相关链接: 你真的理解setState吗? 2、聊聊 react@16.4 + 的生命周期 相关连接: React ...

    React 面试题

    1、setState 是异步还是同步?

    1. 合成事件中是异步
    2. 钩子函数中的是异步
    3. 原生事件中是同步
    4. setTimeout中是同步

    相关链接: 你真的理解setState吗?

    2、聊聊 react@16.4 + 的生命周期

    img

    相关连接: React 生命周期 我对 React v16.4 生命周期的理解

    3、useEffect(fn, []) 和 componentDidMount 有什么差异?

    useEffect 会捕获 propsstate。所以即便在回调函数里,你拿到的还是初始的 propsstate。如果想得到“最新”的值,可以使用 ref

    4、hooks 为什么不能放在条件判断里?

    useState 为例,在 react 内部,每个组件(Fiber)的 hooks 都是以链表的形式存在 memoizeState 属性中:

    img

    update 阶段,每次调用 useState,链表就会执行 next 向后移动一步。如果将 useState 写在条件判断中,假设条件判断不成立,没有执行里面的 useState 方法,会导致接下来所有的 useState 的取值出现偏移,从而导致异常发生。

    参考链接: 烤透 React Hook

    5、fiber 是什么?

    React Fiber 是一种基于浏览器的单线程调度算法。

    React Fiber 用类似 requestIdleCallback 的机制来做异步 diff。但是之前数据结构不支持这样的实现异步 diff,于是 React 实现了一个类似链表的数据结构,将原来的 递归diff 变成了现在的 遍历diff,这样就能做到异步可更新了。

    img

    相关链接: React Fiber 是什么?

    6、聊一聊 diff 算法

    传统 diff 算法的时间复杂度是 O(n^3),这在前端 render 中是不可接受的。为了降低时间复杂度,react 的 diff 算法做了一些妥协,放弃了最优解,最终将时间复杂度降低到了 O(n)。

    那么 react diff 算法做了哪些妥协呢?,参考如下:

    1、tree diff:只对比同一层的 dom 节点,忽略 dom 节点的跨层级移动

    如下图,react 只会对相同颜色方框内的 DOM 节点进行比较,即同一个父节点下的所有子节点。当发现节点不存在时,则该节点及其子节点会被完全删除掉,不会用于进一步的比较。

    这样只需要对树进行一次遍历,便能完成整个 DOM 树的比较。

    img

    这就意味着,如果 dom 节点发生了跨层级移动,react 会删除旧的节点,生成新的节点,而不会复用。

    2、component diff:如果不是同一类型的组件,会删除旧的组件,创建新的组件

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ongm0i56-1610439851876)(https://pic2.zhimg.com/80/v2-d8d3646da454398e0defacb61b5e1081_720w.jpg)]

    3、element diff:对于同一层级的一组子节点,需要通过唯一 id 进行来区分

    如果没有 id 来进行区分,一旦有插入动作,会导致插入位置之后的列表全部重新渲染。

    这也是为什么渲染列表时为什么要使用唯一的 key。

    7、调用 setState 之后发生了什么?

    1. setState 的时候,React 会为当前节点创建一个 updateQueue 的更新列队。
    2. 然后会触发 reconciliation 过程,在这个过程中,会使用名为 Fiber 的调度算法,开始生成新的 Fiber 树, Fiber 算法的最大特点是可以做到异步可中断的执行。
    3. 然后 React Scheduler 会根据优先级高低,先执行优先级高的节点,具体是执行 doWork 方法。
    4. doWork 方法中,React 会执行一遍 updateQueue 中的方法,以获得新的节点。然后对比新旧节点,为老节点打上 更新、插入、替换 等 Tag。
    5. 当前节点 doWork 完成后,会执行 performUnitOfWork 方法获得新节点,然后再重复上面的过程。
    6. 当所有节点都 doWork 完成后,会触发 commitRoot 方法,React 进入 commit 阶段。
    7. 在 commit 阶段中,React 会根据前面为各个节点打的 Tag,一次性更新整个 dom 元素。

    8、为什么虚拟dom 会提高性能?

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

    9、错误边界是什么?它有什么用?

    在 React 中,如果任何一个组件发生错误,它将破坏整个组件树,导致整页白屏。这时候我们可以用错误边界优雅地降级处理这些错误。

    例如下面封装的组件:

    class ErrorBoundary extends React.Component<IProps, IState> {
      constructor(props: IProps) {
        super(props);
        this.state = { hasError: false };
      }
    
      static getDerivedStateFromError() {
        // 更新 state 使下一次渲染能够显示降级后的 UI
        return { hasError: true };
      }
    
      componentDidCatch(error, errorInfo) {
        // 可以将错误日志上报给服务器
        console.log('组件奔溃 Error', error);
        console.log('组件奔溃 Info', errorInfo);
      }
    
      render() {
        if (this.state.hasError) {
          // 你可以自定义降级后的 UI 并渲染
          return this.props.content;
        }
        return this.props.children;
      }
    }
    

    10、什么是 Portals?

    Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案。

    ReactDOM.createPortal(child, container)
    

    11、React 组件间有那些通信方式?

    父组件向子组件通信

    1、 通过 props 传递

    子组件向父组件通信

    1、 主动调用通过 props 传过来的方法,并将想要传递的信息,作为参数,传递到父组件的作用域中

    跨层级通信

    1、 使用 react 自带的 Context 进行通信,createContext 创建上下文, useContext 使用上下文。

    参考下面代码:

    import React, { createContext, useContext } from 'react';
    
    const themes = {
      light: {
        foreground: "#000000",
        background: "#eeeeee"
      },
      dark: {
        foreground: "#ffffff",
        background: "#222222"
      }
    };
    
    const ThemeContext = createContext(themes.light);
    
    function App() {
      return (
        <ThemeContext.Provider value={themes.dark}>
          <Toolbar />
        </ThemeContext.Provider>
      );
    }
    
    function Toolbar() {
      return (
        <div>
          <ThemedButton />
        </div>
      );
    }
    
    function ThemedButton() {
      const theme = useContext(ThemeContext);
      return (
        <button style={{ background: theme.background, color: theme.foreground }}>
          I am styled by theme context!
        </button>
      );
    }
    
    export default App;
    

    2、使用 Redux 或者 Mobx 等状态管理库

    3、使用订阅发布模式

    相关链接: React Docs

    12、React 父组件如何调用子组件中的方法?

    1、如果是在方法组件中调用子组件(>= react@16.8),可以使用 useRefuseImperativeHandle:

    const { forwardRef, useRef, useImperativeHandle } = React;
    
    const Child = forwardRef((props, ref) => {
      useImperativeHandle(ref, () => ({
        getAlert() {
          alert("getAlert from Child");
        }
      }));
      return <h1>Hi</h1>;
    });
    
    const Parent = () => {
      const childRef = useRef();
      return (
        <div>
          <Child ref={childRef} />
          <button onClick={() => childRef.current.getAlert()}>Click</button>
        </div>
      );
    };
    

    2、如果是在类组件中调用子组件(>= react@16.4),可以使用 createRef:

    const { Component } = React;
    
    class Parent extends Component {
      constructor(props) {
        super(props);
        this.child = React.createRef();
      }
    
      onClick = () => {
        this.child.current.getAlert();
      };
    
      render() {
        return (
          <div>
            <Child ref={this.child} />
            <button onClick={this.onClick}>Click</button>
          </div>
        );
      }
    }
    
    class Child extends Component {
      getAlert() {
        alert('getAlert from Child');
      }
    
      render() {
        return <h1>Hello</h1>;
      }
    }
    

    参考阅读: Call child method from parent

    13、React有哪些优化性能的手段?

    类组件中的优化手段

    1、使用纯组件 PureComponent 作为基类。

    2、使用 React.memo 高阶函数包装组件。

    3、使用 shouldComponentUpdate 生命周期函数来自定义渲染逻辑。

    方法组件中的优化手段

    1、使用 useMemo

    2、使用 useCallBack

    其他方式

    1、在列表需要频繁变动时,使用唯一 id 作为 key,而不是数组下标。

    2、必要时通过改变 CSS 样式隐藏显示组件,而不是通过条件判断显示隐藏组件。

    3、使用 Suspenselazy 进行懒加载,例如:

    import React, { lazy, Suspense } from "react";
    
    export default class CallingLazyComponents extends React.Component {
      render() {
        var ComponentToLazyLoad = null;
    
        if (this.props.name == "Mayank") {
          ComponentToLazyLoad = lazy(() => import("./mayankComponent"));
        } else if (this.props.name == "Anshul") {
          ComponentToLazyLoad = lazy(() => import("./anshulComponent"));
        }
    
        return (
          <div>
            <h1>This is the Base User: {this.state.name}</h1>
            <Suspense fallback={<div>Loading...</div>}>
              <ComponentToLazyLoad />
            </Suspense>
          </div>
        )
      }
    }
    

    Suspense 用法可以参考官方文档

    相关阅读: 21个React性能优化技巧

    14、为什么 React 元素有一个 $$typeof 属性?

    img

    目的是为了防止 XSS 攻击。因为 Synbol 无法被序列化,所以 React 可以通过有没有 $$typeof 属性来断出当前的 element 对象是从数据库来的还是自己生成的。

    如果没有 $$typeof 这个属性,react 会拒绝处理该元素。

    在 React 的古老版本中,下面的写法会出现 XSS 攻击:

    // 服务端允许用户存储 JSON
    let expectedTextButGotJSON = {
      type: 'div',
      props: {
        dangerouslySetInnerHTML: {
          __html: '/* 把你想的搁着 */'
        },
      },
      // ...
    };
    let message = { text: expectedTextButGotJSON };
    
    // React 0.13 中有风险
    <p>
      {message.text}
    </p>
    

    相关阅读: Dan Abramov Blog

    15、React 如何区分 Class组件 和 Function组件?

    一般的方式是借助 typeof 和 Function.prototype.toString 来判断当前是不是 class,如下:

    function isClass(func) {
      return typeof func === 'function'
        && /^class\s/.test(Function.prototype.toString.call(func));
    }
    

    但是这个方式有它的局限性,因为如果用了 babel 等转换工具,将 class 写法全部转为 function 写法,上面的判断就会失效。

    React 区分 Class组件 和 Function组件的方式很巧妙,由于所有的类组件都要继承 React.Component,所以只要判断原型链上是否有 React.Component 就可以了:

    AComponent.prototype instanceof React.Component
    

    相关阅读: Dan Abramov Blog

    16、HTML 和 React 事件处理有什么区别?

    在 HTML 中事件名必须小写:

    <button onclick='activateLasers()'>
    

    而在 React 中需要遵循驼峰写法:

    <button onClick={activateLasers}>
    

    在 HTML 中可以返回 false 以阻止默认的行为:

    <a href='#' onclick='console.log("The link was clicked."); return false;' />
    

    而在 React 中必须地明确地调用 preventDefault()

    function handleClick(event) {
      event.preventDefault()
      console.log('The link was clicked.')
    }
    

    17、什么是 suspense 组件?

    Suspense 让组件“等待”某个异步操作,直到该异步操作结束即可渲染。在下面例子中,两个组件都会等待异步 API 的返回值:

    const resource = fetchProfileData();
    
    function ProfilePage() {
      return (
        <Suspense fallback={<h1>Loading profile...</h1>}>
          <ProfileDetails />
          <Suspense fallback={<h1>Loading posts...</h1>}>
            <ProfileTimeline />
          </Suspense>
        </Suspense>
      );
    }
    
    function ProfileDetails() {
      // 尝试读取用户信息,尽管该数据可能尚未加载
      const user = resource.user.read();
      return <h1>{user.name}</h1>;
    }
    
    function ProfileTimeline() {
      // 尝试读取博文信息,尽管该部分数据可能尚未加载
      const posts = resource.posts.read();
      return (
        <ul>
          {posts.map(post => (
            <li key={post.id}>{post.text}</li>
          ))}
        </ul>
      );
    }
    

    Suspense 也可以用于懒加载,参考下面的代码:

    const OtherComponent = React.lazy(() => import('./OtherComponent'));
    
    function MyComponent() {
      return (
        <div>
          <Suspense fallback={<div>Loading...</div>}>
            <OtherComponent />
          </Suspense>
        </div>
      );
    }
    

    18、为什么 JSX 中的组件名要以大写字母开头?

    因为 React 要知道当前渲染的是组件还是 HTML 元素。

    19、redux 是什么?

    Redux 是一个为 JavaScript 应用设计的,可预测的状态容器。

    它解决了如下问题:

    • 跨层级组件之间的数据传递变得很容易
    • 所有对状态的改变都需要 dispatch,使得整个数据的改变可追踪,方便排查问题。

    但是它也有缺点:

    • 概念偏多,理解起来不容易
    • 样板代码太多

    20、react-redux 的实现原理?

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

    参考链接: React.js 小书

    21、reudx 和 mobx 的区别?

    得益于 Mobx 的 observable,使用 mobx 可以做到精准更新;对应的 Redux 是用 dispath 进行广播,通过Provider 和 connect 来比对前后差别控制更新粒度;

    相关阅读: Redux or MobX: An attempt to dissolve the Confusion

    22、redux 异步中间件有什么什么作用?

    假如有这样一个需求:请求数据前要向 Store dispatch 一个 loading 状态,并带上一些信息;请求结束后再向Store dispatch 一个 loaded 状态

    一些同学可能会这样做:

    function App() {
      const onClick = () => {
        dispatch({ type: 'LOADING', message: 'data is loading' })
        fetch('dataurl').then(() => {
          dispatch({ type: 'LOADED' })
        });
      }
    
      return (<div>
        <button onClick={onClick}>click</button>
      </div>);
    }
    

    但是如果有非常多的地方用到这块逻辑,那应该怎么办?

    聪明的同学会想到可以将 onClick 里的逻辑抽象出来复用,如下:

    function fetchData(message: string) {
      return (dispatch) => {
        dispatch({ type: 'LOADING', message })
        setTimeout(() => {
          dispatch({ type: 'LOADED' })
        }, 1000)
      }
    }
    
    function App() {
      const onClick = () => {
        fetchData('data is loading')(dispatch)
      }
    
      return (<div>
        <button onClick={onClick}>click</button>
      </div>);
    }
    

    很好,但是 fetchData('data is loading')(dispatch) 这种写法有点奇怪,会增加开发者的心智负担。

    于是可以借助 rudux 相关的异步中间件,以 rudux-chunk 为例,将写法改为如下:

    function fetchData(message: string) {
      return (dispatch) => {
        dispatch({ type: 'LOADING', message })
        setTimeout(() => {
          dispatch({ type: 'LOADED' })
        }, 1000)
      }
    }
    
    function App() {
      const onClick = () => {
    -   fetchData('data is loading')(dispatch)
    +   dispatch(fetchData('data is loading'))
      }
    
      return (<div>
        <button onClick={onClick}>click</button>
      </div>);
    }
    

    这样就更符合认知一些了,redux 异步中间件没有什么奥秘,主要做的就是这样的事情。

    相关阅读: Why do we need middleware for async flow in Redux?

    23、redux 有哪些异步中间件?

    1、redux-thunk

    源代码简短优雅,上手简单

    2、redux-saga

    借助 JS 的 generator 来处理异步,避免了回调的问题

    3、redux-observable

    借助了 RxJS 流的思想以及其各种强大的操作符,来处理异步问题

    展开全文
  • React面试题

    千次阅读 2019-01-17 19:00:13
    React 面试题 1、当你调用setState的时候,发生了什么事? 当调用 setState 时,React会做的第一件事情是将传递给 setState 的对象合并到组件的当前状态。这将启动一个称为和解(reconciliation)的过程。和解...

    React 面试题

    1、当你调用setState的时候,发生了什么事?

    当调用 setState 时,React会做的第一件事情是将传递给 setState 的对象合并到组件的当前状态。这将启动一个称为和解(reconciliation)的过程。和解(reconciliation)的最终目标是以最有效的方式,根据这个新的状态来更新UI。 为此,React将构建一个新的 React 元素树(您可以将其视为 UI 的对象表示)。

    一旦有了这个树,为了弄清 UI 如何响应新的状态而改变,React 会将这个新树与上一个元素树相比较( diff )。

    通过这样做, React 将会知道发生的确切变化,并且通过了解发生什么变化,只需在绝对必要的情况下进行更新即可最小化 UI 的占用空间。

    2、在 React 当中 Element 和 Component 有何区别?

    简单地说,一个 React element 描述了你想在屏幕上看到什么。换个说法就是,一个 React element 是一些 UI 的对象表示。

    一个 React Component 是一个函数或一个类,它可以接受输入并返回一个 React element t(通常是通过 JSX ,它被转化成一个 createElement 调用)。

    3、什么时候使用功能组件( Class Component )什么时候使用类组件( Functional Component )?

    如果您的组件具有状态( state )或生命周期方法,请使用 Class 组件。否则,使用功能组件

    4、什么是 React 的 refs ,为什么它们很重要?

    refs就像是一个逃生舱口,允许您直接访问DOM元素或组件实例。为了使用它们,您可以向组件添加一个 ref 属性,该属性的值是一个回调函数,它将接收底层的 DOM 元素或组件的已挂接实例,作为其第一个参数。

    class UnControlledForm extends Component {
      handleSubmit = () => {
        console.log("Input Value: ", this.input.value)
      }
      render () {
        return (
          <form onSubmit={this.handleSubmit}>
            <input
              type='text'
              ref={(input) => this.input = input} />
            <button type='submit'>Submit</button>
          </form>
        )
      }
    }
    

    以上注意到我们的输入字段有一个 ref 属性,其值是一个函数。该函数接收我们然后放在实例上的实际的 DOM 元素,以便在 handleSubmit 函数内部访问它。经常误解的是,您需要使用类组件才能使用 ref ,但 ref 也可以通过利用 JavaScript 中的 闭包 与 功能组件( functional components )一起使用。

    function CustomForm ({handleSubmit}) {
      let inputElement
      return (
        <form onSubmit={() => handleSubmit(inputElement.value)}>
          <input
            type='text'
            ref={(input) => inputElement = input} />
          <button type='submit'>Submit</button>
        </form>
      )
    }
    
    

    5、React 中的keys是什么,为什么它们很重要?

    keys是什么帮助 React 跟踪哪些项目已更改、添加或从列表中删除。

      return (
        <ul>
          {this.state.todoItems.map(({task, uid}) => {
            return <li key={uid}>{task}</li>
          })}
        </ul>
      )
    }
    
    

    每个 keys 在兄弟元素之间是独一无二的。我们已经谈过几次关于和解(reconciliation)的过程,而且这个和解过程(reconciliation)中的一部分正在执行一个新的元素树与最前一个的差异。keys 使处理列表时更加高效,因为 React 可以使用子元素上的 keys 快速知道元素是新的还是在比较树时才被移动。

    而且 keys 不仅使这个过程更有效率,而且没有 keys ,React 不知道哪个本地状态对应于移动中的哪个项目。所以当你 map 的时候,不要忽略了 keys 。

    6、受控组件( controlled component )与不受控制的组件( uncontrolled component )有什么区别?

    React 的很大一部分是这样的想法,即组件负责控制和管理自己的状态。

    当我们将 native HTML 表单元素( input, select, textarea 等)投入到组合中时会发生什么?我们是否应该使用 React 作为“单一的真理来源”,就像我们习惯使用React一样? 或者我们是否允许表单数据存在 DOM 中,就像我们习惯使用HTML表单元素一样? 这两个问题是受控(controlled) VS 不受控制(uncontrolled)组件的核心。

    受控组件是React控制的组件,也是表单数据的唯一真理来源。

    如下所示, username 不存在于 DOM 中,而是以我们的组件状态存在。每当我们想要更新 username 时,我们就像以前一样调用setState。

    class ControlledForm extends Component {
      state = {
        username: ''
      }
      updateUsername = (e) => {
        this.setState({
          username: e.target.value,
        })
      }
      handleSubmit = () => {}
      render () {
        return (
          <form onSubmit={this.handleSubmit}>
            <input
              type='text'
              value={this.state.username}
              onChange={this.updateUsername} />
            <button type='submit'>Submit</button>
          </form>
        )
      }
    }
    

    不受控制( uncontrolled component )的组件是您的表单数据由 DOM 处理,而不是您的 React 组件。

    我们使用 refs 来完成这个。

    class UnControlledForm extends Component {
      handleSubmit = () => {
        console.log("Input Value: ", this.input.value)
      }
      render () {
        return (
          <form onSubmit={this.handleSubmit}>
            <input
              type='text'
              ref={(input) => this.input = input} />
            <button type='submit'>Submit</button>
          </form>
        )
      }
    }
    

    虽然不受控制的组件通常更容易实现,因为您只需使用引用从DOM获取值,但是通常建议您通过不受控制的组件来支持受控组件。

    主要原因是受控组件 支持即时字段验证 ,允许您有条件地禁用/启用按钮,强制输入格式,并且更多的是 『the React way』

    7、在哪个生命周期事件中你会发出 AJAX 请求,为什么?

    AJAX 请求应该在 componentDidMount 生命周期事件中。 有几个原因:

    1. Fiber,是下一次实施React的和解算法,将有能力根据需要启动和停止渲染,以获得性能优势。其中一个取舍之一是 componentWillMount ,而在其他的生命周期事件中出发 AJAX 请求,将是具有 “非确定性的”。 这意味着 React 可以在需要时感觉到不同的时间开始调用 componentWillMount。这显然是AJAX请求的不好的方式。
    2. 您不能保证在组件挂载之前,AJAX请求将无法 resolve。如果这样做,那意味着你会尝试在一个未挂载的组件上设置 StState,这不仅不会起作用,反而会对你大喊大叫。 在 componentDidMount 中执行 AJAX 将保证至少有一个要更新的组件。

    8、shouldComponentUpdate 应该做什么,为什么它很重要?

    在生命周期方法 shouldComponentUpdate 中,允许我们选择退出某些组件(和他们的子组件)的 reconciliation 过程。

    我们为什么要这样做?

    如上所述,“和解( reconciliation )的最终目标是以最有效的方式,根据新的状态更新用户界面”。如果我们知道我们的用户界面(UI)的某一部分不会改变,那么没有理由让 React 很麻烦地试图去弄清楚它是否应该渲染。通过从 shouldComponentUpdate 返回 false,React 将假定当前组件及其所有子组件将保持与当前组件相同。

    9、如何告诉React 构建(build)生产模式,该做什么?

    通常,将使用Webpack的 DefinePlugin 方法将 NODE_ENV 设置为 production。这将剥离像 propType 验证和额外的警告。除此之外,还有一个好主意,可以减少你的代码,因为React使用 Uglify 的 dead-code 来消除开发代码和注释,这将大大减少你的包的大小。

    10、为什么要使用 React.Children.map(props.children,()=>) 而不是 props.children.map(()=>)

    因为不能保证props.children将是一个数组。

    以此代码为例,

    <Parent>
      <h1>Welcome.</h1>
    </Parent>
    

    在父组件内部,如果我们尝试使用 props.children.map 映射孩子,则会抛出错误,因为 props.children 是一个对象,而不是一个数组。

    如果有多个子元素,React 只会使props.children成为一个数组。就像下面这样:

    <Parent>
      <h1>Welcome.</h1>
      <h2>props.children will now be an array</h2>
    </Parent>
    

    这就是为什么你喜欢 React.Children.map ,因为它的实现考虑到 props.children 可能是一个数组或一个对象。

    11、描述事件在React中的处理方式。

    为了解决跨浏览器兼容性问题,您的 React 中的事件处理程序将传递 SyntheticEvent 的实例,它是 React 的浏览器本机事件的跨浏览器包装器。

    这些 SyntheticEvent 与您习惯的原生事件具有相同的接口,除了它们在所有浏览器中都兼容。有趣的是,React 实际上并没有将事件附加到子节点本身。React 将使用单个事件监听器监听顶层的所有事件。这对于性能是有好处的,这也意味着在更新DOM时,React 不需要担心跟踪事件监听器。

    12、createElement 和 cloneElement 有什么区别?

    createElement 是 JSX 被转载到的,是 React 用来创建 React Elements 的内容(一些 UI 的对象表示)cloneElement用于克隆元素并传递新的 props。

    13、可以选择性地传递给 setState 的第二个参数是什么,它的目的是什么?

    一个回调函数,当setState结束并 re-rendered 该组件时将被调用。一些没有说出来的东西是 setState 是 异步 的,这就是为什么它需要一个第二个回调函数。通常最好使用另一个生命周期方法,而不是依赖这个回调函数,但是很高兴知道它存在。

    14、redux中间件

    中间件提供第三方插件的模式,自定义拦截 action -> reducer 的过程。变为 action -> middlewares -> reducer 。这种机制可以让我们改变数据流,实现如异步 action ,action 过滤,日志输出,异常报告等功能。
    常见的中间件:
    redux-logger:提供日志输出
    redux-thunk:处理异步操作
    redux-promise:处理异步操作,actionCreator的返回值是promise

    15、redux有什么缺点

    1. 一个组件所需要的数据,必须由父组件传过来,而不能像flux中直接从store取。
    2. 当一个组件相关数据更新时,即使父组件不需要用到这个组件,父组件还是会重新render,可能会有效率影响,或者需要写复杂的shouldComponentUpdate进行判断。

    16、react组件的划分业务组件技术组件?

    根据组件的职责通常把组件分为UI组件和容器组件。
    UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑。
    两者通过React-Redux 提供connect方法联系起来。
    具体使用可以参照如下链接:http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_three_react-redux.html

    17、react生命周期函数

    一、初始化阶段:
    getDefaultProps:获取实例的默认属性
    getInitialState:获取每个实例的初始化状态
    componentWillMount:组件即将被装载、渲染到页面上
    render:组件在这里生成虚拟的DOM节点
    componentDidMount:组件真正在被装载之后

    二、运行中状态:
    componentWillReceiveProps:组件将要接收到属性的时候调用
    shouldComponentUpdate:组件接受到新属性或者新状态的时候(可以返回false,接收数据后不更新,阻止render调用,后面的函数不会被继续执行了)
    componentWillUpdate:组件即将更新不能修改属性和状态
    render:组件重新描绘
    componentDidUpdate:组件已经更新

    三、销毁阶段:
    componentWillUnmount:组件即将销毁

    18、react性能优化是哪个周期函数?

    shouldComponentUpdate 这个方法用来判断是否需要调用render方法重新描绘dom。因为dom的描绘非常消耗性能,如果我们能在shouldComponentUpdate方法中能够写出更优化的dom diff算法,可以极大的提高性能。

    19、为什么虚拟dom会提高性能?

    虚拟dom相当于在js和真实dom中间加了一个缓存,利用dom diff算法避免了没有必要的dom操作,从而提高性能。
    具体实现步骤如下:
    用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
    当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
    把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了。
    参考链接:
    https://www.zhihu.com/question/29504639?sort=created

    20、diff算法?

    把树形结构按照层级分解,只比较同级元素。
    给列表结构的每个单元添加唯一的key属性,方便比较。
    React 只会匹配相同 class 的 component(这里面的class指的是组件的名字)
    合并操作,调用 component 的 setState 方法的时候, React 将其标记为 dirty.到每一个事件循环结束, React 检查所有标记 dirty 的 component 重新绘制.
    选择性子树渲染。开发人员可以重写shouldComponentUpdate提高diff的性能。
    参考链接:
    https//segmentfault.com/a/1190000000606216

    21、react性能优化方案

    (1)重写shouldComponentUpdate来避免不必要的dom操作。
    (2)使用 production 版本的react.js。
    (3)使用key来帮助React识别列表中所有子组件的最小变化。
    参考链接:
    https://segmentfault.com/a/1190000006254212

    22、简述flux 思想

    Flux 的最大特点,就是数据的"单向流动"。
    1.用户访问 View
    2.View 发出用户的 Action
    3.Dispatcher 收到 Action,要求 Store 进行相应的更新
    4.Store 更新后,发出一个"change"事件
    5.View 收到"change"事件后,更新页面
    参考链接:
    http://www.ruanyifeng.com/blog/2016/01/flux.html

    23、React项目用过什么脚手架?Mern? Yeoman?

    Mern:MERN是脚手架的工具,它可以很容易地使用Mongo, Express, React and NodeJS生成同构JS应用。它最大限度地减少安装时间,并得到您使用的成熟技术来加速开发。

    24、reactJS兄弟组件之间如何通信

    事件用DeviceEventEmitter、数据传递刷新等用Redux、数据库

    25、reactJS中和子组件如何通信

    一般是控制子组件的显示状态,可以用改变props,或者调用子组件的方法进行。子组件事件传递给父组件回调就好

    26、reactJS的props.children.map函数来遍历会收到异常提示,为什么?应该如何遍历?

    this.props.children的值有三种可能:如果当前组件没有子节点,它就是undefined;如果有一个子节点,数据类型是object;如果有多个子节点,数据类型就是array。系统提供React.Children.map()方法安全的遍历子节点对象

    展开全文
  • react面试题

    2020-06-03 17:07:03
    react面试题介绍项目经验并提问function component和class component的区别react新增的生命周期redux 相关面试题redux的数据流store和action都是对象, action中必须有一个type字段对进行的操作进行说明, 可能会有...

    介绍项目经验并提问

    function component和class component的区别

    function组件是无状态组件不可以使用this.setstate、this.state
    function组件更容易分离,更容易编写和测试
    function组件没有生命周期

    class组件在需要生命周期的时候使用,需要状态的时候使用、需要性能优化的时候使用(shouldComponentUpdate判断)

    react生命周期

    初始化阶段
    constructor 初始化数据—初始化状态
    componentWillMount 以前建议进行ajax请求,最后一次修改状态的机会,但是现在基本上都componentDidMount中请求
    render 第一次装载(渲染)数据
    componentDidMount ajax请求,更新状态,进入运行时阶段,更新视图,还可以实例化一些对象
    运行时阶段
    componentWillReceiveProps 子组件接收到父组件的数据
    shouldComponentUpdate 本组件是不是需要进行去更新视图,默认为true,要不不写,写了必写返回值,false表示不更新图
    componentWillUpdate 组件即将被更新-----无实际意义
    render 重新渲染数据
    componentDidUpdate 实例化一些对象(特别是如果数据是动态请求的)
    销毁
    componentWillUnmount 清除一些计时器,定时器等

    react新增的生命周期

    16.3废弃了componentWillMount、componentWillRecieveProps、componentWillUpdate生命周期。
    新增getDerivedStateFromProps、getSnapshotBeforeUpdate.

    getDerivedStateFromProps:将传入的props映射到states上,会在每次re-rendering之前调用。
    一个静态函数,也就是这个函数不能通过this访问到class的属性,也并不推荐直接访问属性。
    而是应该通过参数提供的nextProps以及prevState来进行判断,根据新传入的props来映射到state。

    getSnapshotBeforeUpdate.它的含义是在React更新Dom元素之前,获取一个快照,
    它返回的结果将作为componentDidUpdate的第三个参数。一般的用法就是获取更新前的DOM。

    redux 相关面试题

    redux的数据流

    用户触发一个action,通过strore的dispatch方法将action传给reducer,
    reducer接收两个参数,第一个是之前的state后一个action
    然后返回一个新的state给action
    然后可以通过action里面的getstate()获取这个新的state
    具体哪个组件要用修改,通过action里面的subscrbe方法订阅

    store和action都是对象, action中必须有一个type字段对进行的操作进行说明, 可能会有数据.

    store会赋值给组件中的state
    reducer是一个纯函数, 接收两个参数, 第一个参数是累积对象(即state), 第二个参数就是action.
    reducer函数根据action.type的不同对state进行操作, 最后返回一个新的state, 这个新的state同时又是下一次的累积对象.

    action是对象还是函数,action一般是怎么写的

    对象

      handleAdd() {
        const action = {
          type: 'add_number',
        }
        store.dispatch(action)
      }
    

    redux和react-redux的关系

    react-redux就是提供了几个方法,connect、 provider 简化了redux

    connect的实现原理

    首先connect之所以会成功,是因为Provider组件:

    在原应用组件上包裹一层,使原来整个应用成为Provider的子组件
    接收Redux的store作为props,通过context对象传递给子孙组件上的connect

    那connect做了些什么呢?

    它真正连接 Redux 和 React,它包在我们的容器组件的外一层,它接收上面 Provider 提供的 store 里面的 state 和 dispatch,传给一个构造函数,返回一个对象,以属性形式传给我们的容器组件。

    connect是一个高阶函数,首先传入mapStateToProps、mapDispatchToProps,然后返回一个生产Component的函数(wrapWithConnect),
    然后再将真正的Component作为参数传入wrapWithConnect,这样就生产出一个经过包裹的Connect组件,该组件具有如下特点:

    通过props.store获取祖先Component的store
    props包括stateProps、dispatchProps、parentProps,合并在一起得到nextState,作为props传给真正的Component
    componentDidMount时,添加事件this.store.subscribe(this.handleChange),实现页面交互
    shouldComponentUpdate时判断是否有避免进行渲染,提升页面性能,并得到nextState
    componentWillUnmount时移除注册的事件this.handleChange

    provider的功能是什么

    react-redux 是官方提供的解决方案,Provider 本身并没有做很多事情,只是把 store放在 context 里罢了。
    实际上如果你用 react-redux,那么连接视图和数据层最好的办法是使用 connect 函数。
    本质上 Provider 就是给 connect 提供 store 用的。

    高阶组件的用法,什么场景会用到

    定义:如果一个函数 接受一个或多个组件作为参数并且返回一个组件 就可称之为 高阶组件。
    场景:react-redux @connect() 实现了包装一个组件,赋予组件新的功能

    import和require.js的区别。

    require是commonjs的规范,在node中实现的api;,import是es的语法,由编译器处理。
    写法上有差异

    但两者都会做缓存,所以不会有循环引用问题

    跨域怎么实现

    1、jsonp跨域:利用src可以跨域的能力,向服务发送一个callback参数,服务器返回数据时会将这个callback参数作为函数名包裹住JSON数据,
    这样客户端就可以随意定制自己的函数来处理数据了。
    2、CROS跨域:克服了AJAx只能同源使用的限制,在头信息中增加一个orgin字段,对于开发者来说他跟ajax没有什么区别,
    关键还是服务器,只要服务器开通了CROS接口,就可以实现跨域。

    3、WebSocket:是一种全双工通讯协议。他与ajax的区别就是它只需要进行一次连接就可以实现数据的双向传递。

    4、window.name+ iframe:利用一个隐藏的iframe作为中间代理,让iframe的src为另一个页面的url,当页面加载完毕之后,
    我们再让iframe与当前页面处于同一个域下,这样就可以拿到window.name了
    5、window.postMessage:在发送窗口设置iframe,让其src的值为接收窗口,然后再用postMessage()向接收端发送消息,
    接收端通过事件监听的方式判断窗口发送源是否一致,不一致不执行,一致的话通过e.data接收。

    6、服务器上设置代理页面

    jsonp的原理

    jsonp跨域:利用src可以跨域的能力,向服务发送一个callback参数,服务器返回数据时会将这个callback参数作为函数名包裹住JSON数据,
    这样客户端就可以随意定制自己的函数来处理数据了。

    react hooks有哪些

    基础hook useState和useReducer提供了可以刷新(更新)函数组件的途径。同样,也是自定义hook刷新的途径。如果想让自定义hook去刷新函数组件,那只能在自定义组件中使用useState或者useReducer来强制刷新,达到类似以前forUpdate的效果。
    State hook的主要作用就是获取需要的 state 和 更新state的方法

    const [state, setState] = useState(initialState);
    
    import React, { useState } from 'react'
    
    export default function () {
        const [count, setCount] = useState(0)
        return <div>
            <button onClick={() => {
                setCount(count + 1)
            }}>+</button>
            {count}
            <button onClick={() => {
                setCount(count - 1)
            }}>-</button>
        </div>
    }
    

    Effect hook方法是在每次渲染之后执行,可以理解为class写法中的 componentDidMount / componentDidUpdate(为了方便理解可以这么理解,但不完全一样)

    useEffect(didUpdate);
    

    keys 的作用是什么?

    Keys 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。

    this.setState()该方法

    this.setState()该方法接收两种参数:对象或函数。
    对象:即想要修改的state
    函数:接收两个函数,第一个函数接受两个参数,第一个是当前state,第二个是当前props,该函数返回一个对象,和直接传递对象参数是一样的,就是要修改的state;第二个函数参数是state改变后触发的回调。

    子组件的数据依赖于父组件componentWillReceiveProps

    如果子组件的数据依赖于父组件,将会执行一个钩子函数componentWillReceiveProps,在生命周期的第一次render后不会被调用,但是会在之后的每次render中被调用 = 当父组件再次传送props

    shouldComponentUpdate 是做什么的

    shouldComponentUpdate 这个方法用来判断是否需要调用 render 方法重新描绘 dom。因为 dom 的描绘非常消耗性能,如果我们能在 shouldComponentUpdate 方法中能够写出更优化的 dom diff 算法,可以极大的提高性能。

    为什么说虚拟dom会提高性能?

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

    react diff 原理

    把树形结构按照层级分解,只比较同级元素。
    给列表结构的每个单元添加唯一的 key 属性,方便比较。
    React 只会匹配相同 class 的 component(这里面的 class 指的是组件的名字)
    合并操作,调用 component 的 setState 方法的时候, React 将其标记为 dirty.到每一个事件循环结束, React 检查所有标记 dirty 的 component 重新绘制.
    选择性子树渲染。开发人员可以重写 shouldComponentUpdate 提高 diff 的性能。

    状态(state)和属性(props)之间有何不同

    State 是一种数据结构,用于组件挂载时所需数据的默认值。State 可能会随着时间的推移而发生突变,但多数时候是作为用户事件行为的结果。

    Props(properties 的简写)则是组件的配置。props 由父组件传递给子组件,并且就子组件而言,props 是不可变的(immutable)。组件不能改变自身的 props,但是可以把其子组件的 props 放在一起(统一管理)。Props 也不仅仅是数据–回调函数也可以通过 props 传递。

    .call()和.apply()的区别和作用?

    作用:用于实现对象的继承,this指向的第一个参数。
    区别:call用于字符串继承,apply用于数组继承。

    async实现原理

    async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。

    async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。
    async函数对 Generator 函数的改进,体现在以下四点。

    1. 内置器
    Generator 函数的执行必须靠执行器,所以才有了co模块,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。### Generator原理
    语法上面看,Generator函数可以理解成一个状态机,封装了多个内部状态,执行一个Generator函数就会返回一个遍历器对象,可以依次遍历Generator函数内部的每一个状态。
    2. 好的语义。
    async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
    3. 更广的适用性。
    co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。

    4. 返回值是 Promise。
    async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。

    进一步说,async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。

    特点:
    function命令与函数名之间有一个星号
    函数体内使用yield语句定义不同的内部状态
    可以交出函数的执行权,即暂停执行

    async,await什么场景下不能用

    同步请求,需要先处理一个请求再处理下一个的时候不能使用
    比如 多张图片,处理一张保存一张,然后才能处理下一张

    promise

    promise和.then

    react路由相关

    react路由原理

    promise和settimeout的区别

    promise是微任务,
    setTimeout是宏任务,
    微任务的优先级大于宏任务

    这里Promise的执行会先于新task执行。根据HTML标准,一个task执行完后,UI会重渲染,所以micro task更新完数据后再渲染dom的操作要比setTimout的性能要好。如果使用setTimeout的话,会有两次ui重渲染

    es6新增特性

    es6新增特性简介

    展开全文
  • react 面试题

    2020-04-27 16:59:48
    3.简述一下React中的生命周期方法及其作用,以及哪些是在React最新版本中逐渐废弃的? 4.React组件通信的方式(父子组件、兄弟组件、跨层级组件通信)? 5.React中Context API的使用方法以及适用场景。 6.如何在React...

    什么是Umi

    https://umijs.org/

    是一个可插拔的企业级 react 应用框架

    • 开箱即用,内置 react、react-router 等
    • 🏈 类 next.js 且功能完备的路由约定,同时支持配置的路由方式
    • 🎉 完善的插件体系,覆盖从源码到构建产物的每个生命周期
    • 🚀 高性能,通过插件支持 PWA、以路由为单元的 code splitting 等
    • 💈 支持静态页面导出,适配各种环境,比如中台业务、无线业务、egg、支付宝钱包、云凤蝶等
    • 🚄 开发启动快,支持一键开启 dll 等
    • 🐠 一键兼容到 IE9,基于 umi-plugin-polyfills
    • 🍁 完善的 TypeScript 支持,包括 d.ts 定义和 umi test
    • 🌴 与 dva 数据流的深入融合

    zh/guide/#%E7%89%B9%E6%80%A7

    什么是dva

    dva 首先是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为一个轻量级的应用框架

    https://dvajs.com/guide/

    1.什么是JSX?

    https://www.jianshu.com/p/9b4fba29deac

    2.React中有几种构建组件的方式(es5、es6语法)?

    https://www.cnblogs.com/wonyun/p/5930333.html

    3.简述一下React中的生命周期方法及其作用,以及哪些是在React最新版本中逐渐废弃的?

    https://blog.csdn.net/x415329/article/details/92701498

    4.React组件通信的方式(父子组件、兄弟组件、跨层级组件通信)?

    https://www.jianshu.com/p/fb915d9c99c4

    5.React中Context API的使用方法以及适用场景。

    https://www.cnblogs.com/kewenxin/p/12988700.html

    6.如何在React中使用innerHTML。

    https://www.cnblogs.com/rubylouvre/p/4559969.html

    7.什么是受控组件和非受控组件。

    https://www.cnblogs.com/WindrunnerMax/p/14254492.html

    8.如何提高组件渲染效率。

    https://www.jianshu.com/p/100a55978253?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

    9.React-Router的路由有几种模式?

    https://www.cnblogs.com/sunyang-001/p/11191416.html

    10.React-Router 4中<Router>组件有几种类型?

    11.React-Router怎么获取历史对象?

    http://www.voidcn.com/article/p-nsmzqrid-buu.html

    12.React-Router怎么获取URL的参数?

    https://blog.csdn.net/qq_24147051/article/details/78786325

    13.React如何阻止组件渲染?

    https://blog.csdn.net/qq_45922461/article/details/112251222

    14.React中的setState是同步还是异步的呢?为什么state并不一定会同步更新,以及第二个参数的作用?

    https://blog.csdn.net/fesfsefgs/article/details/108036605

    15.React中除了在构造函数中绑定this,还有别的方式吗? 

    https://www.cnblogs.com/suoking/p/10519406.html

    15.谈一谈对高阶组件的理解。

    https://www.cnblogs.com/jasonwang2y60/p/8514451.html

    16.React中refs的使用。

    https://www.cnblogs.com/vincent-c/p/13436061.html

    17.列举一些React Hooks API及其使用方法。

    https://blog.csdn.net/weixin_44824839/article/details/106734407

    18.redux的工作流程,以及React-redux的使用方法。

    https://www.cnblogs.com/ashen1999/p/13901157.html

    19.在redux中如何进行异步操作。

    https://blog.csdn.net/weixin_34367845/article/details/88037246

     

    展开全文
  • Vue or React面试题总结

    2021-06-23 20:35:00
    Vue or React面试题总结

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 15,197
精华内容 6,078
关键字:

react面试题