美文网首页
react+webpack+react-router+redux

react+webpack+react-router+redux

作者: 殊一ONLY | 来源:发表于2018-01-09 22:17 被阅读0次
    (8) Hot Module Replacement

    从上可以发现,每当在js内修改内容,都需要重新编译。因此我们使用webpack的一个热替换插件Hot Module Replacement,当在编辑页面修改代码时,浏览器会自动刷新。使用功能如下:

    a. 配置

    在package.json 增加 --hot

    "dev": "webpack-dev-server --config webpack.dev.config.js --color --progress --hot"
    

    或者

    在webpack.dev.config.js中添加配置

    + const webpack = require('webpack');
    + devServer: { hot: true}
    + plugins:[ new webpack.HotModuleReplacementPlugin()]
    

    我们分别尝试一下上面的配置,并使用下面的重置state案例:
    修改Home.js,增加计数state

    
    import React, {Component} from 'react';
    
    export default class Home extends Component {
     constructor(props) {
     super(props);
     this.state = {
     count: 0
     }
     }
     _handleClick() {
     this.setState({
     count: ++this.state.count
     })
     }
    
     render() {
     return (
     <div>
     This is Home<br/>
      当前计数:{this.state.count}<br/>
     <button onClick={() => this._handleClick()}>自增</button>
     </div>
     ) }
    }
    

    在src/index.js 增加module.hot.accept()。当模块更新的时候,通知index.js。

    import React from 'react';
    import ReactDom from 'react-dom';
    import getRouter from './router/router';
    
    if (module.hot) {
     module.hot.accept();
    }
    
    ReactDom.render(
     getRouter(), document.getElementById('app')
    );
    
    (9) react-hot-loader

    在第(8)节中,当文档内容发生变化时,所有的state会重新被初始化,如果想只刷新页面,但是state不改变这是就要使用react-hot-loader。其学习教程参见https://github.com/gaearon/react-hot-loader

    a.安装

    npm install --save react-hot-loader

    b.配置

    .babel文件,添加

    {
     "plugins": ["react-hot-loader/babel"]
    }
    

    Webpack.dev.config.js文件,入口添加react-hot-loader/patch

    entry: [
     'react-hot-loader/patch',
     path.join(__dirname, 'src/index.js')
     ]
    

    修改src/index.js

    import React from 'react';
    import ReactDom from 'react-dom';
    import {AppContainer} from 'react-hot-loader';
    import getRouter from './router/router';
    /*初始化*/
    // 如果没该步骤,页面会出现空白
    renderWithHotReload(getRouter());
    /*热更新*/
    if (module.hot) {
     module.hot.accept('./router/router.js', () => {
    const getRouter = require('./router/router.js').default;
     renderWithHotReload(getRouter());
     });
    }
    
    function renderWithHotReload(RootElement) {
     ReactDom.render(
     <AppContainer>
     {RootElement}
     </AppContainer>,
     document.getElementById('app')
     )
    }
    

    运行npm ryun start查看效果

    (10)webpack配置文件路径优化

    在引用Home.js时需要使用相对路径,import Home from '../pages/Home/Home';
    然而,在(4).e中提到可以使用webpack配置中的alia,为’../pages’提供别名,如下所示:
    webpack.dev.config.js

     resolve: {
     alias: {
     pages: path.join(__dirname, 'src/pages'),
     component: path.join(__dirname, 'src/component'),
     router: path.join(__dirname, 'src/router')
     }
     }
    

    把之前的相对路径
    import Home from '../pages/Home/Home';改为import Home from 'pages/Home/Home';(

    (11)redux

    通过redux对state管理实现自增、自减和重置。
    想说一说redux中重要的三个概念,其余内容将在redux专题中讲解。

    ① action
    Action 是把数据从应用(这些数据有可能是服务器响应,用户输入或其它非 view 的数据 )传到 store 的有效载荷。它是 store 数据的唯一来源。一般来说你会通过 store.dispatch() 将 action 传到 store。表示发生了什么
    Action 本质上是JavaScript 普通对象。我们约定,action 内必须使用一个字符串类型的 type 字段来表示将要执行的动作。多数情况下,type 会被定义成字符串常量。当应用规模越来越大时,建议使用单独的模块或文件来存放 action。如todo任务:

    const ADD_TODO = 'ADD_TODO'
    {
     type: ADD_TODO,
     text: 'Build my first Redux app'
    }
    

    除了type 字段外,action 对象的结构完全由你自己决定
    Action 创建函数就是生成 action 的方法。
    Action 创建函数也可以是异步非纯函数,我们将在redux专题中详细介绍。
    ② reducer
    Reducers 指定了应用状态的变化如何响应 actions 并发送到 store 的,记住 actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新 state。所以,reducer就是纯函数,接收state 和 action,然后返回一个新的 state。
    后面会详细介绍多个reducer组合使用。
    ③ store
    前面使用 action 来描述“发生了什么”,和使用 reducers 来根据 action 更新 state 的用法。
    Store 就是把它们联系到一起的对象。Store 有以下职责:
    **
    维持应用的 state;
    提供 getState() 方法获取 state;
    提供 dispatch(action) 方法更新 state;
    通过 subscribe(listener) 注册监听器;
    通过 subscribe(listener) 返回的函数注销监听器
    **
    再次强调一下 Redux 应用只有一个单一的 store。当需要拆分数据处理逻辑时,你应该使用 reducer 组合 而不是创建多个 store。
    将多个reducer合并进行后,并导入到redux/store.js,并传递 createStore()。

    import { createStore } from 'redux'
    import todoApp from './reducers'
    let store = createStore(todoApp)
    

    createStore() 的第二个参数是可选的, 用于设置 state 初始状态。这对开发同构应用时非常有用,服务器端 redux 应用的 state 结构可以与客户端保持一致, 那么客户端可以将从网络接收到的服务端 state 直接用于本地数据初始化。

    let store = createStore(todoApp, window.STATE_FROM_SERVER)
    

    在test.js中发起 Actions,

    //Import action
    //注册监听
    const unsubscribe = store.subscribe();
    //发起action
    store.dispatch(…action);
    //停止监听
    Unsubscribe();
    

    ④数据流
    Redux是严格的单向数据流,也就是说,所有的数据遵循相同的生命周期,这么做的优势是可以让应用的数据变化更加简单。当组件发生变化时,只需要关注dispatch它的store,并不需要关心其他组件中的state是否发生变化。
    同时也鼓励做数据范式化(范式化?),这样可以避免使用多个且独立的无法相互引用的重复数据。什么意思

    i.调用 store.dispatch(action)
    Action 就是一个描述“发生了什么”的普通对象。比如:

     { type: 'LIKE_ARTICLE', articleId: 42 };
     { type: 'FETCH_USER_SUCCESS', response: { id: 3, name: 'Mary' } };
     { type: 'ADD_TODO', text: 'Read the Redux docs.'};
    

    可以把 action 理解成新闻的摘要。如“玛丽喜欢42号文章。”或者“任务列表里添加了'学习 Redux 文档'”。

    你可以在任何地方调用 store.dispatch(action),包括组件中、XHR 回调中、甚至定时器中。
    ii. Redux store 调用传入的 reducer 函数。
    Store 会把两个参数传入 reducer: 当前的 state 树和 action。例如,在这个 todo 应用中,根 reducer 可能接收这样的数据:

    // 当前应用的 state(todos 列表和选中的过滤器)
     let previousState = {
     visibleTodoFilter: 'SHOW_ALL',
     todos: [
     {
     text: 'Read the docs.',
     complete: false
     }
     ]
     }
    // 将要执行的 action(添加一个 todo)
     let action = {
     type: 'ADD_TODO',
     text: 'Understand the flow.'
     }
    // reducer 返回处理后的应用状态
     let nextState = todoApp(previousState, action);
    

    iii. 根 reducer 应该把多个子 reducer 输出合并成一个单一的 state 树。
    根 reducer 的结构完全由你决定。Redux 原生提供combineReducers()辅助函数,来把根 reducer 拆分成多个函数,用于分别处理 state 树的一个分支。
    当你触发 action 后,combineReducers 返回的 todoApp 会负责调用两个 reducer:

     let nextTodos = todos(state.todos, action);
     let nextVisibleTodoFilter = visibleTodoFilter(state.visibleTodoFilter, action);
    

    然后会把两个结果集合并成一个 state 树:

     return {
     todos: nextTodos,
     visibleTodoFilter: nextVisibleTodoFilter
     };
    

    iv. Redux store 保存了根 reducer 返回的完整 state 树。
    这个新的树就是应用的下一个 state!所有订阅 store.subscribe(listener) 的监听器都将被调用;监听器里可以调用 store.getState() 获得当前 state。
    现在,可以应用新的 state 来更新 UI。如果你使用了 React Redux 这类的绑定库,这时就应该调用 component.setState(newState) 来更新。

    a.安装redux

    npm install –save redux
    b. 初始化目录结构
    cd src
    mkdir redux
    cd redux
    mkdir actions
    mkdir reducers
    touch reducers.js
    touch store.js
    touch actions/counter.js
    touch reducers/counter.js

    c. action 创建函数
    export const INCREMENT = "counter/INCREMENT";
    export const DECREMENT = "counter/DECREMENT";
    export const RESET = "counter/RESET";
    export function increment() {
     return {type: INCREMENT}
    }
    export function decrement() {
     return {type: DECREMENT}
    }
    export function reset() {
     return {type: RESET}
    }
    
    d. reducer函数

    reducer是一个纯函数,接收action和旧的state,生成新的state。

    import {INCREMENT, DECREMENT, RESET} from '../action/counter';
    /*初始化state*/
    const iniState = {
     count: 0
    };
    
    /*reducer*/
    export default function reducer(state = initState, action) {
     switch(action.type) {
     case INCREMENT:
     return {
     count: state.count + 1
     };
     case DECREMENT:
     return {
     count: state.count - 1
     };
     case RESET:
     return {count: 0};
     default:
     return state
     }
    }
    
    

    在src/redux/reducers.js中把多个reducer整合起来

    import counter from './reducers/counter';
    export default function combineReducers(state={}, action){
     return {
     counter: counter(state.counter, action)
     }
    }
    

    无论是combineReducers函数也好,还是reducer函数也好,都是接收state和action,
    返回更新后的state。区别就是combineReducers函数是处理整棵树,reducer函数是处理树的某一点。

    e. 创建store

    将reducer与action联系到一起。

    import {createStore} from 'redux';
    import combineReducers from './reducers.js';
    let store = createStore(combineReducers);
    export default store;
    

    f. 测试redux
    创建src/redux/testRedux.js
    添加内容

    import {increment, decrement, reset} from './action/counter';
    import store from './store';
    // 打印初始状态
    console.log(store.getState());
    // 每次state更新时,打印日志
    // 注意subscribe()返回一个函数来注销监听器
    let unsubscribe = store.subscribe(() =>
     console.log(store.getState())
     );
    // 发起一系列action
    store.dispatch(increment());
    store.dispatch(decrement());
    store.dispatch(reset());
    // 停止监听state状态
    unsubscribe();
    

    在当前文件夹执行命令
    webpack testRedux.js build.js
    node build.js
    查看输出结果:


    1.png

    相关文章

      网友评论

          本文标题:react+webpack+react-router+redux

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