react相关

595 阅读5分钟

react组件设计

  • 单一职责原则(SRP)
    react思想,一切都是组件。通常react组件分为两种,展示型组件和容器型组件。这样能更好的降低代码耦合,使得组件各司其职,代码更好的复用、维护和测试。
  • 展示型组件
    展示型组件主要表现为组件是怎样渲染的,包含vdom的修改和组合,组件样式。同时不依赖任何形式store,就是只依赖父类传进来的props做数据绑定,一般可以写成无状态组件或者是函数组件,使用Purcomponent或者memo,数据无变化避免组件重复渲染。PurComponent只是浅比较,只比较第一层
  • 容器性组件
    1、主要是实现业务逻辑,主要表现为组件怎样工作,数据怎么更新的。
    2、通常容器性组件使用connect连接store,数据需要映射到redux中。通过dispatch action来修改数据
  • 组件复合
    通过this.props.children相当于vue的slot插槽,接收jsx,实现组件定制化
  • 高阶组件(HOC)
    接收一个组件return一个组件,用于增强组件,产生的新组件可以对属性进行包装,例如我们熟知的andt的form表单,react-redux

    return Form.create()(Component)
    return connect({mapStateToProps,mapDispatchToProps})(Component)

    就是一个高阶组件

VDOM和diff算法

  • 什么是vdom
    真实dom操作会触发重排重绘,比较耗性能,引入vdom的概念。vdom是一个js对象,可以完整描述dom结构。当状态变更时,重新渲染这个js对象。
  • react中的vdom
    jsx是对js语法的扩展,使用它能够代替常规的js,使用html语法写js对象。webpack+babel编译时,会替换jsx为ReaceDom.createElement(type, props, ...children),这个函数返回一个js对象,能够完整描述dom结构,为vdom

    render(){
      return (
        <div id="div">
          <span className="span">hello</span>
        </div>
      )
    }
    webpack+babel=>
    render(){
      return React.createElement('div', {id: 'div'}, 
        React.createElement('span', {className: 'span'}, 'hello')
      )
    }

  • ReactDom.render(vdom,container)可以将vdom转换为真实的dom并追加到container中,通过遍历递归vdom树,根据type不同,执行不同的逻辑,type为1生成原生元素,如div,为2为类组件,需要将类组件实例化并执行其render将返回的dom初始化,为3是函数组件,需要执行函数组件,并将返回的dom初始化

  • 实现react思路
    三个核心api:React.createElement()和ReactDOM.render()
    1、React.createElement()创建出虚拟dom

    // react.js
    createElement(type, props, ...children){
      props.children = children
      let vtype = typeof type === 'string'?1:2 //1: div 2:Component
      return {vtype, type, props} //vdom
    }

    2、ReactDOM.render():将vdom渲染成真实dom

    //reactDom.js
    
    function render(vdom, container){
      const dom = initDom(vdom)
      container.appendChild(dom)
    }
    
    function initDom(vnode){
      // 根据vtype不同,创建不同的node
      const {vtype} = vnode
      if(!vtype){ // 文本节点
        return document.createTextNode()
      }
      if(vtype === 1){ //元素节点
        return createElementNode(vnode)
      }
      if(vtype === 2){ // Component
        return createClassComp(vNode)
      }
    }
    
    function createElementNode({vtype, type, props}){
      const node = document.createElement(type) //创建一个元素节点
      // 处理属性
      const {children, ...rest} = props
      Object.keys(rest).forEach(k=>{
        // className,htmlFor,on,style, ...特殊的属性特殊处理
    
        node.setAttribute(k, rest[k])
      })
      // 递归处理chidren
      children.forEach(c=>{
        node.appendChild(initDom)
      })
    
      return node
    }
    function createClassComp({vtype, type, props}){
    
    }
  • diff算法
    1、diff算法解决了什么问题?
    dom操作很慢,轻微的操作都会导致页面重排重绘。相对于dom对象,js对象处理起来更快更简单。通过diff算法对比新旧js对象之间的差异,可以批量最小化执行dom操作,从而提高性能
    2、react中在哪里使用了diff算法?
    react中用jsx语法描述视图,通过babel-loader转义后变为React.createElement(type, props,...children)的形式,该函数生成vdom用来描述dom结构。如果状态变化,如this.setState(),vdom做出相应变化,在通过diff算法对比新旧vdom区别,做出最终的操作
    3、diff算法如何进行优化的?
    我对diff算法,没有具体研究过它的源码,知道他的一些策略。进行diff算法之前,dom操作复杂度是O(n^3)优化后为O(n)
    策略:
    a、同级比较,不同级没有必要比较了
    b、拥有相同类的组件会生成相似的树形结构,不同类类的组件生成不同的树形结构。如果两个组件类型都不同,它的chidren也不需要比了,直接删除生成一个新的。通过这种方式决定使用删除、新增还是更新操作。
    c、对于同一层级的子节点,通过唯一的key进行区分

实现redux

  • redux的主要工作流程
    dispatch->action->reducer->return new state

    store.js
    import {createStore, applyMiddware} from 'redux'
    //修改store数据
    const reducer =  (state={count: 0}, action)=>{
      switch(action.type){
        case 'add':
          state['count']++
           return state
        case 'minus':
          state['count']--
          return state
         default:
          return state
      }
    }
    const store = createStore(reducer, applyMiddware())

// index.js
import store from './store'
store.subscribe(()=>{
  render ...
})

// action dispatch

const action = {type:'add'}
store.dispatch(action)

核心api:createStore=>store,store.getState,store.subscribe,store.dispatch

redux.js //观察者模式

let listens = []
export function createStore(reducer, enhancer){
  let currentState;
  function getState(){
    return currentState
  }
  function subscribe(fn){
    listens.push(fn)
  }
  function dispatch(action){
    currentState = reducer(currentState, action)
    listens.forEach(v=>{v()})
    return action
  }
  return {getState, subscribe, dispatch}
}

实现react-redux

  • 工作流

    import {connect, Provider} from 'react-redux'
    // 核心 Provider 利用react的Context组件值共享原理,
    <Provider store={store}><App /></Provider>
    const mapStateToProps = state => ({count: state.count}) // actionCreator
    const mapDispatchToProps = {add}
    // connect(mapStateToProps,mapDispatchToProps)(Component) //高阶组件

  • 核心代码

    //react-redux.js
    export Class Provider extends React.Component{
      // Context值共享api
      static childrenContextType = {
        store: PropTypes.object
      }
      getChildContext(){
        return {store: this.store}
      }
      constructor(props, context){
        super(props, context)
        this.store = props.store
      }
      render(){
        {this.props.children}
      }
    }
    
    export connect(mapStateToProps, mapDispatchToProps){
      return Component => {
        return class ConnectComponent extends React.Component{
          static contextType = {
            store: PropsTypes.object
          }
          constructor(props, context){
            this.state = {
              props: {}
            }
          }
          ComponentDidUpdate(){
            const {store} = this.context
            store.subscribe(()=>this.update())
            update()
          }
          update(){
            const {store} = this.context
            const mapState = mapDispatchToProps(store.getState())
            const mapDispatch = this.bindActionCreators(mapDispatchToProps, store.dispatch)
            this.setState({
             props: {...mapState,...mapDispatch}
           
           })
          }
          bindCreators(creators, dispatch){
           let ret = {}
           Object.keys(creators).forEach(key=>{
             ret[key] = ()=>dispatch(creators[key]())
           })
           return ret
          }
         render(){
           return <Component ...this.state.props></Component>
         }
       }
      }
    }