21 Essential React.js面试问题 *

总源的基本问题,最好的React.Js开发人员和工程师可以回答这个问题. 在我们社区的推动下,我们鼓励专家提交问题并提供反馈.

Hire a Top React.js Developer Now
Toptal logo是顶级自由软件开发人员的专属网络吗, designers, finance experts, product managers, 和世界上的项目经理. 顶级公司雇佣Toptal自由职业者来完成他们最重要的项目.

Interview Questions

1.

解释Virtual DOM,以及React如何将其呈现到DOM的实用概述.

View answer

The Virtual DOM is an interesting concept; it’s a complex idea that boils down into a much simpler algorithm.

In React, 如果我们创建简单的ES6类并将其打印出来, 我们有一个函数(在JavaScript中,所有函数都可以用作构造函数):

const app = () => {
    let React = react,
        {Component} = React,
        DOM = reactDom

    类注释扩展组件{
        构造函数(props){super(props)}
        render(){ return 
test
} } console.log(Comments) } 要求(“反应”、“react-dom”).then(app)

The console.log(Comments) 给了我们这样的东西(在Babel从ES6编译到ES5之后):

函数Comments(props) {
    _classCallCheck(评论);

    return _possibleeconstructorreturn (this, Object ..getPrototypeOf(评论).调用(这一点,道具));
}

当我们写一些东西来绘制一个React组件到屏幕上, 我们可能会得到如下内容:

DOM.render(, document.body)

JSX也被Babel翻译成ES5:

DOM.render(React.createElement(评论、空),文档.body);

We can see that 直接翻译成 React.createElement(评论、空). 在这里我们可以看到什么是Virtual DOM对象 is:一个普通的JavaScript对象,表示要呈现在屏幕上的标签.

的输出 React.createElement():

console.log(
) // or console.log(React.createElement (" div " null))

This gives us:

{“类型”:" div "、“关键”:空,“ref”:空,“道具”:{},“_owner”:空,“_store”:{}}

See how the type is a string? DOM.render({...}) 获取上面的对象并查看 type,并决定是否重用现有的

元素,或者创建一个新的
and append it.

虚拟DOM不是一个简单的 Object -它是一个递归结构. 例如,如果我们在

:

console.log(
) // or console.log(React.createElement( 'div', null, React.createElement(“跨越”,零), React.createElement(“按钮”,零) ))

我们得到的是一个嵌套的对象树:

{
    "type":"div",
    "key":null,
    "ref":null,
    "props":{
        "children": [
            {“类型”:“跨越”、“关键”:空,“ref”:空,“道具”:{}},
            {“类型”:“按钮”,“关键”:空,“ref”:空,“道具”:{}}
        ]
    }
}

这就是为什么在React组件的代码中,我们可以通过 this.props.children. React会做的是沿着嵌套对象的深度树走下去(取决于你的UI复杂性), 每个元素都位于它们的父元素中 children.

需要注意的一点是 type 到目前为止只是一个字符串. 当一个React元素是由自定义组件(比如 Comments above), the type is a function:

console.log()
// or
console.log(React.createElement(评论,null))

gives us:

{
    "key":null,
    "ref":null,
    "props":{},
    " type ": function Component() { ... }
}

您可以试用此代码的web版本 在Matthew Keas的github.

2.

解释标准JavaScript工具链, 编译(通过Babel或其他编译器), JSX, 以及这些项目在近期发展中的意义. 在构建步骤中,您可能会使用哪些工具来优化编译后的输出React代码?

View answer

最前沿的JavaScript工具链可能看起来相当复杂, 对工具链有信心,并对各个部分如何组合在一起有一个心理图景,这是非常重要的.

JavaScript工具链中有两个主要支柱:依赖管理, Linting, Style-checking, Transpilation, and Compilation, Minification, Source-Mapping.

通常,我们使用像Gulp, Watchify/Browserify, Broccoli或Webpack这样的构建工具来 监视文件系统 对于文件事件(例如当您添加或编辑文件时). 发生这种情况后,将构建工具配置为执行一组 顺序或并行任务.

这部分是最复杂的部分,也是开发过程的中心.

其余的工具属于这组顺序或并行任务:

  • 风格检查——通常使用像JSCS这样的检查器来确保源代码遵循特定的结构和风格
  • 依赖管理——用于JavaScript项目, most people use other packages from npm; some plugins exist for build systems (e.g. Webpack)和编译器(e.g. Babel),允许包的自动安装 imported or require()‘d
  • 编译-编译的一个特定的子类型, 转译涉及将代码从一个源版本编译到另一个源版本, 只有相似的运行时级别(例如.g. ES6 to ES5)
  • 编译——从ES6和JSX到ES5的编译中分离出来, 是否包括资产, 将CSS文件作为JSON处理, 或者其他可以加载和注入外部资产和代码到文件中的机制. In addition, 有各种各样的构建步骤可以分析你的代码,甚至为你优化它.
  • 缩小和压缩——通常是编译的一部分,但并非完全由编译控制, 是最小化和压缩JS文件到更少和/或更小的文件的行为
  • 源映射——编译的另一个可选部分是构建源映射, 这有助于识别原始源代码中与输出代码(i.e. 发生错误的地方)

对于React,有特定的构建工具插件,例如 babel-react-optimize预设 它包括将代码编译成优化React的格式,例如自动编译任何 React.createElement() 调用直接内联到源代码中的JavaScript对象:

类MyComponent扩展React.Component {
  render() {
    return (
      
Hello World
); } }

becomes

类MyComponent扩展React.Component {
  render() {
    return (
      _jsx('div', {className: this.props.className}, void 0,
        _jsx('span', {}, void 0, 'Hello World')
      )
    );
  }
}

See also:

3.

如何在React中创建高阶组件(hoc)?

View answer

高阶组件(hoc)是为接受动态提供的子组件的自定义组件创造的术语. 例如,让我们做 组件,该组件将子图像标签作为 children, waits until the 组件滚动到视图中, 然后加载它们在背景中指向的图像(在将它们渲染到DOM之前).

HOC通过props接受子节点:

DOM.render(
    
        
        
        
    ,
    document.body)

创建HOC意味着处理 this.props.children 在组件的代码中:

交互式示例可以在 http://goo.gl/ns0B6j

类LazyLoad扩展组件{
    constructor(p){
        super(p)
        this.状态={已加载:0}
        this._scroll = this._scroll.bind(this)
    }
    _scroll(){
        let el = DOM.findDOMNode(this)
        let {top} = el.getBoundingClientRect ()
        让viewporttheight = Math.max(document.documentElement.clientHeight、窗口.innerHeight || 0)
        if(top < (viewportHeight + this.props.top)) {
            window.removeEventListener(“滚动”._scroll)
            this.设置状态({加载:1})
        }
    }
    componentDidMount () {
        window.addEventListener(“滚动”._scroll)
        this._scroll()
    }
    componentWillUnmount () {
        window.removeEventListener(“滚动”._scroll)
    }
    render(){
        让{children} = this.props,
            {loaded} = this.state
        return 
{loaded && children}
} } LazyLoad.defaultProps = { top: 100 }

注意这段代码的一些事情:

  1. 我们建立初始状态(this.state = {loaded: 0}) in the constructor(). This will be set to 1 当父容器滚动到视图中时.
  2. The render() returns the props.children 作为子元素. Extract the src 通过使用ES6解构,其中 {props:{src}} creates a variable src 加上合适的值.
  3. We used a single componentDidMount() lifecycle method. 这样做是因为在挂载时,我们希望组件检查HOC是否可见.
  4. 分量中最大的函数, _scroll(),获取HOC组件的DOM元素 DOM.findDOMNode() 然后得到元素的位置. 这个位置与浏览器窗口的高度进行比较, 如果距离底部小于100px, 然后删除滚动侦听器并 loaded is set to 1.

这种技术被称为HOC(高阶组件),因为我们传入元素为 this.props.children 当我们将这些元素嵌套到容器组件中时:


    
some
children

所有这些嵌套元素(可以是自定义组件)都嵌套在 , thus HOC的代码将能够访问它们 this.props.children.

申请加入Toptal的发展网络

并享受可靠、稳定、远程 Freelance React.js Developer Jobs

申请成为自由职业者
4.

键在React中的重要性是什么?

View answer

Keys in React are used to identify unique VDOM Elements with their corresponding data driving the UI; having them helps React optimize rendering by recycling existing DOM elements. 让我们看一个例子来说明这一点.

We have two 呈现在页面上的组件,按从动项的降序绘制:

-----------
| A - 103 |
-----------
-----------
| B - 92  |
-----------

假设B更新了105个Twitter关注者, 所以应用程序重新渲染, 并切换A和B的顺序:

-----------
| B - 105 |
-----------
-----------
| A - 103 |
-----------

如果没有键,React主要会重新渲染两者 DOM中的元素. 它会重用DOM元素,但React不会 re-order 屏幕上的DOM元素.

With keys, React实际上会对DOM元素重新排序, 而不是呈现大量嵌套的DOM更改. 这可以作为一个巨大的性能增强, 特别是如果使用的DOM和VDOM/React元素渲染成本很高.

Keys themselves should be a unique number or string; so if a React Component is the only child with its key, 那么React将在以后的调用中重新使用由该键表示的DOM元素 render().

让我们用一个简单的用React渲染的待办事项列表来演示一下:

交互式代码示例可在 Matthew Keas的github.

类列表扩展组件{
    constructor(p){
        super(p)
        this.state = {
            items: Array(5).fill(1).map((x,i) => ({id:i}))
        }
    }

    componentDidMount () {
        const random = (a,b) => Math.random() < .5 ? -1 : 1

        setInterval(() => {
            this.setState({
                items: this.state.items.sort(random)
            })
        }, 20)
    }

    render() {
        let {items} = this.state
        return 
    {items.map(item =>
  • {item.id}
  • )}
} } DOM.render(, document.body)

The setInterval() 发生在挂载上重新排序 items array in this.state every 20ms. Computationally, 如果React正在重新排序状态中的项, 然后它将操作DOM元素本身,而不是在元素的位置之间“拖动”它们

    .

    这里值得注意的是,如果您呈现同质的子数组-例如

  • 如上所述- React实际上会 console.warn() 您可以了解潜在的问题,并提供堆栈跟踪和用于调试的行号. 你不必担心React会悄然崩溃.

5.

refs在React中的意义是什么?

View answer

与键类似,refs作为属性添加到 React.createElement() call, such as

  • . The ref 有不同的目的, 它为我们提供了对由React元素表示的DOM元素的快速而简单的访问.

    Refs可以是字符串也可以是函数. 使用字符串会告诉React自动将DOM元素存储为 this.refs[refValue]. For example:

    类列表扩展组件{
        constructor(p){
            super(p)
        }
    
        _printValue(){
            console.log(this.refs.someThing.value)
        }
    
        render() {
            return 
    this._printValue()}>

    test

    } } DOM.render(, document.body)

    this.refs.someThing inside componentDidUpdate () 用来指一个特殊的标识符,我们可以使用 React.findDOMNode (refObject) -这将为我们提供在这个非常具体的实例中存在于DOM上的DOM节点. 现在,React自动将DOM节点附加到ref上,这意味着 this.refs.someThing 将直接指向一个DOM元素实例.

    此外,ref可以是接受单个输入的函数. 这是一种更动态的方法,可以将DOM节点作为变量分配和存储在代码中. For example:

    类列表扩展组件{
        constructor(p){
            super(p)
        }
    
        _printValue(){
            console.log(this.myTextInput.value)
        }
    
        render() {
            return 
    this._printValue()}>

    test

    this.myTextInput = node} />
    } } DOM.render(, document.body)
  • 6.

    [仅限遗留项目], < circa 2016] In a general overview, React Router和它的技术与更传统的JavaScript路由器(比如Backbone的Router)有什么不同?

    View answer

    “传统的”路由器一直很受欢迎 Backbone.Router 建立一组预定义路由, 其中,每条路由定义了触发路由时要采取的一系列动作. 结合主干时.使用React的路由器,当路由发生变化时,你可能需要挂载和卸载React组件:

    var MyRouter = Backbone.Router.extend({
        routes: {
            “家”:“showHome”,
            “搜索/:问:“showSearch”,
            “*违约”:“show404”
        },
        showHome(){
            DOM.unmountComponentAtNode(文档.body)
            DOM.render(, document.body)
        },
        showSearch(q){
            DOM.unmountComponentAtNode(文档.body)
            DOM.render(, document.body)
        },
        show404(){
            DOM.unmountComponentAtNode(文档.body)
            DOM.render(, document.body)
        }
    })
    

    路由器存在于React组件的外部, 并且VDOM必须频繁地挂载和卸载, 这可能会带来一系列问题. React Router不仅关注“单级”路由,还支持——不, empowers -创建可以“自己决定”在其中渲染什么的hoc.

    这就是高级HOC实现可以真正帮助简化看似复杂的概念的地方. 让我们看看使用一个小型路由器来评估在React hoc中嵌入应用路由器的一些好处. 这里,我们定义了一个组件来包装它自己的路由机制(router() 这里没有提供 universal-utils):

    // router(routesObject, callback) --> when a route event occurs, we invoke callback() with
    //通过路由参数传递的React元素和props
    
    类Router扩展组件{
        constructor(...a){
            super(...a)
    
            let p = this.props
    
            this.state = {
                routes: p.routes || {},
                default: p.default || '/'
            }
    
            this.Router = Router (this ..state.routes, (el, props) => {
                this.current = el
            })
    
            this.router.trigger(this.state.default)
        }
        render(){
            return this.current()
        }
    }
    
    DOM.render( ,
        '/search/:q': ({q}) => ,
        '*': () => 
    }}/>, document.body)
    

    This Router 组件选项,用于解析 routes object passed into this.props 而不是读取传递的React组件数组 this.props.children. React Router选择后一种技术. Need proof? 提供的示例代码 React Router’s docs:

    DOM.render(
        
            
                
                
                    
                
                
            
      
    , document.body)
    

    A 组件有一个或多个 作为项的组件 this.props.children, and s can have sub-s. React Router的代码递归地遍历子树,直到没有更多的子树需要处理, 允许开发人员在封装子路由的结构中递归地声明路由, 而不是必须实现一个骨干网式的平面路由列表(i.e. "/", "/about", "/users", "/users/:id", etc).

    7.

    为什么类方法需要绑定到类实例, 怎样才能避免绑定?

    View answer

    的值 this 根据当前上下文进行更改. 在React类组件方法中,开发人员通常期望 this 引用组件的当前实例, 因此有必要将这些方法绑定到实例. 通常这是在构造函数中完成的,例如:

    类SubmitButton扩展React.Component {
      构造函数(道具){
        super(props);
        this.state = {
          isFormSubmitted:假
        };
        this.handleSubmit = this.handleSubmit.bind(this);
      }
    
      handleSubmit() {
        this.setState({
          isFormSubmitted:真
        });
      }
    
      render() {
        return (
          
        )
      }
    }
    

    有几种常用的方法可以避免这种绑定:

    1. 将事件处理程序定义为内联箭头函数

    For example:

    类SubmitButton扩展React.Component {
      构造函数(道具){
        super(props);
        this.state = {
          isFormSubmitted:假
        };
      }
    
      render() {
        return (
          
        )
      }
    }
    

    像这样使用箭头函数是有效的,因为箭头函数没有自己的函数 this context. Instead, this 将引用定义箭头函数的上下文—在本例中, 的当前实例 SubmitButton.

    2. 将事件处理程序定义为分配给类字段的箭头函数

    类SubmitButton扩展React.Component {
      state = {
        isFormSubmitted:假
      }
    
      handleSubmit = () => {
        this.setState({
          isFormSubmitted:真
        });
      }
    
      render() {
        return (
          
        )
      }
    }
    

    注:截至2019年9月, class字段是第三阶段ECMAScript提案,还不是已发布的ECMAScript规范的一部分. However, 它们可以在Google Chrome和Mozilla Firefox中使用,并且通常在React项目中使用.

    3. 使用带有钩子的函数组件

    使用React中的hooks功能,可以使用state而不使用 this,这简化了组件实现和单元测试.

    For example:

    const SubmitButton = () => {
      const [isFormSubmitted, setIsFormSubmitted] = useState(false);
    
      return (
        
      )
    };
    
    8.

    解释测试中浅渲染组件的优点和缺点.

    View answer

    Positives:

    • 浅层渲染组件比完全渲染组件要快. 当React项目包含大量组件时, 这种性能差异会对执行单元测试所花费的总时间产生重大影响.
    • 浅渲染防止在被测试组件的边界之外进行测试——这是单元测试的最佳实践.

    Negatives:

    • 浅渲染与组件作为应用程序一部分的实际使用不太相似, 因此,它可能无法捕捉到某些问题. 以a为例 组件,该组件呈现 component. 在实际应用程序中,如果 组件损坏并抛出错误 将无法渲染. 但是,如果单元测试 只使用浅渲染,那么这个问题将不会被识别,除非 也包含了单元测试.
    9.

    如果您希望组件在初始呈现时只执行一次操作- e.g.做一个web分析调用——你如何用一个类组件实现这一点呢? 如何用一个功能组件来实现它?

    View answer

    使用类组件

    The componentDidMount() 生命周期钩子可以与类组件一起使用:

    类主页扩展React.Component {
      componentDidMount () {
        trackPageView(“主页”);
      }
      render() {
        return 
    Homepage
    ; } }

    类中定义的任何操作 componentDidMount() 生命周期钩子只在组件第一次挂载时调用一次.

    使用功能组件

    The useEffect() Hook可以与函数组件一起使用:

    const Homepage = () => {
      useEffect(() => {
        trackPageView(“主页”);
      }, []);
      
      return 
    Homepage
    ; };

    The useEffect() Hook比用于类组件的生命周期方法更灵活. 它接收两个参数:

    1. 它接受的第一个参数是要执行的回调函数.
    2. 它接受的第二个可选参数是一个数组,其中包含要跟踪的所有变量.

    作为第二个参数传递的值控制何时执行回调:

    • 如果第二个参数 is undefined,该回调函数在每次呈现组件时执行.
    • 如果第二个参数 包含一个变量数组, 然后,回调将作为第一个渲染周期的一部分执行,并将在每次修改数组中的项时再次执行.
    • 如果第二个参数 包含空数组,回调将只执行一次,作为第一个渲染周期的一部分. 上面的示例显示了传递空数组如何导致与 componentDidMount() 钩子在函数组件中.
    10.

    React应用的样式化最常用的方法是什么?

    View answer

    CSS Classes

    React允许为组件指定类名, 就像在HTML中为DOM元素指定类名一样.

    当开发人员在开发传统web应用程序后第一次开始使用React时, 他们经常使用CSS类进行样式化,因为他们已经熟悉这种方法.

    Inline CSS

    使用内联CSS对React元素进行样式化,允许使用易于理解的CSS将样式完全限定在元素范围内, standard approach. 然而,有些样式特性是内联样式不可用的. 的样式 :hover pseudo-classes.

    预处理器如Sass, Stylus和Less

    预处理器通常用于React项目. This is because, like CSS, 它们被开发人员很好地理解,并且如果React被集成到遗留应用程序中,通常已经在使用它们了.

    CSS-in-JS模块,如样式组件,情感,和style -jsx

    CSS-in-JS模块是一种流行的React应用样式选择,因为它们与React组件紧密集成. 例如,它们允许在运行时基于React props更改样式. 另外,在默认情况下,这些系统中的大多数将所有样式限定在被样式化的各自组件中.

    11.

    如果你正在开发一个渲染页面非常慢的React应用程序, 你将如何着手调查和解决这个问题?

    View answer

    如果在React应用中看到一个性能问题,比如渲染缓慢, 第一步是使用React Developer Tools浏览器插件中提供的Profiler工具, 谷歌浏览器和火狐浏览器都可以使用. Profiler工具允许开发人员查找需要很长时间渲染或渲染频率过高的组件.

    React应用程序中最常见的问题之一是组件不必要地重新渲染. 在这些情况下,React提供了两个有用的工具:

    • React.memo()这可以防止不必要的重新呈现功能组件
    • PureComponent这可以防止不必要的重新呈现类组件

    这两种工具都依赖于传入组件的道具的粗略比较——如果道具没有改变的话, 然后组件将不会重新渲染. 虽然这两个工具都非常有用, 这种肤浅的比较带来了额外的性能损失, 因此,如果使用不当,两者都会对性能产生负面影响. 通过使用React Profiler, 可以在使用这些工具之前和之后测量性能,以确保通过进行给定的更改实际提高了性能.

    12.

    在高层次上,什么是虚拟DOM (VDOM)以及React如何使用它来渲染到DOM?

    View answer

    VDOM是一个编程概念,提供了React架构的关键部分. 而不是直接与DOM交互, 更改首先呈现给vdom,这是DOM目标状态的轻量级表示.

    对VDOM所做的更改被批处理在一起,以避免对DOM进行不必要的频繁更改. 每次将这些批处理更改持久化到DOM, React在当前表示和之前保存到DOM的表示之间创建了一个差异, 然后对DOM应用diff.

    DOM的这个抽象层为开发人员提供了一个简单的接口,同时允许React以高效和高性能的方式更新DOM.

    13.

    什么是道具钻井?如何避免?

    View answer

    在构建React应用程序时, 通常需要深度嵌套的组件使用层次结构中更高的另一个组件提供的数据.

    考虑以下示例组件:

    • , which includes selectedUserAddress 在其组件状态下呈现 component
    • , which renders a component
    • , which renders a component
    • A 组件,该组件需要 selectedUserAddress 属性存储在 state

    最简单的方法是简单地传递一个 selectedUserAddress 从源组件到深度嵌套组件,从层次结构中的每个组件到下一个组件. 这叫做道具钻井.

    在这种情况下,支柱钻井的主要缺点是组件不应该知道数据 and -变得不必要的复杂,更难以维护.

    为了避免钻取道具,一个常见的方法是使用React上下文. This allows a Provider 提供要定义的数据的组件, 并允许嵌套组件通过a Consumer component or a useContext hook.

    而上下文可以直接用于共享全局状态, 还可以通过状态管理模块间接使用上下文, such as Redux.

    14.

    What is the StrictMode 组件以及为什么要使用它?

    View answer

    一个组件是否包含在React中,以提供组件中潜在问题的额外可见性. 如果应用程序在开发模式下运行, 任何问题都会记录到开发控制台, 但是,如果应用程序在生产模式下运行,则不会显示这些警告.

    Developers use 查找诸如已弃用的生命周期方法和遗留模式等问题, 以确保所有React组件都遵循当前的最佳实践.

    可以应用于应用程序组件层次结构的任何级别吗, 哪一个允许它在代码库中被增量地采用.

    15.

    JavaScript库(如React)和JavaScript框架(如Angular)在架构上的关键区别是什么? 这将如何影响项目使用其中一个而不是另一个的决策?

    View answer

    React使开发人员能够呈现用户界面. 创建一个完整的前端应用程序, 开发人员需要其他组件, 比如Redux这样的状态管理工具.

    Like React, Angular允许开发者渲染用户界面, 但它是一个“包含电池”的框架,包括规范性, 针对常见需求(如状态管理)的固执己见的解决方案.

    虽然在比较React和Angular时还有很多其他的考虑因素, 这个关键的架构差异意味着:

    • 使用像React这样的库可以让项目更有能力对系统的某些部分进行改进,比如再一次, 状态管理——随时间推移, 当开源社区创建新的解决方案时.
    • 使用Angular这样的框架可以让开发人员更容易上手,还可以简化维护.
    16.

    如何使用自动化工具来提高React应用程序的可访问性?

    View answer

    可用于识别可访问性问题的自动化工具主要有两类:

    静态分析工具

    像ESLint这样的检测工具可以与ESLint -plugin-jsx-a11y这样的插件一起使用,以在组件级别分析React项目. 静态分析工具运行速度非常快,因此它们以较低的成本带来了良好的效益.

    Browser Tools

    浏览器可访问性工具(如aXe和Google Lighthouse)在应用程序级别执行自动可访问性. 这可以发现更多现实世界的问题, 因为浏览器是用来模拟用户与网站交互的方式. 这些工具中的许多都可以在持续集成环境中运行,例如Travis或Jenkins. 因为这些工具需要更长的时间来执行, 许多开发人员只是偶尔在他们的本地浏览器中运行这些工具, 例如当达到项目里程碑时.

    17.

    [仅限遗留项目]: React < 16.[8]什么是纯功能组件?

    View answer

    到目前为止,我们所看到的传统React组件都是创建一个类 类Example扩展React.Component or React.createClass(). 的方法创建有状态组件 state (i.e. this.setState(), getInitialState(), or this.state = {} inside a constructor()).

    如果我们不打算让组件需要状态, 或者需要生命周期方法, 我们实际上可以用纯函数来编写组件, 因此有了“纯功能组件”一词:

    函数日期(道具){
        让{msg="日期是:"}= props
        let now = new Date()
        return 
    {msg}
    }

    这个返回一个React元素的函数可以在任何我们认为合适的地方使用:

    DOM.render(
    )

    你可能注意到了 also takes a prop -我们仍然可以向组件传递信息.

    18.

    React如何处理或限制特定类型的Props,或者要求特定的Props存在?

    View answer

    你可能还记得前面的一个例子,它看起来像这样(代码的一些部分被遗漏了):

    类LazyLoad扩展组件{
        constructor(p){
            super(p)
            this.状态={已加载:0}
        }
        render(){
            让{children} = this.props,
                {loaded} = this.state
            return 
    {loaded && children}
    } }

    When rendering the ,我们可以传入props (i).e. ). Props本质上是从父渲染上下文传递给组件的输入或值, 并且将props传递给元素的代码可能与您的代码不兼容. For example, top 这里似乎只是一个数字, 但是在我的组件被渲染之前,我能够验证道具实际上是一个数字吗? 当然可以用 每一个使用道具的组件. 然而,React为我们提供了一个更简单、更简短的解决方案:Prop类型.

    let p = React.PropTypes
    LazyLoad.PropTypes = {
        top: p.number
    }
    

    当使用React的非小型化开发版本(i.e. 当在开发中构建和测试时), React会抛出一个错误,提醒你在任何实例中,如果一个Prop缺失或类型错误. Above, top should always be a number.

    We can make top a required prop by adding:

    let p = React.PropTypes
    LazyLoad.PropTypes = {
        top: p.number.isRequired
    }
    

    可以使用PropTypes 来测试Props的任何值. 以下是React为JavaScript内置类型提供的几个快速类型检查器:

    • React.PropTypes.array,
    • React.PropTypes.bool,
    • React.PropTypes.func,
    • React.PropTypes.number,
    • React.PropTypes.object,
    • React.PropTypes.string,
    • React.PropTypes.symbol,

    我们还可以测试props是React和DOM类型:

    • React.PropTypes.node,
    • React.PropTypes.element,

    我们有能力测试更复杂的类型, such as “shapes”, “instances of”, 或者" collections of ":

    • React.PropTypes.运算符(消息),
    • React.PropTypes.之一(['新闻','照片']),
    • React.PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.number, React.PropTypes.运算符(消息)])
    • React.PropTypes.arrayOf(React.PropTypes.number),
    • React.PropTypes.形状({颜色:反应.PropTypes.字符串,字体大小:React.PropTypes.number })

    使用这些PropTypes产生错误并跟踪bug. 如果使用得当, PropTypes将防止您的团队在调试和文档编制过程中浪费太多时间, 确保更严格的标准和对不断增长的组件库的理解.

    19.

    [仅限遗留项目]: React < 15.比较和对比在ES5和ES2015(也称为ES6)中创建React组件. 使用其中一个或另一个的优点和缺点是什么? 包括关于默认道具、初始状态、PropTypes和DisplayName的注释.

    View answer

    以ES5的方式创建React组件需要使用 React.createClass() method:

    
    var Comments = React.createClass({
    
        displayName:“评论”,
    
        getInitialState:函数(){
            返回{注释:[]}
        },
    
        getDefaultProps:函数(){
            返回{some_object: {a:1, b:2, c:3}}
        },
    
        _handleClick:函数(){
            alert('hello world!')
        },
    
        render: function(){
            return 
    There are {this.state.comments.length} comments
    } })

    This Comments 组件现在既可以在另一个React组件中渲染,也可以直接在调用 ReactDOM.render():

    ReactDOM.render(, document.querySelector('.app'))
    

    ES5组件有一些特殊的特性,我们会注意到:

    1. 与上面的示例一样,要将状态设置为初始值,请创建 getInitialState() 组件上的函数. 它返回的将是组件在渲染时的初始状态.
    2. 此外,您可以将组件的默认道具设置为具有特定值 getDefaultProps() 在ES5版本中使用.
    3. The displayName 在调试和错误报告中使用React. 如果使用JSX,则 displayName 是自动填写的.
    4. For some, 通常的做法是用下划线前缀来表示添加到React组件中的自定义方法, hence _handleClick. _handleClick is passed as the onClick 上面代码中按钮的回调. 在React的ES6 API中我们不能这么容易地做到这一点,因为ES5版本有 autobinding,但ES6没有. 让我们看看自动绑定提供了什么:

    Auto-binding

    考虑下面这段代码:

    var thing = {
        name: 'jen',
        说话:function(){控制台.log(this.name) }
    }
    
    window.addEventListener(“弹起”,的事情.speak)
    

    Invoking thing.speak() 在控制台中将记录日志 "jen",但按下一个键将登录 undefined because the context 是全局对象. 浏览器的全局对象- window – becomes this inside the speak() function, so this.name becomes window.name, which is undefined.

    ES5中的React会自动执行自动绑定,它可以有效地完成以下工作:

    window.addEventListener(“弹起”,的事情.speak.bind(thing))
    

    Autobinding 自动将我们的函数绑定到React组件实例,以便在 render() works seamlessly.

    以ES6的方式创建React组件的工作方式略有不同,更倾向于ES6 class ... extends ... 语法,没有自动绑定特性:

    类Comments扩展React.Component {
        constructor(props){
            super(props)
            this.状态={评论:[]}
        }
    
        _handleClick(){
            alert('hello world!')
        }
    
        render(){
            return 
    There are {this.state.comments.length} comments
    } } Comments.defaultProps = {a:1, b:2, c:3} Comments.displayName = 'Comments'
    1. 注意,在ES6中,我们有 constructor() 我们用来设置初始状态的,
    2. 我们可以添加默认的道具和显示名作为新创建类的属性
    3. The render() 方法,它可以正常工作,但是我们必须改变传递回调函数的方式. 目前的做法() will create a new function each time the component is re-rendered; so if it becomes a performance bottleneck, 你总是可以手动绑定并存储回调:
    类Comments扩展React.Component {
      constructor(...args) {
        super(...args);
        this.state = {toggledOn: false};
        this._handleClick =这个._handleClick.bind(this);
      }
    
      _handleClick() {
        this.setState(prevState => ({ toggledOn: !prevState.toggledOn });
      }
    
      render() {
        return 
      }
    }
    

    或者使用类字段语法:

    类Comments扩展React.Component {
      state = {toggledOn: false};
    
      _handleClick = () => {
        this.setState(prevState => ({ toggledOn: !prevState.toggledOn }));
      };
    
      render() {
        return 
      }
    }
    

    npm上的许多React实用程序库都提供了一个函数来绑定构造函数中的所有处理程序, 就像React一样.

    20.

    [仅限遗留项目]: React < 15.比较和对比在React组件中加入mixins和实施模块化. (extend, createClass 和混合,hoc)为什么要使用这些技术,每种技术的缺点是什么?

    View answer

    模块化——实际上——是在编码时有意为之, 然后在重构时部分完成.

    让我们首先绘制一个场景,我们将使用上面的每种方法对其建模. 假设我们有三个React组件: onScrollable, Loadable, and Loggable.

    • an onScrollable 组件将监听 window.onscroll 事件,并使用日志记录机制进行记录
    • a Loadable 组件将不会呈现,直到一个或多个异步请求完成加载, 并将使用日志记录机制记录何时发生这种情况
    • a Loggable 组件提供了日志记录机制 console, a Winston Node.js logging setup 在我们自己的服务器上,或者通过JSON请求记录日志的第三方日志服务上

    首先,让我们用React的ES5 API和 mixins.

    交互代码示例见 Matthew Keas的github.

    var onKeypress = {
        componentDidMount () {
            this.onpress && window.addEventListener(“弹起”,这一点.onpress)
        },
        componentWillUnmount () {
            this.onpress && window.removeEventListener(“弹起”,这一点.onpress)
        }
    }
    
    var Loadable = {
        componentDidMount () {
            if(this.load){
                this.设置状态({加载:假})
                Promise.all([].concat(this.load))
                    .then(() =>
                        this.设置状态({加载:真}))
            }
        }
    }
    
    var Loggable = {
        log(...args) {
            alert(args)
        }
    }
    
    var示例= React.createClass({
        mixins: [Loggable, Loadable, onKeypress],
        componentWillMount () {
            this.onpress = (e) => this.log(e.which, 'pressed!')
            this.load = [new Promise((res,rej) => setTimeout(res, 3000))]
            this.log = (...args) => console.log(...args)
        },
        getInitialState(){
            return {}
        },
        render() {
            if(!this.state.loaded)
                return 
    loading...
    return
    test
    } }) DOM.render(, document.body)

    让我们注意一下上面的代码:

    1. 创建了三个pojo(纯JS对象),其中包含生命周期和/或自定义方法.
    2. When creating the Example Component, we add mixins: [Loggable, Loadable, onKeypress],这意味着来自这三个对象的任何函数都包含在 Example class.
    3. Both onKeypress and Loadable add a componentDidMount(),但这并不意味着后者可以抵消前者. In fact, all componentDidMount() 当事件发生时,将调用每个mixin中的函数. 对于添加到mixins中的所有生命周期方法也是如此. This way, both the onKeypress and Loadable Mixins将同时工作!

    混合是可能的,但不是内置到React的ES6 API. However, ES6 API使得创建扩展另一个自定义组件的自定义组件变得更加容易.

    所以我们的Components的原型链如下所示:

    [Example] --- extends ---> [Loggable] --- extends ---> [Loadable] --- extends ---> [onKeypress]
    

    这将导致组件如下所述:

    class onKeypress {}
    类Loadable扩展onKeypress {}
    类Loggable扩展Loadable {}
    类Example扩展Loggable {}
    

    创建匿名类在这里会有所帮助,因为这样Loggable就不必扩展Loadable了 and onKeypress.

    类扩展(类a扩展Loggable扩展 ...) {
    
    }
    

    With a mixin() 函数,这看起来更像:

    类Example扩展mixin(Loggable, Loadable, onKeypress) {
    
    }
    

    Let’s try to write mixin() 通过构建一个匿名类链来扩展 Loggable, Loadable, and onKeypress:

    const mixin = (...classes) =>
        classes.reduce((a,v) => {
            返回(类temp扩展a)
        }, (class temp {}))
    

    不过,这里有一个警告——如果 Loadable extends onKeypress and both implement componentDidMount(), Loadable的版本将在原型链上较低,这意味着从 onKeypress 永远不会被调用.

    这里的要点是,mixin模式不容易仅仅依靠ES6来实现 extends approach. 让我们试着实现 mixin() 再一次,但是建立一个更健壮的函数:

    const mixin = (...classes) => {
        class _mixin {}
        let proto = _mixin.prototype
    
        classes.map(({prototype:p}) => {
            Object.getOwnPropertyNames (p).map(key => {
                let oldFn = proto[key] || (() => {})
                proto[key] = (...args) => {
                    oldFn(...args)
                    return p[key](...args)
                }
            })
        })
    
        return _mixin
    }
    

    This new mixin() 实现映射到每个类,并将父类的函数调用级联 componentDidMount() 在孩子的旁边 componentDidMount().

    有类似的实现 mixin() 可以在npm上使用 react-mixin and es6-react-mixins.

    We use mixin() from above like so:

    交互式代码示例可在 http://goo.gl/VnQ21R

    class A {
        componentDidMount () {
            console.log(1)
        }
    }
    
    class B {
        componentDidMount () {
            console.log(2)
        }
    }
    
    类C扩展mixin(A,B) {
        componentDidMount(...p){
            super.componentDidMount(...p)
            console.log(3)
        }
    }
    
    let c = new C()
    c.componentDidMount() //记录1,2,3
    

    Recently, React提供了对用ES6类声明的React组件的支持,并记录了它的偏好. ES6类允许我们用更少的代码创建组件层次结构, 然而,这使得创建一个从多个mixins继承属性的组件变得更加困难, 而是强迫我们创造原型链.

    21.

    [仅限遗留项目]: React < 16] Compare and contrast the various React Component lifecycle methods. 理解这些如何有助于构建某些接口/功能?

    View answer

    有几个React生命周期方法可以帮助我们管理组件在应用程序生命周期中的异步和非确定性质——我们需要提供方法来帮助我们处理组件创建时的情况, rendered, updates, 或者从DOM中删除.

    我们首先对生命周期方法进行分类和定义:

    The “Will’s” -在表示的事件发生之前调用.

    • componentWillMount () -在客户端和服务器端调用一次,在初始渲染发生之前立即调用. 如果你在这个方法中调用setState, render() 将看到更新后的状态,并且尽管状态发生了更改,但只执行一次.
    • nextProps componentWillReceiveProps(对象) -当组件接收新道具时调用. 初始呈现时不调用此方法. Calling this.setState() 在此函数中不会触发额外的渲染. 一个常见的错误是,在此生命周期方法中执行的代码假设props已经更改.
    • componentWillUnmount () -在组件从DOM卸载之前立即调用. 在此方法中执行任何必要的清理, 例如使计时器失效或清除在componentDidMount中创建的任何DOM元素.
    • componentWillUpdate(对象nextProps,对象nextState) -当接收到新的道具或状态时,在渲染之前立即调用. 初始呈现时不调用此方法.

    The “Did’s”

    • componentDidMount() - Invoked once, 仅在客户机上(不在服务器上), 在初始渲染发生后立即. 在生命周期的这一点上,您可以访问对子节点的任何引用.g.,以访问底层DOM表示). The componentDidMount() 方法在父组件之前调用子组件的方法.
    • componentDidUpdate(对象prevProps,对象prevState) -在组件更新刷新到DOM后立即调用. 初始呈现时不调用此方法. 当组件被更新时,利用这个机会对DOM进行操作.

    The “Should’s”

    • shouldComponentUpdate(对象nextState,对象nextProps) -在接收到新的道具或状态时,在渲染之前调用. 此方法不会在初始呈现时调用 forceUpdate() is used. 利用这个机会 return false 当你确定转换到新的道具和状态不需要更新组件时.

    对这些是如何结合在一起的有深刻的理解 setState() or forceUpdate() 影响生命周期——将帮助有意识的React开发人员构建健壮的ui.

    面试不仅仅是棘手的技术问题, 所以这些只是作为一个指南. 并不是每一个值得雇佣的“A”候选人都能回答所有的问题, 回答所有问题也不能保证成为A级考生. 一天结束的时候, 招聘仍然是一门艺术,一门科学,需要大量的工作.

    Why Toptal

    厌倦了面试候选人? 不知道该问什么才能让你得到一份好工作?

    让Toptal为你找到最合适的人.

    Hire a Top React.js Developer Now

    我们的React独家网络.js Developers

    想找一份React的工作.js Developer?

    让Toptal为你找到合适的工作.

    Apply as a React.js Developer

    工作机会从我们的网络

    提出面试问题

    提交的问题和答案将被审查和编辑, 并可能会或可能不会选择张贴, 由Toptal全权决定, LLC.

    *所有字段均为必填项

    Looking for React.js Developers?

    Looking for React.js Developers? 看看Toptal的React.js developers.

    Sergii Petryk

    Freelance React.js Developer

    CanadaToptal Member Since October 9, 2019

    Sergii是一名高级全栈开发人员,在不同行业的软件开发方面拥有超过10年的经验, 最近的五年主要集中在React和TypeScript上. 作为一名真正了解客户基础设施和痛点的熟练开发人员,他在行业领先的公司中备受尊敬. Sergii还具有设计和构建解决方案所需的动手技术才能.

    Show More

    Matt Reynolds

    Freelance React.js Developer

    United KingdomToptal Member Since November 16, 2022

    Matt是一位经验丰富的全栈软件工程师,拥有近10年的web开发经验, 最近是一家成功的电子商务创业公司. 他专攻Next.js、React和React Native web、移动和电视应用. Matt还曾与大型全球媒体公司合作,为SaaS应用程序开发api和数据库集成.

    Show More

    Maria Szubski

    Freelance React.js Developer

    United StatesToptal Member Since August 25, 2022

    Maria是一名前端开发人员,专攻React和GraphQL. 她有五年建立B2C和B2B网站的经验,并有三年通过当地技术研讨会指导初级开发人员. 她还作为独立/首席开发人员以及跨各种技术的大型开发团队的一员做出了贡献. Maria的UX设计背景帮助她快速将模型转换为响应式ui, 提供可访问性方面的最佳实践, user experience, 以及可维护的代码标准.

    Show More

    Toptal Connects the Top 3% 世界各地的自由职业人才.

    加入Toptal社区.

    Learn more