zoukankan      html  css  js  c++  java
  • 简书react开发项目总结

    react开发简书用到技术有路由、react-redux、immutable库,样式采用styled-components,在App中的代码如下:

    import React from 'react';
    import { GlobalStyle } from './styled';
    import { Font } from './static/iconfont/iconfont'
    import Header from './common/header'
    import { Provider } from 'react-redux'
    import { BrowserRouter,Route } from 'react-router-dom'
    import Home from './pages/home'
    import Detail from './pages/detail/loadable'
    import Login from './pages/login'
    import Write from './pages/write'

    import store from './store'
    function App() {
    return (
    <div>
    <Provider store = {store}>

    <BrowserRouter>
    <div>
    <Header ></Header>
    <Route path = '/' exact component={Home}></Route>
    <Route path = '/login' exact component={Login}></Route>
    <Route path = '/write' exact component={Write}></Route>
    <Route path = '/detail/:id' exact component={Detail}></Route>
    </div>
    </BrowserRouter>
    </Provider>
    <Font></Font>
    <GlobalStyle></GlobalStyle>
    </div>
    );
    }

    export default App;
    Provider将页面全部包裹起来,引入redux建立的store,在store文件夹中有index.js文件和reducer文件;如下图所示结构:

    在index.js文件中代码如下:

    import { createStore,applyMiddleware } from 'redux'
    import reducer from './reducer'
    import thunk from 'redux-thunk'

    const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
    const enhancer = composeEnhancers(
    applyMiddleware(thunk),
    );
    const store = createStore(reducer, enhancer);
    export default store;

    redux提供了
    createStore,applyMiddleware两个方法,createStore负责创建store这个store管理唯一数据源,至于我们担心数据过于庞大的问题,
    combineReducers正是用来解决这个问题的,reducer.js文件中代码如下:
    //import { combineReducers } from 'redux'
    import { combineReducers } from 'redux-immutable'
    //import headerReducer from '../common/header/store/reducer'
    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 reducer = combineReducers({
    header:headerReducer,
    home:homeReducer,
    detail:detailReducer,
    login:loginReducer
    })
    export default reducer
    在开发的简书页面上有头部、home、详情部分、登录部分,每个部分都有自己reducer,没部分的reducer由
    combineReducers结合起来,各部分的reducer处理
    各自部分的代码逻辑。
    以head头部为例结构如下图:
    在head部分的store文件夹中有actionType.js文件action.Creaters.js文件、reducer.js文件、index.js文件,actonType相当于事件类型,如下代码:
    export const SEARCH_FOCUS = 'header/search_focus';
    export const SARCH_BLUR = 'header/search_blur';
    export const CHANGE_LIST = 'header/change_list';
    export const MOUSE_ENTER = 'header/mouse_enter'
    export const MOUSE_LEAVE = 'header/mouse_leave';
    export const change_Page = 'header/chang_page'
    这里以前是字符串,这里用变量方便调错,如果报错能找到是哪个事件出问题了。
    action.Creaters.js代码如下:
    import * as ationType from './actionType'
    import axios from 'axios'
    import {fromJS} from "immutable";
    const getChangeList = (data) =>({
    type:ationType.CHANGE_LIST,
    data:fromJS(data),
    totalPage: Math.ceil(data.length/10)
    });

    export const SEARCH_FOCUS = () =>({
    type:ationType.SEARCH_FOCUS
    });

    export const SEARCH_BLUR = () =>({
    type:ationType.SARCH_BLUR
    });
    export const mouseEnter = () =>({
    type:ationType.MOUSE_ENTER
    });
    export const mouseLeave = () =>({
    type:ationType.MOUSE_LEAVE
    });
    export const changePageList = (page) =>({
    type:ationType.change_Page,
    page
    })
    export const getList = () =>{
    return (dispatch) =>{
    axios.get('/api/headerList.json').then((res) =>{
    const data = res.data.data;
    dispatch(getChangeList(data))
    }).catch(() =>{
    console.log('err')
    })
    }
    }
     redux-thunk引入redux-thunk最重要的思想,就是可以接受一个返回函数的action creator。如果这个action creator 返回的是一个函数,就执行它,如果不是
    ,就按照原来的next(action)执行。例如:在简书开发项目中ajax请求数据就用了这个用处代码如下:
    const getChangeList = (data) =>({
    type:ationType.CHANGE_LIST,
    data:fromJS(data),
    totalPage: Math.ceil(data.length/10)
    });
    export const getList = () =>{
    return (dispatch) =>{
    axios.get('/api/headerList.json').then((res) =>{
    const data = res.data.data;
    dispatch(getChangeList(data))
    }).catch(() =>{
    console.log('err')
    })
    }
    }
    定义的getList方法返回一个ation给reducer,同时dispatch一个ation是getChangeList()函数将ajax获取的数据接收过来给reducer,代码如下:
    import * as ationType from './actionType'
    import { fromJS } from 'immutable'
    const defaultState = fromJS(
    {
    focused:false,
    mouseIn:false,
    list:[],
    page:1,
    totalPage:1
    }
    )
    export default (state = defaultState,action) =>{
    switch (action.type)
    { case ationType.CHANGE_LIST:
    return state.merge({
    'list':action.data,
    'totalPage':action.totalPage
    });
    case ationType.SEARCH_FOCUS:
    return state.set ('focused', true);
    case ationType.SARCH_BLUR:
    return state.set ('focused', false);

    //可以使用merge方法
    //state.set ('list', action.data).set('totalPage',action.totalPage);
    case ationType.MOUSE_ENTER:
    return state.set('mouseIn',true);
    case ationType.MOUSE_LEAVE:
    return state.set('mouseIn',false);
    case ationType.change_Page:
    return state.set('page',action.page);
    default:
    return state;
    }
     
    
    
    import * as ationType from './actionType'在这里需要引入相应的事件集合,在这里创建action函数把相应的action发送到reducer里,
    在reducer里进行变更store里面的状态,reducer.js代码如下:
    import * as ationType from './actionType'
    import { fromJS } from 'immutable'
    const defaultState = fromJS(
    {
    focused:false,
    mouseIn:false,
    list:[],
    page:1,
    totalPage:1
    }
    )
    export default (state = defaultState,action) =>{
    switch (action.type)
    {
    case ationType.SEARCH_FOCUS:
    return state.set ('focused', true);
    case ationType.SARCH_BLUR:
    return state.set ('focused', false);
    case ationType.CHANGE_LIST:
    return state.merge({
    'list':action.data,
    'totalPage':action.totalPage
    })
    //可以使用merge方法
    //state.set ('list', action.data).set('totalPage',action.totalPage);
    case ationType.MOUSE_ENTER:
    return state.set('mouseIn',true);
    case ationType.MOUSE_LEAVE:
    return state.set('mouseIn',false);
    case ationType.change_Page:
    return state.set('page',action.page);
    default:
    return state;

    }
    }
    在组件中发送getList()action代码如下:
    const mapDispatchToProps = (dispatch) =>{
    return {
    handleInputFocus (list) {
    // (list.size === 0) && dispatch(ationCreaters.getList());
    if(list.size<=0){
    dispatch(ationCreaters.getList());
    }
    }
    这样就可以经数据请求到并渲染到页面中。
     
    在reducer代码里引入immutable.js库。
    • Immutable Data 就是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象
    • Immutable 实现的原理是 Persistent Data Structure (持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变
    • 同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗, Immutable 使用了 Structural Sharing···· (结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。

    一个说明不可变的例子:

    // 原生对象
    let a1 = {
        b: 1,
        c: {
            c1: 123
        }
    };
    
    let b1 = a1;
    b1.b = 2;
    
    console.log(a1.b, b1.b); // 2, 2
    console.log(a1 === b1); // true
    console.log(a1.c === b1.c); // true
    
    // immutable.js 的Map
    let a2 = Immutable.fromJS({
        b: 1,
        c: {
            c1: 123
        }
    });
    
    let b2 = a2.set('b', 2);
    
    // 对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象
    console.log(a2.get('b'), b2.get('b')); // 1, 2  对象 a2 的 b 值并没有变成2。
    console.log(a2 === b2); //  false
    
    //如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。
    console.log(a2.get('c') === b2.get('c')); //true

    为什么要在React.js中使用Immutable

    • 它是一个完全独立的库,无论基于什么框架都可以用它。意义在于它弥补了 Javascript 没有不可变数据结构的问题
    • 由于是不可变的,可以放心的对对象进行任意操作。在 React 开发中,频繁操作state对象或是 store ,配合 immutableJS 快、安全、方便
    • 熟悉 React.js 的都应该知道, React.js 是一个 UI = f(states) 的框架,为了解决更新的问题, React.js 使用了 virtual dom , virtual dom 通过 diff 修改 dom ,来实现高效的 dom 更新。
    • 但是有一个问题。当 state 更新时,如果数据没变,你也会去做 virtual dom 的 diff ,这就产生了浪费。这种情况其实很常见
    • 当然你可能会说,你可以使用 PureRenderMixin 来解决呀, PureRenderMixin 是个好东西,我们可以用它来解决一部分的上述问题
    • 但 PureRenderMixin 只是简单的浅比较,不使用于多层比较。那怎么办?自己去做复杂比较的话,性能又会非常差
    • 方案就是使用 immutable.js 可以解决这个问题。因为每一次 state 更新只要有数据改变,那么 PureRenderMixin 可以立刻判断出数据改变,可以大大提升性能

    Immutable 优点

    • Immutable 降低了 Mutable 带来的复杂度

    可变( Mutable )数据耦合了 Time 和 Value 的概念,造成了数据很难被回溯

    • 节省内存

    Immutable.js 使用了 Structure Sharing 会尽量复用内存,甚至以前使用的对象也可以再次被复用。没有被引用的对象会被垃圾回收

    import { Map} from 'immutable';
    let a = Map({
      select: 'users',
      filter: Map({ name: 'Cam' })
    })
    let b = a.set('select', 'people');
    
    a === b; // false
    a.get('filter') === b.get('filter'); // true
    
    • Undo/Redo,Copy/Paste,甚至时间旅行这些功能做起来小菜一碟

    因为每次数据都是不一样的,只要把这些数据放到一个数组里储存起来,想回退到哪里就拿出对应数据即可,很容易开发出撤销重做这种功能。

    • 并发安全

    传统的并发非常难做,因为要处理各种数据不一致问题,因此『聪明人』发明了各种锁来解决。但使用了 Immutable 之后,数据天生是不可变的,并发锁就不需要了。

    • 拥抱函数式编程

    Immutable 本身就是函数式编程中的概念,纯函数式编程比面向对象更适用于前端开发。因为只要输入一致,输出必然一致,这样开发的组件更易于调试和组装。

     

    Immutable 的几种数据类型

    • List : 有序索引集,类似 JavaScript 中的 Array 。
    • Map : 无序索引集,类似 JavaScript 中的 Object 。
    • OrderedMap : 有序的 Map ,根据数据的 set() 进行排序。
    • Set : 没有重复值的集合。
    • OrderedSet : 有序的 Set ,根据数据的 add 进行排序。
    • Stack : 有序集合,支持使用 unshift() 和 shift() 添加和删除。
    • Range() : 返回一个 Seq.Indexed 类型的集合,这个方法有三个参数, start 表示开始值,默认值为 0 , end 表示结束值,默认为无穷大, step 代表每次增大的数值,默认为 1 .如果 start = end ,则返回空集合。
    • Repeat() : 返回一个 vSeq.Indexe 类型的集合,这个方法有两个参数, value 代表需要重复的值, times 代表要重复的次数,默认为无穷大。
    • Record : 一个用于生成 Record 实例的类。类似于 JavaScript 的 Object ,但是只接收特定字符串为 key ,具有默认值。
    • Seq : 序列,但是可能不能由具体的数据结构支持。
    • Collection : 是构建所有数据结构的基类,不可以直接构建

    上面那么多常用的也就是 List 和 Map

    几个重要的API

    1、fromJS()

    • fromJS() 是最最最常用的将原生 JS 数据转换为 ImmutableJS 数据的转换方法。使用方式类似于 JSON.parse() ,接收两个参数: json 数据和 reviver 函数
    • 在不传递 reviver 函数的情况下,默认将原生 JS 的 Array 转为 List , Object 转为 Map
    // 常见
    const t1 = Immutable.fromJS({a: {b: [10, 20, 30]}, c: 40});
    console.log(t1);
    
    // 不常用
    const t2 = Immutable.fromJS({a: {b: [10, 20, 30]}, c: 40}, function(key, value) {
        // 定制转换方式,下这种就是将Array转换为List,Object转换为Map
        const isIndexed = Immutable.Iterable.isIndexed(value);
        return isIndexed ? value.toList() : value.toOrderedMap();
        // true, "b", {b: [10, 20, 30]}
        // false, "a", {a: {b: [10, 20, 30]}, c: 40}
        // false, "", {"": {a: {b: [10, 20, 30]}, c: 40}}
    });
    console.log(t2);
    

    2、toJS()

    先来看官网的一段话: immutable 数据应该被当作值而不是对象,值是表示该事件在特定时刻的状态。这个原则对理解不可变数据的适当使用是最重要的。为了将 Immutable.js 数据视为值,就必须使用 Immutable.is() 函数或 .equals() 方法来确定值相等,而不是确定对象引用标识的 === 操作符

    • 所以 toJS() 就是用来对两个 immutable 对象进行值比较的。使用方式类似于 Object.is(obj1, obj2) ,接收两个参数
    const map1 = Immutable.Map({a:1, b:1, c:1});
    const map2 = Immutable.Map({a:1, b:1, c:1});
    
    // 两个不同的对象
    console.log(map1 === map2); // false
    // 进行值比较
    console.log(Immutable.is(map1, map2)); // true
    
    // 不仅仅只能比较ImmutableJS的类型的数据
    console.log(Immutable.is(undefined, undefined)); // true
    console.log(Immutable.is(null, undefined)); // false
    console.log(Immutable.is(null, null)); // true
    console.log(Immutable.is(NaN, NaN)); // true
    
    // 区别于 Object.is
    console.log(Object.is(0, -0) ,Immutable.is(-0, 0)); // false , true
    

    3、Map

    Map 数据类型,对应原生 Object 数组。最最常用的 数据结构之一,循环时无序( orderedMap 有序),对象的 key 可以是任意值。具体看下面的例子

    console.log(Map().set(List.of(1), 'list-of-one').get(List.of(1)));
    console.log(Map().set(NaN, 'NaN').get(NaN));
    console.log(Map().set(undefined, 'undefined').get(undefined));
    console.log(Map().set(null, 'null').get(null));
    
    • 简单介绍 OrderedMap

    OrderedMap 是 Map 的变体,它除了具有 Map 的特性外,还具有顺序性,当开发者遍历 OrderedMap 的实例时,遍历顺序为该实例中元素的声明、添加顺序。 OrderedMap 比非有序 Map更昂贵,并且可能消耗更多的内存。如果真要求遍历有序,请使用 List

    4、List

    List 数据类型,对应原生 Array 数组。和原生数组,最大区别不存在’空位’。 [, , , , ]

    console.log(List([,,,,]).toJS());// [undefined, undefined, undefined, undefined]
    

     

    
    
     
     
     
  • 相关阅读:
    Java多线程、并发
    Java I/O系统
    Java注解
    Arthas(Java诊断工具)
    Java泛型
    Java内部类
    libpcap使用
    python文件服务器
    设计模式
    protobuf
  • 原文地址:https://www.cnblogs.com/zhx119/p/10868927.html
Copyright © 2011-2022 走看看