关于react.js:前端react面试题总结

38次阅读

共计 10433 个字符,预计需要花费 27 分钟才能阅读完成。

为什么调用 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 的作用是什么?

KeysReact 用于追踪哪些列表中元素被批改、被增加或者被移除的辅助标识

  • 在开发过程中,咱们须要保障某个元素的 key 在其同级元素中具备唯一性。在 React Diff 算法中React 会借助元素的 Key 值来判断该元素是早先创立的还是被挪动而来的元素,从而缩小不必要的元素重渲染。此外,React 还须要借助 Key 值来判断元素与本地状态的关联关系,因而咱们绝不可漠视转换函数中 Key 的重要性

简述 flux 思维

Flux 的最大特点,就是数据的 ” 单向流动 ”。

  • 用户拜访 View
  • View收回用户的 Action
  • Dispatcher 收到Action,要求 Store 进行相应的更新
  • Store 更新后,收回一个 "change" 事件
  • View 收到 "change" 事件后,更新页面

React 组件生命周期有哪些不同阶段?

在组件生命周期中有四个不同的阶段:

  1. Initialization:在这个阶段,组件筹备设置初始化状态和默认属性。
  2. Mounting:react 组件曾经筹备好挂载到浏览器 DOM 中。这个阶段包含 componentWillMountcomponentDidMount生命周期办法。
  3. Updating:在这个阶段,组件以两种形式更新,发送新的 props 和 state 状态。此阶段包含 shouldComponentUpdatecomponentWillUpdatecomponentDidUpdate生命周期办法。
  4. Unmounting:在这个阶段,组件曾经不再被须要了,它从浏览器 DOM 中卸载下来。这个阶段蕴含 componentWillUnmount 生命周期办法。
    除以上四个罕用生命周期外,还有一个错误处理的阶段:
    Error Handling:在这个阶段,不管在渲染的过程中,还是在生命周期办法中或是在任何子组件的构造函数中产生谬误,该组件都会被调用。这个阶段蕴含了 componentDidCatch 生命周期办法。

参考:前端 react 面试题具体解答

diff 算法是怎么运作

每一种节点类型有本人的属性,也就是 prop,每次进行 diff 的时候,react 会先比拟该节点类型,如果节点类型不一样,那么 react 会间接删除该节点,而后间接创立新的节点插入到其中,如果节点类型一样,那么会比拟 prop 是否有更新,如果有 prop 不一样,那么 react 会断定该节点有更新,那么重渲染该节点,而后在对其子节点进行比拟,一层一层往下,直到没有子节点

connect 原理

  • 首先 connect 之所以会胜利,是因为 Provider 组件:
  • 在原利用组件上包裹一层,使原来整个利用成为 Provider 的子组件 接管 Reduxstore作为 props,通过context 对象传递给子孙组件上的connect

connect做了些什么。它真正连贯 ReduxReact,它包在咱们的容器组件的外一层,它接管下面 Provider 提供的 store 外面的statedispatch,传给一个构造函数,返回一个对象,以属性模式传给咱们的容器组件

  • connect是一个高阶函数,首先传入 mapStateToPropsmapDispatchToProps,而后返回一个生产Component 的函数 (wrapWithConnect),而后再将真正的Component 作为参数传入 wrapWithConnect,这样就生产出一个通过包裹的Connect 组件,

该组件具备如下特点

  • 通过 props.store 获取先人 Componentstore props包含 statePropsdispatchPropsparentProps, 合并在一起失去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:1b: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。这种机制能够让咱们扭转数据流,实现如异步actionaction 过滤,日志输入,异样报告等性能

  • 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,目标为了即便不应用这三个生命周期函数,也能实现只有这三个生命周期能实现的性能

正文完
 0