• Unity


    对响应式的数据组织结构不太熟, 直接尝试Redux走起
    参考资料

    Redux的基本概念

    state 一个字段用于存储状态

    存储state的地方称为"store", 类似ModelDataCenter等, 把所有数据(state)存进去(类似playerprefs), 不对外提供setter方法
    实际类似一个全局存储字典, 但难点在于如何组织数据, 比如玩家背包里面N种道具要想办法合理的存进去, 还要方便维护

    action 修改数据的指令

    要修改state只能通过action. 这样的话通过跟踪action的日志可以很清晰的知道到底做了什么

    reducer

    把action和reducer连接起来, 实际相当于一个switch-case函数, 在复杂项目中把action划分为多个模块

    和常见的MVC模式的对比

    普通的MVC模式为每个不同的模块创建一个Model, 通过Controller管理, 优点是对象式存储比较符合直觉
    Redux把所有数据存储到唯一一个state中, 通过action来发送指令, 优点是容易追踪操作

    Redux的三个原则

    1. 单一数据源 - 整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。
    2. State 是只读的 - 唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。
    3. 使用纯函数来执行修改 - 为了描述 action 如何改变 state tree ,你需要编写 reducers。

    前两点上面已经说过, 主要说一下第三点

    Reducer 只是一些纯函数,它接收先前的 state 和 action,并返回新的 state。刚开始你可以只有一个 reducer,随着应用变大,你可以把它拆成多个小的 reducers,分别独立地操作 state tree 的不同部分,因为 reducer 只是函数,你可以控制它们被调用的顺序,传入附加数据,甚至编写可复用的 reducer 来处理一些通用任务,如分页器。

    这里它接收先前的 state 和 action,并返回新的 state, 第一个state指现在的状态参数, 第二个state指返回值

    纯函数:

    1. 对于同一参数,返回同一结果
    2. 结果完全取决于传入的参数
    3. 不产生副作用

    大概说来Reducer就是

    public static Object SomeReducer(Object state, Action action)
    {
        // 对于static变量只使用get方法
        switch (action.actionType)
        {
            case "SOME_ACTION":
                // 
                state.foo = "bar";
            // ... 通过某种方式划分到其他的reducer
        }
    
        return ...;
    }
    

    即假设小明现在14岁(state), 过完生日要加一岁(action), 之后获取最后的年龄结果(state), 伪代码类似这样SomeFunction(14, ["ADD_AGE", 1]) -> 15

    redux的官方文档很好理解

    function visibilityFilter(state = 'SHOW_ALL', action) {
        //...
    }
    
    function todos(state = [], action) {
        //...
    }
    
    import { combineReducers, createStore } from 'redux'
    let reducer = combineReducers({ visibilityFilter, todos })
    let store = createStore(reducer)
    

    Redux的基本概念快速看了一遍, 但真正用起来怎么样, 又是另一回事了, 特别返回state这里, 还是很迷惑的

    Redux接入

    熟悉Redux的概念, 接下来就是接入Redux接口. 首先考虑一个简单的应用:
    界面显示一个数, 有两个按钮, 一个把数字加一, 一个把数字减一, 类似UIWidgets给的示例

    按照之前statefulwidget的写法, 是这样

    using System.Collections.Generic;
    using Unity.UIWidgets.material;
    using Unity.UIWidgets.painting;
    using Unity.UIWidgets.widgets;
    
    namespace UI
    {
        public class HomeRoute : StatefulWidget
        {
            public override State createState()
            {
                return new HomeRouteState();
            }
        }
    
        class HomeRouteState : State<HomeRoute>
        {
            private int m_Count = 0;
    
            public override Widget build(BuildContext context)
            {
                Scaffold scaffold = new Scaffold(
                    appBar: new AppBar(
                        title: new Text("首页")
                        ),
                    body: new Center(
                        child: new Column(
                                children: new List<Widget>
                                {
                                    new Text(
                                        data: m_Count.ToString(),
                                        style: new TextStyle(
                                            fontSize: 30
                                            )
                                        ),
                                    new Padding(padding: EdgeInsets.symmetric(10)),
                                    new RaisedButton(
                                        child: new Icon(Icons.add),
                                        onPressed: () =>
                                        {
                                            setState(() =>
                                            {
                                                m_Count++;
                                            });
                                        }
                                        ),
                                    new Padding(padding: EdgeInsets.symmetric(10)),
                                    new RaisedButton(
                                        child: new Icon(Icons.remove),
                                        onPressed: () =>
                                        {
                                            setState(() =>
                                            {
                                                m_Count--;
                                            });
                                        }
                                        ),
                                }
                            )
                        )
                    );
    
                return scaffold;
            }
        }
    }
    
    

    如果使用redux后, 把存储在控件中的m_Count移到全局的store中, 修改操作也通过action执行.
    但首先需要接入redux

    参考UIWidgets提供的示例"CounterAppSample.cs"

    GlobalState.cs
    
    namespace UI
    {
        public class GlobalState
        {
            public int Count { get; private set; }
    
            public GlobalState(int count = 0)
            {
                Count = count;
            }
        }
    }
    
    Actions.cs
    
    namespace UI
    {
        public class AddAction
        {
            public int amount;
        }
    
        public class RemoveAction
        {
            public int amount;
        }
    }
    
    HomeRoute.cs
    
    using System.Collections.Generic;
    using Unity.UIWidgets;
    using Unity.UIWidgets.material;
    using Unity.UIWidgets.painting;
    using Unity.UIWidgets.Redux;
    using Unity.UIWidgets.widgets;
    
    namespace UI
    {
        public class HomeRoute : StatelessWidget
        {
            public override Widget build(BuildContext context)
            {
                Store<GlobalState> store = new Store<GlobalState>(
                    reducer: Reducer,
                    initialState: new GlobalState()
                    );
    
                return new StoreProvider<GlobalState>(store, GetWidget());
            }
    
            //因为Reducer签名问题, 参数为object, 可以修改源码"xxxuiwidgetsRuntime
    eduxstore.cs"自定义为IAction接口, 不推荐
            public static GlobalState Reducer(GlobalState state, object action)
            {
                if (action is AddAction)
                {
                    return new GlobalState(state.Count + ((AddAction)action).amount);
                }
                else if (action is RemoveAction)
                {
                    return new GlobalState(state.Count - ((RemoveAction)action).amount);
                }
                else
                {
                    // action不匹配时返回原state
                    return state;
                }
            }
    
            private Widget GetWidget()
            {
                Scaffold scaffold = new Scaffold(
                    appBar: new AppBar(
                        title: new Text("首页")
                        ),
                    body: new Center(
                        child: new Column(
                            children: new List<Widget>
                            {
                                new StoreConnector<GlobalState, string>(
                                    converter: (state) => state.Count.ToString(),
                                    builder: (context, text, dispatcher) => new Text(
                                        data: text,
                                        style: new TextStyle(
                                            fontSize: 30
                                            )
                                        ),
                                    pure: true // 这个参数不知道干嘛用的
                                    ),
                                new Padding(padding: EdgeInsets.symmetric(10)),
                                new StoreConnector<GlobalState, object>(
                                    converter: (state) => null,
                                    builder: (context, _, dispatcher) => new RaisedButton(
                                        child: new Icon(Icons.add),
                                        onPressed: () =>
                                        {
                                            dispatcher.dispatch(new AddAction { amount = 1 });
                                        }
                                        ),
                                    pure: true
                                    ),
                                new Padding(padding: EdgeInsets.symmetric(10)),
                                new StoreConnector<GlobalState, object>(
                                    converter: (state) => null,
                                    builder: (context, _, dispatcher) => new RaisedButton(
                                        child: new Icon(Icons.remove),
                                        onPressed: () =>
                                        {
                                            dispatcher.dispatch(new RemoveAction { amount = 1 });
                                        }
                                        ),
                                    pure: true
                                    ),
                            }
                            )
                        )
                    );
    
                return scaffold;
            }
        }
    }
    

    到现在产生的疑问

    上面的代码运行起来没问题, 但在之前没完全理解的地方, 疑问出现了. 把state类起名为"GlobalState", 说明我是把这个看做存储所有全局状态的, 但参照reducer, 每次返回一个新的GlobalState类, 如果每次复制更新造成很大浪费, 似乎不太对

    redux的文档中提到

    注意 todos 依旧接收 state,但它变成了一个数组!现在 todoApp 只把需要更新的一部分 state 传给 todos 函数,todos 函数自己确定如何更新这部分数据。这就是所谓的 reducer 合成,它是开发 Redux 应用最基础的模式。

    即有的地方是传state的一个部分过去的

    
    function todoApp(state = initialState, action) {
      switch (action.type) {
        case SET_VISIBILITY_FILTER:
          return Object.assign({}, state, {
            visibilityFilter: action.filter
          })
        case ADD_TODO:
          return Object.assign({}, state, {
            todos: todos(state.todos, action)    // 注意这里, 传递的是一个部分
          })
        case TOGGLE_TODO:
          return Object.assign({}, state, {
            todos: todos(state.todos, action)
          })
        default:
          return state
      }
    }
    

    在js这么搞没问题, 强类型的C#UIWidgets中要怎么实现?

  • 相关阅读:
    MIB Browser使用方法
    RestTemplate调用https接口跳过证书验证
    Looper&Handler
    Thread类以及常见得同步类
    Android 关键类(RefBase,sp, wp)
    JNI
    binder
    struct enum union区别
    Q_PROPERTY
    linux 命令
  • 原文地址:https://www.cnblogs.com/lunoctis/p/12238650.html
Copyright © 2020-2023  润新知