共计 10287 个字符,预计需要花费 26 分钟才能阅读完成。
React 申明组件有哪几种办法,有什么不同?
React 申明组件的三种形式:
- 函数式定义的
无状态组件
- ES5 原生形式
React.createClass
定义的组件 - ES6 模式的
extends React.Component
定义的组件
(1)无状态函数式组件 它是为了创立纯展现组件,这种组件只负责依据传入的 props 来展现,不波及到 state 状态的操作
组件不会被实例化,整体渲染性能失去晋升,不能拜访 this 对象,不能拜访生命周期的办法
(2)ES5 原生形式 React.createClass // RFC React.createClass 会自绑定函数办法,导致不必要的性能开销,减少代码过期的可能性。
(3)E6 继承模式 React.Component // RCC 目前极为举荐的创立有状态组件的形式,最终会取代 React.createClass 模式;绝对于 React.createClass 能够更好实现代码复用。
无状态组件绝对于于后者的区别: 与无状态组件相比,React.createClass 和 React.Component 都是创立有状态的组件,这些组件是要被实例化的,并且能够拜访组件的生命周期办法。
React.createClass 与 React.Component 区别:
① 函数 this 自绑定
- React.createClass 创立的组件,其每一个成员函数的 this 都有 React 主动绑定,函数中的 this 会被正确设置。
- React.Component 创立的组件,其成员函数不会主动绑定 this,须要开发者手动绑定,否则 this 不能获取以后组件实例对象。
② 组件属性类型 propTypes 及其默认 props 属性 defaultProps 配置不同
- React.createClass 在创立组件时,无关组件 props 的属性类型及组件默认的属性会作为组件实例的属性来配置,其中 defaultProps 是应用 getDefaultProps 的办法来获取默认组件属性的
- React.Component 在创立组件时配置这两个对应信息时,他们是作为组件类的属性,不是组件实例的属性,也就是所谓的类的动态属性来配置的。
③ 组件初始状态 state 的配置不同
- React.createClass 创立的组件,其状态 state 是通过 getInitialState 办法来配置组件相干的状态;
- React.Component 创立的组件,其状态 state 是在 constructor 中像初始化组件属性一样申明的。
react 实现一个全局的 dialog
import React, {Component} from 'react';
import {is, fromJS} from 'immutable';
import ReactDOM from 'react-dom';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import './dialog.css';
let defaultState = {
alertStatus:false,
alertTip:"提醒",
closeDialog:function(){},
childs:''
}
class Dialog extends Component{
state = {...defaultState};
// css 动画组件设置为指标组件
FirstChild = props => {const childrenArray = React.Children.toArray(props.children);
return childrenArray[0] || null;
}
// 关上弹窗
open =(options)=>{options = options || {};
options.alertStatus = true;
var props = options.props || {};
var childs = this.renderChildren(props,options.childrens) || '';
console.log(childs);
this.setState({
...defaultState,
...options,
childs
})
}
// 敞开弹窗
close(){this.state.closeDialog();
this.setState({...defaultState})
}
renderChildren(props,childrens) {
// 遍历所有子组件
var childs = [];
childrens = childrens || [];
var ps = {
...props, // 给子组件绑定 props
_close:this.close // 给子组件也绑定一个敞开弹窗的事件
};
childrens.forEach((currentItem,index) => {
childs.push(React.createElement(
currentItem,
{
...ps,
key:index
}
));
})
return childs;
}
shouldComponentUpdate(nextProps, nextState){return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
}
render(){
return (
<ReactCSSTransitionGroup
component={this.FirstChild}
transitionName='hide'
transitionEnterTimeout={300}
transitionLeaveTimeout={300}>
<div className="dialog-con" style={this.state.alertStatus? {display:'block'}:{display:'none'}}>
{this.state.childs} </div>
</ReactCSSTransitionGroup>
);
}
}
let div = document.createElement('div');
let props = { };
document.body.appendChild(div);
let Box = ReactD
子类:
// 子类 jsx
import React, {Component} from 'react';
class Child extends Component {constructor(props){super(props);
this.state = {date: new Date()};
}
showValue=()=>{this.props.showValue && this.props.showValue()
}
render() {
return (
<div className="Child">
<div className="content">
Child <button onClick={this.showValue}> 调用父的办法 </button>
</div>
</div>
);
}
}
export default Child;
css:
.dialog-con{
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.3);
}
React 组件的 state 和 props 有什么区别?
(1)props
props 是一个从内部传进组件的参数,次要作为就是从父组件向子组件传递数据,它具备可读性和不变性,只能通过内部组件被动传入新的 props 来从新渲染子组件,否则子组件的 props 以及展示模式不会扭转。
(2)state
state 的次要作用是用于组件保留、管制以及批改本人的状态,它只能在 constructor 中初始化,它算是组件的公有属性,不可通过内部拜访和批改,只能通过组件外部的 this.setState 来批改,批改 state 属性会导致组件的从新渲染。
(3)区别
- props 是传递给组件的(相似于函数的形参),而 state 是在组件内被组件本人治理的(相似于在一个函数内申明的变量)。
- props 是不可批改的,所有 React 组件都必须像纯函数一样爱护它们的 props 不被更改。
- state 是在组件中创立的,个别在 constructor 中初始化 state。state 是多变的、能够批改,每次 setState 都异步更新的。
diff 算法如何比拟?
- 只对同级比拟,跨层级的 dom 不会进行复用
- 不同类型节点生成的 dom 树不同,此时会间接销毁老节点及子孙节点,并新建节点
- 能够通过 key 来对元素 diff 的过程提供复用的线索
- 单节点 diff
- 单点 diff 有如下几种状况:
- key 和 type 雷同示意能够复用节点
- key 不同间接标记删除节点,而后新建节点
- key 雷同 type 不同,标记删除该节点和兄弟节点,而后新创建节点
React Hooks 解决了哪些问题?
React Hooks 次要解决了以下问题:
(1)在组件之间复用状态逻辑很难
React 没有提供将可复用性行为“附加”到组件的路径(例如,把组件连贯到 store)解决此类问题能够应用 render props 和 高阶组件。然而这类计划须要从新组织组件构造,这可能会很麻烦,并且会使代码难以了解。由 providers,consumers,高阶组件,render props 等其余形象层组成的组件会造成“嵌套天堂”。只管能够在 DevTools 过滤掉它们,但这阐明了一个更深层次的问题:React 须要为共享状态逻辑提供更好的原生路径。
能够应用 Hook 从组件中提取状态逻辑,使得这些逻辑能够独自测试并复用。Hook 使咱们在无需批改组件构造的状况下复用状态逻辑。这使得在组件间或社区内共享 Hook 变得更便捷。
(2)简单组件变得难以了解
在组件中,每个生命周期经常蕴含一些不相干的逻辑。例如,组件经常在 componentDidMount 和 componentDidUpdate 中获取数据。然而,同一个 componentDidMount 中可能也蕴含很多其它的逻辑,如设置事件监听,而之后需在 componentWillUnmount 中革除。互相关联且须要对照批改的代码被进行了拆分,而齐全不相干的代码却在同一个办法中组合在一起。如此很容易产生 bug,并且导致逻辑不统一。
在少数状况下,不可能将组件拆分为更小的粒度,因为状态逻辑无处不在。这也给测试带来了肯定挑战。同时,这也是很多人将 React 与状态治理库联合应用的起因之一。然而,这往往会引入了很多抽象概念,须要你在不同的文件之间来回切换,使得复用变得更加艰难。
为了解决这个问题,Hook 将组件中互相关联的局部拆分成更小的函数(比方设置订阅或申请数据),而并非强制依照生命周期划分。你还能够应用 reducer 来治理组件的外部状态,使其更加可预测。
(3)难以了解的 class
除了代码复用和代码治理会遇到困难外,class 是学习 React 的一大屏障。咱们必须去了解 JavaScript 中 this 的工作形式,这与其余语言存在微小差别。还不能遗记绑定事件处理器。没有稳固的语法提案,这些代码十分冗余。大家能够很好地了解 props,state 和自顶向下的数据流,但对 class 却束手无策。即使在有教训的 React 开发者之间,对于函数组件与 class 组件的差别也存在一致,甚至还要辨别两种组件的应用场景。
为了解决这些问题,Hook 使你在非 class 的状况下能够应用更多的 React 个性。从概念上讲,React 组件始终更像是函数。而 Hook 则拥抱了函数,同时也没有就义 React 的精力准则。Hook 提供了问题的解决方案,无需学习简单的函数式或响应式编程技术
mobox 和 redux 有什么区别?
(1)共同点
- 为了解决状态管理混乱,无奈无效同步的问题对立保护治理利用状态;
- 某一状态只有一个可信数据起源(通常命名为 store,指状态容器);
- 操作更新状态形式对立,并且可控(通常以 action 形式提供更新状态的路径);
- 反对将 store 与 React 组件连贯,如 react-redux,mobx- react;
(2)区别 Redux 更多的是遵循 Flux 模式的一种实现,是一个 JavaScript 库,它关注点次要是以下几方面∶
-
Action∶ 一个 JavaScript 对象,形容动作相干信息,次要蕴含 type 属性和 payload 属性∶
o type∶ action 类型; o payload∶ 负载数据;
- Reducer∶ 定义利用状态如何响应不同动作(action),如何更新状态;
-
Store∶ 治理 action 和 reducer 及其关系的对象,次要提供以下性能∶
o 保护利用状态并反对拜访状态(getState()); o 反对监听 action 的散发,更新状态(dispatch(action)); o 反对订阅 store 的变更(subscribe(listener));
- 异步流∶ 因为 Redux 所有对 store 状态的变更,都应该通过 action 触发,异步工作(通常都是业务或获取数据工作)也不例外,而为了不将业务或数据相干的工作混入 React 组件中,就须要应用其余框架配合治理异步工作流程,如 redux-thunk,redux-saga 等;
Mobx 是一个通明函数响应式编程的状态治理库,它使得状态治理简略可伸缩∶
- Action∶定义扭转状态的动作函数,包含如何变更状态;
- Store∶ 集中管理模块状态(State)和动作(action)
- Derivation(衍生)∶ 从利用状态中派生而出,且没有任何其余影响的数据
比照总结:
- redux 将数据保留在繁多的 store 中,mobx 将数据保留在扩散的多个 store 中
- redux 应用 plain object 保留数据,须要手动解决变动后的操作;mobx 实用 observable 保留数据,数据变动后主动解决响应的操作
- redux 应用不可变状态,这意味着状态是只读的,不能间接去批改它,而是应该返回一个新的状态,同时应用纯函数;mobx 中的状态是可变的,能够间接对其进行批改
- mobx 相对来说比较简单,在其中有很多的形象,mobx 更多的应用面向对象的编程思维;redux 会比较复杂,因为其中的函数式编程思维把握起来不是那么容易,同时须要借助一系列的中间件来解决异步和副作用
- mobx 中有更多的形象和封装,调试会比拟艰难,同时后果也难以预测; 而 redux 提供可能进行工夫回溯的开发工具,同时其纯函数以及更少的形象,让调试变得更加的容易
React 中的高阶组件使用了什么设计模式?
应用了装璜模式,高阶组件的使用:
function withWindowWidth(BaseComponent) {
class DerivedClass extends React.Component {
state = {windowWidth: window.innerWidth,}
onResize = () => {
this.setState({windowWidth: window.innerWidth,})
}
componentDidMount() {window.addEventListener('resize', this.onResize)
}
componentWillUnmount() {window.removeEventListener('resize', this.onResize);
}
render() {return <BaseComponent {...this.props} {...this.state}/>
}
}
return DerivedClass;
}
const MyComponent = (props) => {return <div>Window width is: {props.windowWidth}</div>
};
export default withWindowWidth(MyComponent);
装璜模式的特点是不须要扭转 被装璜对象 自身,而只是在里面套一个外壳接口。JavaScript 目前曾经有了原生装璜器的提案,其用法如下:
@testable
class MyTestableClass {}
HOC 相比 mixins 有什么长处?
HOC 和 Vue 中的 mixins 作用是统一的,并且在晚期 React 也是应用 mixins 的形式。然而在应用 class 的形式创立组件当前,mixins 的形式就不能应用了,并且其实 mixins 也是存在一些问题的,比方:
- 隐含了一些依赖,比方我在组件中写了某个
state
并且在mixin
中应用了,就这存在了一个依赖关系。万一下次他人要移除它,就得去mixin
中查找依赖 - 多个
mixin
中可能存在雷同命名的函数,同时代码组件中也不能呈现雷同命名的函数,否则就是重写了,其实我始终感觉命名真的是一件麻烦事。。 - 雪球效应,尽管我一个组件还是应用着同一个
mixin
,然而一个mixin
会被多个组件应用,可能会存在需要使得mixin
批改本来的函数或者新增更多的函数,这样可能就会产生一个保护老本
HOC 解决了这些问题,并且它们达成的成果也是统一的,同时也更加的政治正确(毕竟更加函数式了)。
react 强制刷新
component.forceUpdate() 一个不罕用的生命周期办法, 它的作用就是强制刷新
官网解释如下
默认状况下,当组件的 state 或 props 发生变化时,组件将从新渲染。如果 render() 办法依赖于其余数据,则能够调用 forceUpdate() 强制让组件从新渲染。
调用 forceUpdate() 将以致组件调用 render() 办法,此操作会跳过该组件的 shouldComponentUpdate()。但其子组件会触发失常的生命周期办法,包含 shouldComponentUpdate() 办法。如果标记发生变化,React 仍将只更新 DOM。
通常你应该防止应用 forceUpdate(),尽量在 render() 中应用 this.props 和 this.state。
shouldComponentUpdate 在初始化 和 forceUpdate 不会执行
参考:前端 react 面试题具体解答
什么是 Props
Props 是 React 中属性的简写。它们是只读组件,必须放弃纯,即不可变。它们总是在整个利用中从父组件传递到子组件。子组件永远不能将 prop 送回父组件。这有助于保护单向数据流,通常用于出现动静生成的数据。
React 16 中新生命周期有哪些
对于 React16 开始利用的新生命周期:能够看出,React16 自上而下地对生命周期做了另一种维度的解读:
- Render 阶段:用于计算一些必要的状态信息。这个阶段可能会被 React 暂停,这一点和 React16 引入的 Fiber 架构(咱们前面会重点解说)是无关的;
- Pre-commit 阶段:所谓“commit”,这里指的是“更新真正的 DOM 节点”这个动作。所谓 Pre-commit,就是说我在这个阶段其实还并没有去更新实在的 DOM,不过 DOM 信息曾经是能够读取的了;
- Commit 阶段:在这一步,React 会实现实在 DOM 的更新工作。Commit 阶段,咱们能够拿到实在 DOM(包含 refs)。
与此同时,新的生命周期在流程方面,依然遵循“挂载”、“更新”、“卸载”这三个狭义的划分形式。它们别离对应到:
-
挂载过程:
- constructor
- getDerivedStateFromProps
- render
- componentDidMount
-
更新过程:
- getDerivedStateFromProps
- shouldComponentUpdate
- render
- getSnapshotBeforeUpdate
- componentDidUpdate
-
卸载过程:
- componentWillUnmount
在 React 中,refs 的作用是什么
Refs 能够用于获取一个 DOM 节点或者 React 组件的援用。何时应用 refs 的好的示例有治理焦点 / 文本抉择,触发命令动画,或者和第三方 DOM 库集成。你应该防止应用 String 类型的 Refs 和内联的 ref 回调。Refs 回调是 React 所举荐的。
React-Router 的实现原理是什么?
客户端路由实现的思维:
-
基于 hash 的路由:通过监听
hashchange
事件,感知 hash 的变动- 扭转 hash 能够间接通过 location.hash=xxx
-
基于 H5 history 路由:
- 扭转 url 能够通过 history.pushState 和 resplaceState 等,会将 URL 压入堆栈,同时可能利用
history.go()
等 API - 监听 url 的变动能够通过自定义事件触发实现
- 扭转 url 能够通过 history.pushState 和 resplaceState 等,会将 URL 压入堆栈,同时可能利用
react-router 实现的思维:
- 基于
history
库来实现上述不同的客户端路由实现思维,并且可能保留历史记录等,磨平浏览器差别,下层无感知 - 通过保护的列表,在每次 URL 发生变化的回收,通过配置的 路由门路,匹配到对应的 Component,并且 render
React Hook 的应用限度有哪些?
React Hooks 的限度次要有两条:
- 不要在循环、条件或嵌套函数中调用 Hook;
- 在 React 的函数组件中调用 Hook。
那为什么会有这样的限度呢?Hooks 的设计初衷是为了改良 React 组件的开发模式。在旧有的开发模式下遇到了三个问题。
- 组件之间难以复用状态逻辑。过来常见的解决方案是高阶组件、render props 及状态治理框架。
- 简单的组件变得难以了解。生命周期函数与业务逻辑耦合太深,导致关联局部难以拆分。
- 人和机器都很容易混同类。常见的有 this 的问题,但在 React 团队中还有类难以优化的问题,心愿在编译优化层面做出一些改良。
这三个问题在肯定水平上妨碍了 React 的后续倒退,所以为了解决这三个问题,Hooks 基于函数组件 开始设计。然而第三个问题决定了 Hooks 只反对函数组件。
那为什么不要在循环、条件或嵌套函数中调用 Hook 呢?因为 Hooks 的设计是基于数组实现。在调用时按程序退出数组中,如果应用循环、条件或嵌套函数很有可能导致数组取值错位,执行谬误的 Hook。当然,本质上 React 的源码里不是数组,是链表。
这些限度会在编码上造成肯定水平的心智累赘,老手可能会写错,为了防止这样的状况,能够引入 ESLint 的 Hooks 查看插件进行预防。
何为纯函数(pure function)
一个纯函数是一个不依赖于且不扭转其作用域之外的变量状态的函数,这也意味着一个纯函数对于同样的参数总是返回同样的后果。
react 的渲染过程中,兄弟节点之间是怎么解决的?也就是 key 值不一样的时候
通常咱们输入节点的时候都是 map 一个数组而后返回一个
ReactNode
,为了不便react
外部进行优化,咱们必须给每一个reactNode
增加key
,这个key prop
在设计值处不是给开发者用的,而是给 react 用的,大略的作用就是给每一个reactNode
增加一个身份标识,不便 react 进行辨认,在重渲染过程中,如果 key 一样,若组件属性有所变动,则react
只更新组件对应的属性;没有变动则不更新,如果 key 不一样,则 react 先销毁该组件,而后从新创立该组件
React 组件中怎么做事件代理?它的原理是什么?
React 基于 Virtual DOM 实现了一个 SyntheticEvent 层(合成事件层),定义的事件处理器会接管到一个合成事件对象的实例,它合乎 W3C 规范,且与原生的浏览器事件领有同样的接口,反对冒泡机制,所有的事件都主动绑定在最外层上。
在 React 底层,次要对合成事件做了两件事:
- 事件委派: React 会把所有的事件绑定到构造的最外层,应用对立的事件监听器,这个事件监听器上维持了一个映射来保留所有组件外部事件监听和处理函数。
- 主动绑定: React 组件中,每个办法的上下文都会指向该组件的实例,即主动绑定 this 为以后组件。
redux 中间件
中间件提供第三方插件的模式,自定义拦挡 action -> reducer 的过程。变为 action -> middlewares -> reducer。这种机制能够让咱们扭转数据流,实现如异步 action,action 过 滤,日志输入,异样报告等性能
常见的中间件:
- redux-logger: 提供日志输入;
- redux-thunk: 解决异步操作;
- redux-promise: 解决异步操作;
- actionCreator 的返回值是 promise
在哪个生命周期中你会收回 Ajax 申请?为什么?
Ajax 申请应该写在组件创立期的第五个阶段,即 componentDidMount 生命周期办法中。起因如下。
在创立期的其余阶段,组件尚未渲染实现。而在存在期的 5 个阶段,又不能确保生命周期办法肯定会执行(如通过 shouldComponentUpdate 办法优化更新等)。在销毀期,组件行将被销毁,申请数据变得无意义。因而在这些阶段发岀 Ajax 申请显然不是最好的抉择。
在组件尚未挂载之前,Ajax 申请将无奈执行结束,如果此时发出请求,将意味着在组件挂载之前更新状态(如执行 setState),这通常是不起作用的。
在 componentDidMount 办法中,执行 Ajax 即可保障组件曾经挂载,并且可能失常更新组件。
什么是 state
- 在组件初始化的时候 通过 this.state 给组件设置一个初始化的 state,第一次 render 的时候会用 state 来渲染组件
- 通过 this.setState 办法来更新 state