不折腾的前端,和咸鱼有什么区别html
返回目录
React 是现现在流行的前端框架,也是不少大厂面试必备。前端
React 与 Vue 虽有不一样,但一样做为一款 MV*
框架,虽然实现可能不同,但在一些理念上仍是有类似的,例如数据驱动、组件化、虚拟 DOM 等。vue
固然,还有一些问题,可能最近都无法搞清楚的了,毕竟罗马不是一天能建成的:react
这些每每要更深刻挖掘方可得知本身的结论,任重道远,不停歇。webpack
返回目录
[x] React 和原生对比git
[x] React 和 Vue 对比github
[x] React 生命周期web
constructor
、getDerivedStateFromProps
、render
、componentDidMount
getDerivedStateFromProps
、shouldComponentUpdate
、render
、getSnapshotBeforeUpdate
、componentDidUpdate
componentWillUnmount
[x] setState
面试
[x] this 指向问题算法
bind
修正bind
和箭头函数的区别value
和 defaultValue
[x] 组件通信
props
Context
Redux
Redux
:Redux
、React-Redux
以及 Redux-Saga
工做流Mixin
、HOC
和 Hook
[x] 性能优化
prerender-spa-plugin
react-placeholder
html-webpack-plugin
Tree Shaking
SplitChunkPlugin
Code Splitting
react-lazyload
返回目录
经过 Ajax
从后端获取数据,而后经过 jQuery 生成 DOM 结果更新到页面中。
可是随着业务发展,项目愈来愈复杂,交互性愈来愈强,每每用户在某个时刻可能操做好几块内容,从而 DOM 的操做愈来愈频繁,页面性能逐步下降,用户也不满意这样卡慢的现状了。
这时候有了 MVVM,双向数据绑定让数据在修改的时候同步 DOM 的更新,反之亦可。
这个设定大大下降手动维护 DOM 的成本,而 MVVM 为 React 的特性之一,虽然 React 属于单项数据流,须要咱们手动实现双向数据绑定。
光靠绑定是不够的,这样无法解决频繁操做 DOM 的问题。
因此 React 内部实现了一套虚拟 DOM 的更新,它将真实 DOM 在 JS 中作一套缓存,每次有数据更新的时候,先内部经过 Diff
算法进行比对,而后收集一箩筐更新后,才对 DOM 进行更新,这样就大大下降了 DOM 的操做次数。
那么,Diff
怎么运做呢?
Diff
获取虚拟 DOM 节点变动的 4 种状况比较:节点类型变了、节点类型同样,仅仅属性或者属性值变了、文本变了、增长、删除或者移动了子节点。
React 不一样于 Vue,能够经过 v-model
的形式,让用户的操做和 JavaScript 存储的数据同步更新,它须要经过 setState
来更新组件内容。
可是,若是想经过一个组件来渲染它兄弟组件,React 一开始在这块作得并非那么好,因此就须要引入一个状态管理中心,来帮助咱们管理状态(state
),于是就有了 Redux
。
在 Redux
中,当 state
有变化的时候,依赖这个 state
的组件就会从新渲染,这样就解决了组件间数据传递的问题。
Redux
有个问题,就是修改某个 state
的时候,须要通过 action.js
、types.js
、reducers.js
这一系列文件,这样子 Redux
的数据流虽然很是正规,可是写起来复杂啊。
因此,社区又出现了另外一套解决方案,也就是 Mobx
。
Mobx
推崇代码简约移动,只须要定义一个可贯彻的对象,而后在哪一个组件中使用到了这个可观察对象,而且这个对象的数据有更改,那就会从新渲染,这使得 Mobx
开发项目的时候能够简单快速地完成不少功能。
可是 Mobx
也有缺点,就是数据流太过随意,出了 Bug 很差定位。
因此,针对于小项目来讲,社区推荐使用 MobX,对大项目推荐使用 Redux。
本段文本参考的内容已记录在参考文献中
返回目录
返回目录
返回目录
Webpack
+ Babel
去搭建脚手架。Vue-router
和 Vuex
,而 React 有 React-router
和 React-Redux
。返回目录
HTML
的模板进行渲染,而 React 推荐 JSX 的书写方式。v-model
绑定的数据,用户改变输入值后对应的值也相应改变。而 React 须要经过 setState
进行设置变化。Diff
队列保存须要更新的 DOM,获得 patch
树,再统一批量更新 DOM。实话实说是看别人说的异同,担忧有 “小伙伴” 有本身观点,而后在这里被喷, jsliang 不敢持有任何本身的观点,可是面试我老是要这么回答面试官的 /滑稽
返回目录
React 的核心流程能够分为两个部分:
reconciliation
(调度算法,也可称为 render
):
state
与 props
diff
算法,获取 VDOM change
commit
为何须要 Fiber
?
随着应用变得愈来愈庞大,整个更新渲染的过程开始变得吃力,大量的组件渲染会致使主进程长时间被占用,致使一些动画或高频操做出现卡顿和掉帧的状况。
而关键点,即是 同步阻塞。
在以前的调度算法中,React 须要实例化每一个类组件,生成一颗组件树,使用 同步递归 的方式进行遍历渲染,而这个过程最大的问题就是没法 暂停和恢复。
因此,为了解决这个问题(同步阻塞),一般有两种方法: 异步 与 任务分割。
而 React Fiber
即是为了实现任务分割而诞生的。
React Fiber
简述:
stack reconciler
重构成新版的 fiber reconciler
,变成了具备链表和指针的 单链表树遍历算法。经过指针映射,每一个单元都记录着遍历当下的上一步与下一步,从而使遍历变得能够被暂停和重启。React Fiber
核心:
返回目录
React 逐渐废弃的生命周期方法:
componentWillMount
componentWillReceiveProps
componentWillUpdate
返回目录
返回目录
返回目录
constructor
:构造函数,最早被执行,一般在构造函数中初始化 state
对象或者给自定义方法绑定 this
getDerivedStateFromProps
:static getDerivedStateFromProps(nextProps, prevState)
,这是个静态方法,当咱们接收到新的属性想去修改咱们 state
,可使用 getDerivedStateFromProps
。render
:render
函数是个纯函数,只返回须要渲染的东西,不该该包含其余的业务逻辑,能够返回原生 DOM、React 组件、Fragment
、Portals
、字符串和数字等内容。componentDidMount
:组件装载以后调用,此时咱们能够获取到 DOM 节点并操做,好比对 Canvas
、SVG
等操做。服务器请求、订阅均可以写这个里面,可是记得在 componentWillUnmount
中取消订阅。React 的接口请求是放在 componentDidMount
里面比较合适,旧版本有人放在 componentWillMount
里面,从而致使屡次请求,如今 componentWillMount
不推荐使用了,因此转 componentDidMount
就很是科学了。
存在如下问题:
getDerivedStateFromProps
是静态的?当它设置为静态函数,代表这个函数不能经过 this
访问到 class
的属性,也并不推荐直接访问属性。
setState
?能够在 componentDidMount
和 componentDidUpdate
中使用,此时 DOM 已经稳定下来了,能够进行数据的操做了。
返回目录
getDerivedStateFromProps
:此方法在更新阶段也会被调用。shouldComponentUpdate
:shouldComponentUpdate(nextProps, nextState)
,有两个参数,表示新的属性和变化以后的 state
,返回一个布尔值。若是是 true
表示会触发从新渲染,false
表示不会触发从新渲染,默认返回 true
。能够利用这个生命周期来优化 React 程序性能。render
:同挂载阶段 render
。getSnapshotBeforeUpdate
:getSnapshotBeforeUpdate(prevProps, prevState)
,这个方法会在 render
以后,componentDidUpdate
以前调用,有两个参数,表示以前属性和以前的 state
。这个函数有一个返回值,会做为第三个参数传给 componentDidUpdate
,若是不须要返回值,能够返回 null
,这个方法必须和 componentDidUpdate
配合使用。componentDidUpdate
:componentDidUpdate(prevProps, prevState, snapshot)
,在 getSnapshotBeforeUpdate
props
,以前的 state
,以及 snapshot
。参数 snapshot
是 getSnapshotBeforeUpdate
返回的,若是触发某些回调函数时须要用到 DOM
元素的状态,则将对比或者计算过程迁移到 getSnapshotBeforeUpdate
,而后在 componentDidUpdate
中统一触发回调或者更新状态。返回目录
componentWillUnmount
:当组件被卸载或者销毁时会被调用,在这里清除定时器,或者取消网络请求,用来清理无效的 DOM 元素等垃圾回收工做。返回目录
setState
是 React 中用于修改状态,更新视图的方法。
返回目录
在代码中调用 setState
以后,React 会将传入的参数对象与组件当前的状态合并,触发所谓的调和过程(Reconciliation
)。
通过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树而且着手从新渲染整个 UI 界面。
在 React 获得元素树以后,React 会自动计算新树和老树之间的节点差别,而后根据差别对界面进行最小化从新渲染。
在差别计算算法(Diff
)中,React 可以相对精确地知道哪些位置发生了改变以及英国如何改变,保证了按需更新,而不是所有从新渲染。
简单来讲:
Diff
)返回目录
回答:有时候同步,有时候异步。
setState
在合成事件和钩子函数中是异步的,在原生事件和 setTimeout
是同步的。setState
的异步,并非说内部由异步代码实现,它自己执行的过程和代码是同步的,只是合成事件和钩子函数的调用顺序在更新以前,致使在合成事件和钩子函数中无法立马拿到更新后的值,从而造成了所谓的异步。setState
能够经过第二个参数 setState(partialState, callback)
,在回调方法中拿到更新后的结果。返回目录
在 React 中有几种方法能够修正 this
的指向,这里例举 4 种方法:
import React, { Component } from 'react' class App extends Component { constructor (props) { super(props); this.handleClick = this.handleClick.bind(this); } handleClick () { console.log('jsliang 2020'); } handleClick2 = () => { console.log('jsliang 2021'); } render () { // 四种绑定方法 return ( <div className='App'> {/* 方法一:经过 constructor 中进行 bind 绑定 */} <button onClick={this.handleClick}>btn 1</button> {/* 方法二:在里边绑定 this */} <button onClick={this.handleClick.bind(this)}>btn 2</button> {/* 方法三:经过箭头函数返回事件 */} <button onClick={() => this.handleClick()}>btn 3</button> {/* 方法四:让方法变成箭头函数 */} <button onClick={this.handleClick2}>btn 4</button> {/* 额外:直接调用不须要绑定 this */} {this.handleClick()} </div> ) } } export default App;
那么,使用 bind
和箭头函数有什么区别吗?
箭头函数除了代码少,与普通函数最大的不一样就是:this
是由声明该函数时候定义的,通常是隐性定义为声明该函数时的做用域 this
。
经过 bind
的话,至关于:Foo.prototype.a = function() {}
,是经过原型链的一个指正绑定。
而经过箭头函数的话,就至关于:
class Foo { constructor() { this.a = () => {}; } }
返回目录
在 Ant Design
中,对 Input
输入框进行操做,若是是改变 defaultValue
会发现毫无做用。
这是由于 React 的 form
表单组件中的 defaultValue
一经传递值后,后续改变 defaultValue
都将不起做用,被忽略了。
具体来讲这是一种 React 非受控组件,其状态是在 input
的 React 内部控制,不受调用者控制。
因此受控组件就是能够被 React 状态控制的组件。双向数据绑定就是受控组件,你能够为 form
中某个输入框添加 value
属性,而后控制它的一个改变。而非受控组件就是没有添加 value
属性的组件,你并不能对它的固定值进行操做。
返回目录
props
方式,向子组件进行通信。props
中传递方法,而后子组件调用这个方法,将自身须要传递的信息,传递到父组件的做用域中。Context
,或者 Redux
进行数据通信。返回目录
网上有挺多关于 Redux
、React-Redux
、Redux-Saga
的使用,这里就不废话介绍了,仍是讲讲 jsliang 在工做中的一个使用吧。
工做目录
- 某个页面文件夹 - View.jsx 当前页面主入口 - Child.jsx 子组件 - Brother.jsx 兄弟组件 - action.js 动做 - types.js 类型 - saga.js 调用接口 - reducers.js 处理数据
正常的一个工做目录如上所示,咱们工做中是怎么个使用方式呢?
首先,在 View.jsx
中经过 React-Redux
链接 React
和 Redux
而后,假设如今 Child.jsx
须要调用接口(异步处理),那么会:
action.js
中定义这个方法,会传递什么参数。types.js
是辅助 action.js
的一个内容,为了防止方法体的重复,咱们会在 types.js
中定义大写的 action
名字。View.jsx
中经过 dispatch
触发方法,例如 dispatch(getPage(page, perPage))
。reducers.js
中和 sage.js
中都能监听到这个方法,可是咱们是在 sage.js
中调用接口并处理数据。sage.js
中的传递给 reducers.js
中,让它去处理数据。接着,若是 Brother.jsx
只是单纯地想处理数据并在 Child.jsx
中使用,那么咱们处理方式是跟上面同样的,只是直接在 reducers.js
中处理,而不须要再在 sage.js
中调用接口而已。
最后,咱们再看看 redux
和 react-reduxt
的工做流程加深印象:
Redux
React-Redux
返回目录
前端发展速度很是之快,页面和组件变得愈来愈复杂,如何更好的实现 状态逻辑复用 一直都是应用程序中重要的一部分,这直接关系着应用程序的质量以及维护的难易程度。
Mixin
、HOC
和 Hook
是 React 采用的 3 种 状态逻辑复用 的技术,Mixin
已被抛弃,HOC
正当壮年,Hook
初露锋芒,掌握它迭代因素和规律很是重要。
返回目录
Mixin
(混入)是一种经过扩展收集功能的方式,它本质上是将一个对象的属性拷贝到另外一个对象上面去。
不过你能够拷贝任意多个对象的任意个方法到一个新对象上去,这是继承所不能实现的。
它的出现主要就是为了解决代码复用问题。
可是,它会带来一些危害:
Mixin
相互依赖、相互耦合,不利于代码维护Mixin
中的方法可能会互相冲突Mixin
很是多时,组件是能够感知到的,甚至还要为其作相关处理,这样给代码形成滚雪球式的复杂性。返回目录
基于 Mixin
的问题,React 推出对装饰模式的一种实现:高阶组件(HOC
)。
高阶组件接收一个组件做为参数,并返回一个新的组件。
高阶组件(
HOC
)是 React 中的高级技术,用来重用组件逻辑。但高阶组件自己并非 React API。它只是一种模式,这种模式是由 React 自身的组合性质必然产生的。
function visible(WrappedComponent) { return class extends Component { render() { const { visible, ...props } = this.props; if (visible === false) return null; return <WrappedComponent {...props} />; } } }
高阶组件能够应用于 日志打点、可用权限控制、双向绑定、表单校验等。
高阶组件解决了 Mixin
带来的问题:
可是,有光的地方总有暗,高阶组件也存在一些缺陷:
HOC
须要在原组件上进行包裹或者嵌套,若是大量使用 HOC
,将会产生很是多的嵌套,这让调试变得很是困难。HOC
能够劫持 props
,在不遵照约定的状况下也可能形成冲突。返回目录
Hook
是 React v16.7.0-alpha
中加入的新特性。它可让你在 class
之外使用 state
和其余 React 特性。
使用 Hook
,你能够在将含有 state
的逻辑从组件中抽象出来,这将可让这些逻辑容易被测试。
同时,Hook
能够帮助你在不重写组件结构的状况下复用这些逻辑。
因此,它也能够做为一种实现状态逻辑复用的方案。
Hook
使用带来的好处:
Hook
和 Mixin
在用法上有必定的类似之处,可是 Mixin
引入的逻辑和状态是能够相互覆盖的,而多个 Hook
之间互不影响,这让咱们不须要在把一部分精力放在防止避免逻辑复用的冲突上。HOC
的状况下让咱们的代码变得嵌套层级很是深,使用 Hook
,咱们能够实现扁平式的状态逻辑复用,而避免了大量的组件嵌套。class
组件构建咱们的程序时,他们各自拥有本身的状态,业务逻辑的复杂使这些组件变得愈来愈庞大,各个生命周期中会调用愈来愈多的逻辑,愈来愈难以维护。使用 Hook
,可让你更大限度的将公用逻辑抽离,将一个组件分割成更小的函数,而不是强制基于生命周期方法进行分割。class
可能须要掌握更多的知识,须要注意的点也越多,好比 this
指向、绑定事件等等。另外,计算机理解一个函数比理解一个 class
更快。Hooks
让你能够在 class
以外使用更多 React 的新特性。返回目录
<div id="root"> SVG </div>
,也可使用插件 prerender-spa-plugin
插件进行首屏渲染。html-webpack-plugin
插件自动插入 loading
,这样切换的时候,就不须要在每一个页面都写一套 loading
。Tree Shaking
来减小一些代码。SplitChunkPlugin
自动拆分业务基础库,减小大文件的存在。Code Splitting
来懒加载代码,提升用户的加载体验。例如经过 React Loadable
来将组件改写成支持动态 import
的形式。react-lazyload
这种成熟组件来进行懒加载的支持。react-placeholder
能够解决这种状况。返回目录
本系列有 67 篇参考文献。
返回目录
2019:
2018:
2017:
返回目录
2017:
返回目录
2020:
2017:
返回目录
最新:
2019:
2018:
2017:
返回目录
2019:
2016:
返回目录
2019:
2018:
2017:
2015:
返回目录
2018:
2017:
2015:
返回目录
2015:
返回目录
2019:
2017:
返回目录
2019:
2018:
返回目录
2018:
返回目录
返回目录
2019:
2018:
2017:
返回目录
2019:
2018:
2017:
2015:
jsliang 的文档库由 梁峻荣 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议 进行许可。<br/>基于 https://github.com/LiangJunrong/document-library 上的做品创做。<br/>本许可协议受权以外的使用权限能够从 https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ 处得到。