共计 7258 个字符,预计需要花费 19 分钟才能阅读完成。
diff 算法是怎么运作
每一种节点类型有本人的属性,也就是 prop,每次进行 diff 的时候,react 会先比拟该节点类型,如果节点类型不一样,那么 react 会间接删除该节点,而后间接创立新的节点插入到其中,如果节点类型一样,那么会比拟 prop 是否有更新,如果有 prop 不一样,那么 react 会断定该节点有更新,那么重渲染该节点,而后在对其子节点进行比拟,一层一层往下,直到没有子节点
react 和 vue 的区别
相同点:
- 数据驱动页面,提供响应式的试图组件
- 都有 virtual DOM, 组件化的开发,通过 props 参数进行父子之间组件传递数据,都实现了 webComponents 标准
- 数据流动单向,都反对服务器的渲染 SSR
- 都有反对 native 的办法,react 有 React native,vue 有 wexx
不同点:
- 数据绑定:Vue 实现了双向的数据绑定,react 数据流动是单向的
- 数据渲染:大规模的数据渲染,react 更快
- 应用场景:React 配合 Redux 架构适宜大规模多人合作简单我的项目,Vue 适宜小快的我的项目
- 开发格调:react 举荐做法 jsx + inline style 把 html 和 css 都写在 js 了
vue 是采纳 webpack +vue-loader 单文件组件格局,html, js, css 同一个文件
在哪个生命周期中你会收回 Ajax 申请?为什么?
Ajax 申请应该写在组件创立期的第五个阶段,即 componentDidMount 生命周期办法中。起因如下。
在创立期的其余阶段,组件尚未渲染实现。而在存在期的 5 个阶段,又不能确保生命周期办法肯定会执行(如通过 shouldComponentUpdate 办法优化更新等)。在销毀期,组件行将被销毁,申请数据变得无意义。因而在这些阶段发岀 Ajax 申请显然不是最好的抉择。
在组件尚未挂载之前,Ajax 申请将无奈执行结束,如果此时发出请求,将意味着在组件挂载之前更新状态(如执行 setState),这通常是不起作用的。
在 componentDidMount 办法中,执行 Ajax 即可保障组件曾经挂载,并且可能失常更新组件。
简述 flux 思维
Flux
的最大特点,就是数据的 ” 单向流动 ”。
- 用户拜访
View
View
收回用户的Action
Dispatcher
收到Action
,要求Store
进行相应的更新Store
更新后,收回一个"change"
事件View
收到"change"
事件后,更新页面
说说 React 组件开发中对于作用域的常见问题。
在 EMAScript5 语法标准中,对于作用域的常见问题如下。
(1)在 map 等办法的回调函数中,要绑定作用域 this(通过 bind 办法)。
(2)父组件传递给子组件办法的作用域是父组件实例化对象,无奈扭转。
(3)组件事件回调函数办法的作用域是组件实例化对象(绑定父组件提供的办法就是父组件实例化对象),无奈扭转。
在 EMAScript6 语法标准中,对于作用域的常见问题如下。
(1)当应用箭头函数作为 map 等办法的回调函数时,箭头函数的作用域是以后组件的实例化对象(即箭头函数的作用域是定义时的作用域),毋庸绑定作用域。
(2)事件回调函数要绑定组件作用域。
(3)父组件传递办法要绑定父组件作用域。
总之,在 EMAScript6 语法标准中,组件办法的作用域是能够扭转的。
如何用 React 构建(build)生产模式?
通常,应用 Webpack 的 DefinePlugin 办法将 NODE ENV 设置为 production。这将剥离 propType 验证和额定的正告。除此之外,还能够缩小代码,因为 React 应用 Uglify 的 dead-code 来打消开发代码和正文,这将大大减少包占用的空间。
参考 前端进阶面试题具体解答
如何防止反复发动 ajax 获取数据?
- 数据放在 redux 外面
React 如何进行组件 / 逻辑复用?
抛开曾经被官网弃用的 Mixin, 组件形象的技术目前有三种比拟支流:
-
高阶组件:
- 属性代理
- 反向继承
- 渲染属性
- react-hooks
setState 到底是异步还是同步?
先给出答案: 有时体现出异步, 有时体现出同步
setState
只在合成事件和钩子函数中是“异步”的,在原生事件和setTimeout
中都是同步的setState
的“异步”并不是说外部由异步代码实现,其实自身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用程序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,造成了所谓的“异步”,当然能够通过第二个参数setState(partialState, callback)
中的callback
拿到更新后的后果setState
的批量更新优化也是建设在“异步”(合成事件、钩子函数)之上的,在原生事件和setTimeout
中不会批量更新,在“异步”中如果对同一个值进行屡次setState
,setState
的批量更新策略会对其进行笼罩,取最初一次的执行,如果是同时setState
多个不同的值,在更新时会对其进行合并批量更新
Redux 中间件原理
- 指的是 action 和 store 之间,沟通的桥梁就是 dispatch,action 就是个对象。比方你用了 redux-thunk,action 也能够是个函数,怎么实现这个过程,就是通过中间件这个桥梁帮你实现的。action 达到 store 之前会走中间件,这个中间件会把函数式的 action 转化为一个对象,在传递给 store
HOC(高阶组件)
HOC(Higher Order Componennt) 是在 React 机制下社区造成的一种组件模式,在很多第三方开源库中体现弱小。
简述:
- 高阶组件不是组件,是 加强函数,能够输出一个元组件,返回出一个新的加强组件;
- 高阶组件的次要作用是 代码复用,操作 状态和参数;
用法:
- 属性代理 (Props Proxy): 返回出一个组件,它基于被包裹组件进行 性能加强;
- 默认参数: 能够为组件包裹一层默认参数;
function proxyHoc(Comp) {
return class extends React.Component {render() {
const newProps = {
name: 'tayde',
age: 1,
}
return <Comp {...this.props} {...newProps} />
}
}
}
- 提取状态: 能够通过 props 将被包裹组件中的 state 依赖外层,例如用于转换受控组件:
function withOnChange(Comp) {
return class extends React.Component {constructor(props) {super(props)
this.state = {name: '',}
}
onChangeName = () => {
this.setState({name: 'dongdong',})
}
render() {
const newProps = {
value: this.state.name,
onChange: this.onChangeName,
}
return <Comp {...this.props} {...newProps} />
}
}
}
应用姿态如下,这样就能十分疾速的将一个 Input 组件转化成受控组件。
const NameInput = props => (<input name="name" {...props} />)
export default withOnChange(NameInput)
包裹组件: 能够为被包裹元素进行一层包装,
function withMask(Comp) {
return class extends React.Component {render() {
return (
<div>
<Comp {...this.props} />
<div style={{
width: '100%',
height: '100%',
backgroundColor: 'rgba(0, 0, 0, .6)',
}}
</div>
)
}
}
}
反向继承 (Inheritance Inversion): 返回出一个组件,继承于被包裹组件,罕用于以下操作
function IIHoc(Comp) {
return class extends Comp {render() {return super.render();
}
};
}
渲染劫持 (Render Highjacking)
条件渲染: 依据条件,渲染不同的组件
function withLoading(Comp) {
return class extends Comp {render() {if(this.props.isLoading) {return <Loading />} else {return super.render()
}
}
};
}
能够间接批改被包裹组件渲染出的 React 元素树
操作状态 (Operate State) : 能够间接通过 this.state 获取到被包裹组件的状态,并进行操作。但这样的操作容易使 state 变得难以追踪,不易保护,审慎应用。
利用场景:
权限管制,通过形象逻辑,对立对页面进行权限判断,按不同的条件进行页面渲染:
function withAdminAuth(WrappedComponent) {
return class extends React.Component {constructor(props){super(props)
this.state = {isAdmin: false,}
}
async componentWillMount() {const currentRole = await getCurrentUserRole();
this.setState({isAdmin: currentRole === 'Admin',});
}
render() {if (this.state.isAdmin) {return <Comp {...this.props} />;
} else {return (<div> 您没有权限查看该页面,请分割管理员!</div>);
}
}
};
}
性能监控,包裹组件的生命周期,进行对立埋点:
function withTiming(Comp) {
return class extends Comp {constructor(props) {super(props);
this.start = Date.now();
this.end = 0;
}
componentDidMount() {super.componentDidMount && super.componentDidMount();
this.end = Date.now();
console.log(`${WrappedComponent.name} 组件渲染工夫为 ${this.end - this.start} ms`);
}
render() {return super.render();
}
};
}
代码复用,能够将反复的逻辑进行形象。
应用留神:
- 纯函数: 加强函数应为纯函数,防止侵入批改元组件;
- 防止用法净化: 现实状态下,应透传元组件的无关参数与事件,尽量保障用法不变;
- 命名空间: 为 HOC 减少特异性的组件名称,这样能便于开发调试和查找问题;
- 援用传递 : 如果须要传递元组件的 refs 援用,能够应用 React.forwardRef;
-
静态方法 : 元组件上的静态方法并无奈被主动传出,会导致业务层无奈调用;解决:
- 函数导出
- 静态方法赋值
- 从新渲 染: 因为加强函数每次调用是返回一个新组件,因而如果在 Render 中应用加强函数,就会导致每次都从新渲染整个 HOC,而且之前的状态会失落;
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 {}
在生命周期中的哪一步你应该发动 AJAX 申请
咱们该当将 AJAX 申请放到
componentDidMount
函数中执行,次要起因有下
React
下一代和谐算法Fiber
会通过开始或进行渲染的形式优化利用性能,其会影响到componentWillMount
的触发次数。对于componentWillMount
这个生命周期函数的调用次数会变得不确定,React
可能会屡次频繁调用componentWillMount
。如果咱们将AJAX
申请放到componentWillMount
函数中,那么不言而喻其会被触发屡次,天然也就不是好的抉择。- 如果咱们将
AJAX
申请搁置在生命周期的其余函数中,咱们并不能保障申请仅在组件挂载结束后才会要求响应。如果咱们的数据申请在组件挂载之前就实现,并且调用了setState
函数将数据增加到组件状态中,对于未挂载的组件则会报错。而在componentDidMount
函数中进行AJAX
申请则能无效防止这个问题
你了解“在 React 中,一切都是组件”这句话。
组件是 React 利用 UI 的构建块。这些组件将整个 UI 分成小的独立并可重用的局部。每个组件彼此独立,而不会影响 UI 的其余部分。
组件更新有几种办法
- this.setState() 批改状态的时候 会更新组件
- this.forceUpdate() 强制更新
- 组件件 render 之后,子组件应用到父组件中状态,导致子组件的 props 属性产生扭转的时候 也会触发子组件的更新
组件之间传值
-
父组件给子组件传值
在父组件中用标签属性的 = 模式传值
在子组件中应用 props 来获取值
-
子组件给父组件传值
在组件中传递一个函数
在子组件中用 props 来获取传递的函数,而后执行该函数
在执行函数的时候把须要传递的值当成函数的实参进行传递
-
兄弟组件之间传值
利用父组件
先把数据通过【子组件】===》【父组件】
而后在数据通过【父组件】===〉【子组件】
音讯订阅
应用 PubSubJs 插件
(在构造函数中)调用 super(props) 的目标是什么
在 super()
被调用之前,子类是不能应用 this
的,在 ES2015 中,子类必须在 constructor
中调用 super()
。传递 props
给 super()
的起因则是便于 (在子类中) 能在 constructor
拜访 this.props
。
diff 虚构 DOM 比拟的规定
-
【旧虚构 DOM】与【新虚构 DOM】中雷同 key
若虚构 DOM 中的内容没有产生扭转,间接应用旧的虚构 DOM
若虚构 DOM 中的内容产生扭转了,则生成新实在的 DOM,随后替换页面中之前的实在 DOM
-
【旧虚构 DOM】中未找到 与【新虚构 DOM】雷同的 key
依据数据创立实在 DOM,随后渲染到页面
React 性能优化在哪个生命周期?它优化的原理是什么?
react 的父级组件的 render 函数从新渲染会引起子组件的 render 办法的从新渲染。然而,有的时候子组件的承受父组件的数据没有变动。子组件 render 的执行会影响性能,这时就能够应用 shouldComponentUpdate 来解决这个问题。
应用办法如下:
shouldComponentUpdate(nexrProps) {if (this.props.num === nexrProps.num) {return false}
return true;
}
shouldComponentUpdate 提供了两个参数 nextProps 和 nextState,示意下一次 props 和一次 state 的值,当函数返回 false 时候,render()办法不执行,组件也就不会渲染,返回 true 时,组件照常重渲染。此办法就是拿以后 props 中值和下一次 props 中的值进行比照,数据相等时,返回 false,反之返回 true。
须要留神,在进行新旧比照的时候,是 浅比照,也就是说如果比拟的数据时援用数据类型,只有数据的援用的地址没变,即便内容变了,也会被断定为 true。
面对这个问题,能够应用如下办法进行解决:
(1)应用 setState 扭转数据之前,先采纳 ES6 中 assgin 进行拷贝,然而 assgin 只深拷贝的数据的第一层,所以说不是最完满的解决办法:
const o2 = Object.assign({},this.state.obj)
o2.student.count = '00000';
this.setState({obj: o2,})
(2)应用 JSON.parse(JSON.stringfy())进行深拷贝,然而遇到数据为 undefined 和函数时就会错。
const o2 = JSON.parse(JSON.stringify(this.state.obj))
o2.student.count = '00000';
this.setState({obj: o2,})
ref 是一个函数又有什么益处?
- 不便 react 销毁组件、从新渲染的时候去清空 refs 的货色,避免内存泄露