关于React的入门知识

  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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • [toc] REACT react :1.用来构建用户界面的 JAVASCRIPT 库2.react 专注于视图层...
    拨开云雾0521阅读 5,330评论 0 1
  • 深入JSX date:20170412笔记原文其实JSX是React.createElement(componen...
    gaoer1938阅读 12,459评论 2 35
  • React 中 keys 的作用是什么? Keys 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除...
    e2ee31170666阅读 5,027评论 1 3
  • 今天的React题没有太多的故事…… 半个月前出了248个Vue的知识点,受到很多朋友的关注,都强烈要求再出多些R...
    浪子神剑阅读 13,403评论 6 106
  • 项目地址: 项目地地址参考地址: bilibili 1.火热的0配置的打包工具parcel 地址: parcel官...
    第十人i阅读 2,481评论 0 0