为什么调用 setState 而不是间接扭转 state?
解答
如果您尝试间接扭转组件的状态,React 将无奈得悉它须要从新渲染组件。通过应用setState()
办法,React 能够更新组件的UI。
另外,您还能够谈谈如何不保障状态更新是同步的。如果须要基于另一个状态(或属性)更新组件的状态,请向setState()
传递一个函数,该函数将 state 和 props 作为其两个参数:
this.setState((state, props) => ({ counter: state.counter + props.increment}));
React 数据长久化有什么实际吗?
封装数据长久化组件:
let storage={ // 减少 set(key, value){ localStorage.setItem(key, JSON.stringify(value)); }, // 获取 get(key){ return JSON.parse(localStorage.getItem(key)); }, // 删除 remove(key){ localStorage.removeItem(key); }};export default Storage;
在React我的项目中,通过redux存储全局数据时,会有一个问题,如果用户刷新了网页,那么通过redux存储的全局数据就会被全副清空,比方登录信息等。这时就会有全局数据长久化存储的需要。首先想到的就是localStorage,localStorage是没有工夫限度的数据存储,能够通过它来实现数据的长久化存储。
然而在曾经应用redux来治理和存储全局数据的根底上,再去应用localStorage来读写数据,这样不仅是工作量微小,还容易出错。那么有没有联合redux来达到持久数据存储性能的框架呢?当然,它就是redux-persist。redux-persist会将redux的store中的数据缓存到浏览器的localStorage中。其应用步骤如下:
(1)首先要装置redux-persist:
npm i redux-persist
(2)对于reducer和action的解决不变,只需批改store的生成代码,批改如下:
import {createStore} from 'redux'import reducers from '../reducers/index'import {persistStore, persistReducer} from 'redux-persist';import storage from 'redux-persist/lib/storage';import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';const persistConfig = { key: 'root', storage: storage, stateReconciler: autoMergeLevel2 // 查看 'Merge Process' 局部的具体情况};const myPersistReducer = persistReducer(persistConfig, reducers)const store = createStore(myPersistReducer)export const persistor = persistStore(store)export default store
(3)在index.js中,将PersistGate标签作为网页内容的父标签:
import React from 'react';import ReactDOM from 'react-dom';import {Provider} from 'react-redux'import store from './redux/store/store'import {persistor} from './redux/store/store'import {PersistGate} from 'redux-persist/lib/integration/react';ReactDOM.render(<Provider store={store}> <PersistGate loading={null} persistor={persistor}> {/*网页内容*/} </PersistGate> </Provider>, document.getElementById('root'));
这就实现了通过redux-persist实现React长久化本地数据存储的简略利用。
redux 中间件
中间件提供第三方插件的模式,自定义拦挡 action -> reducer 的过程。变为 action -> middlewares -> reducer 。这种机制能够让咱们扭转数据流,实现如异步 action ,action 过 滤,日志输入,异样报告等性能
常见的中间件:
- redux-logger:提供日志输入;
- redux-thunk:解决异步操作;
- redux-promise: 解决异步操作;
- actionCreator 的返回值是 promise
类组件和函数组件之间的区别是啥?
- 类组件能够应用其余个性,如状态
state
和生命周期钩子。 当组件只是接管
props
渲染到页面时,就是无状态组件,就属于函数组件,也被称为哑组件或展现组件。
函数组件和类组件当然是有区别的,而且函数组件的性能比类组件的性能要高,因为类组件应用的时候要实例化,而函数组件间接执行函数取返回后果即可。为了进步性能,尽量应用函数组件。区别 函数组件 类组件 是否有 this
没有 有 是否有生命周期 没有 有 是否有状态 state
没有 有
React 中 keys 的作用是什么?
Keys
是React
用于追踪哪些列表中元素被批改、被增加或者被移除的辅助标识
- 在开发过程中,咱们须要保障某个元素的
key
在其同级元素中具备唯一性。在React Diff
算法中React
会借助元素的Key
值来判断该元素是早先创立的还是被挪动而来的元素,从而缩小不必要的元素重渲染。此外,React 还须要借助Key
值来判断元素与本地状态的关联关系,因而咱们绝不可漠视转换函数中Key
的重要性
简述flux 思维
Flux
的最大特点,就是数据的"单向流动"。
- 用户拜访
View
View
收回用户的Action
Dispatcher
收到Action
,要求Store
进行相应的更新Store
更新后,收回一个"change"
事件View
收到"change"
事件后,更新页面
React 组件生命周期有哪些不同阶段?
在组件生命周期中有四个不同的阶段:
- Initialization:在这个阶段,组件筹备设置初始化状态和默认属性。
- Mounting:react 组件曾经筹备好挂载到浏览器 DOM 中。这个阶段包含
componentWillMount
和componentDidMount
生命周期办法。 - Updating:在这个阶段,组件以两种形式更新,发送新的 props 和 state 状态。此阶段包含
shouldComponentUpdate
、componentWillUpdate
和componentDidUpdate
生命周期办法。 - Unmounting:在这个阶段,组件曾经不再被须要了,它从浏览器 DOM 中卸载下来。这个阶段蕴含
componentWillUnmount
生命周期办法。
除以上四个罕用生命周期外,还有一个错误处理的阶段:
Error Handling:在这个阶段,不管在渲染的过程中,还是在生命周期办法中或是在任何子组件的构造函数中产生谬误,该组件都会被调用。这个阶段蕴含了componentDidCatch
生命周期办法。
参考:前端react面试题具体解答
diff算法是怎么运作
每一种节点类型有本人的属性,也就是prop,每次进行diff的时候,react会先比拟该节点类型,如果节点类型不一样,那么react会间接删除该节点,而后间接创立新的节点插入到其中,如果节点类型一样,那么会比拟prop是否有更新,如果有prop不一样,那么react会断定该节点有更新,那么重渲染该节点,而后在对其子节点进行比拟,一层一层往下,直到没有子节点
connect原理
- 首先
connect
之所以会胜利,是因为Provider
组件: - 在原利用组件上包裹一层,使原来整个利用成为
Provider
的子组件 接管Redux
的store
作为props
,通过context
对象传递给子孙组件上的connect
connect
做了些什么。它真正连贯Redux
和React
,它包在咱们的容器组件的外一层,它接管下面Provider
提供的store
外面的state
和dispatch
,传给一个构造函数,返回一个对象,以属性模式传给咱们的容器组件
connect
是一个高阶函数,首先传入mapStateToProps
、mapDispatchToProps
,而后返回一个生产Component
的函数(wrapWithConnect
),而后再将真正的Component
作为参数传入wrapWithConnect
,这样就生产出一个通过包裹的Connect
组件,
该组件具备如下特点
- 通过
props.store
获取先人Component
的store props
包含stateProps
、dispatchProps
、parentProps
,合并在一起失去nextState
,作为props
传给真正的Component componentDidMount
时,增加事件this.store.subscribe(this.handleChange)
,实现页面交互 shouldComponentUpdate
时判断是否有防止进行渲染,晋升页面性能,并失去nextState
componentWillUnmount
时移除注册的事件this.handleChange
因为connect
的源码过长,咱们只看次要逻辑
export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) { return function wrapWithConnect(WrappedComponent) { class Connect extends Component { constructor(props, context) { // 从先人Component处取得store this.store = props.store || context.store this.stateProps = computeStateProps(this.store, props) this.dispatchProps = computeDispatchProps(this.store, props) this.state = { storeState: null } // 对stateProps、dispatchProps、parentProps进行合并 this.updateState() } shouldComponentUpdate(nextProps, nextState) { // 进行判断,当数据产生扭转时,Component从新渲染 if (propsChanged || mapStateProducedChange || dispatchPropsChanged) { this.updateState(nextProps) return true } } componentDidMount() { // 扭转Component的state this.store.subscribe(() = { this.setState({ storeState: this.store.getState() }) }) } render() { // 生成包裹组件Connect return ( <WrappedComponent {...this.nextState} /> ) } } Connect.contextTypes = { store: storeShape } return Connect; } }
组件更新有几种办法
- this.setState() 批改状态的时候 会更新组件
- this.forceUpdate() 强制更新
- 组件件render之后,子组件应用到父组件中状态,导致子组件的props属性产生扭转的时候 也会触发子组件的更新
React和vue.js的相似性和差异性是什么?
相似性如下。
(1)都是用于创立UI的 JavaScript库。
(2)都是疾速和轻量级的代码库(这里指 React外围库)。
(3)都有基于组件的架构。
(4)都应用虚构DOM。
(5)都能够放在独自的HTML文件中,或者放在 Webpack设置的一个更简单的模块中。
(6)都有独立但罕用的路由器和状态治理库。
它们最大的区别在于 Vue. js通常应用HTML模板文件,而 React齐全应用 JavaScript创立虚构DOM。 Vue. js还具备对于“可变状态”的“ reactivity”的从新渲染的自动化检测零碎。
React 性能优化
- shouldCompoentUpdate
- pureComponent 自带shouldCompoentUpdate的浅比拟优化
- 联合Immutable.js达到最优
如何用 React构建( build)生产模式?
通常,应用 Webpack的 DefinePlugin办法将 NODE ENV设置为 production。这将剥离 propType验证和额定的正告。除此之外,还能够缩小代码,因为 React应用 Uglify的dead-code来打消开发代码和正文,这将大大减少包占用的空间。
componentWillReceiveProps调用机会
- 曾经被废除掉
- 当props扭转的时候才调用,子组件第二次接管到props的时候
这三个点(...)在 React 干嘛用的?
...
在React(应用JSX)代码中做什么?它叫什么?
<Modal {...this.props} title='Modal heading' animation={false}/>
这个叫扩大操作符号或者开展操作符,例如,如果this.props
蕴含a:1
和b:2
,则
<Modal {...this.props} title='Modal heading' animation={false}>
等价于上面内容:
<Modal a={this.props.a} b={this.props.b} title='Modal heading' animation={false}>
扩大符号不仅实用于该用例,而且对于创立具备现有对象的大多数(或全副)属性的新对象十分不便,在更新state
咱们就常常这么做:
this.setState((prevState) => { return { foo: { ...prevState.foo, a: "updated" } };});
redux中间件
中间件提供第三方插件的模式,自定义拦挡action
->reducer
的过程。变为action
->middlewares
->reducer
。这种机制能够让咱们扭转数据流,实现如异步action
,action
过滤,日志输入,异样报告等性能
redux-logger
:提供日志输入redux-thunk
:解决异步操作redux-promise
:解决异步操作,actionCreator
的返回值是promise
createElement过程
React.createElement(): 依据指定的第一个参数创立一个React元素
React.createElement( type, [props], [...children])
- 第一个参数是必填,传入的是似HTML标签名称,eg: ul, li
- 第二个参数是选填,示意的是属性,eg: className
- 第三个参数是选填, 子节点,eg: 要显示的文本内容
//写法一:var child1 = React.createElement('li', null, 'one'); var child2 = React.createElement('li', null, 'two'); var content = React.createElement('ul', { className: 'teststyle' }, child1, child2); // 第三个参数能够离开也能够写成一个数组 ReactDOM.render( content, document.getElementById('example') );//写法二:var child1 = React.createElement('li', null, 'one'); var child2 = React.createElement('li', null, 'two'); var content = React.createElement('ul', { className: 'teststyle' }, [child1, child2]); ReactDOM.render( content, document.getElementById('example') );
什么是高阶组件?
高阶组件(HOC)是承受一个组件并返回一个新组件的函数。基本上,这是一个模式,是从 React 的组合个性中衍生进去的,称其为纯组件,因为它们能够承受任何动静提供的子组件,但不会批改或复制输出组件中的任何行为。
const EnhancedComponent = higherOrderComponent(WrappedComponent);
HOC 能够用于以下许多用例
- 代码重用、逻辑和疏导形象
- 渲染劫持
- state 形象和操作
- props 解决
什么是虚构DOM?
虚构 DOM (VDOM)是实在 DOM 在内存中的示意。UI 的示意模式保留在内存中,并与理论的 DOM 同步。这是一个产生在渲染函数被调用和元素在屏幕上显示之间的步骤,整个过程被称为和谐。
为什么有些react生命周期钩子被标记为UNSAFE
componentWillMount
componentWillMount生命周期产生在首次渲染前,个别应用的小伙伴大多在这里初始化数据或异步获取内部数据赋值。初始化数据,react官网倡议放在constructor外面。而异步获取内部数据,渲染并不会期待数据返回后再去渲染
class Example extends React.Component { state = { value: '' }; componentWillMount() { this.setState({ value: this.props.source.value }); this.props.source.subscribe(this.handleChange); } componentWillUnmount() { this.props.source.unsubscribe(this.handleChange ); } handleChange = source => { this.setState({ value: source.value }); }; }
- 试想一下,如果组件在第一次渲染的时候被中断,因为组件没有实现渲染,所以并不会执行componentWillUnmount生命周期(注:很多人常常认为componentWillMount和componentWillUnmount总是配对,但这并不是肯定的。只有调用componentDidMount后,React能力保障稍后调用componentWillUnmount进行清理)。因而handleSubscriptionChange还是会在数据返回胜利后被执行,这时候setState因为组件曾经被移除,就会导致内存透露。所以倡议把异步获取内部数据写在componentDidMount生命周期里,这样就能保障componentWillUnmount生命周期会在组件移除的时候被执行,防止内存透露的危险。
- 当初,小伙伴分明为什么了要用
UNSAFE_componentWillMount
替换componentWillMount
了吧
componentWillReceiveProps
componentWillReceiveProps生命周期是在props更新时触发。个别用于props参数更新时同步更新state参数。但如果在componentWillReceiveProps生命周期间接调用父组件的某些有调用setState的函数,会导致程序死循环
// 如下是子组件componentWillReceiveProps里调用父组件扭转state的函数示例class Parent extends React.Component{ constructor(){ super(); this.state={ list: [], selectedData: {} }; } changeSelectData = selectedData => { this.setState({ selectedData }); } render(){ return ( <Clild list={this.state.list} changeSelectData={this.changeSelectData}/> ); }}...class Child extends React.Component{ constructor(){ super(); this.state={ list: [] }; } componentWillReceiveProps(nextProps){ this.setState({ list: nextProps.list }) nextProps.changeSelectData(nextProps.list[0]); //默认抉择第一个 } ...}
- 如上代码,在Child组件的componentWillReceiveProps里间接调用Parent组件的changeSelectData去更新Parent组件state的selectedData值。会触发Parent组件从新渲染,而Parent组件从新渲染会触发Child组件的componentWillReceiveProps生命周期函数执行。如此就会陷入死循环。导致程序解体。
- 所以,React官网把componentWillReceiveProps替换为UNSAFE_componentWillReceiveProps,让小伙伴在应用这个生命周期的时候留神它会有缺点,要留神防止,比方下面例子,Child在componentWillReceiveProps调用changeSelectData时先判断list是否有更新再确定是否要调用,就能够防止死循环。
componentWillUpdate
componentWillUpdate生命周期在视图更新前触发。个别用于视图更新前保留一些数据不便视图更新实现后赋值。 案例三:如下是列表加载更新后回到以后滚动条地位的案例
class ScrollingList extends React.Component { listRef = null; previousScrollOffset = null; componentWillUpdate(nextProps, nextState) { if (this.props.list.length < nextProps.list.length) { this.previousScrollOffset = this.listRef.scrollHeight - this.listRef.scrollTop; } } componentDidUpdate(prevProps, prevState) { if (this.previousScrollOffset !== null) { this.listRef.scrollTop = this.listRef.scrollHeight - this.previousScrollOffset; this.previousScrollOffset = null; } } render() { return ( `<div>` {/* ...contents... */}`</div>` ); } setListRef = ref => { this.listRef = ref; };
- 因为componentWillUpdate和componentDidUpdate这两个生命周期函数有肯定的时间差(componentWillUpdate后通过渲染、计算、再更新DOM元素,最初才调用componentDidUpdate),如果这个时间段内用户刚好拉伸了浏览器高度,那componentWillUpdate计算的previousScrollOffset就不精确了。如果在componentWillUpdate进行setState操作,会呈现屡次调用只更新一次的问题,把setState放在componentDidUpdate,能保障每次更新只调用一次。
- 所以,react官网倡议把componentWillUpdate替换为UNSAFE_componentWillUpdate。如果真的有以上案例的需要,能够应用16.3新退出的一个周期函数getSnapshotBeforeUpdat
论断
- React意识到componentWillMount、componentWillReceiveProps和componentWillUpdate这三个生命周期函数有缺点,比拟容易导致解体。然而因为旧的我的项目曾经在用以及有些老开发者习惯用这些生命周期函数,于是通过给它加UNSAFE_来揭示用它的人要留神它们的缺点
- React退出了两个新的生命周期函数getSnapshotBeforeUpdate和getDerivedStateFromProps,目标为了即便不应用这三个生命周期函数,也能实现只有这三个生命周期能实现的性能