前端框架(react.js)
知识点:
- 三大JS框架的区别
- React和Vue的对比
- React的优点和特点
- Hooks
- 生命周期
- Redux
- 纯函数
- 高阶组件
- react15、16、17的区别
- 扩展
1.三大JS框架的区别:
- Angular带有比较强的排它性的
- React主张是函数式编程的理念,侵入性没有Angular那么强,主要因为它是软性侵入。
- Vue 渐进式的
你如何看待react /什么是react react就是一个框架 他相对于mvc来说只是其中的v 他适用于开发数据不断变化的大型应用程序
react相对其他框架优势: 高性能高效率 实现了前端界面的高性能高效率开发,所以说react很擅长处理组件化的页面
2.React与Vue对比
2.1 相同之处
- 虚拟 DOM。映射真实 DOM,通过新旧 DOM 的 diff 对比,更好的跟踪渲染页面。
- 组件化。将应用拆分成一个个功能明确的模块,每个模块之间可以通过合适的方式互相联系。
- 构建工具。都有自己的构建工具,通过
Webpack
+Babel
去搭建脚手架。 - Chrome 开发工具。两者都有很好的 Chrome 扩展去帮助查找 Bug。
- 配套框架。Vue 有
Vue-router
和Vuex
,而 React 有React-router
和React-Redux
2.2 不同之处
- 模板 VS JSX。Vue 推荐编写近似常规
HTML
的模板进行渲染,而 React 推荐 JSX 的书写方式。 - 监听数据变化的不同。Vue 使用的是可变数据,而 React 更强调数据的不可变。在 Vue 中通过
v-model
绑定的数据,用户改变输入值后对应的值也相应改变。而 React 需要通过setState
进行设置变化。 - Diff 不同。Vue 通过双向链表实现边对比边更新 DOM,而 React 通过
Diff
队列保存需要更新的 DOM,得到patch
树,再统一批量更新 DOM。 - 开发团队。Vue 一开始核心就是 Evan You,后面再招了其他人组成团队;React 的话是一开始就是 Facebook 团队搞的。所以网上的人比对源码情况的话,Vue 的比 React 的简单易懂点。
3.react特点和优势:
3.1.虚拟dom
虚拟DOM是在DOM的基础上建立了一个抽象层,对数据和状态所做的任何改动,都会被自动且高效的同步到虚拟DOM,最后再批量同步到DOM中。
是一个JavaScript对象,重新渲染的时候,会对比这一次产生的Virtual DOM和上一次渲染的Virtual DOM,对比发现差异之后,只需修改真正的DOM树时就只需要触及差别中的部分就行,使用这个虚拟dom,避免对原生dom的创建和对比,这样就大大提升了性能,因为原生dom的创建是非常 消耗性能的,而对js对象的对比和创建对性能开销很小,从这种方式来提供应用的性能
前端进阶之React VDom的理解https://blog.csdn.net/halations/article/details/122377054
3.2.组件
组件化 组件指的就是同时包含了html、css、js、image元素的聚合体
每个组件和组件都相互独立 便于维护
组件是通过React.createClass创建的(ES5),在es6中直接通过class关键字来创建
组件的划分要满足高内聚,低耦合的原则。
- 高内聚是指把逻辑紧密相关的内容放在一个组件中。
- 低耦合指的是不同组件的依赖关系要尽量弱化,每个组件要尽量独立。
组件其实就是一个构造器,每次使用组件都相当于在实例化组件
react的组件必须使用render函数来创建组件的虚拟dom结构
组件需要使用ReactDOM.render方法将其挂载在某一个节点上
组件的首字母必须大写
3.3.单向数据流 数据绑定 父到子
3.4.jsx树
通过跟babel结合把jsx里面的东西转成JavaScript对象
3.5.diff算法
前端进阶之React diff算法https://blog.csdn.net/halations/article/details/109284050
3.5.1 react 的渲染过程中,兄弟节点之间是怎么处理的?也就是key值不一样的时候。
通常我们输出节点的时候都是map一个数组然后返回一个ReactNode,为了方便react内部进行优化,我们必须给每一个reactNode添加key,这个key prop在设计值处不是给开发者用的,而是给react用的,大概的作用就是给每一个reactNode添加一个身份标识,方便react进行识别,在重渲染过程中,如果key一样,若组件属性有所变化,则react只更新组件对应的属性;没有变化则不更新,如果key不一样,则react先销毁该组件,然后重新创建该组件。
3.5.2遍历子节点的时候,不要用 index 作为组件的 key 进行传入。
主要是因为react是基于虚拟dom跟diff算法提高性能的,而提高性能意味着会做一些dom节点的复用,key就是负辅助diff算法的,在某些情况下用index是没问题的,例如只是循环渲染展示,但是如果有涉及中间节点的增删的操作,这样就会造成react无法正确识别哪些节点是删的,哪些节点是增加的,会发生很多看似奇怪的现象
4.Hooks
React Hooks的基本使用https://blog.csdn.net/halations/article/details/107481185
5.react生命周期流程
#####初始化阶段
- constructor:构造函数,最先被执行,通常在构造函数中初始化 state 对象或者给自定义方法绑定 this
- getDerivedStateFromProps:static getDerivedStateFromProps(nextProps, prevState),这是个静态方法,当我们接收到新的属性想去修改我们 state,可以使用 getDerivedStateFromProps。
- render:render 函数是个纯函数,只返回需要渲染的东西,不应该包含其他的业务逻辑,可以返回原生 DOM、React 组件、Fragment、Portals、字符串和数字等内容。
- componentDidMount:组件装载之后调用,此时我们可以获取到 DOM 节点并操作,比如对 Canvas、SVG 等操作。服务器请求、订阅都可以写这个里面,但是记得在 componentWillUnmount 中取消订阅。
##### 运行中阶段
- 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,
做一些善后工作 比如说你切换路由的时候 上一个路由还在请求数据 但是你已经销毁了那个组件 这个时候就会警告你不能对已经已经销毁的组件获取数据 ,所以就可以设置一个开关 当销毁的时候停止获取数据
##### 错误处理
当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法:
- static getDerivedStateFromError()
- componentDidCatch()
6.Redux
6.1 react-reduxs
使用一个react-redux 的库使得redux的使用更简洁,它提供了provider和connect方法
react-redux将所有的组件分为两大类 把Provider放在最外面并传入store connect进行连接
UI 组件有以下几个特征。
- 只负责 UI 的呈现,不带有任何业务逻辑
- 没有状态(即不使用this.state这个变量)
- 所有数据都由参数(this.props)提供
- 不使用任何 Redux 的 API
容器组件的特征恰恰相反。
- 负责管理数据和业务逻辑,不负责 UI 的呈现
- 带有内部状态
- 使用 Redux 的 API
只要记住一句话就可以了:UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑。
6.1.1 Provider
Provider就是把我们用rudux创建的store传递到内部的其他组件。让内部组件可以享有这个store并提供对state的更新。
他是通过使用context树来实现的,将store放到context树上,然后内部的所有的容器组件在通过this.context.store来使用到context树上的store
6.1.2 connect
connect()能生成一个高阶组件,高阶组件接收一个UI组件之后能生成一个新的容器组件
connect是一个函数,这个函数接收到mapStateToProps、mapDispatchToProps属性,connect会返回一个高阶组件,在这个高阶组件中接收UI组件,返回一个新组件就是容器组件,因为有闭包的特性,所以容器组件可以使用到connect中传入的mapStateToProps、mapDispatchToProps属性,然后容器组件可以从context树中取出Provider放入的store,然后利用mapStateToProps、mapDispatchToProps将状态和调用dispatch的方法传入给UI组件,当store数据变化的时候,容器组件会rerender,重新给UI组件传入最新的状态
dispatch的作用 中间件的作用就是让actionCreator的方法能返回一个可以接收到dispatch的函数,在这个函数里就可以做一些异步的操作了
6.2 redux (redux的详细文档后续更新)
缺点:一个组件的所有数据都必须由父组件传过去,当一个组件相关数据更新时,及时父元素不需要用到这个组件的,父组件还是会重新render,可能会有效率的影响。
需要使用redux的项目:
- 某个组件的状态,需要共享
- 某个状态需要在任何地方都可以拿到
- 一个组件需要改变全局状态
- 一个组件需要改变另一个组件的状态
步骤:
- 通过createStore来创建store,并为其绑定reducer
- reducer每次都能返回一个新的数据,同步到store的状态上
- 在视图中通过store.getState()来获取到状态
- 当想要更新数据的时候,也就是用户产生了操作,调用actionCreator的方法生成action传入到reducer中
- reducer返回一个新状态,store的state被reducer更改为新state的时候,store.subscribe方法里的回调函数会执行(生命周期里执行),此时就可以通知view去重新获取state
注意:redux都不是必须和react搭配使用的,因为flux和redux是完整的架构,在react中,只是将react的组件作为redux中的视图层去使用了。
6.2.1划分reducer
因为一个应用中只能有一个大的state,这样的话reducer中的代码将会特别特别的多,那么就可以使用combineReducers方法将已经分开的reducer合并到一起
applyMiddleware(thunk)//使用中间件
在这里这个中间件的作用就是让actionCreator的方法能返回一个可以接收到dispatch的函数,在这个函数里
就可以做一些异步的操作了
7.纯函数
react纯函数、函数组件https://blog.csdn.net/halations/article/details/106623209
7.1纯函数性能优化
优化react性能的时候,对于太多深层嵌套的组件,由于父组件的state改变会导致包括子组件也重新render可以根据情况使用纯函数组件,由于纯函数组件是没有状态的组件这个时候可以用高阶组件给函数组件封装一个类似PureComponet的功能
对于性能优化 还可以使用React.memo
React.memo
是 React 16.6 新的一个 API,用来缓存组件的渲染,避免不必要的更新,其实也是一个高阶组件,与 PureComponent
十分类似,但不同的是, React.memo
只能用于函数组件 。
import { memo } from 'react';
function Button(props) {
// Component code
}
export default memo(Button);
7.2纯函数性能优化高级用法
默认情况下其只会对 props 做浅层对比,遇到层级比较深的复杂对象时,表示力不从心了。对于特定的业务场景,可能需要类似 shouldComponentUpdate
这样的 API,这时通过 memo
的第二个参数来实现:
function arePropsEqual(prevProps, nextProps) {
// your code
return prevProps === nextProps;
}
export default memo(Button, arePropsEqual);
注意:与 shouldComponentUpdate
不同的是,arePropsEqual
返回 true
时,不会触发 render,如果返回 false
,则会。而 shouldComponentUpdate
刚好与其相反。
8.高阶组件
高阶组件是一个函数,接收一个不是路由的组件再返回一个新组件,这样就可以给新组件上添加一
些api了,实现代码复用
React高阶组件https://blog.csdn.net/halations/article/details/106624363
9.React15与React16的架构区别以及React17
9.1 React 15 架构分为两部分:
- Reconciler(协调器):找出变化的组件。通过 diff 算法找出变化的组件交给 Renderer 渲染器。
- Renderer(渲染器):将变化的组件重新渲染。
Diff机制:首先由 Reconciler(协调器)通过 diff 算法计算出需要更新的组件,然后通过 Renderer(渲染器)去执行更新并渲染组件。接着继续由 Reconciler 计算出需要更新的组件,Renderer 继续更新渲染。整个过程是同步的,Reconciler 与 Renderer交替进行的。并且使用递归实现,所以不可中断。
9.2 React 16 架构分为三部分:
- Scheduler(调度器):调度任务优先级,使优先级高的任务进入 Reconciler。
- Reconciler(协调器):负责找出变化的组件。通过 diff 算法找出变化的组件交给 Renderer 渲染器。
- Renderer(渲染器):负责将变化的组件重新渲染。
Diff机制:首先由 Scheduler(调度器)去调度任务的优先级,将优先级比较高的任务加入到 Reconciler(协调器)中。Reconciler(协调器)通过 diff 算法计算出需要更新的组件,并标记更新状态。等整个组件更新完成之后,再通过 Renderer(渲染器)去执行更新并渲染组件。
9.3 React17:
没有新增新特性,
9.3.1 改动一:事件委托不再挂到 document 上
之前多版本并存的主要问题在于React 事件系统默认的委托机制,出于性能考虑,React 只会给document
挂上事件监听,DOM 事件触发后冒泡到document
,React 找到对应的组件,造一个 React 事件出来,并按组件树模拟一遍事件冒泡(此时原生 DOM 事件早已冒出document
了):
因此,不同版本的 React 组件嵌套使用时,e.stopPropagation()
无法正常工作(两个不同版本的事件系统是独立的,都到document
已经太晚了)
而React17解决了这个问题,React 17 不再往document
上挂事件委托,而是挂到 DOM 容器上
另一方面,将事件系统从document
缩回来,也让 React 更容易与其它技术栈共存(至少在事件机制上少了一些差异)
9.3.2 改动二:向浏览器原生事件靠拢
- onScroll不再冒泡
- onFocus/onBlur直接采用原生focusin/focusout事件
- 捕获阶段的事件监听直接采用原生 DOM 事件监听机制
注意,onFocus/onBlur
的下层实现方案切换并不影响冒泡,也就是说,React 里的onFocus
仍然会冒泡(并且不打算改,认为这个特性很有用)
9.3.3 改动三:DOM 事件复用池被废弃
之前出于性能考虑,为了复用 SyntheticEvent,维护了一个事件池,导致 React 事件只在传播过程中可用,之后会立即被回收释放,例如:
<button onClick={(e) => {
console.log(e.target.nodeName);
// 输出 BUTTON
// e.persist();
setTimeout(() => {
// 报错 Uncaught TypeError: Cannot read property 'nodeName' of null
console.log(e.target.nodeName);
});
}}>
Click Me!
</button>
传播过程之外的事件对象上的所有状态会被置为null
,除非手动e.persist()
(或者直接做值缓存)
React 17 去掉了事件复用机制,因为在现代浏览器下这种性能优化没有意义,反而给开发者带来了困扰
9.3.4 改动四:Effect Hook 清理操作改为异步执行
useEffect本身是异步执行的,但其清理工作却是同步执行的(就像 Class 组件的componentWillUnmount
同步执行一样),可能会拖慢切 Tab 之类的场景,因此 React 17 改为异步执行清理工作:
useEffect(() => {
// This is the effect itself.
return () => {
// 以前同步执行,React 17之后改为异步执行
// This is its cleanup.
};
});
同时还纠正了清理函数的执行顺序,按组件树上的顺序来执行(之前并不严格保证顺序)
以上就是React17的主要一些改动
9.4 调用 setState 之后发生了什么?
在代码中调用 setState 之后,React 会将传入的参数对象与组件当前的状态合并,触发所谓的调和过程(Reconciliation)。
经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个 UI 界面。
在 React 得到元素树之后,React 会自动计算新树和老树之间的节点差异,然后根据差异对界面进行最小化重新渲染。
在差异计算算法(Diff)中,React 能够相对精确地知道哪些位置发生了改变以及英国如何改变,保证了按需更新,而不是全部重新渲染。
简单来说:
- 合并参数对象,触发调和过程
- 计算新树和老树差异(Diff)
- 根据差异进行最小化重新渲染
9.5 setState 是同步还是异步?
回答:有时候同步,有时候异步。
- setState 在合成事件和钩子函数中是异步的,在原生事件和 setTimeout 是同步的。
- setState 的异步,并不是说内部由异步代码实现,它本身执行的过程和代码是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,从而形成了所谓的异步。
- setState 可以通过第二个参数 setState(partialState, callback),在回调方法中拿到更新后的结果。
10.扩展
10.1 react怎么从虚拟dom中拿出真实dom?(ref)
// react怎么从虚拟dom中拿出真实dom?(ref)
var MyComponent = React.createClass({
handleClick: function() {
this.refs.myTextInput.focus();
},
render: function() {
return (
<div>
<input type="text" ref="myTextInput" />
<input type="button" value="Focus the text input" onClick={this.handleClick} />
</div>
);
}
});
10.2 react语法规范
-
{...this.props}
(不要滥用,请只传递component需要的props,传得太多,或者层次传得太深,都会加重shouldComponentUpdate里面的数据比较负担,因此,请慎用spread attributes(<Component {...props} />))。 -
::this.handleChange()
。(请将方法的bind一律置于constructor) -
this.handleChange.bind(this,id)
-
复杂的页面不要在一个组件里面写完。
-
请尽量使用
const element
。 -
map里面添加key,并且key不要使用index(可变的)。
-
尽量少用
setTimeOut
或不可控的refs、DOM操作。 -
props
和state
的数据尽可能简单明了,扁平化。 -
使用
return null
而不是CSS的display:none
来控制节点的显示隐藏。保证同一时间页面的DOM节点尽可能的少。
10.3 React this.props.children
this.props对象的属性与组件的属性一一对应,但是有一个例外,就是this.props.children
属性。它表示组件的所有子节点。
这里需要注意,this.props.children
的值有三种可能:如果当前组件没有子节点,它就是undefined
;如果有一个子节点,数据类型是Object
;如果有多个子节点,数据类型就是array
。所以,处理this.props.children
的时候要小心。
React提供一个工具方法React.Children
来处理this.props.children
。我们可以用React.Children.map
来遍历子节点,而不用担心this.props.children
的数据类型是undefined
还是object
。
10.4 webpack的一些plugin,怎么使用webpack对项目进行优化。
正好最近在做webpack构建优化和性能优化的事儿,插件请见webpack插件归纳总结。
构建优化:
-
减少编译体积 ContextReplacementPugin、IgnorePlugin、babel-plugin-import、babel-plugin-transform-runtime
-
并行编译 happypack、thread-loader、uglifyjsWebpackPlugin开启并行
-
缓存 cache-loader、hard-source-webpack-plugin、uglifyjsWebpackPlugin开启缓存、babel-loader开启缓存
-
预编译 dllWebpackPlugin && DllReferencePlugin、auto-dll-webapck-plugin
性能优化:
- 首屏渲染优化。
<div id="root"> SVG </div>
,也可以使用插件prerender-spa-plugin
插件进行首屏渲染。 - 页面切换优化。使用
html-webpack-plugin
插件自动插入loading
,这样切换的时候,就不需要在每个页面都写一套loading
。 - 减少业务代码体积。通过
Tree Shaking
来减少一些代码。 - 提取公共代码。通过
SplitChunkPlugin
自动拆分业务基础库,减少大文件的存在。 - 切分代码。通过
Code Splitting
来懒加载代码,提高用户的加载体验。例如通过React Loadable
来将组件改写成支持动态import
的形式。 - 懒加载。React 可以通过
react-lazyload
这种成熟组件来进行懒加载的支持。 - 页面占位。有时候加载页面的文本、图片的时候,会出现 “闪屏” 的情况,比如图片或者文字没有加载完毕,对应位置空白,然后加载完毕,会突然撑开页面,导致闪屏。这时候使用第三方组件
react-placeholder
可以解决这种情况。