共计 11691 个字符,预计需要花费 30 分钟才能阅读完成。
redux 有什么毛病
- 一个组件所须要的数据,必须由父组件传过来,而不能像
flux
中间接从store
取。 - 当一个组件相干数据更新时,即便父组件不须要用到这个组件,父组件还是会从新
render
,可能会有效率影响,或者须要写简单的shouldComponentUpdate
进行判断。
React-Router 的路由有几种模式?
React-Router 反对应用 hash(对应 HashRouter)和 browser(对应 BrowserRouter)两种路由规定,react-router-dom 提供了 BrowserRouter 和 HashRouter 两个组件来实现利用的 UI 和 URL 同步:
- BrowserRouter 创立的 URL 格局:xxx.com/path
- HashRouter 创立的 URL 格局:xxx.com/#/path
(1)BrowserRouter
它应用 HTML5 提供的 history API(pushState、replaceState 和 popstate 事件)来放弃 UI 和 URL 的同步。由此能够看出,BrowserRouter 是应用 HTML 5 的 history API 来管制路由跳转的:
<BrowserRouter
basename={string}
forceRefresh={bool}
getUserConfirmation={func}
keyLength={number}
/>
其中的属性如下:
- basename 所有路由的基准 URL。basename 的正确格局是后面有一个前导斜杠,但不能有尾部斜杠;
<BrowserRouter basename="/calendar">
<Link to="/today" />
</BrowserRouter>
等同于
<a href="/calendar/today" />
- forceRefresh 如果为 true,在导航的过程中整个页面将会刷新。个别状况下,只有在不反对 HTML5 history API 的浏览器中应用此性能;
- getUserConfirmation 用于确认导航的函数,默认应用 window.confirm。例如,当从 /a 导航至 /b 时,会应用默认的 confirm 函数弹出一个提醒,用户点击确定后才进行导航,否则不做任何解决;
// 这是默认的确认函数
const getConfirmation = (message, callback) => {const allowTransition = window.confirm(message);
callback(allowTransition);
}
<BrowserRouter getUserConfirmation={getConfirmation} />
须要配合
<Prompt>
一起应用。
- KeyLength 用来设置 Location.Key 的长度。
(2)HashRouter
应用 URL 的 hash 局部(即 window.location.hash)来放弃 UI 和 URL 的同步。由此能够看出,HashRouter 是通过 URL 的 hash 属性来管制路由跳转的:
<HashRouter
basename={string}
getUserConfirmation={func}
hashType={string}
/>
其参数如下:
- basename, getUserConfirmation 和
BrowserRouter
性能一样; -
hashType window.location.hash 应用的 hash 类型,有如下几种:
- slash – 前面跟一个斜杠,例如 #/ 和 #/sunshine/lollipops;
- noslash – 前面没有斜杠,例如 # 和 #sunshine/lollipops;
- hashbang – Google 格调的 ajax crawlable,例如 #!/ 和 #!/sunshine/lollipops。
对于 store 的了解
Store 就是把它们分割到一起的对象。Store 有以下职责:
- 维持利用的 state;
- 提供 getState() 办法获取 state;
- 提供 dispatch(action) 办法更新 state;
- 通过 subscribe(listener)注册监听器;
- 通过 subscribe(listener)返回的函数登记监听器
React 中的状态是什么?它是如何应用的
状态是 React 组件的外围,是数据的起源,必须尽可能简略。基本上状态是确定组件出现和行为的对象。与 props 不同,它们是可变的,并创立动静和交互式组件。能够通过 this.state()
拜访它们。
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.Component 和 React.PureComponent 的区别
PureComponent 示意一个纯组件,能够用来优化 React 程序,缩小 render 函数执行的次数,从而进步组件的性能。
在 React 中,当 prop 或者 state 发生变化时,能够通过在 shouldComponentUpdate 生命周期函数中执行 return false 来阻止页面的更新,从而缩小不必要的 render 执行。React.PureComponent 会主动执行 shouldComponentUpdate。
不过,pureComponent 中的 shouldComponentUpdate() 进行的是 浅比拟,也就是说如果是援用数据类型的数据,只会比拟不是同一个地址,而不会比拟这个地址外面的数据是否统一。浅比拟会疏忽属性和或状态渐变状况,其实也就是数据援用指针没有变动,而数据产生扭转的时候 render 是不会执行的。如果须要从新渲染那么就须要从新开拓空间援用数据。PureComponent 个别会用在一些纯展现组件上。
应用 pureComponent 的 益处:当组件更新时,如果组件的 props 或者 state 都没有扭转,render 函数就不会触发。省去虚构 DOM 的生成和比照过程,达到晋升性能的目标。这是因为 react 主动做了一层浅比拟。
参考 前端进阶面试题具体解答
React Hook 的应用限度有哪些?
React Hooks 的限度次要有两条:
- 不要在循环、条件或嵌套函数中调用 Hook;
- 在 React 的函数组件中调用 Hook。
那为什么会有这样的限度呢?Hooks 的设计初衷是为了改良 React 组件的开发模式。在旧有的开发模式下遇到了三个问题。
- 组件之间难以复用状态逻辑。过来常见的解决方案是高阶组件、render props 及状态治理框架。
- 简单的组件变得难以了解。生命周期函数与业务逻辑耦合太深,导致关联局部难以拆分。
- 人和机器都很容易混同类。常见的有 this 的问题,但在 React 团队中还有类难以优化的问题,心愿在编译优化层面做出一些改良。
这三个问题在肯定水平上妨碍了 React 的后续倒退,所以为了解决这三个问题,Hooks 基于函数组件 开始设计。然而第三个问题决定了 Hooks 只反对函数组件。
那为什么不要在循环、条件或嵌套函数中调用 Hook 呢?因为 Hooks 的设计是基于数组实现。在调用时按程序退出数组中,如果应用循环、条件或嵌套函数很有可能导致数组取值错位,执行谬误的 Hook。当然,本质上 React 的源码里不是数组,是链表。
这些限度会在编码上造成肯定水平的心智累赘,老手可能会写错,为了防止这样的状况,能够引入 ESLint 的 Hooks 查看插件进行预防。
React.forwardRef 是什么?它有什么作用?
React.forwardRef 会创立一个 React 组件,这个组件可能将其承受的 ref 属性转发到其组件树下的另一个组件中。这种技术并不常见,但在以下两种场景中特地有用:
- 转发 refs 到 DOM 组件
- 在高阶组件中转发 refs
React 废除了哪些生命周期?为什么?
被废除的三个函数都是在 render 之前,因为 fber 的呈现,很可能因为高优先级工作的呈现而打断现有工作导致它们会被执行屡次。另外的一个起因则是,React 想束缚使用者,好的框架可能让人不得已写出容易保护和扩大的代码,这一点又是从何谈起,能够从新减少以及行将废除的生命周期剖析动手
1) componentWillMount
首先这个函数的性能齐全能够应用 componentDidMount 和 constructor 来代替,异步获取的数据的状况下面曾经阐明了,而如果抛去异步获取数据,其余的即是初始化而已,这些性能都能够在 constructor 中执行,除此之外,如果在 willMount 中订阅事件,但在服务端这并不会执行 willUnMount 事件,也就是说服务端会导致内存透露所以 componentWilIMount 齐全能够不应用,但使用者有时候不免因为各 种各样的状况在 componentWilMount 中做一些操作,那么 React 为了束缚开发者,罗唆就抛掉了这个 API
2) componentWillReceiveProps
在老版本的 React 中,如果组件本身的某个 state 跟其 props 密切相关的话,始终都没有一种很优雅的解决形式去更新 state,而是须要在 componentWilReceiveProps 中判断前后两个 props 是否雷同,如果不同再将新的 props 更新到相应的 state 下来。这样做一来会毁坏 state 数据的繁多数据源,导致组件状态变得不可预测,另一方面也会减少组件的重绘次数。相似的业务需要也有很多,如一个能够横向滑动的列表,以后高亮的 Tab 显然隶属于列表本身的时,依据传入的某个值,间接定位到某个 Tab。为了解决这些问题,React 引入了第一个新的生命周期:getDerivedStateFromProps。它有以下的长处∶
- getDSFP 是静态方法,在这里不能应用 this,也就是一个纯函数,开发者不能写出副作用的代码
- 开发者只能通过 prevState 而不是 prevProps 来做比照,保障了 state 和 props 之间的简略关系以及不须要解决第一次渲染时 prevProps 为空的状况
- 基于第一点,将状态变动(setState)和低廉操作(tabChange)辨别开,更加便于 render 和 commit 阶段操作或者说优化。
3) componentWillUpdate
与 componentWillReceiveProps 相似,许多开发者也会在 componentWillUpdate 中依据 props 的变动去触发一些回调。但不论是 componentWilReceiveProps 还 是 componentWilUpdate,都有可能在一次更新中被调用屡次,也就是说写在这里的回调函数也有可能会被调用屡次,这显然是不可取的。与 componentDidMount 类 似,componentDidUpdate 也不存在这样的问题,一次更新中 componentDidUpdate 只会被调用一次,所以将原先写在 componentWillUpdate 中 的 回 调 迁 移 至 componentDidUpdate 就能够解决这个问题。
另外一种状况则是须要获取 DOM 元素状态,然而因为在 fber 中,render 可打断,可能在 wilMount 中获取到的元素状态很可能与理论须要的不同,这个通常能够应用第二个新增的生命函数的解决 getSnapshotBeforeUpdate(prevProps, prevState)
4) getSnapshotBeforeUpdate(prevProps, prevState)
返回的值作为 componentDidUpdate 的第三个参数。与 willMount 不同的是,getSnapshotBeforeUpdate 会在最终确定的 render 执行之前执行,也就是能保障其获取到的元素状态与 didUpdate 中获取到的元素状态雷同。官网参考代码:
class ScrollingList extends React.Component {constructor(props) {super(props);
this.listRef = React.createRef();}
getSnapshotBeforeUpdate(prevProps, prevState) {
// 咱们是否在 list 中增加新的 items?// 捕捉滚动地位以便咱们稍后调整滚动地位。if (prevProps.list.length < this.props.list.length) {
const list = this.listRef.current;
return list.scrollHeight - list.scrollTop;
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
// 如果咱们 snapshot 有值,阐明咱们刚刚增加了新的 items,// 调整滚动地位使得这些新 items 不会将旧的 items 推出视图。//(这里的 snapshot 是 getSnapshotBeforeUpdate 的返回值)if (snapshot !== null) {
const list = this.listRef.current;
list.scrollTop = list.scrollHeight - snapshot;
}
}
render() {
return (<div ref={this.listRef}>{/* ...contents... */}</div>
);
}
}
state 和 props 触发更新的生命周期别离有什么区别?
state 更新流程: 这个过程当中波及的函数:
- shouldComponentUpdate: 当组件的 state 或 props 产生扭转时,都会首先触发这个生命周期函数。它会接管两个参数:nextProps, nextState——它们别离代表传入的新 props 和新的 state 值。拿到这两个值之后,咱们就能够通过一些比照逻辑来决定是否有 re-render(重渲染)的必要了。如果该函数的返回值为 false,则生命周期终止,反之持续;
留神:此办法仅作为 性能优化的形式 而存在。不要希图依附此办法来“阻止”渲染,因为这可能会产生 bug。应该 思考应用内置的 PureComponent 组件,而不是手动编写
shouldComponentUpdate()
- componentWillUpdate:当组件的 state 或 props 产生扭转时,会在渲染之前调用 componentWillUpdate。componentWillUpdate 是 React16 废除的三个生命周期之一。过来,咱们可能心愿能在这个阶段去收集一些必要的信息(比方更新前的 DOM 信息等等),当初咱们齐全能够在 React16 的 getSnapshotBeforeUpdate 中去做这些事;
- componentDidUpdate:componentDidUpdate() 会在 UI 更新后会被立刻调用。它接管 prevProps(上一次的 props 值)作为入参,也就是说在此处咱们依然能够进行 props 值比照(再次阐明 componentWillUpdate 的确鸡肋哈)。
props 更新流程: 绝对于 state 更新,props 更新后惟一的区别是减少了对 componentWillReceiveProps 的调用。对于 componentWillReceiveProps,须要晓得这些事件:
- componentWillReceiveProps:它在 Component 承受到新的 props 时被触发。componentWillReceiveProps 会接管一个名为 nextProps 的参数(对应新的 props 值)。该生命周期是 React16 废除掉的三个生命周期之一。在它被废除前,能够用它来比拟 this.props 和 nextProps 来从新 setState。在 React16 中,用一个相似的新生命周期 getDerivedStateFromProps 来代替它。
React-Router 怎么设置重定向?
应用 <Redirect>
组件实现路由的重定向:
<Switch>
<Redirect from='/users/:id' to='/users/profile/:id'/>
<Route path='/users/profile/:id' component={Profile}/>
</Switch>
当申请 /users/:id
被重定向去 '/users/profile/:id'
:
- 属性
from: string
:须要匹配的将要被重定向门路。 - 属性
to: string
:重定向的 URL 字符串 - 属性
to: object
:重定向的 location 对象 - 属性
push: bool
:若为真,重定向操作将会把新地址退出到拜访历史记录外面,并且无奈回退到后面的页面。
如何将两个或多个组件嵌入到一个组件中?
能够通过以下形式将组件嵌入到一个组件中:
class MyComponent extends React.Component{render(){
return(
<div>
<h1>Hello</h1>
<Header/>
</div>
);
}
}
class Header extends React.Component{render(){
return
<h1>Header Component</h1>
};
}
ReactDOM.render(<MyComponent/>, document.getElementById('content')
);
在 React 中如何防止不必要的 render?
React 基于虚构 DOM 和高效 Diff 算法的完满配合,实现了对 DOM 最小粒度的更新。大多数状况下,React 对 DOM 的渲染效率足以业务日常。但在个别简单业务场景下,性能问题仍然会困扰咱们。此时须要采取一些措施来晋升运行性能,其很重要的一个方向,就是防止不必要的渲染(Render)。这里提下优化的点:
- shouldComponentUpdate 和 PureComponent
在 React 类组件中,能够利用 shouldComponentUpdate 或者 PureComponent 来缩小因父组件更新而触发子组件的 render,从而达到目标。shouldComponentUpdate 来决定是否组件是否从新渲染,如果不心愿组件从新渲染,返回 false 即可。
- 利用高阶组件
在函数组件中,并没有 shouldComponentUpdate 这个生命周期,能够利用高阶组件,封装一个相似 PureComponet 的性能
- 应用 React.memo
React.memo 是 React 16.6 新的一个 API,用来缓存组件的渲染,防止不必要的更新,其实也是一个高阶组件,与 PureComponent 非常相似,但不同的是,React.memo 只能用于函数组件。
React 如何获取组件对应的 DOM 元素?
能够用 ref 来获取某个子节点的实例,而后通过以后 class 组件实例的一些特定属性来间接获取子节点实例。ref 有三种实现办法:
- 字符串格局:字符串格局,这是 React16 版本之前用得最多的,例如:
<p ref="info">span</p>
- 函数格局:ref 对应一个办法,该办法有一个参数,也就是对应的节点实例,例如:
<p ref={ele => this.info = ele}></p>
- createRef 办法 :React 16 提供的一个 API,应用 React.createRef() 来实现
在 React 中组件的 this.state 和 setState 有什么区别?
this.state 通常是用来初始化 state 的,this.setState 是用来批改 state 值的。如果初始化了 state 之后再应用 this.state,之前的 state 会被笼罩掉,如果应用 this.setState,只会替换掉相应的 state 值。所以,如果想要批改 state 的值,就须要应用 setState,而不能间接批改 state,间接批改 state 之后页面是不会更新的。
react router
import React from 'react'
import {render} from 'react-dom'
import {browserHistory, Router, Route, IndexRoute} from 'react-router'
import App from '../components/App'
import Home from '../components/Home'
import About from '../components/About'
import Features from '../components/Features'
render(<Router history={browserHistory}> // history 路由
<Route path='/' component={App}>
<IndexRoute component={Home} />
<Route path='about' component={About} />
<Route path='features' component={Features} />
</Route>
</Router>,
document.getElementById('app')
)
render(<Router history={browserHistory} routes={routes} />,
document.getElementById('app')
)
React Router 提供一个 routerWillLeave 生命周期钩子,这使得 React 组件能够拦挡正在产生的跳转,或在来到 route 前提醒用户。routerWillLeave 返回值有以下两种:
return
false
勾销此次跳转 return
返回提示信息,在来到 route 前提醒用户进行确认。
对虚构 DOM 的了解?虚构 DOM 次要做了什么?虚构 DOM 自身是什么?
从实质上来说,Virtual Dom 是一个 JavaScript 对象,通过对象的形式来示意 DOM 构造。将页面的状态形象为 JS 对象的模式,配合不同的渲染工具,使跨平台渲染成为可能。通过事务处理机制,将屡次 DOM 批改的后果一次性的更新到页面上,从而无效的缩小页面渲染的次数,缩小批改 DOM 的重绘重排次数,进步渲染性能。
虚构 DOM 是对 DOM 的形象,这个对象是更加轻量级的对 DOM 的形容。它设计的最后目标,就是更好的跨平台,比方 node.js 就没有 DOM,如果想实现 SSR,那么一个形式就是借助虚构 dom,因为虚构 dom 自身是 js 对象。在代码渲染到页面之前,vue 或者 react 会把代码转换成一个对象(虚构 DOM)。以对象的模式来形容实在 dom 构造,最终渲染到页面。在每次数据发生变化前,虚构 dom 都会缓存一份,变动之时,当初的虚构 dom 会与缓存的虚构 dom 进行比拟。在 vue 或者 react 外部封装了 diff 算法,通过这个算法来进行比拟,渲染时批改扭转的变动,原先没有产生扭转的通过原先的数据进行渲染。
另外古代前端框架的一个根本要求就是毋庸手动操作 DOM,一方面是因为手动操作 DOM 无奈保障程序性能,多人合作的我的项目中如果 review 不严格,可能会有开发者写出性能较低的代码,另一方面更重要的是省略手动 DOM 操作能够大大提高开发效率。
为什么要用 Virtual DOM:
(1)保障性能上限,在不进行手动优化的状况下,提供过得去的性能
上面比照一下批改 DOM 时实在 DOM 操作和 Virtual DOM 的过程,来看一下它们重排重绘的性能耗费∶
- 实在 DOM∶ 生成 HTML 字符串+ 重建所有的 DOM 元素
- Virtual DOM∶ 生成 vNode+ DOMDiff+必要的 DOM 更新
Virtual DOM 的更新 DOM 的筹备工作消耗更多的工夫,也就是 JS 层面,相比于更多的 DOM 操作它的生产是极其便宜的。尤雨溪在社区论坛中说道∶ 框架给你的保障是,你不须要手动优化的状况下,我仍然能够给你提供过得去的性能。(2)跨平台 Virtual DOM 实质上是 JavaScript 的对象,它能够很不便的跨平台操作,比方服务端渲染、uniapp 等。
在 React 中,refs 的作用是什么
Refs 能够用于获取一个 DOM 节点或者 React 组件的援用。何时应用 refs 的好的示例有治理焦点 / 文本抉择,触发命令动画,或者和第三方 DOM 库集成。你应该防止应用 String 类型的 Refs 和内联的 ref 回调。Refs 回调是 React 所举荐的。
React 中能够在 render 拜访 refs 吗?为什么?
<>
<span id="name" ref={this.spanRef}>{this.state.title}</span>
<span>{this.spanRef.current ? '有值' : '无值'}</span>
</>
不能够,render 阶段 DOM 还没有生成,无奈获取 DOM。DOM 的获取须要在 pre-commit 阶段和 commit 阶段:
**
React 与 Vue 的 diff 算法有何不同?
diff 算法是指生成更新补丁的形式,次要利用于虚构 DOM 树变动后,更新实在 DOM。所以 diff 算法肯定存在这样一个过程:触发更新 → 生成补丁 → 利用补丁。
React 的 diff 算法,触发更新的机会次要在 state 变动与 hooks 调用之后。此时触发虚构 DOM 树变更遍历,采纳了深度优先遍历算法。但传统的遍历形式,效率较低。为了优化效率,应用了分治的形式。将繁多节点比对转化为了 3 种类型节点的比对,别离是树、组件及元素,以此晋升效率。
- 树比对:因为网页视图中较少有跨层级节点挪动,两株虚构 DOM 树只对同一档次的节点进行比拟。
- 组件比对:如果组件是同一类型,则进行树比对,如果不是,则间接放入到补丁中。
- 元素比对:次要产生在同层级中,通过标记节点操作生成补丁,节点操作对应实在的 DOM 剪裁操作。
以上是经典的 React diff 算法内容。自 React 16 起,引入了 Fiber 架构。为了使整个更新过程可随时暂停复原,节点与树别离采纳了 FiberNode 与 FiberTree 进行重构。fiberNode 应用了双链表的构造,能够间接找到兄弟节点与子节点。整个更新过程由 current 与 workInProgress 两株树双缓冲实现。workInProgress 更新实现后,再通过批改 current 相干指针指向新节点。
Vue 的整体 diff 策略与 React 对齐,尽管不足工夫切片能力,但这并不意味着 Vue 的性能更差,因为在 Vue 3 初期引入过,前期因为收益不高移除掉了。除了高帧率动画,在 Vue 中其余的场景简直都能够应用防抖和节流去进步响应性能。