实际上, 想要去了解某人对React的理解程度,仅凭这些面试题或许远远不够。 react面试题 这篇文章更应该取名为关于react你不是非知不可的东西,但如果了解了的话总是有好处的。
当你调用setState的时候实际发生了什么?
当你调用setState这个方法,React会做的第一件事就是把你传递给setState的参数对象合并到组件原先的state。这个事件会导致一个“reconciliation”(调和)的过程。reconciliation的最终目标就是,尽可能以最高效的方法,去基于新的state来更新UI。为了达到这个目的,React会构建一个React元素树(你可以把这个想象成一个表示UI的一个对象)。一旦这个树构建完毕,React为了根据新的state去决定UI要怎么进行改变,它会找出这棵新树和旧树的不同之处。React能够相对精确地找出哪些位置发生了改变以及如何发生了什么变化,并且知道如何只通过必要的更新来最小化重渲染。
React元素(Element) 和 React组件(Component)之间的区别 ?
简而言之, React的element可以看作是你在屏幕想看到的东西。高级的说法就是UI的对象表述。
一个React组件是可以接受参数并且返回一个react element的函数或者类(通常通过JSX来触发createElement这个方法)
想了解更多,可以查看这篇文章-> React Elements vs React Components
什么时候使用 Class Component 而非 Functional Component?
如果你的组件有state或者使用了生命周期函数,那么请使用Class component。 否则,使用Functional component。
refs 是什么,还有为什么它很重要?
Refs是你访问DOM元素或者组件实例的一个安全门。为了使用它们,你可以在组件加上一个ref属性,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属性,它的值是一个函数。这个函数接受这个input对应的真实DOM元素,我们绑定到this后得到该实例以在handleSubmit这个方法里访问它。
人们常常会误解,为了使用refs必须使用class component,但实际refs还可以通过闭包在functional component中使用。
function CustomForm ({handleSubmit}) {
let inputElement
return (
<form onSubmit={() => handleSubmit(inputElement.value)}>
<input
type='text'
ref={(input) => inputElement = input} />
<button type='submit'>Submit</button>
</form>
)
}
什么是keys 而且为什么他们很重要
Keys负责帮助React跟踪列表中哪些元素被改变/添加/移除。
render () {
return (
<ul>
{this.state.todoItems.map(({task, uid}) => {
return <li key={uid}>{task}</li>
})}
</ul>
)
}
在同级元素中每一个元素的key都必须是唯一的,我们已经多次提到了reconciliation和它其中的一个过程是比较新的element tree和上一次的element tree。keys使列表进行diff的过程更加高效,因为React可以利用子元素的key在比较两棵树的时候快速得知一个元素是新的还是刚刚被移除。没有keys,React便不知道当前哪一个对应的item被移除了。所以别小看了keys.
如果你创建了一个像下面的Twitter元素,那么Twitter的组件(类定义)应该是什么样的?
<Twitter username='tylermcginnis33'>
{(user) => user === null
? <Loading />
: <Badge info={user} />}
</Twitter>
import React, { Component, PropTypes } from 'react'
import fetchUser from 'twitter'
// fetchUser take in a username returns a promise
// which will resolve with that username's data.
class Twitter extends Component {
// finish this
}
如果你对回调渲染模式(Render Callback Pattern)不熟悉,上面的代码看起来可能有点奇怪。在这种模式下,组件接受某个函数作为它的子元素。注意一下里面包含的东西。与之前看到的嵌入一个组件的方式有所不同,这个Twitter组件的子元素是个函数,也就是说,Twitter元素接受一个函数作为子组件时,我们在渲染函数中以props.children进行调用。
以下是我的答案:
import React, { Component, PropTypes } from 'react'
import fetchUser from 'twitter'
class Twitter extends Component {
state = {
user: null,
}
static propTypes = {
username: PropTypes.string.isRequired,
}
componentDidMount () {
fetchUser(this.props.username)
.then((user) => this.setState({user}))
}
render () {
return this.props.children(this.state.user)
}
}
从上面看到,props.children这个函数被调用,我可以还可以给它传递user这个状态。
这种模式的好处是父组件和子组件进行解耦。父组件专注于管理状态,可以直接访问子组件的内部状态,从而控制子组件的UI要如何显示。
为了进一步说明,加入我们想要渲染Profile而不是Badge。接下来利用回调渲染模式,我们无需改变我们对父组件(Twitter)的实现,通过修改回调函数就可以很容易的替换需要显示的UI。
<Twitter username='tylermcginnis33'>
{(user) => user === null
? <Loading />
: <Profile info={user} />}
</Twitter>
Controlled Component和Uncontrolled Component 有什么区别?
React大部分的情况是组件可以控制和管理它们自己的state。当我们引入原生的HTML表单元素(input,select,textarea,等)时,我们是要遵循react的“单一数据源”将数据托管到react组件还是和以往处理HTML表单一样交由DOM进行控制?这两个问题是controlled Component和Uncontrolled Component的主要区别。
一个 controlled 组件是由react进行控制并遵循单一数据源的原则。就像底下的代码,username不存在于DOM中,而是存在于我们组件的state中。我们想要更新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 组件是由DOM来存放你的表单数据,而不是由React组件中。
通常使用refs 来操作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>
)
}
}
虽然非受控组件看起来相对简单,因为你只需要通过refs就可以获取DOM的值,但是通常实际开发中我们都会推荐使用受控组件。主要的原因就是受控组件有助于进行表单验证,控制按钮是否可点击,强制输入格式,并且它也更符合“React way”
在生命周期的哪个阶段发生ajax请求而且为什么?
AJAX请求应该在 componentDidMount函数 进行。 有以下几个原因:
shouldComponentUpdate 的作用以及它的重要性?
上面我们了解了reconciliation这个过程和调用setState发生的事情. shouldComponentUpdate是一个允许我们自行决定某些组件(以及他们的子组件)是否进行更新的生命周期函数。为什么想要这么做?原因就是上面提过的“reconciliation的最终目的是尽可能以最有效的方式去根据新的state更新UI”。如果我们已经知道UI的哪些状态无需发生改变,也就没必要去让React去决定它是否该改变。 shouldComponentUpdate返回falss, React就会知道当前的组件和其子组件只需保留原样。
如何告诉React它应该编译生产环节版本?
通常你会使用Webpack的 DefinePlugin方法设置 NODE_ENV 为 production. 它会忽略propType validation和一些警告信息。更重要的是,它也是减少代码的好方法,因为React使用Uglify插件来移除了生产环境下不需要的注释等信息。
为什么使用
React.Children.map(props.children, () => )
而非props.children.map(() => )
因为你不能保证 props.children 就是一个数组。
拿以下的代码作为例子
<Parent>
<h1>Welcome.</h1>
</Parent>
在Parent里面如果我们用props.children.map
来遍历我们的子元素,它就会报错,因为子元素是个对象,不是数组。
只有当子元素个数超过一个的情况下,React会将props.children
设置为数组,比如下面的代码:
<Parent>
<h1>Welcome.</h1>
<h2>props.children will now be an array</h2>
</Parent>
这就是为什么要优先使用React.Children.map
,因为它考虑了 props.children可能是个数组也可能是个对象。
描述一下React的事件处理逻辑
为了解决浏览器的兼容问题,React的事件处理程序会被传递给SyntheticEvent实例,它是对浏览器的原生事件的一层封装。这种合成的事件和你所使用的原生事件拥有同样的接口,但是它们能保证了不同浏览器行为的一致性。
有趣的一点是,React并不会真正地把事件附着到子节点。React使用一个单独的事件监听器来将所有事件发送到顶层处理。这对性能有很大的好处,因为它让React无需在更新DOM的时候去跟踪附着在DOM的每一个事件监听器。
createElement 和 cloneElement有什么不同?
createElement 是JSX进行编译之后React用来创建一个React Elements(UI的对象表述)的东西。cloneElement则是用来克隆一个元素并且给它传递新的props.它们的名字就是区别 ?。
setState 第二个参数是什么,它有什么作用?
一个可以在setState调用完成component重新渲染后被调用的回调函数,
setState是异步操作函数,这也是它为什么把一个回调函数作为第二个参数的原因。虽然通常我更建议用一个生命周期函数去取代这个回调函数,但是知道这个东西的存在也不是什么坏事。
this.setState(
{ username: 'tylermcginnis33' },
() => console.log('setState has finished and the component has re-rendered.')
)
下面这段代码有什么问题吗?
this.setState((prevState, props) => {
return {
streak: prevState.streak + props.count
}
})
这段代码并没错 ?. 它只是比较少见,你可以传递一个接受组件的state和props然后计算返回一个新state 的函数给setState ,就像上面这段代码。这段代码不仅没有错,而且如果你是要基于上一次的state来设置新的state,这种做法是值得推荐的。
往期精选文章 |
---|
ES6中一些超级好用的内置方法 |
浅谈web自适应 |
使用Three.js制作酷炫无比的无穷隧道特效 |
一个治愈JavaScript疲劳的学习计划 |
全栈工程师技能大全 |
WEB前端性能优化常见方法 |
一小时内搭建一个全栈Web应用框架 |
干货:CSS 专业技巧 |
四步实现React页面过渡动画效果 |
让你分分钟理解 JavaScript 闭包 |
小手一抖,资料全有。长按二维码关注京程一灯,阅读更多技术文章和业界动态。