派发器深入之TodoList组件设计

250 阅读1分钟

派发器深入之TodoList组件设计

    <!-- src/components/TodoList/Index.vue -->
    <template>
        <div>
            <td-title :title="title" />
            <td-form @dispatch="dispatch" />
            <td-list :todoData="todoData" @dispatch="dispatch" />
        </div>
    </template>
    <script>
    import TdTitle from "./Title";
    import TdForm from "./Form";
    import TdList from "./List/Index";

    import dispatch from "@/dispatchers/todoList"

    export default {
        name: "TodoList",
        components: {
            TdTitle, TdForm, TdList
        },
        data () {
            return {
                title: "TodoList",
                todoData: []
            }
        },
        methods: {
            dispatch (...args) {
                dispatch(this)(...args);
            }
        }
    }
    </script>

    <!-- src/components/TodoList/Title.vue -->
    <template>
        <h1>{{title}}</h1>
    </template>
    <script>
    export default {
        name: "TdTitle",
        props: {
            title: String
        }
    }
    </script>
    <!-- src/components/TodoList/Form.vue -->
    <template>
        <div>
            <form @submit="submitText">
                <input type="text" v-model.trim="todoText" placeholder="Input what you wanna add!" />
            </form>
        </div>
    </template>
    <script>
    export default {
        name: "TdForm",
        data () {
            return {
                todoText: ""
            }
        },
        methods: {
            submitText (e) {
                e.preventDefault();

                if (this.todoText.length === 0) {
                    return;
                }

                this.$emit("dispatch", "ADD", {
                    id: Date.now(),
                    text: this.todoText,
                    completed: false
                });

                this.todoText = "";
            }
        }
    }
    </script>
    <!-- src/components/TodoList/List/Index.vue -->
    <template>
        <div>
            <ul>
                <list-item 
                    v-for="(item, index) of todoData" 
                    :key="index"
                    :item="item"
                    @handelItem="handelItem"
                />
            </ul>
        </div>
    </template>
    <script>
    import ListItem from './ListItem.vue'
    export default {
        name: "TdList",
        components: {
            ListItem
        },
        props: {
            todoData: Array
        },
        methods: {
            handelItem (...args) {
                this.$emit("dispatch", ...args);
            }
        }
    }
    </script>
    <!-- src/components/TodoList/List/ListItem.vue -->
    <template>
        <li>
            <input 
                type="checkbox" 
                :checked="item.completed ? 'checked' : '' "
                @click="handelItem('COMPLETED', item.id)"
            />
            <span :class="{completed: item.completed}">{{item.text}}</span>
            <button @click="handelItem('REMOVE', item.id)">删除</button>
        </li>
    </template>
    <script>
    export default {
        name: "ListItem",
        props: {
            item: Object
        },
        methods: {
            handelItem (...args) {
                this.$emit("handelItem", ...args);
            }
        }
    }
    </script>
    <style scoped>
        .completed {
            text-decoration: line-through;
        }
    </style>
    <!-- src/actions/todoList.js -->
    const ADD = "ADD";
    const REMOVE = "REMOVE";
    const COMPLETED = "COMPLETED";

    export {
        ADD,
        REMOVE,
        COMPLETED
    }
    <!-- src/reducers/todoList.js -->
    function todoListReducers (data) {
        function addItem (newItem) {
            return data.concat(newItem);
        }
        function removeItem (id) {
            return data.filter(item => item.id !== id);
        }
        function changeCompleted (id) {
            return data.map((item) => {
                if (item.id === id) {
                    item.completed = !item.completed;
                }
                return item;
            })
        }

        return {
            addItem,
            removeItem,
            changeCompleted
        }
    }

    export default todoListReducers;
    <!-- src/dispatchers/todoList.js -->
    import todoListReducers from "../reducers/todoList";
    import { ADD, REMOVE, COMPLETED } from "@/actions/todoList";

    export default (ctx) => {
        const {
            addItem,
            removeItem,
            changeCompleted
        } = todoListReducers(ctx.todoData);

        return function (type, args) {
            console.log(type, args)
            switch (type) {
                case ADD:
                    ctx.todoData = addItem(args);
                    break;
                case REMOVE:
                    ctx.todoData = removeItem(args);
                    break;
                case COMPLETED:
                    ctx.todoData = changeCompleted(args);
                    break;
                default: 
                    break;
            }
        }
    }