React MST

React MST

0.为什么选择使用框架而不是原生?

框架的好处:
1.组件化:可以把每个功能分为不同的组件,容易维护和各个组件的组合;
2.天jq的代码耦合严重,代码解耦易于读写。
3.UI框架易于开发;增加了开发效果,因为现在的框架默认自动更新DOM,不是手动,解决了UI与状态同步问题。

1.虚拟DOM原理 && 虚拟、真实DOM区别 && key的作用(Diff)

1.虚拟DOM原理

       React的渲染函数从React组件中创建一个节点树,然后它响应数据模型中的变化来更新,该变化是由用户或系统完成的各种动作引起的。每当底层数据发生改变时,整个UI都将在虚拟DOM描述中重新渲染。

2.区别

Real DOMVirtual DOM
更新缓慢。更新更快。
更新可以直接更新 HTML。无法直接更新 HTML。
如果元素更新,则创建新DOM。如果元素更新,则更新 JSX 。
DOM操作代价很高。DOM 操作非常简单。
消耗的内存较多。很少的内存消耗。

3.diff算法和key的作用

1.什么是diff算法

diff算法是调和的具体实现。而调和是将虚拟DOM树转换成真实DOM树的最少操作的过程。
他的作用是计算出虚拟DOM真正变化的部分,并只针对该部分进行原生DOM操作,而不是重新渲染整个页面

key的作用

1、简单来说是虚拟DOM对象的标识,当状态中的数据发生变化时,react会根据[新数据]生成[新的虚拟DOM],随后React进行[新虚拟DOM]和[旧虚拟DOM]的diff比较:
a、旧虚拟DOM找到了与新虚拟DOM相同的key:
     (1)、若虚拟DOM中内容没变,直接使用之前的真实DOM
     (2)、若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
b、旧虚拟DOM中未找到与新虚拟DOM相同的key,根据数据创建新的真实DOM。随后渲染到页面


用index作为key会引发问题:
1、若对数据进行添加逆序删除等操作,会产生没必要的真实DOM更新---->界面效果没问题,但效率低。
2、如果结构中包含输入类的DOM:会产生错误DOM更新---->界面有问题
3、如果不存在对数据的逆序添加、删除等顺序操作,仅展示,是没问题的

2.React的生命周期

1、初始化阶段,初次渲染
constructor():组件的初始化,构造函数,一般在这里初始化state对象或给自定义方法绑定this
componentWillMount():组件将要挂载时触发的函数
**render()**渲染
componentDidMount():组件挂载完成时触发的函数,例如开定时器,发请求
getDerivedStateFromProps: static DerivedStateFromProps(nextProps, prevState),这是个静态方法,当我们接收到新的属性想去修改我们state,可以使用,代替(componentWillReceiveProps)

import React ,{Component} from 'react'
class Demo extends Component{
	constructor(props){
		console.log('01构造函数')		
		super(props)
		this.state={
		}
	}
	//组件将要挂载时候触发的生命周期函数
	componentWillMount(){
		console.log('02组件将要挂载')
	}
	//组件挂载完成时候触发的生命周期函数
	componentDidMount(){
		console.log('04组件将要挂载')
	}
	render(){
		console.log('03数据渲染render')
		return(
			<div>
				生命周期函数演示
			</div>
		) 
	}
}
export default Demo 

2、更新阶段,由组件内部this,setState()或父组件重新render触发
shouldComponentUpdate:有两个参数nextProps和nextSatate,表示新的属性和变化之后的state,返回true触发重新渲染。优化React性能;
getSnapshotBeforeUpdate,该方法在render之后,componentDidUpdate之前调用,有两个参数prevProps和PrevState,表示之前的属性和state,返回值作为第三个参数传给componentDidUpdate,如果不想要返回值,可以返回null,必须和componentDidUpdate搭配使用;代替(componentWillUpdate())
componentDidUpdate():组件更新完成触发(该方法在getSnapshotBeforeUpdate方法之后被调用,有三个参数prevProps,prevState,snapshot),表示之前的props和state和snapshot,第三个参数是getSnapshotBeforeUpdate的返回,如果触发回调需要用到DOM元素的状态。

import React ,{Component} from 'react'
class Demo extends Component{
	constructor(props){
		super(props)
		this.state={
			msg:'我是一个msg数据'
		}
	}
	//是否要更新数据,如果返回true才会更新数据
	shouldComponentUpdate(nextProps,nextState){
		console.log('01是否要更新数据')
		console.log(nextProps)		//父组件传给子组件的值,这里没有会显示空
		console.log(nextState)		//数据更新后的值
		return true;				//返回true,确认更新
	}
	//将要更新数据的时候触发的
	componentWillUpdate(){
		console.log('02组件将要更新')
	}
	//更新数据时候触发的生命周期函数
	componentDidUpdate(){
		console.log('04组件更新完成')
	}
	//更新数据
	setMsg(){
		this.setState({
			msg:'我是改变后的msg数据'
		})
	}
	render(){
		console.log('03数据渲染render')
		return(
			<div>
				{this.state.msg}
				<br/>
				<hr/>
				<button onClick={()=>this.setMsg()}>更新msg的数据</button>
			</div>
		) 
	}
}
export default Demo

UNSAFE_componentWillReceiveProps() 从父类接收到 props 并且在调用另一个渲染器之前调用。他可以传入参数,nextProps是父组件传给子组件的值,(在父组件定义个初始数据,写一个onclick事件去改变)
父组件

import React, { Component } from 'react';
import Demo1 from './Demo1 '
class App extends Component {
	constructor(props){
		super(props)
		this.state={
			flag:true,
			title:"我是app组件的标题"
		}
	}
	//创建/销毁组件
	setFlag(){
		this.setState({
			flag:!this.state.flag
		})
	}
	//改变title
	setTitle(){
		this.setState({
			title:'我是app组件改变后的title'
		})
	}
  	render() {
	    return (
	      <div className="App">
				{
					this.state.flag?<Demo1 title={this.state.title}/>:''
				}
				<button onClick={()=>this.setFlag()}>挂载/销毁生命周期函数组件</button>
				<button onClick={()=>this.setTitle()}>改变app组件的title</button>
	      </div>
	    );
 	}
}
export default App;

子组件

import React ,{Component} from 'react'
class Demo1 extends Component{
	constructor(props){
		super(props)
		this.state={
			msg:'我是一个msg数据'
		}
	}
	//组件将要挂载时候触发的生命周期函数
	componentWillMount(){
		console.log('02组件将要挂载')
	}
	//组件挂载完成时候触发的生命周期函数
	componentDidMount(){
		//Dom操作,请求数据放在这个里面
		console.log('04组件挂载完成')
	}
	//是否要更新数据,如果返回true才会更新数据
	shouldComponentUpdate(nextProps,nextState){
		console.log('01是否要更新数据')
		console.log(nextProps)		//父组件传给子组件的值,这里没有会显示空
		console.log(nextState)		//数据更新后的值
		return true;				//返回true,确认更新
	}
	//将要更新数据的时候触发的
	componentWillUpdate(){
		console.log('02组件将要更新')
	}
	//更新数据时候触发的生命周期函数
	componentDidUpdate(){
		console.log('04组件更新完成')
	}
	//你在父组件里面改变props传值的时候触发的函数
	componentWillReceiveProps(){
		console.log('父子组件传值,父组件里面改变了props的值触发的方法')
	}
	setMsg(){
		this.setState({
			msg:'我是改变后的msg数据'
		})
	}
	//组件将要销毁的时候触发的生命周期函数,用在组件销毁的时候执行操作
	componentWillUnmount(){
		console.log('组件销毁了')
	}
	render(){
		console.log('03数据渲染render')
		return(
			<div>
				生命周期函数演示--{this.state.msg}--{this.props.title}
				<br/>
				<hr/>
				<button onClick={()=>this.setMsg()}>更新msg的数据</button>
			</div>
		) 
	}
}
export default Demo1 

3、卸载
componentWillUnmount():组件销毁的时候触发,如清理定时器,取消请求
react生命周期的网站

3.React的特点和优点&&请求在哪个生命周期

1.特点
(1)它使用虚拟DOM。不是真正的DOM
(2)它可以用服务器端渲染;单向数据绑定
2.优点
(1)提高应用的性能;方便在客户端和服务器端使用
(2)用JSX,代码的可读性好。编写UI测试用例会比较简单
3.请求一般放在componentDidmount,如果放在componentWillMount请求数据,fetch data会执行两次,一次在服务端一次在客户端,会造成多余的请求,在React 16进行React Fiber重写后,可能在一次渲染中多次调用。如果有特殊情况可以在constryctor中请求 。

4.受控组件和非受控组件

       受控组件:值收到react控制的表单元素,可变状态保存在state里,只能通过setState()方法来修改,state和表单元素值value绑定在一起,有state的值来控制表单值;
       非受控组件:表单数据由DOM节点来处理;表单组件没有value prop;调用React.createRef()方法创建ref对象,通过ref对象获取到文本框的值;

5.React有状态组件和无状态组件的区别

有:他属于一个class类,有继承,可以通过this来接收状态和属性,如果要用react的生命周期或者对数据进行增删改查的话就要用到有状态组件。
无:他属于一个函数,没继承功能,没生命周期,他动态数据都是通过父组件传值给子组件通过props接收渲染;对一些简单的逻辑判断可以用。
注意的是大部分建议使用无状态组件,因为大量的有状态组件容易触发生命周期和钩子函数,页面会出现加载慢等问题;

6.Ref是什么 && setState是同步还是异步

ref是React提供用来操作组件实例或者DOM元素的接口
      用法:
1、字符串:通过this.refs[refName]来引用真实的dom节点
2、回调函数:ref接受一个回调函数,在组建被加载或卸载时立即执行
3、React.createRef():创建ref,然后赋值给一个变量,通过ref挂载在dom节点或者足尖上,ref的current属性能拿到组件的实例。

setState
(1)setState只在合成事件和钩子函数中是异步的,在原生时间和setTimeout中是同步的。所谓"异步"不是说内部由异步代码实现,它本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,会导致合成事件和钩子函数中没法立马拿到更新后的值,就形成了所谓的"异步",也可以通过第二个参数setState(partState,callback)的回调函数拿到更新后的结果。
(2)setState的批量更新也是建立在"异步"(合成事件、钩子函数)智商,在原生时间和setTimeout中不会批量更新,在"异步"中如果对同一个值进行多次setState,setState的批量更新策略会对其进行覆盖,取最后一次执行,如果是同时setState多个不同的值,在更新时会对其进行合并批量更新。

7.React组件通信方式

1、父组件向子组件传

父组件可以向子组件通过传props的方式,向子组件进行通讯

2、子组件向父组件传

props+回调的方式,父向子传递props进行通讯,此props为作用域为父组件自身的函数,子调用该函数,将子想要传递的信息作为参数,传递到父的作用域中。

3、兄弟组件通信

找到这两个兄弟节点共同的父节点,结合上面两种方式由父节点转发信息进行通信

4、跨层级通信:用Context

Context设计目的是为了共享那些杜宇一个组件树而言是"全局"的数据,例如当前认证的用户,主题或首选语言,对于跨越多层的全局数据通过Context通信更合适

5、发布订阅模式

发布者发布事件,订阅者监听事件并作出反应,可以通过event模块进行通信

6、全局状态管理工具

借助Redux或者Mobx等全局状态管理工具进行通信,该工具会维护一个全局状态中心Store,并根据不同的事件产生新的状态。

7、context

API:createContext(defaultValue?)提供一种方式,让数据在组件树中传递。

const a=createContext()
------------------------------
<BatteryContext.Provider value={60}>
     <Middle />  //子组件
</BatteryContext.Provider>
-------------------------------------
孙组件需要BatteryContext.Consumer来接收值,
Consumer里面不能直接渲染其他组件,而是要声明一个函数。
函数的参数就是context的值。
class Leaf extends Component {
  render() {
    return (
      <BatteryContext.Consumer>
        {
          battery => <h1>Battery : {battery}</h1>
        }
      </BatteryContext.Consumer>
    )
  }
}

8.什么是高阶组件(HOC)

1、它通常用一个函数来实现,接收一个组件为参数,返回一个增强的组件
作用:代码复用,对state和props进行抽象和操作,渲染劫持。
实现高阶组件的方式:
属性代理:

//高阶组件定义
const HOC = (WrappedComponent) =>
  class WrapperComponent extends Component {
    render() {
      return <WrappedComponent {...this.props} />;
    }
}
//普通的组件
class WrappedComponent extends Component{
    render(){
        //....
    }
}
//高阶组件使用
export default HOC(WrappedComponent)

反向继承:指返回的组件去继承之前的组件(这里都用WrappedComponent代指)

const HOC = (WrappedComponent) =>
  class extends WrappedComponent {
    render() {
      return super.render();
    }
  }
我们可以看见返回的组件确实都继承自WrappedComponent,
那么所有的调用将是反向调用的(例如:super.render()),
这也就是为什么叫做反向继承。   

9.什么是Redux

什么是redux

redux是一个独立专门用于管理状态的库,作用是:集中式管理react多个组件共享的状态;他遵循是哪个原则:1、单一事实来源2、状态只读3、使用纯函数更改

什么时候需要用

1、总体原则:能不用就不用,如果不用比较吃力才考虑使用
2、某个组件的状态,需要共享
3、某个状态需要再任何地方都可以拿到
4、一个组件需要改变全局状态
5、一个组件需要改变另一个组件的状态

redux 的核心API

createStore():创建包含指定reducer的store对象
store对象:redux最核心的管理对象;内部维护state、reducer
核心方法store.getState() :得到state
store.dispatch(action) :分发action,触发reducer调用,产生新的state
store.subscribe(listener):注册监听,当产生了新的state时,自动调用
编码Lstore.getState() store.dispatch({type:‘INCREMENT’,data})

redux 的四个文件

action-type 包含所有action的type名称常量
action 包含所有的action create(action的工厂函数)
reducer 包含n个reducer函数(根据老的state和action返回一个新的state)
store redux最核心的管理对象store

10.redux的流程

       用户会通过dispatch方法发出Action,然后Store自动调用Reducer,并且传入两个参数:当前State和收到的Action,然后Reducer会返回新的State,State一旦有变化,Store就会调用监听函数来更新View。
在这里插入图片描述

Store:保存数据的地方,可以把它看成一个容器,整个应用只能有一个Store。
State:Store对象包含所有数据,如果想得到某个时点的数据,就要对Store生成快照,这种时点的数据集合,就叫做State。
Action:State的变化,会导致View的变化。用来描述发生了什么事情的对象。
Action Creator:View要发送多少种消息,就会有多少种Action。如果都手写,会很麻烦,所以我们定义一个函数来生成Action,这个函数就叫Action Creator。
Reducer:Store收到Action以后,必须给出一个新的State,这样View才会发生变化。这种State的计算过程就叫做Reducer。Reducer是一个函数,它接受Action和当前State作为参数,返回一个新的State。
dispatch:是View发出Action的唯一方法。

11.React-Redux如何工作的?

1)Provider: Provider的作用是从最外部封装了整个应用,并向connect模块传递store
2)connect: 负责连接React和Redux
       1、获取state: connect通过context获取Provider中的store,通过store.getState()获取整个store tree 上所有state。
       2、包装原组件: 将state和action通过props的方式传入到原组件内部wrapWithConnect返回一个ReactComponent对象Connect,Connect重新render外部传入的原组件WrappedComponent,并把connect中传入的mapStateToProps, mapDispatchToProps与组件上原有的props合并后,通过属性的方式传给WrappedComponent
       3、监听store tree变化: connect缓存了store tree中state的状态,通过当前state状态和变更前state状态进行比较,从而确定是否调用this.setState()方法触发Connect及其子组件的重新渲染
在这里插入图片描述

12.Redux和mobx的区别

1)redux将数据保存在单一的store中,mobx将数据保存在分散的多个store中
2)redux使用plain object保存数据,需要手动处理变化后的操作;mobx适用observable保存数据,数据变化后自动处理响应的操作
3)redux使用不可变状态,这意味着状态是只读的,不能直接去修改它,而是应该返回一个新的状态,同时使用纯函数;mobx中的状态是可变的,可以直接对其进行修改
4)mobx相对来说比较简单,在其中有很多的抽象,mobx更多的使用面向对象的编程思维;redux会比较复杂,因为其中的函数式编程思想掌握起来不是那么容易,同时需要借助一系列的中间件来处理异步和副作用
5)mobx中有更多的抽象和封装,调试会比较困难,同时结果也难以预测;而redux提供能够进行时间回溯的开发工具,同时其纯函数以及更少的抽象,让调试变得更加的容易
总结:mobx适合数据不复杂的应用:mobx不好调试,很多状态无法回溯;但是上手简单,样板代码少,提高开发效率;redux适合有回溯需求的应用:例如表格应用:很多适合需要撤销、重做等操作;

13.redux异步中间件

1.Redux-thunk:

优点
1、体积小;
2、使用简单: redux-thunk不用引入像redux-saga或者redux-observable额外的范式,上手简单
缺点
1、样板代码过多: 与redux本身一样,通常一个请求需要大量的代码,而且很多都是重复性质的
2、耦合严重: 异步操作与redux的action偶合在一起,不方便管理
3、功能孱弱: 有一些实际开发中常用的功能需要自己进行封装

2.Redux-saga

优点
1、异步解耦: 异步操作被被转移到单独 saga.js 中,不再是掺杂在 action.js 或 component.js 中
2、action摆脱thunk function: dispatch 的参数依然是一个纯粹的 action (FSA),而不是充满 “黑魔法” thunk function
3、异常处理: 受益于 generator function 的 saga 实现,代码异常/请求失败 都可以直接通过 try/catch 语法直接捕获处理
4、功能强大: redux-saga提供了大量的Saga 辅助函数和Effect 创建器供开发者使用,开发者无须封装或者简单封装即可使用
5、灵活: redux-saga可以将多个Saga可以串行/并行组合起来,形成一个非常实用的异步flow
6、易测试,提供了各种case的测试方案,包括mock task,分支覆盖等等
缺点
1、额外的学习成本: redux-saga不仅在使用难以理解的 generator function,而且有数十个API,学习成本远超redux-thunk,最重要的是你的额外学习成本是只服务于这个库。
2、体积庞大: 体积略大,代码近2000行,min版25KB左右
3、功能过剩: 实际上并发控制等功能很难用到,但是我们依然需要引入这些代码
4、ts支持不友好: yield无法返回TS类型

3.redux-observable

优点
1、功能最强: 由于背靠rxjs这个强大的响应式编程的库,借助rxjs的操作符,你可以几乎做任何你能想到的异步处理
2、背靠rxjs: 由于有rxjs的加持,如果你已经学习了rxjs,redux-observable的学习成本并不高,而且随着rxjs的升级redux-observable也会变得更强大
缺点
1、学习成本奇高: 如果你不会rxjs,则需要额外学习两个复杂的库
2、社区一般: redux-observable的下载量只有redux-saga的1/5,社区也不够活跃,在复杂异步流中间件这个层面redux-saga仍处于领导地位

14.React hooks

API

1.useState(),状态钩子。为函数组建提供内部状态
2.useContext(),共享钩子,作用是在组件之间共享状态,可以解决react逐层通过props传递数据。可以接受React.createContext()的返回结果作为参数。使用它将不再需要Provider和Consumer。
3.useReducer(),Action钩子。接收两个参数,第一个是进行的异步操作,第二个是数组,用来给出Effect的依赖项
4.useRef(),获取组件的实例,渲染周期之间共享数据的存储(state不能存储跨渲染周期的数据,因为state的保存会触发组件重渲染),它传入一个参数initValue,并创建一个对象{current:initValue}给函数组件使用,在整个生命周期中该对象保持不变;
5.useMemo和useCallback:可缓存函数的引用或值,useMemo用在计算值的缓存,经常用于:对于组件内部用到的object、array、函数等,如果用在了其他hook的依赖数组中,或者作为props传递给了下游组件应该使用这俩;
6.useLayoutEffect:会在所有的DOM变更之后同步调用effect,可以使用它来读取DOM布局并同步触发重渲染

类组件和函数组件之间的区别

类组件:无论是使用函数或是类来声明一个组件,它不能修改它自己的props;所有的React组件必须是纯函数,并禁止修改自身的props。,React是单向数据流,父组件改变了属性,子组件视图会更新,属性props是外界传递过来的,状态state是组件本身,状态可以在组件中任意修改;
函数组件:接收一个单一的props对象并返回一个React元素;
区别:函数组件的性能比类组件要高,因为类组件使用时要实例化,而函数组件直接执行函数取返回结果。

为什么使用HOOKS,优势

hooks就是不用写clss组件就可以用state和其他的react特性,也可以编写自己的hooks在不同组件之间复用;
优势:1.没破坏性改动;更容易复用代码,通过自定义hooks复用状态,从而解决类组件有些时候难以复用逻辑的问题;更容易拆分组件;

useState、useEffect

useState接收一个参数作为state的初始值,按照需要使用数字或字符串进行赋值,返回值为当前state以及更新的state的函数。
useEffect相当于class组件的componentDidMount、componentDidUpdate 和componentWillUnmount 具有相同的用途,只不过被合并成了一个 API。
他接收两个参数,第一个参数是函数,,它的作用是,在页面渲染后执行这个函数,可以放ajax请求,第二个参数是一个数组

参数情况效果注意
不传每次渲染后都执行清理或者执行effect这可能会导致性能问题,比如两次渲染的数据完全一样
传空数组只运行一次的 effect(仅在组件挂载和卸载时执行)比如两次渲染的数据完全一样这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值,所以它永远都不需要重复执行
传[count]React 将对前一次渲染的count和后一次渲染的count进行比较。若相等React 会跳过这个 effect,实现了性能的优化

总结:第二个参数一般不要使用引用类型
useEffect通过return清除

1.当useEffect没有第二个参数时,会不停的调用

例子:

 const [count,setCount]=useState(0)
  const [isLoading,setIsLoading]=useState(false);
useEffect(() => {
  setIsLoading(true)
  setTimeout(() => {
    setIsLoading(false)
  }, 1000);
  return () => {
  }
});
function handleClick(){
  setCount(count+1)
}
return(
  <>
  {
    isLoading? (
      <div>Loading...</div>
    ):(
      <div>
        <button onClick={handleClick}></button>
        <p>you click me {count} times!</p>
      </div>
    )
  }
  </>
)

在这里插入图片描述

2.当useEffect第二个参数为空数组时,useEffect调用一次后就不再调用
useEffect(() => {
  setIsLoading(true)
  setTimeout(() => {
    setIsLoading(false)
  }, 1000);
  return () => {
  }
},[]);

在这里插入图片描述

3.当useEffect第二个参数为变量时,useEffect在每次count发生变化时调用
useEffect(() => {
  setIsLoading(true)
  setTimeout(() => {
    setIsLoading(false)
  }, 1000);
  return () => {
  }
},[count]);

在这里插入图片描述

4.useEffect的使用:
function Effect() {
  const [count, setCount] = useState(0)
  useEffect(() => {
    setTimeout(() => {
      console.log(count);
    }, 1000);
    return () => {
    }
  }, [count]);
  function handleClick() {
    setCount(count + 1)
  }
  return (
    <div>
      <button onClick={handleClick}>Click me</button>
      <p>you click me {count} times!</p>
    </div>
  )
}

在这里插入图片描述

class Effect extends React.Component{
  constructor(props){
    super(props)
    this.state={
      count:0
    }
  }
 componentDidUpdate(){
   setTimeout(() => {
     console.log(this.state.count);
   }, 3000);
 }

 handleClick(){
   const {count}=this.state;
   this.setState({
     count:count+1
   })
 }

render(){
  const {count}=this.state
  return(
    <div>
      <button onClick={this.handleClick.bind(this)}>Click me</button>
      <p>you click me {count} times! </p>
    </div>
  )
}
}

在这里插入图片描述
以上例子可以看出来使用useEffect和和class component使用生命周期函数时的区别,使用useEffect将每次count变化的数据都打印出来,而使用componentDidUpdate打印了最后一个数据很多次,因为class component里面的state随着render是发生变化的,而useEffect里面的所有东西都是每次render独立的。

15.mixin、hoc、render props、react-hooks的优劣如何?

1、Mixin的缺陷
(1)、组件与 Mixin 之间存在隐式依赖(Mixin 经常依赖组件的特定方法,但在定义组件时并不知道这种依赖关系)
(2)、多个 Mixin 之间可能产生冲突(比如定义了相同的state字段)
(3)、Mixin 倾向于增加更多状态,这降低了应用的可预测性(The more state in your application, the harder it is to reason about it.),导致复杂度剧增
(4)、隐式依赖导致依赖关系不透明,维护成本和理解成本迅速攀升:
1.难以快速理解组件行为,需要全盘了解所有依赖 Mixin 的扩展行为,及其之间的相互影响
2.组价自身的方法和state字段不敢轻易删改,因为难以确定有没有 Mixin 依赖它
3.Mixin 也难以维护,因为 Mixin 逻辑最后会被打平合并到一起,很难搞清楚一个 Mixin 的输入输出

2、HOC相比Mixin的优势:
(1)、HOC通过外层组件通过 Props 影响内层组件的状态,而不是直接改变其 State不存在冲突和互相干扰,这就降低了耦合度
(2)、不同于 Mixin 的打平+合并,HOC 具有天然的层级结构(组件树结构),这又降低了复杂度
HOC的缺陷:
(1)、扩展性限制: HOC 无法从外部访问子组件的 State因此无法通过shouldComponentUpdate滤掉不必要的更新,React 在支持 ES6 Class 之后提供了React.PureComponent来解决这个问题
(2)、Ref 传递问题: Ref 被隔断,后来的React.forwardRef 来解决这个问题
(3)、Wrapper Hell: HOC可能出现多层包裹组件的情况,多层抽象同样增加了复杂度和理解成本
(4)、命名冲突: 如果高阶组件多次嵌套,没有使用命名空间的话会产生冲突,然后覆盖老属性
(5)、不可见性: HOC相当于在原有组件外层再包装一个组件,你压根不知道外层的包装是啥。
3、Render Props优点:
上述HOC的缺点Render Props都可以解决
Render Props缺陷:
(1)、使用繁琐: HOC使用只需要借助装饰器语法通常一行代码就可以进行复用,Render Props无法做到如此简单
(2)、嵌套过深。
4、React Hooks优点:
(1)、简洁: React Hooks解决了HOC和Render Props的嵌套问题,更加简洁
(2)、解耦: React Hooks可以更方便地把 UI 和状态分离,做到更彻底的解耦
(3)、组合: Hooks 中可以引用另外的 Hooks形成新的Hooks,组合变化万千
(4)、函数友好: React Hooks为函数组件而生,从而解决了类组件的几大问题:
1.this指向容易错误
2. 分割在不同生命周期中的逻辑使得代码难以理解和维护
3. 代码复用成本高(高阶组件容易使代码量剧增)
React Hooks缺陷:
(1)、额外的学习成本(函数组件 与 类组件之间的困惑);写法上有限制(不能出现在条件、循环中),并且写法限制增加了重构成本
(2)、破坏了PureComponent、React.memo浅比较的性能优化效果(为了取最新的props和state,每次render()都要重新创建事件处函数)
(3)、在闭包场景可能会引用到旧的state、props值
(4)、内部实现上不直观(依赖一份可变的全局状态,不再那么“纯”)
(5)、React.memo并不能完全替代shouldComponentUpdate(因为拿不到 state change,只针对 props change)

16.如何理解fiber?

React Fiber 是一种基于浏览器的单线程调度算法;React 16之前 ,reconcilation 算法实际上是递归,想要中断递归是很困难的,React 16 开始使用了循环来代替之前的递归.
Fiber:一种将 recocilation (递归 diff),拆分成无数个小任务的算法;它随时能够停止,恢复。停止恢复的时机取决于当前的一帧(16ms)内,还有没有足够的时间允许计算。

17.Time Slice的理解?

时间分片:
1)React 在渲染(render)的时候,不会阻塞现在的线程
2)如果你的设备足够快,你会感觉渲染是同步的
3)如果你设备非常慢,你会感觉还算是灵敏的
4)虽然是异步渲染,但是你将会看到完整的渲染,而不是一个组件一行行的渲染出来
5)同样书写组件的方式

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值