1.React15生命周期及各自的作用
组件在整个 ReactJS 的生命周期中,主要会经历这4个阶段:创建阶段、实例化阶段、更新阶段、销毁阶段。
1.创建阶段:
该阶段主要发生在创建组件类的时候,即调用 React.createClass 时触发
这个阶段只会触发一个 getDefaultProps 方法,该方法返回一个对象并缓存起来。然后与父组件指定的 props 对象合并,最后赋值给 this.props 作为该组件的默认属性。
2.实例化阶段:
该阶段主要发生在实例化组件类的时候,也就是该组件类被调用的时候触发。这个阶段会触发一系列的流程,按执行顺序如下:
(1).getInitialState:初始化组件的 state 的值。其返回值会赋值给组件的 this.state 属性。
(2)componentWillMount:根据业务逻辑来对 state 进行相应的操作。
(3)render:根据 state 的值,生成页面需要的虚拟 DOM 结构,并返回该结构。
(4)componentDidMount:对根据虚拟 DOM 结构而生的真实 DOM 进行相应的处理。组件内部可以通过 ReactDOM.findDOMNode(this) 来获取当前组件的节点,然后就可以像 Web 开发中那样操作里面的 DOM 元素了。
3.更新阶段:
这主要发生在用户操作之后或者父组件有更新的时候,此时会根据用户的操作行为进行相应得页面结构的调整。这个阶段也会触发一系列的流程,按执行顺序如下:
(1)componentWillReceiveProps:当组件接收到新的 props 时,会触发该函数。在改函数中,通常可以调用 this.setState方法来完成对 state 的修改。
(2)shouldComponentUpdate:该方法用来拦截新的 props 或 state,然后根据事先设定好的判断逻辑,做出最后要不要更新组件的决定
(3)componentWillUpdate:当上面的方法拦截返回 true 的时候,就可以在该方法中做一些更新之前的操作。
(4)render:根据一系列的 diff 算法,生成需要更新的虚拟 DOM 数据。(注意:在 render 中最好只做数据和模板的组合,不应进行 state 等逻辑的修改,这样组件结构更加清晰)
(5)componentDidUpdate:该方法在组件的更新已经同步到 DOM 中去后触发,我们常在该方法中做一 DOM 操作。
4.销毁阶段
(1)这个阶段只会触发一个叫 componentWillUnmount 的方法。
(2)当组件需要从 DOM 中移除的时候,我们通常会做一些取消事件绑定、移除虚拟 DOM 中对应的组件数据结构、销毁一些无效的定时器等工作。这些事情都可以在这个方法中处理。
2.react16生命周期及作用
1.组件初始化(initialization)阶段:
也就是以下代码中类的构造方法( constructor() ),Test类继承了react Component这个基类,也就继承这个react的基类,才能有render(),生命周期等方法可以使用,这也说明为什么函数组件不能使用这些方法的原因。
super(props)用来调用基类的构造方法( constructor() ), 也将父组件的props注入给子组件,功子组件读取(组件中props只读不可变,state可变)。而constructor()用来做一些组件的初始化工作,如定义this.state的初始内容。
import React, { Component } from 'react';
class Test extends Component {
constructor(props) {
super(props);
}
}
2.组件的挂载(Mounting)阶段
此阶段分为componentWillMount,render,componentDidMount三个时期。
(1).componentWillMount:
在组件挂载到DOM前调用,且只会被调用一次,在这边调用this.setState不会引起组件重新渲染,也可以把写在这边的内容提前到constructor()中,所以项目中很少用。
(2).render:
根据组件的props和state(无两者的重传递和重赋值,论值是否有变化,都可以引起组件重新render) ,return 一个React元素(描述组件,即UI),不负责组件实际渲染工作,之后由React自身根据此元素去渲染出页面DOM。render是纯函数(Pure function:函数的返回结果只依赖于它的参数;函数执行过程里面没有副作用),不能在里面执行this.setState,会有改变组件状态的副作用。
(3)componentDidMount:
组件挂载到DOM后调用,且只会被调用一次
3.组件的更新(update)阶段
此阶段分为componentWillReceiveProps,
shouldComponentUpdate,
componentWillUpdate,
render,
componentDidUpdate
(1)componentWillReceiveProps(nextProps)
此方法只调用于props引起的组件更新过程中,参数nextProps是父组件传给当前组件的新props。但父组件render方法的调用不能保证重传给当前组件的props是有变化的,所以在此方法中根据nextProps和this.props来查明重传的props是否改变,以及如果改变了要执行啥,比如根据新的props调用this.setState出发当前组件的重新render
(2)shouldComponentUpdate(nextProps, nextState)
此方法通过比较nextProps,nextState及当前组件的this.props,this.state,返回true时当前组件将继续执行更新过程,返回false则当前组件更新停止,以此可用来减少组件的不必要渲染,优化组件性能。
(3)componentWillUpdate(nextProps, nextState)
此方法在调用render方法前执行,在这边可执行一些组件更新发生前的工作,一般较少用。
(4)render
根据组件的props和state(无两者的重传递和重赋值,论值是否有变化,都可以引起组件重新render) ,return 一个React元素(描述组件,即UI),不负责组件实际渲染工作,之后由React自身根据此元素去渲染出页面DOM。render是纯函数(Pure function:函数的返回结果只依赖于它的参数;函数执行过程里面没有副作用),不能在里面执行this.setState,会有改变组件状态的副作用。
(5)componentDidUpdate(prevProps, prevState)
此方法在组件更新后被调用,可以操作组件更新的DOM,prevProps和prevState这两个参数指的是组件更新前的props和state
4卸载阶段:
componentWillUnmount:
此方法在组件被卸载前调用,可以在这里执行一些清理工作,比如清楚组件中使用的定时器,清楚componentDidMount中手动创建的DOM元素等,以避免引起内存泄漏。
3.ComponentWillUnMount用来干啥
在组件被卸载并销毁之前立即被调用。在此方法中执行任何必要的清理,例如使定时器无效, 取消网络请求或清理在`componentDidMount`中创建的任何监听。
4.shouldComponentUpdate怎么优化的
shouldComponentUpdate函数是重渲染时render()函数调用前被调用的函数,它接受两个参数:nextProps和nextState,分别表示下一个props和下一个state的值。并且,当函数返回false时候,阻止接下来的render()函数的调用,阻止组件重渲染,而返回true时,组件照常重渲染。
5.componentReceiveProps什么情况下会调用
在生命周期的第一次render后不会被调用,但是会在之后的每次render中被调用 = 当父组件再次传送props。在react的update life cycle中,不得不提到 componentWillReceiveProps方法。 该方法在props被传递给组件实例时被调用
6.受控组件和非受控组件
受控组件
就形式上来说,受控组件就是为某个form表单组件添加value属性;非受控组件就是没有添加value属性的组件;,受控组件的形式如下形式:
render:(
return <input type="text" value="Hello!" />
)
添加了value 属性的表单组件元素其内部是不会 维护自己状态state,组件的value值一旦设置某个具体值就始终是这个值,所以需要调用者来控制组件value的改变。
这种写法带来一个问题:渲染后的input组件的用户交互,用户输入的任何值将不起作用,input输入框中的值始终为Hello!。这与HTML中input表现不一致。
为此,为了控制该组件,就需要能能够控制input组件的值,需要借助其内部的状态state,即组件内部要维护一个状态state以便配合input组件的onChange和setState方法来完成对组件的控制;例如对上面形式可以进行封装一个inputItem组件,其内部维护一个state状态,具体代码如下:
export default class InputItem extends React.Component{
constructor(props){
super(props);
this.state = {
value: ""
}
}
componentWillReceiveProps(nextProps){
this.setState({
value: nextProps.value
})
}
_onChange(evt){
this.setState({
value: evt.target.value
})
}
render(){
return (
<input type="text"
value={this.state.value}
onChange={this._onChange.bind(this)}/>
)
}
}
这样就可以在外部像下面这样调用InputItem组件了:
这样就可以控制react的Input组件了,其实就是需要借助react的有状态component来完成,因为有状态component可以内部维护state。
非受控组件
表现形式上,react中没有添加value属性的表单组件元素就是非受控组件。表现形式如下:
非受控组件在底层实现时是在其内部维护了自己的状态state;这样表现出用户输入任何值都能反应到元素上。
欢迎加入我们
在使用react component时,都会遇到受控组件或者非受控组件;在目前,react组件推荐使用stateless component,但是使用该形式来实现react component时使用非受控组件到倒是没有什么大问题,若是需要控制受控元素就会有出现问题,表现在:
受控组件需要主动维护一个内部state状态的,而stateless component是无需维护组件的state状态的,二者有冲突。
所以,受控元素就不能使用stateless component来创建。
鉴于受控组件与非受控组件的特点,二者应用的地方也有所不同,主要表现在:
受控元素,一般用在需要动态设置其初始值的情况;例如某些form表单信息编辑时,input表单元素需要初始显示服务器返回的某个值然后进行编辑。
非受控元素, 一般用于无任何动态初始值信息的情况; 例如form表单创建信息时,input表单元素都没有初始值,需要用户输入的情况
7. 高阶组件
一个包装了另一个基础组件的组件
拥有两种形式
a属性代理 (Props Proxy)
组件的包裹,在包裹过程中对组件进行操作,再返回,形成高阶组件
b反向继承(Inheritance Inversion)
组装,和属性代理不同的是,反向继承是继承自基础组件,它可以直接获取基础组件的props,操作基础组件的state,可以通过反向继承的形式,配合compose讲携带不同功能模块的高阶组件组装到基础组件上,加强基础组件
props的操作
这个高阶组件是一个相对简单的无状态组件,只是这个高阶组件返回了一个新的组件,而这个新的组件是对基础组件的一个包装。对基础组件的props进行了增加,也可以进行改变,删除和读取。
反向继承(Inheritance Inversion)
反向继承是继承自基础组件,并不是高阶组件继承传入的基础组件,所以成为反向继承。由于高阶组件继承了基础组件,那么高阶组件通过this可以操作基础组件的state,props以及基础组件的方法,甚至可以通过super来操作基础组件的生命周期。
a、渲染劫持
所谓渲染劫持就是,高阶组件控制了基础组件渲染生成的结果,因为基础组件的渲染被控制,高阶组件就可以对它做点什么。。。比如:
(1)、控制渲染的结果;
(2)、改变dom树的样式;
(3)、改变基础组件中一下被渲染元素的props;
(4)、操作dom树中的元素。
8. Redux的缺点:
- 一个组件所有的数据,必须由父组件传过来,而不能像flux中直接从store获取。
2. 当一个组件相关数据更新时,即使父组件不需要用到这个组件,父组件还是会重新render,可能会有效率影响,需要写复杂的shouldComponentUpdate进行判断。
9. react生命周期
react的生命周期分为四个阶段:初始化阶段,更新阶段,销毁阶段,错误处理阶段(错误处理是16.3新出的) UNSFE为过时的版本
初始化阶段
- constructor
- static getDerivedStateFromProps//新版本才有的
- componentWillMount( ) / UNSAFE_componentWillMount()//未来淘汰版本
- render ()
- componentDidmount ()
更新阶段
- componentWillReceiveProps / UNSAFE_componentWillReceiveProps ()
- static getDerivedStateFromprops ()
- shouldComponentUpdate ()
- componentWillUpdeta () / UNSAFE_componentWillUpdate ()
- render()
- getSnapshotBeforeUpdate ()
- componentDidUpdate ()
销毁阶段
- componentWillUnmount ()
错误处理
- componentDidCatch ()
初始阶段详细
constructor//自动执行,一开始就会执行
React组件的构造函数在挂载之前被调用。在写constructor
构造函数时,需要先在添加其他内容前,调用super(props)
,用来将父组件传来的props
绑定到这个类中,使用this.props
将会得到。
官方建议不要在constructor
引入任何具有副作用和订阅功能的代码,这些应当使用componentDidMount()
。也就是不要在这里声明或者执行事件
constructor
中应当做些初始化的动作,如:初始化state
,将事件处理函数绑定到类实例上,但也不要使用setState()
。如果没有必要初始化state或绑定方法,则不需要构造constructor
,或者把这个组件换成纯函数写法。
当然也可以利用props
初始化state
,在之后修改state
不会对props
造成任何修改,但仍然建议大家提升状态到父组件中,或使用redux
统一进行状态管理。
constructor(props) {
super(props);//不需要可以不写
this.state = {
isLiked: props.isLiked
};
}
static getDerivedStateFromProps( nextprops )//自动执行,必须有返回值,输出参数为新值,16.3之后新出的
getDerivedStateFromProps
是react16.3之后新增,在组件实例化后,和接受新的props
后被调用。他必须返回一个对象来更新状态,或者返回null表示新的props不需要任何state的更新。使用了这个钩子就不能使用 componentWillRecieveProps()
如果是由于父组件的props
更改,所带来的重新渲染,也会触发此方法。
调用steState()
不会触发getDerivedStateFromProps()
。
之前这里都是使用constructor
+componentWillRecieveProps
完成相同的功能的
componentWillMount()//render()前调用,可以直接同步修改state,自动执行,即将挂载组件
componentWillMount()
将在React未来版本(官方说法 17.0)中被弃用。UNSAFE_componentWillMount()
在组件挂载前被调用,在这个方法中调用setState()
不会起作用,是由于他在render()
前被调用。
为了避免副作用和其他的订阅,官方都建议使用componentDidMount()
代替。这个方法是用于在服务器渲染上的唯一方法。这个方法因为是在渲染之前被调用,也是惟一一个可以直接同步修改state的地方。
render()//直接执行,必须要有的,一般返回html代码
render()方法是必需的。当他被调用时,他将计算this.props
和this.state
,并返回以下一种类型:
- React元素。通过jsx创建,既可以是dom元素,也可以是用户自定义的组件。
- 字符串或数字。他们将会以文本节点形式渲染到dom中。
- Portals。react 16版本中提出的新的解决方案,可以使组件脱离父组件层级直接挂载在DOM树的任何位置。
4. null,什么也不渲染 - 布尔值。也是什么都不渲染。
当返回null
,false
,ReactDOM.findDOMNode(this)
将会返回null,什么都不会渲染。
render()
方法必须是一个纯函数,他不应该改变state
,也不能直接和浏览器进行交互,应该将事件放在其他生命周期函数中。
如果shouldComponentUpdate()
返回false
,render()
不会被调用。
componentDidMount() //直接执行,可以在这里进行ajax’请求,第三方实例化,拿到真实dom,写死的数据可以直接写在这里,如果是获取到的会变的数据,则需要使用定时器,不写的话函数一直在触发,会导致更新不了视图,需要在dom元素更新完之后执行
componentDidMount
在组件被装配后立即调用。初始化使得DOM节点应该进行到这里。
通常在这里进行ajax请求
如果要初始化第三方的dom库,也在这里进行初始化。只有到这里才能获取到真实的dom.
更新阶段
componentWillReceiveProps () / UNSAFE_componentWillReceiveProps(nextProps)//改变state或者props时触发
官方建议使用getDerivedStateFromProps
函数代替componentWillReceiveProps
。当组件挂载后,接收到新的props
后会被调用。如果需要更新state
来响应props
的更改,则可以进行this.props
和nextProps
的比较,并在此方法中使用this.setState()
。
如果父组件会让这个组件重新渲染,即使props
没有改变,也会调用这个方法。
React不会在组件初始化props时调用这个方法。调用this.setState
也不会触发。
shuouldComponentUpdate ( nextprops,nextstate )//参数为新的状态和属性,如果返回值为false,状态改变,视图不更新,数据改变时触发,性能优化的方法之一
调用shouldComponentUpdate
使React知道,组件的输出是否受state
和props
的影响。默认每个状态的更改都会重新渲染,大多数情况下应该保持这个默认行为。
在渲染新的props
或state
前,shouldComponentUpdate
会被调用。默认为true
。这个方法不会在初始化时被调用,也不会在forceUpdate()
时被调用。返回false
不会阻止子组件在state
更改时重新渲染。
如果shouldComponentUpdate()
返回false
,componentWillUpdate
,render
和componentDidUpdate
不会被调用。
官方并不建议在
shouldComponentUpdate()
中进行深度查询或使用JSON.stringify()
,他效率非常低,并且损伤性能。
UNSAFE_componentWillUpdate( nextprops,nextstate )//渲染新的数据时调用
在渲染新的state
或props
时,UNSAFE_componentWillUpdate
会被调用,将此作为在更新发生之前进行准备的机会。这个方法不会在初始化时被调用。
在这里改变数据会导致死循环,这个函数没有太大的实际作用
不能在这里使用this.setState(),也不能做会触发视图更新的操作。如果需要更新state
或props
,调用getDerivedStateFromProps
。
getsnapshotBeforeUpdate()//新的数据渲染前调用,可以记录scroll滚动条
在react render()
后的输出被渲染到DOM之前被调用。它使您的组件能够在它们被潜在更改之前捕获当前值(如滚动位置)。这个生命周期返回的任何值都将作为参数传递给componentDidUpdate()。
componentDidUpdate( prevprops,prevstate,snapshot )//第三个参数来自getSnapshopBeforeUpdate()函数的返回值
在更新发生后立即调用componentDidUpdate()
。此方法不用于初始渲染。当组件更新时,将此作为一个机会来操作DOM。只要您将当前的props与以前的props进行比较(例如,如果props没有改变,则可能不需要网络请求),这也是做网络请求的好地方。
如果组件实现getSnapshotBeforeUpdate()
生命周期,则它返回的值将作为第三个“快照”参数传递给componentDidUpdate()
。否则,这个参数是undefined
。
销毁阶段
componentWillUnmount ()//连dom结构都会清除掉
在组件被卸载并销毁之前立即被调用。在此方法中执行任何必要的清理,例如使定时器无效,取消网络请求或清理在componentDidMount
中创建的任何监听。
componentDidCatch(err,info)
出错时页面不会崩溃,但是会在控制台输出错误
错误边界是React组件,可以在其子组件树中的任何位置捕获JavaScript错误,记录这些错误并显示回退UI,而不是崩溃的组件树。错误边界在渲染期间,生命周期方法以及整个树下的构造函数中捕获错误。
如果类组件定义了此生命周期方法,则它将成错误边界。在它中调用setState()
可以让你在下面的树中捕获未处理的JavaScript错误,并显示一个后备UI。只能使用错误边界从意外异常中恢复; 不要试图将它们用于控制流程。
错误边界只会捕获树中下面组件中的错误。错误边界本身不能捕获错误。
import React from 'react'
class Error extends React.Component {
constructor(props) {
super(props);
this.state = { error: false };//定义一个错误值
}
componentDidCatch(error, info) {//错误处理函数,当错误值发生改变是触发
console.log('错误处理-componentDidCatch')
this.setState({ error, info });
}
errorHandle = () => {//改变错误值
this.setState({
error: true
})
}
render() {
if (this.state.error) {
return (
<div>
<p> 报错了 </p>
{
console.log(new Error("YOLO"))
}
</div>
)
}
return (
<div>
<button onClick = { this.errorHandle }>//改变错误值
抛出错误
</button>
</div>
);
}
}
export default Error
10. Redux-Saga
redux-saga 是一个用于管理应用程序副作用(例如异步获取数据,访问浏览器缓存等)的javascript库,它的目标是让副作用管理更容易,执行更高效,测试更简单,处理故障更容易。
redux-saga相当于一个放置在action与reducer中的垫片。
之所以称之谓副作用呢,就是为了不让触发一个action时,立即执行reducer。也就是在action与reducer之间做一个事情,比如异步获取数据等。
redux-saga使用了ES6中的Generator功能,避免了像redux-thunk的回调地狱。
11. react生命周期
初始化阶段
constructor(props)
-
React组件的构造函数在
挂载之前被调用
。在实现React.Component
构造函数时,需要先在添加其他内容前,调用super(props)
,用来将父组件传来的props
绑定到这个类中,使用this.props
将会得到。 -
官方建议不要在
constructor
引入任何具有副作用和订阅功能的代码,这些应当使用componentDidMount()
。 -
constructor
中应当做些初始化的动作,如:初始化
state,将事件处理函数绑定到类实例上,但也不要使用
setState()。如果没有必要初始化state或绑定方法,则不需要构造
constructor,或者把这个组件换成纯函数写法。 -
当然也可以利用
props
初始化state
,在之后修改state
不会对props
造成任何修改,但仍然建议大家提升状态到父组件中(也就是将组件的state放在其父组件中,父组件通过props将state传递给子组件,这样便于管理维护),或使用redux
统一进行状态管理。 -
static getDerivedStateFromProps(nextProps, prevState)
getDerivedStateFromProps
是react16.3之后新增,在组件实例化后,和接受新的props
后getDerivedStateFromProps
被调用。他必须返回一个对象来更新状态
,或者返回null
表示新的props不需要任何state的更新。- 如果是由于父组件的
props
更改,所带来的重新渲染,也会触发此方法。 - 调用
steState()
不会触发getDerivedStateFromProps()
。 - ***之前***这里都是使用
constructor
+componentWillRecieveProps
完成相同的功能的
它接收两个参数
- nextProps 即将被改变后的state值
- prevState 自身的state值和次钩子返回的对象
componentWillMount() / UNSAFE_componentWillMount()
组件即将挂载
componentWillMount()
将在React未来版本(官方说法 17.0)中被弃用。UNSAFE_componentWillMount()
在组件挂载前
被调用,在这个方法中调用setState()
不会重新渲染,是由于他在render()
前被调用。state在渲染前被更改了? 我们通常用它进行数据请求和数据修改
? 假如我们将**getDerivedStateFromProps()和componentWillMount()一起使用,则会出现警告,如果使用了getDerivedStateFromProps()**则相当于包含了componentWillMount()。
是因为getDerivedStateFromProps就是用来替代componentWillMount()钩子。在未来版本中我们更应该使用getDerivedStateFromProps,而componentWillMount()更多应该是用来兼容老版本
?为了避免副作用和其他的订阅,官方都建议使用
componentDidMount()
代替。这个方法是用于在服务器渲染上的唯一方法。这个方法因为是在渲染之前被调用,也是惟一一个可以直接同步修改state的地方。render
虚拟DOM渲染
render()方法是必需的。当他被调用时,他将计算
this.props
和this.state
,并返回以下一种类型
:- React元素。通过jsx创建,既可以是dom元素,也可以是用户自定义的组件。
- 字符串或数字。他们将会以文本节点形式渲染到dom中。
- Portals。react 16版本中提出的新的解决方案,可以使组件脱离父组件层级直接挂载在DOM树的任何位置。
- null,什么也不渲染
- 布尔值。也是什么都不渲染。
- 当返回
null
,false
,ReactDOM.findDOMNode(this)
将会返回null,什么都不会渲染。 render()
方法必须是一个纯函数,他不应该改变state
,也不能直接和浏览器进行交互,应该将事件放在其他生命周期函数中。- 如果
shouldComponentUpdate()
返回false
,render()
不会被调用。
componentDidMount (已经生成真实Dom)
组件挂载后调用,组件插入真实DOM树中后调用。
componentDidMount`在组件被装配后立即调用。初始化使得DOM节点应该进行到这里。
- 如需通过网络请求获取数据,此处是实例化请求的好地方。因为已经可以操作真实DOM
- 通常在这里进行ajax请求
- 依赖于 DOM 节点的初始化应该放在这里 (第三方实例化)
- 这个方法是比较适合添加订阅的地方。如果添加了订阅,请不要忘记在
componentWillUnmount()
里取消订阅 这里可以进行事件处理,在进行事件的订阅后,需要在componentWillUnmount()取消订阅
更新阶段
componentWillReceiveProps(nextProps) / UNSAFE_componentWillReceiveProps(nextProps)
当组件的props对象的属性被更改时
,触发此钩子函数,通常是父组件传递数据给子属性的props,当父组件传递的数据更改时,子组件的props也更改,子组件触发此钩子函数。? 注意,此钩子和getDerivedStateFromProps钩子不要一起使用。
此钩子接收一个参数nextProps ,代表改变后的props对象
shouldComponentUpdate(nextProps, nextState)
-
调用
shouldComponentUpdate
使React知道,组件的输出是否受state
和props
的影响。默认每个状态的更改都会重新渲染,大多数情况下应该保持这个默认行为。 -
在渲染新的
props
或state
前,shouldComponentUpdate
会被调用。默认为true
。这个方法不会在初始化时被调用
,也不会在forceUpdate()
时被调用。返回false
不会阻止子组件在state
更改时重新渲染。 -
如果
shouldComponentUpdate()
返回false
,componentWillUpdate
,render
和componentDidUpdate
不会被调用。 -
当props属性或者状态被更改时,将会触发此钩子,此钩子是更新阶段的必须钩子,它默认返回true,表示渲染组件props属性或state状态改变后的数据。如果我们手动设置为return false,则不会触发render渲染组件props属性或state状态改变后的数据。
-
componentWillUpdate(nextProps, nextState)/UNSAFE_componentWillUpdate(nextProps, nextState)
在组件更新结束后触发
- 在渲染新的
state
或props
时,UNSAFE_componentWillUpdate
会被调用,将此作为在更新发生之前进行准备的机会。这个方法不会在初始化时被调用。 - 不能在这里使用this.setState(),也不能做会触发视图更新的操作。如果需要更新
state
或props
,调用getDerivedStateFromProps
。
getSnapshotBeforeUpdate()
- 当调用 getSnapshotBeforeUpdate()时,不允许有componentWillMount,componentWillReceiveProps,componentWillUpdate这三个钩子。
- 并且它有一个返回值,这个生命周期的任何返回值都会作为参数传递给componentDidUpdate钩子的第三个参数
- 在react
render()
后的输出被渲染到DOM之前被调用。它使您的组件能够在它们被潜在更改之前捕获当前值(如滚动位置)
componentDidUpdate(prevProps, prevState, snapshot)
在props或state更新发生后
立即调用componentDidUpdate()
。此方法不用于初始渲染
。当组件更新时,将此作为一个机会来操作DOM。只要您将当前的props与以前的props进行比较(例如,如果props没有改变,则可能不需要网络请求),这也是做网络请求的好地方。
组件卸载阶段
componentWillUnmount()
当组件被卸载后执行 - 在渲染新的
12. react创建组件的方式
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
同时还可以使用 ES6 的 class 来定义组件:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
13. react和其他框架相比有什么特点
- react严格意义上讲只针对MVC中的view层,Vue则是MVVM模式
- .Vitual Dom 不一样,Vue会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树,而react每当应用的状态被改变时,全部组件都会被渲染,所以react需要shouldComponentUpdate这个生命周期函数方法来进行控制
- .组件写法不同,react主张jsx+inline style,也就是讲HTML、css写到js中,一切皆js,vue则是webpack+Vue-loader的单文件组件格式,即HTML、css、js组成一个文件形成.vue文件
- 数据绑定,Vue实现了数据的双向绑定,react数据流动是单向的
- state对象在react应用中不可变,需要使用setState方法更新状态,在Vue中,state对象不是必须的,数据由data属性在Vue对象中管理;
14. react的虚拟DOM怎么实现
步骤一:用JS对象模拟DOM树
步骤二:比较两棵虚拟DOM树的差异
步骤三:把差异应用到真正的DOM树上
15. react的单项数据流有什么优点
单向绑定的优点是相应的可以带来单向数据流,这样做的好处是所有状态变化都可以被记录、跟踪,状态变化通过手动调用通知,源头易追溯,没有“暗箱操作”。同时组件数据只有唯一的入口和出口,使得程序更直观更容易理解,有利于应用的可维护性
16.react为什么循环产生的组件中带上key这个东西?
1.react利用key来识别组件,它是一种身份标识。keys是react用于追踪哪些列表中元素 被修改、被添加或者被移除的辅助标识。
2.react根据key来决定是销毁重新创建组件还是更新组件
key相同,若组件属性有所变化,则react只更新组件对应的属性,没有变化则不更新。
key不同,则react先销毁该组件(有状态组件的componentWillUnmount会执行),然 后重新创建该组件(constructor和componentWillUnmount都会执行)
17.react中那些情况下虚拟Dom没有真实Dom好用
在多次操作修改单一元素属性的情况下,VirtualDom所需要的时间可能会稍微长一些。
18.在react里面会和redux绑定使用吗,redux的数据流是什么
首先数据流是是行为与响应的抽象。
用户在页面上输入表单、按下按钮、拖拽等行为,页面会根据用户的行为给出一些响应,
如刷新、跳转、局部刷新、Ajax局部刷新、数据更新等。以对象、方法来把它们抽象出来,这就是数据流。
使用数据流可以帮助我们明确行为以及行为对应的响应其次React与数据流的关系React是纯V的框架,只负责视图,把视图做成了组件化,不涉及任何的数据和控制,需要数据流进行支撑才能完成一个完整的前端项目。
19.什么时候需要引入redux
Redux是用来统一管理状态的,项目比较复杂,很多需要全局性的变量,跨组件操作比较多就需要。如果页面不是很复杂,各个页面之间相对独立,就没必要用Redux
20.react中的setState()的理解
- setState有两个参数
- 第一个参数可以是一个对象
```javascript
this.setState( {
状态: 新状态值
})
```
- 第一个参数可以是一个函数,这个函数我们给起了名称,叫做 updater
```javascript
this.setState( ( preState, props) => {
return {
状态: 新状态值
}
})
```
- 第二个参数是一个回调函数,回调函数的目的是为了做异步
```javascript
// this.setState( obj||fn,callback )
this.setState({
状态: 新状态值
}, () => {
//目的是为了异步
})
```
21. react hooks的用法
React Hooks 是 React 16.7.0-alpha
版本推出的新特性, 有了React Hooks,在react 函数组件中,也可以使用类组件(classes components)的 state 和 组件生命周期。通过下面几个例子来学习React Hooks。
- State Hook
// useState是react包提供的一个方法
import React, { useState } from "react";
import ReactDOM from "react-dom";
const Counter = () => {
// useState 这个方法可以为我们的函数组件拥有自己的state,它接收一个用于初始 state 的值,返回一对变量。这里我们把计数器的初始值设置为0, 方法都是以set开始
const [count, setCount] = useState(0);
return (
<div>
<p>你点击了{count}次</p>
<button onClick={() => setCount(count + 1)}>点击</button>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<Counter />, rootElement);
- Effect Hook
// useState是react包提供的一个方法
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
const Counter = () => {
// useState 这个方法可以为我们的函数组件拥有自己的state,它接收一个用于初始 state 的值,返回一对变量。这里我们把计数器的初始值设置为0, 方法都是以set开始
const [count, setCount] = useState(0);
// 类似于componentDidMount或者componentDidUpdate:
useEffect(() => {
// 更改网页的标题,还可以做其它的监听
document.title = `你点击了${count}次`;
});
return (
<div>
<p>你点击了{count}次</p>
<button onClick={() => setCount(count + 1)}>点击</button>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<Counter />, rootElement);
- React Hooks 的规则
- 只能在顶层调用Hooks。不要在循环,条件或嵌套函数中调用Hook。
- 不要从常规JavaScript函数中调用Hook。只在React函数式组件调用Hooks。
22.对fiber(react)了解
fiber是react 16之后发布的一种react 核心算法,React Fiber是对核心算法的一次重新实现(官网说法)。之前用的是diff算法。
在之前React中,更新过程是同步的,这可能会导致性能问题。
当React决定要加载或者更新组件树时,会做很多事,比如调用各个组件的生命周期函数,计算和比对Virtual DOM,最后更新DOM树,这整个过程是同步进行的,也就是说只要一个加载或者更新过程开始,中途不会中断。因为JavaScript单线程的特点,如果组件树很大的时候,每个同步任务耗时太长,就会出现卡顿。
React Fiber的方法其实很简单——分片。把一个耗时长的任务分成很多小片,每一个小片的运行时间很短,虽然总时间依然很长,但是在每个小片执行完之后,都给其他任务一个执行的机会,这样唯一的线程就不会被独占,其他任务依然有运行的机会。
23. vue和react都有使用的吗?
-
React 和 Vue 的相同点
- 使用Virtual DOM,用JS模拟DOM结构,DOM变化的对比,放在JS层做,以提高重绘性能
DOM操作昂贵,JS运行效率高,要减少DOM操作 - 提供了响应式(Reactive)和组件化(Composable)的视图组件。
- 将注意力集中保存在核心库,而将其他功能如路由和全局状态管理交给相关的库。
- 使用Virtual DOM,用JS模拟DOM结构,DOM变化的对比,放在JS层做,以提高重绘性能
-
不同之处:
-
性能:
vue和React的性能都非常高,在 React 应用中,当某个组件的状态发生变化时,它会以该组件为根,重新渲染整个组件子树。如要避免不必要的子组件的重渲染,你需要手动实现;在 Vue 应用中,组件的依赖是在渲染过程中自动追踪的,所以系统能精确知晓哪个组件确实需要被重渲染,开发者不需要考虑组件是否需要重新渲染之类的优化。
-
HTML & CSS
在React中,一切都是JavaScript,所有的组件的渲染功能都依靠 JSX。JSX 是使用 XML 语法编写 JavaScript 的一种语法糖。你可以使用完整的编程语言 JavaScript 功能来构建你的视图页面;在Vue中有自带的渲染函数,Vue也支持JSX,Vue官方推荐使用模板渲染视图。组件分为逻辑类组件和表现类组件。
-
组件作用域内的 CSS
CSS 作用域在 React 中是通过 CSS-in-JS 的方案实现的 (比如 styled-components、glamorous 和 emotion);在Vue中是通过给style标签加scoped标记实现的。
-
规模
Vue 的路由库和状态管理库都是由官方维护支持且与核心库同步更新的。React 则是选择把这些问题交给社区维护,因此创建了一个更分散的生态系统。但相对的,React 的生态系统相比 Vue 更加繁荣。
-
原生渲染
React Native 能使你用相同的组件模型编写有本地渲染能力的 APP (iOS 和 Android)。能同时跨多平台开发,对开发者是非常棒的。
-
24. react 16与15的区别?
-
React 15 到 16 生命周期函数变动
预废弃:
componentWillMount,componentWillUpdate,componentWillReceiveProps
已新增:
static getDerivedStateFromProps(props, state) getSnapshotBeforeUpdate(prevProps, prevState) UNSAFE_componentWillMount UNSAFE_componentWillReceiveProps UNSAFE_componentWillUpdate
到目前为止(React 16.4),React的渲染机制遵循同步渲染:
- 首次渲染: willMount > render > didMount,
- props更新时: receiveProps > shouldUpdate > willUpdate > render > didUpdate
- state更新时: shouldUpdate > willUpdate > render > didUpdate
- 卸载时: componentWillUnmount()
-
react 16 没有 diff 算法
diff 算法是递归算法,react 16 没有递归,没有树,改为一条 三向链表
react 16 所谓的 diff 只是新旧 fiber 节点的简单比对,不需要算法
-
更小的资源
react+react dom打包之后 相较于上一个版本大小减少了约30% -
更强的渲染性能
React 16是第一个对React核心代码进行了重构命名为React Fiber,Fiber 相较于之前最大的不同是它可以支持异步渲染,异步渲染的意义在于能够将渲染任务划分为多块。浏览器的渲染引擎是单线程的,这意味着几乎所有的行为都是同步发生的。React 16 使用原生的浏览器 API 来间歇性地检查当前是否还有其他任务需要完成,从而实现了对主线程和渲染过程的管理,这块具体的逻辑比较复杂,想要深入研究的可以参照React Fiber的源码。笔者尝试了将原来频繁setState的场景,例如拖动效果:
onTouchMove(e) { let po = this.getPosition(e); this.setState({ top: po.y, left: po.x }); }
切换到React 16之后在某些低端机型上的拖动体验要比之前好一些,关于前端的渲染性能有待进一步的数据验证。
-
服务端渲染性能的提升
React 16的SSR被完全重写,新的实现非常快,接近3倍性能于React 15,现在提供一种流模式streaming,可以更快地把渲染的字节发送到客户端。关于服务端性能渲染的数据有待和直出共同端统计:升级前后内存占用量对比 (蓝线升级后 绿线升级前)减低10% 的cpu内存使用量 -
render方法支持返回数组
之前使用React时,会经常遇到的一个问题就是在返回多余一个组件时要使用一个div包裹起来在升级之后就可以直接返回一个数组:render(){ return [ <div key="1">1</div>, <span key="2">2</span>, <p key="3">3</p> ] }
-
更强的错误处理机制
在React 15的时候,react的错误处理机制借助unstable_handleError来处理异常,这个有局限性也不是很好用,React 16将这一功能进行了升级,新增了组件生命周期的componentDidCatch方法: -
React 16不再支持采用es5写法定义的Component例如:
var Main = React.createClass({
render: function () {
return null;
}
});
25. react16中getSnapshotBeforeUpdate返回值,return null的情况?
getSnapshotBeforeUpdate
生命周期在更新之前被调用(例如,在DOM被更新之前)。此生命周期的返回值将作为第三个参数传递给componentDidUpdate
。返回null则为不返回任何东西
26. react组件化的了解
react的最大的特性就是组件化,组件化的目的就是为了能够进行代码的复用,减少代码的冗余
组件化的本质,其实就是面向对象的设计思想。组件化可以对应于 一个类,每个类都对外输出不变的接⼝,只要接⼝不变,类与类间 的通讯可以根据接⼝进行,由此不同组件间的耦合度就降低了。 同时复杂的类可以通过若干个简单的类组合而成,这样一来,不但 可以提高组件的重用性,同时通过组合的思想来设计复杂的控件, 也极大的降低了复杂控件的设计难度
原文:https://blog.youkuaiyun.com/jane617_min/article/details/79573019
27.设计一个组件的方法?如何实现?
已知组件无状态组件,类组件。
28.react如何实现权限管理
https://blog.hypers.io/2017/07/22/react-permission/
29 公司中是否有react npm私有仓库?怎么开发公共组件的
https://blog.youkuaiyun.com/fghsfeyhdf/article/details/88596427
31.bable底层原理是什么?
- Babel是一个广泛使用的转码器,可以将ES6代码转为ES5代码
- 和编译器类似,babel 的转译过程也分为三个阶段
解析 Parse
将代码解析生成抽象语法树( 即AST ),也就是计算机理解我们代码的方式(扩展:一般来说每个 js 引擎都有自己的 AST,比如熟知的 v8,chrome 浏览器会把 js 源码转换为抽象语法树,再进一步转换为字节码或机器代码),而 babel 则是通过 babylon 实现的 。简单来说就是一个对于 JS 代码的一个编译过程,进行了词法分析与语法分析的过程。
转换 Transform
对于 AST 进行变换一系列的操作,babel 接受得到 AST 并通过 babel-traverse 对其进行遍历,在此过程中进行添加、更新及移除等操作。
生成 Generate
将变换后的 AST 再转换为 JS 代码, 使用到的模块是 babel-generator。
32.使用过webpack里面哪些plugin和loader
常见的loader
1.loaders之预处理
- css-loader 处理css中路径引用等问题
- style-loader 动态把样式写入css
- sass-loader scss编译器
- less-loader less编译器
- postcss-loader scss再处理
2.loader的 js处理
- babel-loader:转化ES6代码
- jsx-loader:识别js中的 jsx 语法
3.其他loader加载器
- url-loader: 图片处理 npm install --save-dev url-loadr
- file-loader: 文件加载 npm install --save-dev file-loader
- json-loader:json处理 npm install --save-dev json-loader
- raw-loader: html处理 npm install --save-dev raw-loader
常见的plugin
- 1.DedupePlugin
打包的时候删除重复或者相似的文件 - 2.MinChunkSizePlugin
把多个小模块进行合并,以减少文件的大小 - 3.UglifyJsPlugin
压缩js - 4.HtmlWebpackPlugin
生成html - 5.CommonsChunkPlugin
多个 html共用一个js文件(chunk)
33.webpack里面的插件是怎么实现的
webpack 插件是由「具有 apply 方法的 prototype 对象」所实例化出来的。这个 apply 方法在安装插件时,会被 webpack compiler 调用一次。apply 方法可以接收一个 webpack compiler 对象的引用,从而可以在回调函数中访问到 compiler 对象。
一个webpack000插件有以下特性
+ 本质是一个构造函数
+ 原型prototype实现了apply方法,可以通过参数注入compiler对象
+ 可以在compiler的钩子里面获取compilation对象
34.webpack中loader的原理是什么
由于webpack是基于Node的所以webpack只能识别.js文件,所以针对其他的文件就需要转译,这时候就需要用到我们的loader了。
loader是文件加载器,能够加载资源文件,并对这些文件进行特定的处理,然后打包的指定文件中,简单来说就是将webpack传入的字符串做出特定的处理修改。
35.nodejs如何设置cors?
使用cors这个模块可以实现跨域的功能
在express项目下面
执行npm install cors
然后在app.js里面
var cors = require(‘cors’);
app.use(cors());
36.Node.js用过没,主要用来做什么?
```
node.js是一个运行在chromeJavascript运行环境下(俗称GoogleV8引擎)的开发平台,用来方便快捷的创建服务器端网络应用程序。你可以把它理解为一个轻量级的JSP或PHP环境,但是用来开发Web应用的话,有时要便捷很多。
```
37.使用Node.js做过什么,有真实项目经历吗
运用node.js模拟过学生管理系统,数据表使用的是Mongo。后端使用的是node.js,采用事件驱动和异步编程。且Node.js非阻塞模式的IO处理给Node.js带来在相对低系统资源耗用下的高性能与出众的负载能力,非常适合用作依赖其它IO资源的中间层服务
38.Node.js用于文件操作的模块是哪个:
是fs模块: 具有文件读和写操作: fs.readFile, fs.writeFile
39.fs中获取文件路径哪个API,还有其他操作API吗?
```
fs.readFile(path[,options],callback)
```
40.nodejs的了解
1.Node.js是一个基于Chrome V8引擎的javascipt的运行环境。 2. Node.js使用了一个事件驱动、非阻塞I/O的模型, 3. Node.js轻量又高效,能够使我们在本地运行javascript 4.Node.js采用一系列“非阻塞”库来支持事件循环的方式。本质上就是为文件系统、数据库之类的资源提供接口。 5. Node.js使用事件驱动,非阻塞I/O模型而得以轻量和高效,非常适合在分布式设备上运行数据密集型的实时应用。 6.node.js是平台,JavaScript是编程语言; 7.javascript是客户端编程语言,需要浏览器的javascript解释器进行解释执行; 8.node.js是一个基于Chrome JavaScript运行时建立的平台,它是对Google V8引擎进行了封装的运行环境; 9.node.js就是把浏览器的解释器封装起来作为服务器运行平台,用类似javascript的结构语法进行编程,在node.js上运行。
41.path.join与path.resolve的区别
```
path.join():
path.join()方法可以连接任意多个路径字符串。要连接的多个路径可做为参数传入。
path.join()方法在接边路径的同时也会对路径进行规范化
path.resolve():
path.resolve()方法可以将多个路径解析为一个规范化的绝对路径。其处理方式类似于对这些路径逐一进行cd操作,与cd操作不同的是,这引起路径可以是文件,并且可不必实际存在(resolve()方法不会利用底层的文件系统判断路径是否存在,而只是进行路径字符串操作)
```xxxxxxxxxx path.join():path.join()方法可以连接任意多个路径字符串。要连接的多个路径可做为参数传入。path.join()方法在接边路径的同时也会对路径进行规范化path.resolve():path.resolve()方法可以将多个路径解析为一个规范化的绝对路径。其处理方式类似于对这些路径逐一进行cd操作,与cd操作不同的是,这引起路径可以是文件,并且可不必实际存在(resolve()方法不会利用底层的文件系统判断路径是否存在,而只是进行路径字符串操作)