diff 算法?
- 把树形构造依照层级合成,只比拟同级元素
- 给列表构造的每个单元增加惟一的 key 属性,不便比拟
- React 只会匹配雷同 class 的 component(这外面的 class 指的是组件的名字)
- 合并操作,调用 component 的 setState 办法的时候, React 将其标记为 dirty. 到每一个 事件循环完结, React 查看所有标记 dirty 的 component 从新绘制.
- 抉择性子树渲染。开发人员能够重写 shouldComponentUpdate 进步 diff 的性能。
在 React 中组件的 props 扭转时更新组件的有哪些办法?
在一个组件传入的 props 更新时从新渲染该组件罕用的办法是在 componentWillReceiveProps
中将新的 props 更新到组件的 state 中(这种 state 被成为派生状态(Derived State)),从而实现从新渲染。React 16.3 中还引入了一个新的钩子函数 getDerivedStateFromProps
来专门实现这一需要。
(1)componentWillReceiveProps(已废除)
在 react 的 componentWillReceiveProps(nextProps)生命周期中,能够在子组件的 render 函数执行前,通过 this.props 获取旧的属性,通过 nextProps 获取新的 props,比照两次 props 是否雷同,从而更新子组件本人的 state。
这样的益处是,能够将数据申请放在这里进行执行,须要传的参数则从 componentWillReceiveProps(nextProps)中获取。而不用将所有的申请都放在父组件中。于是该申请只会在该组件渲染时才会收回,从而加重申请累赘。
(2)getDerivedStateFromProps(16.3 引入)
这个生命周期函数是为了代替 componentWillReceiveProps
存在的,所以在须要应用 componentWillReceiveProps
时,就能够思考应用 getDerivedStateFromProps
来进行代替。
两者的参数是不雷同的,而 getDerivedStateFromProps
是一个动态函数,也就是这个函数不能通过 this 拜访到 class 的属性,也并不举荐间接拜访属性。而是应该通过参数提供的 nextProps 以及 prevState 来进行判断,依据新传入的 props 来映射到 state。
须要留神的是,如果 props 传入的内容不须要影响到你的 state,那么就须要返回一个 null,这个返回值是必须的,所以尽量将其写到函数的开端:
static getDerivedStateFromProps(nextProps, prevState) {const {type} = nextProps;
// 当传入的 type 发生变化的时候,更新 state
if (type !== prevState.type) {
return {type,};
}
// 否则,对于 state 不进行任何操作
return null;
}
为什么应用 jsx 的组件中没有看到应用 react 却须要引入 react?
实质上来说 JSX 是 React.createElement(component, props, ...children)
办法的语法糖。在 React 17 之前,如果应用了 JSX,其实就是在应用 React,babel
会把组件转换为 CreateElement
模式。在 React 17 之后,就不再须要引入,因为 babel
曾经能够帮咱们主动引入 react。
父子组件的通信形式?
父组件向子组件通信:父组件通过 props 向子组件传递须要的信息。
// 子组件: Child
const Child = props =>{return <p>{props.name}</p>
}
// 父组件 Parent
const Parent = ()=>{return <Child name="react"></Child>}
子组件向父组件通信:: props+ 回调的形式。
// 子组件: Child
const Child = props =>{
const cb = msg =>{return ()=>{props.callback(msg)
}
}
return (<button onClick={cb("你好!")}> 你好 </button>
)
}
// 父组件 Parent
class Parent extends Component {callback(msg){console.log(msg)
}
render(){return <Child callback={this.callback.bind(this)}></Child>
}
}
React 组件的构造函数有什么作用?它是必须的吗?
构造函数次要用于两个目标:
- 通过将对象调配给 this.state 来初始化本地状态
- 将事件处理程序办法绑定到实例上
所以,当在 React class 中须要设置 state 的初始值或者绑定事件时,须要加上构造函数,官网 Demo:
class LikeButton extends React.Component {constructor() {super();
this.state = {liked: false};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {this.setState({liked: !this.state.liked});
}
render() {
const text = this.state.liked ? 'liked' : 'haven\'t liked';
return (<div onClick={this.handleClick}>
You {text} this. Click to toggle. </div>
);
}
}
ReactDOM.render(
<LikeButton />,
document.getElementById('example')
);
构造函数用来新建父类的 this 对象;子类必须在 constructor 办法中调用 super 办法;否则新建实例时会报错;因为子类没有本人的 this 对象,而是继承父类的 this 对象,而后对其进行加工。如果不调用 super 办法;子类就得不到 this 对象。
留神:
- constructor () 必须配上 super(), 如果要在 constructor 外部应用 this.props 就要 传入 props , 否则不必
- JavaScript 中的 bind 每次都会返回一个新的函数, 为了性能等思考, 尽量在 constructor 中绑定事件
react 强制刷新
component.forceUpdate() 一个不罕用的生命周期办法, 它的作用就是强制刷新
官网解释如下
默认状况下,当组件的 state 或 props 发生变化时,组件将从新渲染。如果 render() 办法依赖于其余数据,则能够调用 forceUpdate() 强制让组件从新渲染。
调用 forceUpdate() 将以致组件调用 render() 办法,此操作会跳过该组件的 shouldComponentUpdate()。但其子组件会触发失常的生命周期办法,包含 shouldComponentUpdate() 办法。如果标记发生变化,React 仍将只更新 DOM。
通常你应该防止应用 forceUpdate(),尽量在 render() 中应用 this.props 和 this.state。
shouldComponentUpdate 在初始化 和 forceUpdate 不会执行
参考 前端进阶面试题具体解答
虚构 DOM 的引入与间接操作原生 DOM 相比,哪一个效率更高,为什么
虚构 DOM 绝对原生的 DOM 不肯定是效率更高,如果只批改一个按钮的文案,那么虚构 DOM 的操作无论如何都不可能比实在的 DOM 操作更快。在首次渲染大量 DOM 时,因为多了一层虚构 DOM 的计算,虚构 DOM 也会比 innerHTML 插入慢。它能保障性能上限,在实在 DOM 操作的时候进行针对性的优化时,还是更快的。所以要依据具体的场景进行探讨。
在整个 DOM 操作的演化过程中,其实主要矛盾并不在于性能,而在于开发者写得爽不爽,在于研发体验 / 研发效率。虚构 DOM 不是别的,正是前端开发们为了谋求更好的研发体验和研发效率而发明进去的高阶产物。虚构 DOM 并不一定会带来更好的性能,React 官网也素来没有把虚构 DOM 作为性能层面的卖点对外输入过。** 虚构 DOM 的优越之处在于,它可能在提供更爽、更高效的研发模式(也就是函数式的 UI 编程形式)的同时,依然放弃一个还不错的性能。
React 的事件和一般的 HTML 事件有什么不同?
区别:
- 对于事件名称命名形式,原生事件为全小写,react 事件采纳小驼峰;
- 对于事件函数解决语法,原生事件为字符串,react 事件为函数;
- react 事件不能采纳 return false 的形式来阻止浏览器的默认行为,而必须要地明确地调用
preventDefault()
来阻止默认行为。
合成事件是 react 模仿原生 DOM 事件所有能力的一个事件对象,其长处如下:
- 兼容所有浏览器,更好的跨平台;
- 将事件对立寄存在一个数组,防止频繁的新增与删除(垃圾回收)。
- 不便 react 对立治理和事务机制。
事件的执行程序为原生事件先执行,合成事件后执行,合成事件会冒泡绑定到 document 上,所以尽量避免原生事件与合成事件混用,如果原生事件阻止冒泡,可能会导致合成事件不执行,因为须要冒泡到 document 上合成事件才会执行。
跨级组件的通信形式?
父组件向子组件的子组件通信,向更深层子组件通信:
- 应用 props,利用两头组件层层传递, 然而如果父组件构造较深,那么两头每一层组件都要去传递 props,减少了复杂度,并且这些 props 并不是两头组件本人须要的。
- 应用 context,context 相当于一个大容器,能够把要通信的内容放在这个容器中,这样不论嵌套多深,都能够随便取用,对于逾越多层的全局数据能够应用 context 实现。
// context 形式实现跨级组件通信
// Context 设计目标是为了共享那些对于一个组件树而言是“全局”的数据
const BatteryContext = createContext();
// 子组件的子组件
class GrandChild extends Component {render(){
return (
<BatteryContext.Consumer>
{color => <h1 style={{"color":color}}> 我是红色的:{color}</h1>
} </BatteryContext.Consumer>
)
}
}
// 子组件
const Child = () =>{
return (<GrandChild/>)
}
// 父组件
class Parent extends Component {
state = {color:"red"}
render(){const {color} = this.state
return (<BatteryContext.Provider value={color}>
<Child></Child>
</BatteryContext.Provider>
)
}
}
React.Children.map 和 js 的 map 有什么区别?
JavaScript 中的 map 不会对为 null 或者 undefined 的数据进行解决,而 React.Children.map 中的 map 能够解决 React.Children 为 null 或者 undefined 的状况。
React 中 refs 的作用是什么?有哪些利用场景?
Refs 提供了一种形式,用于拜访在 render 办法中创立的 React 元素或 DOM 节点。Refs 应该审慎应用,如下场景应用 Refs 比拟适宜:
- 解决焦点、文本抉择或者媒体的管制
- 触发必要的动画
- 集成第三方 DOM 库
Refs 是应用 React.createRef()
办法创立的,他通过 ref
属性附加到 React 元素上。要在整个组件中应用 Refs,须要将 ref
在构造函数中调配给其实例属性:
class MyComponent extends React.Component {constructor(props) {super(props)
this.myRef = React.createRef()}
render() {return <div ref={this.myRef} />
}
}
因为函数组件没有实例,因而不能在函数组件上间接应用 ref
:
function MyFunctionalComponent() {return <input />;}
class Parent extends React.Component {constructor(props) {super(props);
this.textInput = React.createRef();}
render() {
// 这将不会工作!return (<MyFunctionalComponent ref={this.textInput} />
);
}
}
但能够通过闭合的帮忙在函数组件外部进行应用 Refs:
function CustomTextInput(props) {
// 这里必须申明 textInput,这样 ref 回调才能够援用它
let textInput = null;
function handleClick() {textInput.focus();
}
return (
<div>
<input
type="text"
ref={(input) => {textInput = input;}} /> <input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
留神:
- 不应该适度的应用 Refs
-
ref
的返回值取决于节点的类型:- 当
ref
属性被用于一个一般的 HTML 元素时,React.createRef()
将接管底层 DOM 元素作为他的current
属性以创立ref
。 - 当
ref
属性被用于一个自定义的类组件时,ref
对象将接管该组件已挂载的实例作为他的current
。
- 当
- 当在父组件中须要拜访子组件中的
ref
时可应用传递 Refs 或回调 Refs。
对 React 和 Vue 的了解,它们的异同
相似之处:
- 都将注意力集中放弃在外围库,而将其余性能如路由和全局状态治理交给相干的库
- 都有本人的构建工具,能让你失去一个依据最佳实际设置的我的项目模板。
- 都应用了 Virtual DOM(虚构 DOM)进步重绘性能
- 都有 props 的概念,容许组件间的数据传递
- 都激励组件化利用,将利用分拆成一个个性能明确的模块,进步复用性
不同之处:
1)数据流
Vue 默认反对数据双向绑定,而 React 始终提倡单向数据流
2)虚构 DOM
Vue2.x 开始引入 ”Virtual DOM”,打消了和 React 在这方面的差别,然而在具体的细节还是有各自的特点。
- Vue 声称能够更快地计算出 Virtual DOM 的差别,这是因为它在渲染过程中,会跟踪每一个组件的依赖关系,不须要从新渲染整个组件树。
- 对于 React 而言,每当利用的状态被扭转时,全副子组件都会从新渲染。当然,这能够通过 PureComponent/shouldComponentUpdate 这个生命周期办法来进行管制,但 Vue 将此视为默认的优化。
3)组件化
React 与 Vue 最大的不同是模板的编写。
- Vue 激励写近似惯例 HTML 的模板。写起来很靠近规范 HTML 元素,只是多了一些属性。
- React 举荐你所有的模板通用 JavaScript 的语法扩大——JSX 书写。
具体来讲:React 中 render 函数是反对闭包个性的,所以咱们 import 的组件在 render 中能够间接调用。然而在 Vue 中,因为模板中应用的数据都必须挂在 this 上进行一次直达,所以 import 完组件之后,还须要在 components 中再申明下。
4)监听数据变动的实现原理不同
- Vue 通过 getter/setter 以及一些函数的劫持,能准确晓得数据变动,不须要特地的优化就能达到很好的性能
- React 默认是通过比拟援用的形式进行的,如果不优化(PureComponent/shouldComponentUpdate)可能导致大量不必要的 vDOM 的从新渲染。这是因为 Vue 应用的是可变数据,而 React 更强调数据的不可变。
5)高阶组件
react 能够通过高阶组件(Higher Order Components– HOC)来扩大,而 vue 须要通过 mixins 来扩大。
起因高阶组件就是高阶函数,而 React 的组件自身就是纯正的函数,所以高阶函数对 React 来说大海捞针。相同 Vue.js 应用 HTML 模板创立视图组件,这时模板无奈无效的编译,因而 Vue 不采纳 HOC 来实现。
6)构建工具
两者都有本人的构建工具
- React ==> Create React APP
- Vue ==> vue-cli
7)跨平台
- React ==> React Native
- Vue ==> Weex
Redux 状态管理器和变量挂载到 window 中有什么区别
两者都是存储数据以供前期应用。然而 Redux 状态更改可回溯——Time travel,数据多了的时候能够很清晰的晓得改变在哪里产生,残缺的提供了一套状态管理模式。
随着 JavaScript 单页利用开发日趋简单,JavaScript 须要治理比任何时候都要多的 state(状态)。这些 state 可能包含服务器响应、缓存数据、本地生成尚未长久化到服务器的数据,也包含 UI 状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等。
治理一直变动的 state 十分艰难。如果一个 model 的变动会引起另一个 model 变动,那么当 view 变动时,就可能引起对应 model 以及另一个 model 的变动,顺次地,可能会引起另一个 view 的变动。直至你搞不清楚到底产生了什么。state 在什么时候,因为什么起因,如何变动未然不受管制。当零碎变得盘根错节的时候,想重现问题或者增加新性能就会变得举步维艰。
如果这还不够蹩脚,思考一些来自前端开发畛域的新需要,如更新调优、服务端渲染、路由跳转前申请数据等等。前端开发者正在禁受前所未有的复杂性,难道就这么放弃了吗? 当然不是。
这里的复杂性很大水平上来自于:咱们总是将两个难以理清的概念混同在一起:变动和异步。能够称它们为曼妥思和可乐。如果把二者离开,能做的很好,但混到一起,就变得一团糟。一些库如 React 视图在视图层禁止异步和间接操作 DOM 来解决这个问题。美中不足的是,React 仍旧把解决 state 中数据的问题留给了你。Redux 就是为了帮你解决这个问题。
对 React 中 Fragment 的了解,它的应用场景是什么?
在 React 中,组件返回的元素只能有一个根元素。为了不增加多余的 DOM 节点,咱们能够应用 Fragment 标签来包裹所有的元素,Fragment 标签不会渲染出任何元素。React 官网对 Fragment 的解释:
React 中的一个常见模式是一个组件返回多个元素。Fragments 容许你将子列表分组,而无需向 DOM 增加额定节点。
import React, {Component, Fragment} from 'react'
// 个别模式
render() {
return (
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
);
}
// 也能够写成以下模式
render() {
return (
<>
<ChildA />
<ChildB />
<ChildC />
</>
);
}
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>
);
}
}
应用箭头函数 (arrow functions) 的长处是什么
- 作用域平安:在箭头函数之前,每一个新创建的函数都有定义本身的
this
值(在构造函数中是新对象;在严格模式下,函数调用中的this
是未定义的;如果函数被称为“对象办法”,则为根底对象等),但箭头函数不会,它会应用关闭执行上下文的this
值。 - 简略:箭头函数易于浏览和书写
- 清晰:当一切都是一个箭头函数,任何惯例函数都能够立刻用于定义作用域。开发者总是能够查找 next-higher 函数语句,以查看
this
的值
何为 JSX
JSX 是 JavaScript 语法的一种语法扩大,并领有 JavaScript 的全副性能。JSX 生产 React “ 元素 ”,你能够将任何的 JavaScript 表达式封装在花括号里,而后将其嵌入到 JSX 中。在编译实现之后,JSX 表达式就变成了惯例的 JavaScript 对象,这意味着你能够在 if
语句和 for
循环外部应用 JSX,将它赋值给变量,承受它作为参数,并从函数中返回它。
HOC 相比 mixins 有什么长处?
HOC 和 Vue 中的 mixins 作用是统一的,并且在晚期 React 也是应用 mixins 的形式。然而在应用 class 的形式创立组件当前,mixins 的形式就不能应用了,并且其实 mixins 也是存在一些问题的,比方:
- 隐含了一些依赖,比方我在组件中写了某个
state
并且在mixin
中应用了,就这存在了一个依赖关系。万一下次他人要移除它,就得去mixin
中查找依赖 - 多个
mixin
中可能存在雷同命名的函数,同时代码组件中也不能呈现雷同命名的函数,否则就是重写了,其实我始终感觉命名真的是一件麻烦事。。 - 雪球效应,尽管我一个组件还是应用着同一个
mixin
,然而一个mixin
会被多个组件应用,可能会存在需要使得mixin
批改本来的函数或者新增更多的函数,这样可能就会产生一个保护老本
HOC 解决了这些问题,并且它们达成的成果也是统一的,同时也更加的政治正确(毕竟更加函数式了)。
state 是怎么注入到组件的,从 reducer 到组件经验了什么样的过程
通过 connect 和 mapStateToProps 将 state 注入到组件中:
import {connect} from 'react-redux'
import {setVisibilityFilter} from '@/reducers/Todo/actions'
import Link from '@/containers/Todo/components/Link'
const mapStateToProps = (state, ownProps) => ({active: ownProps.filter === state.visibilityFilter})
const mapDispatchToProps = (dispatch, ownProps) => ({setFilter: () => {dispatch(setVisibilityFilter(ownProps.filter))
}
})
export default connect(
mapStateToProps,
mapDispatchToProps
)(Link)
下面代码中,active 就是注入到 Link 组件中的状态。mapStateToProps(state,ownProps)中带有两个参数,含意是∶
- state-store 治理的全局状态对象,所有都组件状态数据都存储在该对象中。
- ownProps 组件通过 props 传入的参数。
reducer 到组件经验的过程:
- reducer 对 action 对象解决,更新组件状态,并将新的状态值返回 store。
- 通过 connect(mapStateToProps,mapDispatchToProps)(Component)对组件 Component 进行降级,此时将状态值从 store 取出并作为 props 参数传递到组件。
高阶组件实现源码∶
import React from 'react'
import PropTypes from 'prop-types'
// 高阶组件 contect
export const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent) => {
class Connect extends React.Component {
// 通过对 context 调用获取 store
static contextTypes = {store: PropTypes.object}
constructor() {super()
this.state = {allProps: {}
}
}
// 第一遍需初始化所有组件初始状态
componentWillMount() {
const store = this.context.store
this._updateProps()
store.subscribe(() => this._updateProps()); // 退出_updateProps()至 store 里的监听事件列表}
// 执行 action 后更新 props,使组件能够更新至最新状态(相似于 setState)_updateProps() {
const store = this.context.store;
let stateProps = mapStateToProps ?
mapStateToProps(store.getState(), this.props) : {} // 避免 mapStateToProps 没有传入
let dispatchProps = mapDispatchToProps ?
mapDispatchToProps(store.dispatch, this.props) : {dispatch: store.dispatch} // 避免 mapDispatchToProps 没有传入
this.setState({
allProps: {
...stateProps,
...dispatchProps,
...this.props
}
})
}
render() {return <WrappedComponent {...this.state.allProps} />
}
}
return Connect
}
React.forwardRef 是什么?它有什么作用?
React.forwardRef 会创立一个 React 组件,这个组件可能将其承受的 ref 属性转发到其组件树下的另一个组件中。这种技术并不常见,但在以下两种场景中特地有用:
- 转发 refs 到 DOM 组件
- 在高阶组件中转发 refs