美文网首页
react进阶

react进阶

作者: zxhnext | 来源:发表于2019-05-15 12:14 被阅读0次

    1. 虚拟dom

    来看下对比:


    image image

    react native可以开发原生应用的原因是因为虚拟dom,在浏览器中虚拟dom可以生成网页,在原生应用中可以生成其他组件
    虚拟dom比对,diff算法,同级比对

    image

    当最上层的节点就不一致时,react就不会往下比对了,直接删掉该节点下的所有dom,直接重新生成

    image

    给每个节点加key值比较就简单多了,不用像左图一样再去循环比对

    2. 测试

    通用测试框架 jest:https://jestjs.io/docs/zh-Hans/getting-started

    react官方推荐:https://airbnb.io/enzyme/
    首先安装安装

    npm install enzyme-adapter-react-16 enzyme --save-dev
    

    在package.json中加上test测试命令:

    "test": "react-scripts test --env=jsdom"
    

    具体用法可参考文档

    3. PropTypes类型检查

    proptypes类型检查工具:https://react.docschina.org/docs/typechecking-with-proptypes.html
    简单的demo

    Home.propTypes = {
      data: PropTypes.object.isRequired,
      actions: PropTypes.object.isRequired,
      history: PropTypes.object,
    }
    
    

    4. 引入antd组件并实现按需加载

    安装ui组件: antd,安装babel-plugin-import可以实现antd按需加载

    配置babal-plugin-import: https://blog.csdn.net/u010565037/article/details/86154544

    5. react router

    目前使用的是react-router-dom
    参考文档:http://react-china.org/t/react-router4/15843

    详细使用方法:https://www.jianshu.com/p/97e4af32811a

    image image image image
    image image image image

    React-异步组件withRouter用法:
    https://www.cnblogs.com/superlizhao/p/9497021.html
    withRouter作用:
    https://www.cnblogs.com/luowenshuai/p/9526341.html

    6. createContext

    Context 通过组件树提供了一个传递数据的方法,从而避免了在每一个层级手动的传递 props 属性。
    参考文档:https://react.docschina.org/docs/context.html

    我们首先建一个AppContext.js文件

    import React from 'react'
    export const AppContext = React.createContext()
    

    然后我们建一个createContext的高阶组件WithContext.js

    import React from 'react'
    import { AppContext } from './AppContext'
    
    const withContext = (Component) => {
      return (props) => (
        <AppContext.Consumer>
          {({ state, actions }) => {
            return <Component {...props} data={state} actions={actions} />
          }}
        </AppContext.Consumer>
      )
    }
    
    export default withContext
    

    在app.js中注册

    <AppContext.Provider value={{
            state: this.state,
            actions: this.actions,
          }}>
          <Router>
            <div className="App">
              <div className="container pb-5">
                <Route path="/" exact component={Home} />
                <Route path="/create" component={Create} />
                <Route path="/edit/:id" component={Create} />
              </div>
            </div>
          </Router>
    </AppContext.Provider>
    

    7. redux

    参考文档:https://www.redux.org.cn/
    安装react-redux(作用是将顶层组件包裹在Provider组件之中,这样的话,所有组件就都可以在react-redux的控制之下了,但是store必须作为参数放到Provider组件中去)
    详情参考:http://cn.redux.js.org/docs/react-redux/

    react与react-redux: https://www.cnblogs.com/bax-life/p/8440326.html

    来看一个简单的图例:


    image

    首先我们需要安装redux与react-redux

    yarn add redux react-redux --save
    

    redux的集成:
    创建action模块
    创建reducer模块
    创建store模块
    通过connect方法将react组件和redux连接起来
    添加provider作为项目根组件,用于数据存储

    redux调试工具安装

    1. 在chrome中安装Redux Devtools扩展
    2. 在项目中安装redux-devtools-extension

    来看一个简单的redux例子:
    新建store文件:创建index.js与reducer.js
    reducer.js

    const defaultState = {
        inputValue: '',
        list: []
    }
    
    export default (state = defaultState, action) => {
        if(action.type === "change_input_value") {
            const newState = JSON.parse(JSON.stringify(state));
            newState.inputValue = action.value;
            return newState;
        }
        if(action.type === "add_item") {
            const newState = JSON.parse(JSON.stringify(state));
            newState.list.push(newState.inputValue);
            newState.inputValue = '';
            return newState;
        }
        return state;
    }
    

    index.js

    import { createStore } from 'redux';
    import reducer from './reducer';
    
    const store = createStore(reducer);
    
    export default store;
    

    app.js

    import { Provider } from 'react-redux';
    import store from './store';
    const App = (
        <Provider store={store}>
            <TodoList />
        </Provider>
    )
    

    组件todolist.js

    import React from 'react';
    import { connect } from 'react-redux';
    
    const TodoList = (props) => {
        const { inputValue, changeInputValue, handleClick, list } = props;
        return (
            <div>
              
            </div>  
        )
    }
    
    const mapStateToProps = (state) => {
        return {
            inputValue: state.inputValue,
            list: state.list
        }
    }
    
    // store.dispatch props
    const mapDispatchToProps = (dispatch) => {
        return {
            changeInputValue(e) {
                const action = {
                    type: "change_input_value",
                    value: e.target.value
                }
                dispatch(action);
            },
            handleClick() {
                const action = {
                    type: 'add_item'
                }
                dispatch(action);
            }
        }
    }
    // connect执行返回结果是一个容器组件
    export default connect(mapStateToProps, mapDispatchToProps)(TodoList);
    

    redux = reducer + flux
    来看下图:


    image

    修改store组件->action->reducer->store

    redux中间件:


    image

    注意事项:

    1. store是唯一的
    2. 只有store能够改变自己的内容
    3. reducer必须是纯函数
    4. reducer并没有改变store的内容,他生成了深拷贝,然后将值又返回给了store

    开发中写法:
    新建store文件夹,新建index.js与reducer.js文件
    reducer.js

    // 可以在每个组件中设置reducer,然后在总的reducer中引入它们
    import { combineReducers } from 'redux-immutable'; // 创建combineReducers与Immutable.js状态一起使用的Redux的等效函数
    import { reducer as headerReducer } from '../common/header/store';
    import { reducer as homeReducer } from '../pages/home/store/';
    import { reducer as detailReducer } from '../pages/detail/store/';
    import { reducer as loginReducer } from '../pages/login/store/';
    const redcuer = combineReducers({
        header: headerReducer,
        home: homeReducer,
        detail: detailReducer,
        login: loginReducer
    });
    
    export default redcuer;
    

    index.js

    // redux-devtools-extension 可视化工具
    import { createStore, compose, applyMiddleware } from 'redux';
    import thunk from 'redux-thunk'; // 实现ajax异步请求,使得支持action中为一个异步请求函数
    import reducer from './reducer';
    const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
    
    const store = createStore(reducer, composeEnhancers(
        applyMiddleware(thunk)
    ));
    
    export default store;
    

    这里以home中的为例,来看一个完整的过程
    home文件夹store中的index.js

    import reducer from './reducer';
    import * as actionCreators from './actionCreators';
    import * as actionTypes from './actionTypes';
    export { reducer, actionCreators, actionTypes };
    

    reducer.js

    import { fromJS } from 'immutable';
    import * as actionTypes from './actionTypes';
    // immutable为了防止不小心改变state,所以用immutable(不可改变的数据)
    
    const defaultState = fromJS({
        topicList: [],
        articleList: [],
        recommendList: [],
        articlePage: 1,
        showScroll: false
    });
    
    const changeHomeData = (state, action) => {
        return state.merge({
            topicList: action.topicList,
            articleList: action.articleList,
            recommendList: action.recommendList
        });
    }
    
    const addHomeList = (state, action) => {
        return state.merge({
            'articlePage': action.nextPage,
            'articleList': state.get('articleList').concat(action.list)
        });
    }
    
    export default (state = defaultState, action) => {
        switch(action.type) {
            case actionTypes.CHANGE_HOME_DATA:
                return changeHomeData(state, action);
            case actionTypes.ADD_HOME_LIST:
                return addHomeList(state, action);
            case actionTypes.TOGGLE_TOP_SHOW:
                return state.set('showScroll', action.show)
            default:
                return state;
        }
    }
    

    actionCreators.js

    import * as actionTypes from './actionTypes';
    import { fromJS } from 'immutable';
    import axios from 'axios';
    
    const addHomeList = (list, nextPage) => ({
        type: actionTypes.ADD_HOME_LIST,
        list: fromJS(list), // List方法,将普通数组转换为immutable数组
        nextPage
    })
    
    export const getMoreList = (page) => {
        return (dispatch) => {
            axios.get('/api/homeList.json?page='+page)
            .then((res) => {
                const result = res.data.data;
                dispatch(addHomeList(result, page + 1));
            })
        }
        
    }
    
    export const toggleTopShow = (show) => ({
        type: actionTypes.TOGGLE_TOP_SHOW,
        show
    })
    

    actionTypes.js

    export const CHANGE_HOME_DATA = 'header/CHANGE_HOME_DATA';
    export const ADD_HOME_LIST = 'header/ADD_HOME_LIST';
    export const TOGGLE_TOP_SHOW = 'header/actionTypes.TOGGLE_TOP_SHOW';
    

    组件index.js

    class Home extends PureComponent { // PureComponent底层实现了shouldComponentUpdate,以防止state某些数据更新时所有页面都要更新, PureComponent最好用immutable,否则就不要用,用shouldComponentUpdate即可
    }
    const mapState = (state) => ({
        showScroll: state.getIn(['home', 'showScroll'])
    })
    
    const mapDispatch = (dispatch) => ({
        changeHomeData() {
            dispatch(actionCreators.getHomeInfo());
        },
        changeScrollTopShow() {
            if (document.documentElement.scrollTop > 100) {
                dispatch(actionCreators.toggleTopShow(true))
            } else {
                dispatch(actionCreators.toggleTopShow(false))
            }
        }
    })
    
    export default connect(mapState, mapDispatch)(Home);
    

    8. React.Children的用法

    https://blog.csdn.net/uuihoo/article/details/79710318

    相关文章

      网友评论

          本文标题:react进阶

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