24.redux

我与影子孤独终老i 提交于 2020-01-24 10:59:08

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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!