1、redux中间件
中间件提供第三方插件的模式,自定义拦截 action -> reducer 的过程。变为 action -> middlewares -> reducer 。这种机制可以让我们改变数据流,实现如异步 action ,action 过滤,日志输出,异常报告等功能。
常见的中间件:
redux-logger:提供日志输出
redux-thunk:处理异步操作
redux-promise:处理异步操作,actionCreator的返回值是promise
2、redux有什么缺点
1.一个组件所需要的数据,必须由父组件传过来,而不能像flux中直接从store取。
2.当一个组件相关数据更新时,即使父组件不需要用到这个组件,父组件还是会重新render,可能会有效率影响,或者需要写复杂的shouldComponentUpdate进行判断。
3、react组件的划分业务组件技术组件?
根据组件的职责通常把组件分为UI组件和容器组件。
UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑。
两者通过React-Redux 提供connect方法联系起来。
具体使用可以参照如下链接:http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_three_react-redux.html
4、react生命周期函数
这个问题要考察的是组件的生命周期
一、初始化阶段:
getDefaultProps:获取实例的默认属性
getInitialState:获取每个实例的初始化状态
componentWillMount:组件即将被装载、渲染到页面上
render:组件在这里生成虚拟的DOM节点
componentDidMount:组件真正在被装载之后
二、运行中状态:
componentWillReceiveProps:组件将要接收到属性的时候调用
shouldComponentUpdate:组件接受到新属性或者新状态的时候(可以返回false,接收数据后不更新,阻止render调用,后面的函数不会被继续执行了)
componentWillUpdate:组件即将更新不能修改属性和状态
render:组件重新描绘
componentDidUpdate:组件已经更新
三、销毁阶段:
componentWillUnmount:组件即将销毁
5、react性能优化是哪个周期函数?
shouldComponentUpdate 这个方法用来判断是否需要调用render方法重新描绘dom。因为dom的描绘非常消耗性能,如果我们能在shouldComponentUpdate方法中能够写出更优化的dom diff算法,可以极大的提高性能。
详细参考:
https//segmentfault.com/a/1190000006254212
6、为什么虚拟dom会提高性能?
虚拟dom相当于在js和真实dom中间加了一个缓存,利用dom diff算法避免了没有必要的dom操作,从而提高性能。
具体实现步骤如下:
用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了。
参考链接:
https://www.zhihu.com/question/29504639?sort=created
7、diff算法?
把树形结构按照层级分解,只比较同级元素。
给列表结构的每个单元添加唯一的key属性,方便比较。
React 只会匹配相同 class 的 component(这里面的class指的是组件的名字)
合并操作,调用 component 的 setState 方法的时候, React 将其标记为 dirty.到每一个事件循环结束, React 检查所有标记 dirty 的 component 重新绘制.
选择性子树渲染。开发人员可以重写shouldComponentUpdate提高diff的性能。
参考链接:
https//segmentfault.com/a/1190000000606216
8、react性能优化方案
(1)重写shouldComponentUpdate来避免不必要的dom操作。
(2)使用 production 版本的react.js。
(3)使用key来帮助React识别列表中所有子组件的最小变化。
参考链接:
https://segmentfault.com/a/1190000006254212
9、简述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
10、React项目用过什么脚手架?Mern? Yeoman?
Mern:MERN是脚手架的工具,它可以很容易地使用Mongo, Express, React and NodeJS生成同构JS应用。它最大限度地减少安装时间,并得到您使用的成熟技术来加速开发。
参考链接:http://www.open-open.com/lib/view/open1455953055292.html
11React 中的keys是什么,为什么它们很重要?
keys是什么帮助 React 跟踪哪些项目已更改、添加或从列表中删除。
return (
{this.state.todoItems.map(({task, uid}) => {
return - {task}
})}
)
- 在开发过程中,我们需要保证某个元素的 key 在其同级元素中具有唯一性。在 React Diff 算法中 React 会借助元素的 Key 值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染。此外,React 还需要借助 Key 值来判断元素与本地状态的关联关系,因此我们绝不可忽视转换函数中 Key 的重要性。
每个 keys 在兄弟元素之间是独一无二的。我们已经谈过几次关于和解(reconciliation)的过程,而且这个和解过程(reconciliation)中的一部分正在执行一个新的元素树与最前一个的差异。keys 使处理列表时更加高效,因为 React 可以使用子元素上的 keys 快速知道元素是新的还是在比较树时才被移动。
而且 keys 不仅使这个过程更有效率,而且没有 keys ,React 不知道哪个本地状态对应于移动中的哪个项目。所以当你 map 的时候,不要忽略了 keys 。
12调用 setState 之后发生了什么?
在代码中调用setState函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个UI界面。在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染。在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。
- React会将当前传入的参数对象与组件当前的状态合并,然后触发调和过程,在调和的过程中,React会以相对高效的方式根据新的状态构建React元素树并且重新渲染整个UI界面.
React得到的元素树之后,React会自动计算出新的树与老的树的节点的差异,然后根据差异对界面进行最小化的渲染,在React的差异算法中,React能够精确的知道在哪些位置发生看改变以及应该如何去改变,这样就保证了UI是按需更新的而不是重新渲染整个界面.
13React 中 Element 与 Component 的区别是?
简单而言,React Element 是描述屏幕上所见内容的数据结构,是对于 UI 的对象表述。典型的 React Element 就是利用 JSX 构建的声明式代码片然后被转化为createElement的调用组合。而 React Component 则是可以接收参数输入并且返回某个 React Element 的函数或者类。
14在什么情况下你会优先选择使用 Class Component 而不是 Functional Component?
在组件需要包含内部状态或者使用到生命周期函数的时候使用 Class Component ,否则使用函数式组件。
React 中 refs 的作用是什么?
- 注意,根据React最新文档,下面这种用法已经被弃用了,统一改为回调函数模式
this.refs.textInput
Refs 是 React 提供给我们的安全访问 DOM 元素或者某个组件实例的句柄。我们可以为元素添加ref属性然后在回调函数中接受该元素在 DOM 树中的句柄,该值会作为回调函数的第一个参数返回:
class CustomForm extends Component {
handleSubmit = () => {
console.log("Input Value: ", this.input.value)
}
render () {
return (
)
}
}
上述代码中的input域包含了一个ref属性,该属性声明的回调函数会接收input对应的 DOM 元素,我们将其绑定到this指针以便在其他的类函数中使用。另外值得一提的是,refs 并不是类组件的专属,函数式组件同样能够利用闭包暂存其值:
function CustomForm ({handleSubmit}) {
let inputElement
return (
)
}
16 Controlled Component 与 Uncontrolled Component 之间的区别是什么?15
React 的核心组成之一就是能够维持内部状态的自治组件,不过当我们引入原生的HTML表单元素时(input,select,textarea 等),我们是否应该将所有的数据托管到 React 组件中还是将其仍然保留在 DOM 元素中呢?这个问题的答案就是受控组件与非受控组件的定义分割。受控组件(Controlled Component)代指那些交由 React 控制并且所有的表单数据统一存放的组件。譬如下面这段代码中username变量值并没有存放到DOM元素中,而是存放在组件状态数据中。任何时候我们需要改变username变量值时,我们应当调用setState函数进行修改。
class ControlledForm extends Component {
state = {
username: ''
}
updateUsername = (e) => {
this.setState({
username: e.target.value,
})
}
handleSubmit = () => {}
render () {
return (
)
}
}
而非受控组件(Uncontrolled Component)则是由DOM存放表单数据,并非存放在 React 组件中。我们可以使用 refs 来操控DOM元素:
class UnControlledForm extends Component {
handleSubmit = () => {
console.log("Input Value: ", this.input.value)
}
render () {
return (
)
}
}
竟然非受控组件看上去更好实现,我们可以直接从 DOM 中抓取数据,而不需要添加额外的代码。不过实际开发中我们并不提倡使用非受控组件,因为实际情况下我们需要更多的考虑表单验证、选择性的开启或者关闭按钮点击、强制输入格式等功能支持,而此时我们将数据托管到 React 中有助于我们更好地以声明式的方式完成这些功能。引入 React 或者其他 MVVM 框架最初的原因就是为了将我们从繁重的直接操作 DOM 中解放出来。
在生命周期中的哪一步你应该发起 AJAX 请求?
- 我们应当将AJAX 请求放到 componentDidMount 函数中执行,主要原因有下:
React 下一代调和算法 Fiber 会通过开始或停止渲染的方式优化应用性能,其会影响到 componentWillMount 的触发次数。对于 componentWillMount 这个生命周期函数的调用次数会变得不确定,React 可能会多次频繁调用 componentWillMount。如果我们将 AJAX 请求放到 componentWillMount 函数中,那么显而易见其会被触发多次,自然也就不是好的选择。
如果我们将 AJAX 请求放置在生命周期的其他函数中,我们并不能保证请求仅在组件挂载完毕后才会要求响应。如果我们的数据请求在组件挂载之前就完成,并且调用了setState函数将数据添加到组件状态中,对于未挂载的组件则会报错。而在 componentDidMount 函数中进行 AJAX 请求则能有效避免这个问题。
17 shouldComponentUpdate 的作用是啥以及为何它这么重要?
- shouldComponentUpdate 允许我们手动地判断是否要进行组件更新,根据组件的应用场景设置函数的合理返回值能够帮我们避免不必要的更新。
18 如何告诉 React 它应该编译生产环境版本?
通常情况下我们会使用 Webpack 的 DefinePlugin 方法来将 NODE_ENV 变量值设置为 production。编译版本中 React 会忽略 propType 验证以及其他的告警信息,同时还会降低代码库的大小,React 使用了 Uglify 插件来移除生产环境下不必要的注释等信息。
19 为什么我们需要使用 React 提供的 Children API 而不是 JavaScript 的 map?
React.Children.map(props.children, () => ) instead of props.children.map(() => )
props.children并不一定是数组类型,譬如下面这个元素:
Welcome.
如果我们使用props.children.map函数来遍历时会受到异常提示,因为在这种情况下props.children是对象(object)而不是数组(array)。React 当且仅当超过一个子元素的情况下会将props.children设置为数组,就像下面这个代码片:
Welcome.
props.children will now be an array
这也就是我们优先选择使用React.Children.map函数的原因,其已经将props.children不同类型的情况考虑在内了。
20 createElement 与 cloneElement 的区别是什么?
createElement 函数是 JSX 编译之后使用的创建 React Element 的函数,而 cloneElement 则是用于复制某个元素并传入新的 Props。
简单地说,一个 React element 描述了你想在屏幕上看到什么。
换个说法就是,一个 React element 是一些 UI 的对象表示。
一个 React Component 是一个函数或一个类,
它可以接受输入并返回一个 React element
(通常是通过 JSX ,它被转化成一个 createElement 调用)。
21 传入 setState 函数的第二个参数的作用是什么?
该函数会在setState函数调用完成并且组件开始重渲染的时候被调用,我们可以用该函数来监听渲染是否完成:
this.setState(
{ username: 'tylermcginnis33' },
() => console.log('setState has finished and the component has re-rendered.')
)
22 reactJS的props.children.map函数来遍历会收到异常提示,为什么?应该如何遍历?
this.props.children 的值有三种可能:
1.当前组件没有子节点,它就是 undefined;
2.有一个子节点,数据类型是 object ;
3.有多个子节点,数据类型就是 array 。
系统提供React.Children.map()方法安全的遍历子节点对象
23 面试中的一道题:
constructor() {
super();
this.state = {
val: 0
};
}
componentDidMount() {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 1 次 log
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 2 次 log
setTimeout(() => {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 3 次 log
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 4 次 log
}, 0);
}
render() {
return null;
}
};
问上述代码中 4 次 console.log 打印出来的 val 分别是多少?
答案:4 次 log 的值分别是:0、0、2、3。
(如果想知道到底为什么,可以看另一篇文章,《React中this.setState到底做了什么?》)