美文网首页
关于React的入门知识

关于React的入门知识

作者: 吴一晏 | 来源:发表于2019-07-16 20:42 被阅读0次
    1. 受控组件 V.S. 非受控组件

       <FInput value={x} onChange={fn}/> 受控组件:渲染表单的React组件 还控制着 用户输入表单时变化的操作,每次状态变化都调用onChange,这种组件叫受控组件。表单数据是由 React 组件来管理的。
       <FInput defaultValue={x} ref={input}/> 非受控组件:React赋予组件一个初始值,但是不去控制后续的更新。表单数据将交由 DOM 节点来处理。
      
      
    1. React 有哪些生命周期函数?分别有什么用?(Ajax请求放在哪个阶段?)
      初始化阶段:
      componentWillMount()组件即将被渲染到页面之前触发,此时可以进行开启定时器、向服务器发送请求等操作
      render()组件渲染
      componentDidMount()组件已经被渲染到页面中后触发:此时页面中有了真正的DOM的元素,可以进行DOM相关的操作
      运行中阶段:
      componentWillReceiveProps()组件接收到属性时触发
      shouldComponentUpdate()当组件接收到新属性,或者组件的状态发生改变时触发。组件首次渲染时并不会触发
      componentWillUpdate()组件即将被更新时触发
      render()
      componentDidUpdate()组件被更新完成后触发。页面中产生了新的DOM的元素,可以进行DOM操作
      销毁阶段
      componentWillUnmount() 组件被销毁时触发。
      1. componentDidMount()中请求数据。
    2. React 如何实现组件间通信?
      1. 父子靠props 传函数
      2. 爷孙可以穿两次props
      3. 任意组件用 Redux(也可以自己写一个 eventBus
    3. shouldComponentUpdate 有什么用?
      1. 用于在没有必要更新 UI 的时候返回 false,以提高渲染性能。
      2. 但是不要滥用,这是React提供的一个紧急出口,必要的时候再使用,因为它的维护成本比较高,比如你新加了一个props,但是又忘记在shouldComponentUpdate 写,就会引起bug。
      3. 如何优化?
    4. 虚拟 DOM 是什么?
      1. 要点:虚拟 DOM 就是用来模拟 DOM 的一个对象,这个对象拥有一些重要属性,并且更新 UI 主要就是通过对比(DIFF)旧的虚拟 DOM 树 和新的虚拟 DOM 树的区别完成的。
      2. 参考:http://foio.github.io/virtual-dom/
    5. 什么是高阶组件?
      1. 文档原话——高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。
      2. React-Redux 里 connect 就是一个高阶组件,比如 connect(mapState)(MyComponent) 接受组件 MyComponent,返回一个具有状态的新 MyComponent 组件。
    6. React diff 的原理是什么?
      首先,React diff遵循三个策略:1. Web UI 中,DOM节点跨层级的移动操作特别少,可以忽略不计。2.拥有相同类的两个组件将生成相似的树形结构,拥有不同类的两个组件将生成不同的树形结构。3. 对于同一层级的一组子节点,可以通过唯一id来区分它们。基于以上三个策略,React分别对tree diff、component diff、element diff进行了算法优化。
    • tree diff

      React对树的算法进行了优化,对树进行分层比较,两棵树只比较同一层级的节点。DOM节点跨层级的操作少到忽略不计,针对这一点,React通过updateDepth对Virtual DOM树进行层级控制,只会对相同颜色方块内的DOM节点进行比较,当发现节点不存在,则会删除该节点以及所有子节点,不会再进行进一步比较,所以只要对DOM树进行一次遍历,就能完成整个DOM树的比较。 tree diff.jpg
    • component diff
      如果是同一类型的组件,则按照原策略进行Virtual DOM Tree的比较。如果不是呢,则讲组件判断为dirty component,从而替换整个组件下的所有子节点。
      对于同一类型的组件,React允许用户使用 shouldComponentUpdate()来判断该组件是否需要diff。
      如下图,当component D 改变为 component G时,即使两个组件结构相似,一旦React判断两者为不同类型的组件,则不会进行比较,而是直接删除component D,重新创建component G以及所有子节点。

      component diff.jpg
    • element diff
      当节点处于同一层级时,React diff提供了三种节点操作
      INSERT_MARKUP(插入):新的component类型不在老集合里,即是全新的节点,则需要对新节点执行插入操作。
      MOVE_EXISTING(移动):新的component类型在老集合里,且element是可更新的类型,generateComponentChildren 已调用receiveComponent,这种情况下prevChild=nextChild,就需要执行移动操作,可以复用以前的DOM节点。
      REMOVE_NODE(删除):老component类型在新集合里面也有,但对应的element不同则不能直接进行复用和更新,需要执行删除操作。老component类型在新集合里没有,也要执行删除操作。
      开发者对同一层级的子节点,可以添加唯一索引进行区分,这样在diff时,涉及到只是位置变化的,可以只移动元素,避免删除创建等重复的操作。

    1. Redux 是什么?
      1. ReduxJavaScript 状态管理工具,提供可预测化的状态管理。
      2. Action:是把数据从应用传到 Store 的有效载荷。它是 Store 数据的唯一来源。改变 State 的唯一办法,就是使用 Action。
    import { ADD_TODO } from "../actionTypes";
    
    export const addTodo = (payload:any) => {
        return {
            type: ADD_TODO,
            payload
        }
    }
    

    Reducers: 指定了应用状态的变化如何响应 actions并发送到store 的,记住 actions只是描述了有事情发生了这一事实,并没有描述应用如何更新 state
    Reducer 是一个函数,它接受 Action 和当前State 作为参数,返回一个新的 State
    我们还可以将拆分后的Reducer 放到不同的文件中, 以保持其独立性并用于专门处理不同的数据域。然后使用combineReducers将多个Reducer合并成一个,输出成一个大的对象。

    import { ADD_TODO } from "../actionTypes";
    
    export default function (state = [],action:any) {
        switch (action.type){
            case ADD_TODO:
                return [...state,action.payload]
            default:
                return state
        }
    }
    
    import { ADD_TOMATO } from "../actionTypes";
    
    export default function (state = [], action: any) {
        switch(action.type) {
            case ADD_TOMATO:
                return [...state, action.payload]
            default:
                return state
        }
    }
    //使用combineReducers
    import { combineReducers } from "redux";
    import todos from './todos'
    import tomatoes from './tomatoes'
    
    export default combineReducers({ todos, tomatoes });
    

    Store: 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 StoreRedux提供createStore这个函数,接受reducers,用来生成 Store

    import { createStore } from "redux";
    import rootReducer from "./reducers"; 
    
    const store = createStore(rootReducer)
    
    export default store
    

    Store有以下职责:

    • 维持应用的 state
    • 提供 getState() 方法返回应用当前的 state 树。它与 store 的最后一个 reducer 返回值相同。
    • 提供 dispatch(action) 方法分发action。这是触发 state 变化的惟一途径。
    • 通过 subscribe(listener) 注册监听器;
    • 通过 subscribe(listener) 返回的函数注销监听器。

    connect:该API连接React组件与 Redux store,连接操作不会改变原来的组件类。反而返回一个新的已与 Redux store 连接的组件类。connect可以接受参数,将参数注入到组件当中

    const mapStateToProps = (state: { todos: any; }, ownProps: any) => ({//注入props
        ...ownProps
    })
    
    const mapDispatchToProps = {//注入属性
        editTodo, 
        updateTodo
    }
    
    export default connect(mapStateToProps, mapDispatchToProps)(todoItem)
    

    <Provider store> 使组件层级中的 connect()方法都能够获得 Redux store。正常情况下,你的根组件应该嵌套在 <Provider> 中才能使用 connect() 方法。

    ReactDOM.render(
      <Provider store={store}>
        <MyRootComponent />
      </Provider>,
      rootEl
    )
    
    1. connect 的原理是什么?
      react-redux 库提供的一个 API,connect的作用是让你把组件和store连接起来,产生一个新的组件(connect 是高阶组件)。

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

    2.connect做了什么:它真正连接ReduxReact,它包在我们的容器组件的外一层,它接收上面Provider 提供的store里面的statedispatch,传给一个构造函数,返回一个对象,以属性形式传给我们的容器组件。

    3.主逻辑源码

    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;
        }
      }
    

    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

    相关文章

      网友评论

          本文标题:关于React的入门知识

          本文链接:https://www.haomeiwen.com/subject/nsdxlctx.html