当前位置: 首页 > 工具软件 > react-assign > 使用案例 >

react学习笔记 react-router-dom react-redux基础使用及手写基础源码 组件反射 react原理

邵崇凛
2023-12-01

vdom diff

  • 高效的diff算法 新老vdom树比较
  • 更新只需要更新节点
  • 数据变化检测 batch dom读写

组件多重继承

//parent components
export default class Parent extends Component {
    render(){
        return <div>
            Parent
            {this._render()}
        </div>
    }
}
//children component
export default class Children extends Parent {
    _render(){
        return <div>
            Children
        </div>
    }
}
//最终结果
<div>
    Parent
    <div>
    Children
    </div>
</div>

HOC(高阶组件)

高阶组件是参数为组件,返回值为新组件的函数。

//HOC
import React, { Component } from 'react'

export function ho(Comp) {
    return class extends Component {
        render(){
            return <div className={['item', Comp.classify]}>
                <Comp />
                我被高阶组件包裹
            </div>
        }
    }
}
//调用高阶组件
import React, { PureComponent } from 'react'
import {ho} from './ho'

class List extends PureComponent {
    // 通过给组件加静态属性, 可以调用高阶组件时自动生成类名
    static classify = 'list'
    render() {
        return (
            <div>
                多图组件
            </div>
        )
    }
}

export default ho(List)

柯里化函数组件

//hoc
import React, { Component } from 'react'

export function ho(Comp) {
    return function(clickAble = true){
        return class extends Component {
            render(){
                return <div onClick={clickAble ? this.props.onClick : ()=>{}} className="comp">
                    <Comp />
                    我被高阶组件包裹
                </div>
            }
        }
    }
}
// 调用包装组件
//该组件无法点击
import React, { PureComponent } from 'react'
import {ho} from './ho'

//也可以用装饰器
// @ho(false)
class List extends PureComponent {
    render() {
        return (
            <div>
                单图组件
            </div>
        )
    }
}

export default ho(List)(false)

异步组件

异步组件本质是promise 最后return resolve(component)

const List = React.lazy( () => import('./components/List'))

export class extends React.components {
    render(){
        return <React.Fragment>
            <React.Suspense fallback={<div>loading...</div>}>
            <List />
            </React.Suspense>
         </React.Fragment>
    }
}

context

//context.js
export default React.createContext({
    defVal: 'xxx'
})
//parent.jsx
import context from './context.js'
import Children from './Children'

export default class extends React.Component {
    render(){
        return <div>
            <React.Provider value={['tom', 'jack']}>
                <Children  />
            </ React.Provider>
        </ div>
    }
}
//childred.jsx
import tabContext from './context.js'

export default class extends React.Component {
    //将需要使用conetxt的组件内声明static属性 context
    static context = tabContext

    render(){
        return <React.Fragment>
        //使用context值
            {this.context.xxxx}
         </ React.Fragment>
    }
}

hooks

  1. useState useEffect
import React, {useState} from 'react'

function FuncComponent(props) {
  let [price, setPrice] = useState(10)
    useEffect(()=>{
        //更新时调用
        return () => {
            //销毁调用
        }
    })
  return (<>
    <h3 onClick={() => setPrice(100)}> {price} </h3>
  </>)
}

export default class App extends React.Component {
    render(){
        return <FuncComponent>
    }
}

redux react-redux 数据流

redux底层state更新后直接替换currentState对象 subscribe订阅会将回调插入listers数组 dispatch时先执行reducer 然后遍历listers数组执行函数
redux响应式更新数图 订阅action
react-redux 是用context.Provider实现所有后代共享数据

// store.js
import { createStore, applyMiddleware } from 'redux'
const listProcessor = (state, action) => {
    console.log('state-action', state, action)
    switch (action.type) {
        case 'TEST':
            return {
                name: 'tom'
            }
        case 'CHANGE_NAME':
            return {
                ...state,
                name: action.name
            }
    
        default:
            break;
    }
    return state
}
//next是dispatch 三层函数 redux源码applyMiddleware(createStore)()
//applyMiddleware 是在dispatch执行前执行middleware钩子 用户dispatch的是前置dispatch 真正的dispatch会作为next函数传到第二层柯里化函数
//next 和 dispatch 不是一个函数 next是最终执行reducer(同步)
// redux-thunk
let thunk = ({dispatch, getState}) => next => action => {
    if( typeof action === 'function' ){
        // 如果action不是对象是函数, 就将next(真正的dispatch)交给用户写的action函数
        //传dispatch是防止多异步
        return action(dispatch, getState)
    }
    //对象执行同步操作
    return next(action)
}

const store = createStore(listProcessor, applyMiddleware(thunk))

store.dispatch({
    type: 'TEST'
})

console.log(store.getState())

// index.js
import {Provider} from 'react-redux'
import App from './App'
import store from './store/js'

ReactDOM.render(
    <Provider store={store}>
    <App />
    </ Provider>
)

//Tab.jsx
class Tab extends React.Component {
    // connect函数会将需要的state注入到props.state中,同时dispatch也会被注入 props.dispatch, state更新会自动更新引用state的组件render
}

export default connect( state => {
    // 过滤state
    return {}
}, dispatch => {
    // 预写dispatch函数
    return {
        getData(){
            dispatch({
                type: 'XXX'
            })
        }
    }
}, combin => {
    // 合并state
    return {
    }
})(Tab)

使用状态来反射组件

this.state = {
    status: 'loadMore
}
render() {
    const mapStatus = {
        error: <Error />,
        loading: <Loading />,
        loadMore: <LoadMore />
    }

    const Component = mapStatus[this.state.status]
    return <Component>
}

react-redux源码demo

/**
 * @file custom react-redux
 * @author karry
 */
import React from 'react'

const ReduxContext = React.createContext(null)
//provider
export class Provider extends React.Component {

    render() {
        const {store} = this.props
        const {children} = this.props
        // 穿件组件根节点provider 将store挂载到context
        return <ReduxContext.Provider value={store}>
            {children}
        </ ReduxContext.Provider>
    }
}

//connect连接函数
export function connect(mapStateToProps, mapDispatchToProps) {

    return function (ConnectComponent) {

        return class extends React.Component {
            constructor() {
                super()
                this.state = {
                    mergedProps: null
                }
            }

            static contextType = ReduxContext

            componentDidMount() {
                let store = this.context
            //监听dispatch, 计算出心得state 刷新视图
                store.subscribe( () => {
                    console.log('state change', store.getState())
                    let mergedProps = this.computeProps(store)
                   if(mergedProps !== this.state.mergedProps) {
                        this.setState({
                        mergedProps
                    })
                   }
                })
            }
        //计算state返回新的state
            computeProps(store) {
                let stateStore = mapStateToProps(store.getState())
                let eventProps = mapDispatchToProps(store.dispatch)
                return {
                    ...stateStore,
                    ...eventProps,
                    dispatch: store.dispatch
                }
            }

            render() {
                console.log('context', this.context)
                let mergedProps = this.state.mergedProps || this.computeProps(this.context)
                return  <ConnectComponent {...mergedProps} />
            }
        }
    }
}

react-router-dom

相同路由让组件刷新connect外套witchRouter

  • api
    1. history.push/back/replace
    2. match.params xxx
    3. match.query
import {BrowserRouter, Route, Switch} from 'react-router-dom'

const AppContainer = () => {
  return <BrowserRouter>
  <Switch>
  <Route path="/" exact component={App}></Route>
  <Route path="/detail" render={ props => {
    return ( 
      <div>
   <React.Suspense fallback={<div>loading...</div>}>
      <Detail />
    </React.Suspense>
      </div>
    )
  }}></Route>
  </Switch>
  </BrowserRouter>
}

ReactDOM.render(
  <Provider store={store}>
    <AppContainer />
  </Provider>,
  document.getElementById('root')
);

react-router-dom源码(demo) 路由

/**
 * @file react-router-dom demo
 * @author karry
 */

 import React, {Component} from 'react'

 /**
  * @param {string} path location url
  * @param {object} state 透传的state对象
  */
 //解析url
function replaceLocation(path, state) {
    let pathInfo = /^([^\?]*?)(\?[^#]*?)?(\#.*?)?$/.exec(path)
    return {
        pathname: pathInfo[1],
        search: pathInfo[2],
        hash: pathInfo[3],
        state
    }
}

function createContext() {
    return React.createContext(null)
}

// 事件队列
let eventEmitter = {
    listener: [],
    notify(...args) {
        this.listener.forEach( func => {
            func(...args)
        })
    },
    listen( func) {
        this.listener.push(func)
    }
}

//监听路由变化 路由跳转
function createBrowserHistory() {

    function listen(func) {
        eventEmitter.listen(func)
    }

    const domListen = func => {
        window.addEventListener('popstate', func)
    }

    domListen( (event) => {
        let action = 'POP'
        let location = getDomLocation(event.state)
        setState({
            location,
            action
        })
    })

    //路由跳转
    function push(path, state) {
        let location = replaceLocation(path, state)
        let action = 'POP'
        window.history.pushState({
            state
        }, null, path)
        setState({
            location,
            action
        })
    }

    //获取最新history 触发事件队列监听
    const setState = (nextState) => {
        let history = window.history
        console.log(history)
        Object.assign(history, nextState)
        eventEmitter.notify(history)
    }
    
    return {
        push,
        listen
    }
}

//获取location.history path search hash
function getDomLocation(state) {
    let window$location = window.location
    let pathname = window$location.pathname
    let search = window$location.search
    let path = window$location.hash
    // console.log('getDomLocation', createBrowserHistory(`${pathname}${search}${path}` , state))
    // return createBrowserHistory(`${pathname}${search}${path}` , state)
    return  window$location
}

let RouterContext = createContext()

export class Router extends Component {
    constructor(props) {
        super(props)

        this.state = {
            action: '',
            location: getDomLocation()
        }
        // 监听路由变化更新组件
        props.history.listen( ({action, location}) => {
        console.log('Router!!!! 更新', props.history)
            this.setState({
                action,
                location
            })
        })
    }

    render() {
        let contextValue = {
            location: this.state.location,
            history: this.props.history
        }
        // 将根节点路由值注入provider
        return <RouterContext.Provider value={contextValue}>
            {this.props.children}
        </RouterContext.Provider>
    }
}

// 初始化路由信息
 export class BrowserRouter extends Component{
    constructor(props) {
        super()
    
        this.history = createBrowserHistory()
    }

    render() {
        console.log("Browser", this.props.children)
        return <Router history={this.history}>
            {this.props.children}
            </Router>
    }
 }

function matcher(pathname, location) {
    return (new RegExp(pathname)).exec(location.pathname)
}

 export class Route extends Component {
    static contextType = RouterContext

    render() {
        console.log('Route  render!', this.props, this.context)
        let match = matcher(this.props.path, this.context.location)
        let DynamicComponent = this.props.component
        console.log(match)
        return <React.Fragment>
            {match ? <DynamicComponent {...this.context} /> : null}
        </ React.Fragment>
    }
 }

 export class Link extends Component {
     static contextType = RouterContext

    render() {
        return <a onClick={this.skip.bind(this)}>
            {this.props.children}
        </a>
    }

    skip() {
        this.context.history.push(this.props.to)
    }
 }

 export class Switch extends Component {

 }

react原理

  1. 渲染过程
  • react.createElement
  • 创建createElement对象(vdom)
  • reactDom.render => 真实dom
  1. vdom组成
  • type
  • key
  • ref
  • props (children, state, style, event …)
  • owner
  • self
  • _source
  1. 比较策略
  • tree同层比较
  • component比较
  • element比较
  1. 更新
  • 跨层 重新渲染跨层组件
  • 删除 不渲染
  • 新增 渲染
  • 排序 没key全渲染 有key交换位置
  1. 性能
  • 组件解构稳定
  • 减少排序
  • shouldComponentUpdate控制更新
  1. react批更新 time slice 时间切片
  • 在一帧(16ms)内收集收据更新操作,同一批处理,减少页面渲染次数 requestIdleCallback游览器闲置时执行回调操作
 类似资料: