zoukankan      html  css  js  c++  java
  • 24.redux

    Flux:Flux 是一种架构思想

    image

    https://facebook.github.io/flux/ 官网

    资料:
    http://www.ruanyifeng.com/blog/2016/01/flux.html


    Redux:

    Redux 由 Flux 演变而来,但受 Elm 的启发,避开了 Flux 的复杂性。 不管你有没有使用过它们,只需几分钟就能上手 Redux。

    Redux 是 JavaScript 状态容器,提供可预测化的状态管理。 (如果你需要一个 WordPress 框架,请查看 Redux Framework。)

    可以让你构建一致化的应用,运行于不同的环境(客户端、服务器、原生应用),并且易于测试。不仅于此,它还提供 超爽的开发体验,比如有一个时间旅行调试器可以编辑后实时预览。

    Redux 除了和 React 一起用外,还支持其它界面库。 它体小精悍(只有2kB,包括依赖)。
    image

    https://redux.js.org/ 官网
    http://www.redux.org.cn/ 中文

    资料:
    https://segmentfault.com/a/1190000011474522


    Vuex:

    view <------> store
     
     									
     								 ------------->actions
                     | disptach      |
    组件             |               |
    view   --------->|               |commit
    								 |               |
    								 | commit        ↓
    								-------------> mutations   -----> state  ----> View
    

    Flux:

    Flux 的最大特点,就是数据的"单向流动"。

    用户访问 View
    View 发出用户的 Action
    Dispatcher 收到 Action,要求 Store 进行相应的更新
    Store 更新后,发出一个"change"事件
    View 收到"change"事件后,更新页面


    Redux:

    安装

    npm install --save redux

    基本概念:

    vuex:store/state/commit/dispatch/getter/module

    redux:Action 、Reducer 、Store

    1、Store createStore

    const store = createStore(reducer);

    const {subscribe, dispatch, getState} = store;

    getState:获取你所有的state(数据)
    dispatch:分发--触发一个动作

    subscribe:订阅

    2、Reducer 用来做计算 ―― 产生一个新的状态 newState

    state + action ==> newState

    3、Action 描述 json

    格式:

    {   
        type:"动作"添加 删除 。。。 type是固定     
        payload:"数据"         
    }
    

    Redux:使用步骤

    1、先安装

    npm i -S redux

    2、引入
        import {createStore} from "redux";     ES6
    	const {createStore} require("redux");  ES5
    
    3、写一个计算函数―― reducer
    state+action => newState
    	 function reducer(state,action){
    	 	  switch(action.type){
    	 	  		case "xxx"
    	 	  		  return newState;
    	 	  		default:
    	 	  			return state;
    	 	  }
    	 		
    	 }
    
    4、创建store

    const store = createStore(reducer);

    5、操作store下的数据―― state

    展现:{store.getState()}

    修改:store.dispatch(action);

    const {createStore,bindActionCreators,combineReducers} = require("redux");

    bindActionCreators:
    1、action
    2、dispatch

    使用格式:

    const bindAction = bindActionCreators(fnAction,dispatch);

    bindAction();

    combineReducers:
    //合并reducer 只能有一个reducer

    const reducer = combineReducers({
    			reducer1,
    			reducer2,
    			......
    });
    

    Redux 应用只有一个单一的 store 只有一个单一reducer

    exp1:

    // import { createStore } from "redux";
    const { createStore } = require("redux");
    
    /**
     * 这是一个 reducer,形式为 (state, action) => newState 的纯函数。
     * 描述了 action 如何把 state 转变成下一个 state。
     *
     * state 的形式取决于你,可以是基本类型、数组、对象、
     * 甚至是 Immutable.js 生成的数据结构。惟一的要点是
     * 当 state 变化时需要返回全新的对象,而不是修改传入的参数。
     *
     * 下面例子使用 `switch` 语句和字符串来做判断,但你可以写帮助类(helper)
     * 根据不同的约定(如方法映射)来判断,只要适用你的项目即可。
     */
    function reducer(state = 10, action = {}) {
      switch (action.type) {
        case "INCREMENT":
            return state + 1;
        case "DECREMENT":
            return state - 1;
        default:
            return state;
        }
    }
    
    // 创建 Redux store 来存放应用的状态。
    // API 是 { subscribe, dispatch, getState }。
    let store = createStore(reducer);
    
    function log(){
        console.log("state:",store.getState());
    }
    
    // // 可以手动订阅更新,也可以事件绑定到视图层。
    store.subscribe(log);
    
    // log();
    store.dispatch({type:"INCREMENT"});
    // log();
    
    store.dispatch({type:"INCREMENT"});
    
    // log();
     
    
    // 改变内部 state 惟一方法是 dispatch 一个 action。
    // action 可以被序列化,用日记记录和储存下来,后期还可以以回放的方式执行
    

    res:
    image

    exp2:

    //1、引入
    const {createStore,bindActionCreators} = require("redux");
    
    //2、创建reducer
    
    //state数据初始化
    
    const initState = {
        count:0
    };
    
    const counterReducer = (state = initState,action = {})=>{
        //加减 plus/minus 自定义数据 custom
        switch(action.type){
            case "PLUS_ONE":
            return {count:state.count+1};
            case "MINUS_ONE":
                return {count:state.count-1};
            case "CUSTOM_COUNT":
                return {count:state.count+action.payload.count};
            default:
                return state;
        }
    }
    //3 创建store
    const store = createStore(counterReducer);
    
    store.subscribe(()=> console.log(store.getState()));
    
    //action
    store.dispatch({type:"PLUS_ONE"});
    store.dispatch({type:"MINUS_ONE"});
    store.dispatch({type:"CUSTOM_COUNT",payload:{count:5}});
    
    //action creaters
    
    function plusAction(){
        return {type:"PLUS_ONE"};
    }
    function minusAction(){
        return {type:"MINUS_ONE"};
    }
    function customCountAction(count){
        return {type:"CUSTOM_COUNT",payload:{count}};
    }
    store.dispatch(plusAction());
    store.dispatch(minusAction());
    store.dispatch(customCountAction(5));
    
    
    function plusActionWithDispatch(){
        const action = {type:"PLUS_ONE"};
        store.dispatch(action);
    }
    function minusActionWithDispatch(){
        const action = {type:"MINUS_ONE"};
        store.dispatch(action);
    }
    function customCountActionWithDispatch(count){
        const action = {type:"CUSTOM_COUNT",payload:{count}};
        store.dispatch(action);
    }
    plusActionWithDispatch();
    minusActionWithDispatch();
    customCountActionWithDispatch(5);
    
    
    const bindPlusAction = bindActionCreators(plusAction,store.dispatch);
    
    bindPlusAction();
    

    res:
    image
    exp3:

    <!DOCTYPE html>
    <html>
      <head>
        <title>Redux basic example</title>
        <script src="./node_modules/redux/dist/redux.js"></script>
      </head>
      <body>
        <div>
          <p>
            Clicked: <span id="value">0</span> times
            <button id="increment">+</button>
            <button id="decrement">-</button>
            <button id="incrementIfOdd">Increment if odd</button>
            <button id="incrementAsync">Increment async</button>
          </p>
        </div>
        <script>
          function reducer(state = 10, action) {
           
            switch (action.type) {
              case 'INCREMENT':
                return state + 1
              case 'DECREMENT':
                return state - 1
              default:
                return state
            }
          }
          var store = Redux.createStore(reducer)
    
          var valueEl = document.getElementById('value')
          function render() {
            valueEl.innerHTML = store.getState().toString()
          }
          render()
          store.subscribe(render)
          document.getElementById('increment')
            .addEventListener('click', function () {
              store.dispatch({ type: 'INCREMENT' })
            })
          document.getElementById('decrement')
            .addEventListener('click', function () {
              store.dispatch({ type: 'DECREMENT' })
            })
          document.getElementById('incrementIfOdd')
            .addEventListener('click', function () {
              if (store.getState() % 2 !== 0) {
                store.dispatch({ type: 'INCREMENT' })
              }
            })
          document.getElementById('incrementAsync')
            .addEventListener('click', function () {
              setTimeout(function () {
                store.dispatch({ type: 'INCREMENT' })
              }, 1000)
            })
        </script>
      </body>
    </html>
    

    res:
    image

    exp4:

    
    //1、引入
    const {createStore,combineReducers} = require("redux");
    
    //2、创建reducer
    
    //state数据初始化
    
    const initState = {
        count:0
    };
    
    const counterReducer = (state = initState,action = {})=>{
        //加减 plus/minus 自定义数据 custom
        switch(action.type){
            case "PLUS_ONE":
            return {count:state.count+1};
            case "MINUS_ONE":
                return {count:state.count-1};
            case "CUSTOM_COUNT":
                return {count:state.count+action.payload.count};
            default:
                return state;
        }
    }
    const myReducer = (state = [],action={}) => state;
    
    //合并reducer 只能有一个reducer
    const reducer = combineReducers({
        counterReducer,
        myReducer,
    });
    
    
    //3 创建store
    //const store = createStore(counterReducer);
    const store = createStore(reducer);
    
    console.log("state:",store.getState());
    

    res:
    image

    Object.assign(target目标,数据源多个);

    作用:

    1、拷贝 ―― 浅拷贝
    2、合并对象―― 后面的属性会覆盖前面的属性
    3、继承

    和jquery extend一样

    $.extend默认是浅拷贝 如果要实现深度拷贝

    $.extend(true,target目标,数据源多个);

    自己实现一个深度拷贝

    JSON.parse/stringify

    exp1:
    复制,合并

    <script>
    //Object.assign $.extend
    
    var a = {a:"a"};
    var b = {b:"b"}
    
    var x = Object.assign({},a,b);
    
    console.log(x==a,x,a,b);
    
    </script>
    

    res:
    image

    exp2:
    //属性重复 后面的属性会覆盖前面的属性

    <script>
    //Object.assign $.extend
    
    //属性重复 后面的属性会覆盖前面的属性
    var a = {a:"a"};
    var b = {a:"b",b:"b"};
    var c = {a:"c",b:"c",c:"c"}
    
    var x = Object.assign({},a,b,c);
    
    console.log(x,a,b,c);
    
    </script>
    

    res:
    image
    exp3:
    //Object.assign是一个浅拷贝
    浅拷贝指的是拷贝后的元素变化会影响的原来的元素; 只拷贝了地址

    <script>
    //Object.assign $.extend
    //深拷贝 浅拷贝
    //属性重复 后面的属性会覆盖前面的属性
    
    //Object.assign是一个浅拷贝
    var a = {a:1,b:2,arr:[1,2]};
    var b = {}
    Object.assign(b,a);
    
    b.arr[0] = "bbb";
    
    console.log(a,b,a==b);
    
    </script>
    

    res:
    image

    exp4:
    深拷贝

    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script>
    //Object.assign === $.extend//浅拷贝 默认是浅拷贝
    //深拷贝 浅拷贝
    //属性重复 后面的属性会覆盖前面的属性
    
    //true表示深拷贝
    var a = {a:1,b:2,arr:[1,2]};
    var b = {};
    $.extend(true,b,a);
    
    b.arr[0] = "bbb";
    
    console.log(a,b,a==b);
    
    </script>
    

    res:
    image

    exp5:

    <script>
    //copy JSON.parse/stringify
    var a = {a:1,b:2,arr:[1,2]};
    var b = copy(a);
    
    b.arr[0] = "bbb";
    
    function copy(obj){
    //转成字符串,再转成json,地址变了,   
        return JSON.parse(JSON.stringify(obj));
    }
    
    console.log(a,b,a==b);
    
    </script>
    

    res:
    image

    exp6:

    <script>
    
    function Person(name,age){
        this.name = name;
        this.age = age;
    }
    
    Person.prototype.getName = function(){
        return this.name;
    }
    
    function Worker(name,age,job){
        //Person.call(this,name,age);
        Object.assign(this,{name,age,job});
    }
    //Worker.prototype = new Person();
    
    Object.assign(Worker,Person);
    Worker.prototype.getJob = function(){
        return this.job;
    }
    
    
    
    console.log(Worker.prototype,Person.prototype);
    
    
    </script>
    

    res:
    image

    exp7:
    react状态

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Page Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="react.js"></script>
    <script src="react-dom.js"></script>
    <script src="babel.js"></script>
    <script type="text/babel">
    //状态
    class Test extends React.Component{
        constructor(...args){
            super(...args);
            this.state = {
                a:1,b:2,
                count:0
            }
        }
        plus(){
            let oldSate = this.state;
            let obj = {
                count:this.state.count+1
            };
            this.setState(obj);
            
            setTimeout(()=>{
                console.log(1,this.state == obj,this.state,obj);
            },0);
        }
    
        render(){
            return <div>
                count:{this.state.count}
                <input onClick={this.plus.bind(this)} type="button" value="按钮"/>
            </div>
        }
    } 
    ReactDOM.render(
        <Test/>,
        document.getElementById("app")
    );
    </script>
    <body>
    <div id="app"></div> 
    </body>
    </html>
    

    res:
    image
    exp8:

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Page Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="react.js"></script>
    <script src="react-dom.js"></script>
    <script src="babel.js"></script>
    <script type="text/babel">
    //状态
    let state = {a:1,b:2,count:1,msg:""};
    
    function setState(newState){
        //复制,形同的取后面的
        //state = Object.assign({},state,newState);
        //结构去重
        state = {...state,...newState};
        renderApp();
    }
    
    class Test extends React.Component{
       
        plus(){
            let oldState = state;
            setState({
                count:state.count+1
            });
    
            console.log(state == oldState,state);
        }
    
        show(ev){
            setState({
                msg:ev.target.value
            });
        }
        render(){
            console.log("渲染了",state);
            return <div>
                count:{state.count}
                <input onClick={this.plus.bind(this)} type="button" value="按钮"/> <br />
                <input onChange={this.show.bind(this)} type="text" value={state.msg}/> {state.msg}
    
            </div>
        }
    }
    renderApp();
    //把渲染封装成函数
    function renderApp(){
        ReactDOM.render(
            <Test/>,
            document.getElementById("app")
        );
    }
    
    </script>
    <body>
    <div id="app"></div> 
    </body>
    </html>
    

    res:
    image


    不用react-redux:

    页面渲染:
    1.this.setState({})
    此函数自动渲染页面.

    2.给将render封装成函数,改变数据需要更新页面时调用,

    3.index.js中的React.render(),封装成函数,添加store.subscribe(函数名)监听,适合页面较多的情况.

    react-redux: 帮你渲染页面

    cnpm i -S react-redux

    import {Provider,Connect} from "react-redux";

    index.js:

    import React from 'react';
    import ReactDOM from 'react-dom';
    import {Provider} from "react-redux";
    import store from "./react-redux/store";
    import App from './react-redux/Counter';
    import registerServiceWorker from './registerServiceWorker';
    
    ReactDOM.render(
    //组件Provider,引入store.
        <Provider store={store}>
            <App />
        </Provider>, 
        document.getElementById('root'));
    
    registerServiceWorker();
    
    

    Counter.js:
    高阶组件HOC

    import React,{Component} from "react";
    
    import {connect} from "react-redux";
    
    class Counter extends Component{
        constructor(...args){
            super(...args);
            console.log("counet props",this.props);
        }
        render(){
            console.log("渲染了");
            const {count,dispatch} = this.props;
            return <div>
            <input onClick={()=>dispatch({type:"MINUS_ONE"})} type ="button" value="-"/>
            <span>{count}</span>
            <input onClick={()=> dispatch({type:"PLUS_ONE"})} type="button" value="+"/>
        </div>
        }
    }
    
    //高阶组件HOC
    /* export default connect(function(state){
        console.log(111,state);
        return state;
    })(Counter); */
    //export default connect(state=>({count:state.count}))(Counter);
    
    /* function mapStateToPros(state){
        return {
            count:state.count
        }
    }
    export default connect(mapStateToPros)(Counter); */
    const mapStateToPros = state =>({count:state.count});
    export default connect(mapStateToPros)(Counter);
    
    

    高阶组件:只负责数据 不涉及UI

    exp1:

    <script type="text/babel">
    
    class Clock extends React.Component{
       
        state = {time:new Date()};
        timer = null;
        componentDidMount(){
            this.tick();
        }
    
        componentWillUnmount(){
            clearInterval(this.timer);
        }
    
        tick(){
            this.timer = setInterval(()=>{
                this.setState({
                    time:new Date()
                });
            },1000);
        }
    
        render(){
            return <div>
               <div> time:{this.state.time.toLocaleTimeString()} </div>
            </div>
        }
    }
    ReactDOM.render(
        <Clock/>,
        document.getElementById("app")
    );
    
    </script>
    

    res:
    image

    exp2:

    <script type="text/babel">
    
    /*
    function connect(fn){
        //fn...
        return function(WrappedComponent){
            return class extends React.Component {
                render(){
                    return null;
                }
            }
        }
    
    }
    connect(fn)(WrappedComponent)
    */
    
    //高阶组件 1是一个函数 2 返回一个新组件
    function withTime(WrappedComponent) {
        return class extends React.Component {
            state = {time:new Date()};
            timer = null;
            componentDidMount(){
                this.tick();
            }
    
            componentWillUnmount(){
                clearInterval(this.timer);
            }
    
            tick(){
                this.timer = setInterval(()=>{
                    this.setState({
                        time:new Date()
                    });
                },1000);
            }
            render() {
                return <WrappedComponent time={this.state.time} />;
            }
        };
    }
    
    class Clock0 extends React.Component{
        render(){
            return <div>
               <div> Clock1:{this.props.time.toString()} </div>
            </div>
        }
    }
    
    class Clock1 extends React.Component{
        render(){
            return <div>
               <div> Clock1:{this.props.time.toString()} </div>
            </div>
        }
    }
    
    class Clock2 extends React.Component{
        render(){
            return <div>
               <div> Clock2:{this.props.time.toLocaleString()} </div>
            </div>
        }
    }
    
    Clock1 = withTime(Clock1);
    Clock2 = withTime(Clock2);
    
    ReactDOM.render(
        <div>
            <Clock0 time={new Date()}/>
            <Clock1/>
            <Clock2/>
    
        </div>,
        document.getElementById("app")
    );
    
    </script>
    

    res:

    image

  • 相关阅读:
    Windows2012中安装PHP-5.6.20+Apache httpd2.4.18+Composer+Laravel+MySQL5.7
    CentOS7安装使用MySQL
    使用passenger在Centos7部署Puma+Nginx+Ruby on Rails
    DOS和UNIX文本文件之间相互转换的方法
    CentOS7安装vim7.4
    Python多版本共存之pyenv
    我的Shell + VIM配置
    CentOS7安装Python3.5
    CentOS7系统下搭建Jenkins环境
    Windows系统下搭建Jenkins环境
  • 原文地址:https://www.cnblogs.com/zhongchao666/p/9471290.html
Copyright © 2011-2022 走看看