react进阶

2019-05-15  本文已影响0人  zxhnext

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

上一篇下一篇

猜你喜欢

热点阅读