一步一步學習Vue(十一)

本篇繼續學習vuex,仍是以實例爲主;咱們以一步一步學Vue(四)中講述的例子爲基礎,對其改造,基於vuex重構一遍,這是原始的代碼:css

todolist.jshtml

; (function () {
    var list = [];
    var Todo = (function () {
        var id = 1;
        return function (title, desc) {
            this.title = title;
            this.desc = desc;
            this.id = id++;
        }
    })();
    /**
     * 搜索組件
     */
    var SearchBar = {
        template: `
        <div class="row toolbar">
            <div class="col-md-8">
                keyword:
                <input type="text" v-model="keyword" />
                <input type="button" @click="search()" value="search" class="btn btn-primary"  />
            </div>
        </div>
    `,
        data: function () {
            return {
                keyword: ''
            }
        },
        methods: {
            search: function () {
                this.$emit('onsearch', this.keyword);
            }
        }

    }
    /**
     * 表單組件
     */
    var TodoForm = {
        template: `
     <div class="col-md-6">
        <div class="form-inline">
            <label for="title" class="control-label col-md-4">title:</label>
            <input type="hidden" v-bind:value="todo.id" />
            <input type="text" v-model="todo.title" class="form-control col-md-8">
        </div>
        <div class="form-inline">
            <label for="desc" class="control-label col-md-4">desc</label>
            <input type="text" v-model="todo.desc" class="form-control col-md-8">
        </div>
        <div class="form-inline">
            <input type="button" value="OK" v-on:click="ok()" class="btn btn-primary offset-md-10"  />
        </div>
    </div>
    `,
        props: ['initItem'],

        computed: {
            todo: function () {
                return { id: this.initItem.id, title: this.initItem.title, desc: this.initItem.desc };
            }
        },

        methods: {
            ok: function () {
                this.$emit('onsave', this.todo);

            }
        }

    }
    /**
     * 列表項組件
     */
    var TodoItem = {
        template: `
     <tr>
        <th>{{todo.id}}</th>
        <td>{{todo.title}}</td>
        <td>{{todo.desc}}</td>
        <td>
            <input type="button" value="remove" @click="remove()" class="btn btn-danger" />
            <input type="button" value="edit" @click="edit()" class="btn btn-info" />
        </td>
    </tr>
    `,
        props: ['todo'],
        methods: {
            edit: function () {
                console.log(this.todo);
                this.$emit('onedit', this.todo.id);
            },
            remove: function () {
                this.$emit('onremove', this.todo.id);
            }
        }
    }
    /**
     * 列表組件
     */
    var TodoList = {
        template: `
    <div class="col-md-6">
        <table class="table table-bordered">
            <tr>
                <th></th>
                <th>title</th>
                <th>desc</th>
                <th></th>
            </tr>
            <todo-item  v-for="item in items" :todo="item" :key="item.id"  @onedit="edit($event)" @onremove="remove($event)"></todo-item>
        </table>
    </div>
    `,
        props: ['items'],
        components: {
            'todo-item': TodoItem
        },
        methods: {
            edit: function ($e) {
                this.$emit('onedit', $e);
            },
            remove: function ($e) {
                this.$emit('onremove', $e);
            }
        }
    }
    /**
     * 容器組件
     * 說明:容器組件包括三個字組件
     */
    var TodoContainer = {
        template: `
        <div class="container">
            <search-bar @onsearch="search($event)"></search-bar>
            <div class="row">
                <todo-list :items="items" @onremove="remove($event)" @onedit="edit($event)"></todo-list>            
                <todo-form :init-item="initItem" @onsave="save($event)" ></todo-form>
            </div>
        </div>
    `,
        data: function () {
            return {
                /**
                 * Todolist數據列表
                 * 說明:經過props傳入到Todolist組件中,讓組件進行渲染
                 */
                items: [],
                /**
                 * TodoForm初始化數據
                 * 說明:因爲TodoForm包括兩種操做:新增和編輯;新增操做無需處理,編輯操做須要進行數據綁定,這裏經過傳入initItem屬性進行編輯時數據的初始化
                 * 若是傳入的值爲空,說明爲新增操做,由initItem參數的Id是否爲空,來確認是更新保存仍是新增保存
                 */
                initItem: {
                    title: '',
                    desc: '',
                    id: ''
                }
            }
        },
        components: {
            'search-bar': SearchBar,/**SearchBar組件註冊 */
            'todo-form': TodoForm,/**TodoForm組件註冊 */
            'todo-list': TodoList/**TodoList組件註冊 */
        },
        methods: {
            /**
             * 模擬保存數據方法
             * 輔助方法
             */
            _mock_save: function (lst) {
                list = lst;
            },
            /**
             * 根據id查詢對象
             * 輔助方法
             */
            findById: function (id) {
                return this.items.filter(v => v.id === id)[0] || {};
            },
            /**
             * 查詢方法
             * 由SearchBar組件觸發
             */
            search: function ($e) {
                this.items = list.filter(v => v.title.indexOf($e) !== -1);
            },
            /**
             * 保存方法
             * 響應新增和更新操做,由TodoForm組件觸發
             */
            save: function ($e) {
                //id存在則爲編輯保存
                if (this.initItem.id) {
                    var o = this.findById($e.id);
                    o.title = $e.title;
                    o.desc = $e.desc;
                } else {
                    this.items.push(new Todo($e.title, $e.desc));
                }

                this.initItem = { id: '', title: '', desc: '' };

                this._mock_save(this.items);
            },
           /**
            * 刪除方法
            * 響應刪除按鈕操做
            * 由TodoItem組件觸發
            */
            remove: function ($e) {
                this.items = this.items.filter(v => v.id !== $e);
                this._mock_save(this.items);
            },
            /**
             * 編輯按鈕點擊時,進行表單數據綁定
             */
            edit: function ($e) {
                this.initItem = this.findById($e);
            }
        }
    }

    var app = new Vue({
        el: '#app',
        components: {
            'todo-container': TodoContainer
        }
    });

})();

/**
 * 
 * 
 * <div id="app">
 *     <todo-container></todo-container>
 * </app>
 */
View Code

index.htmlvue

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>demo1</title>
    <script src="https://cdn.bootcss.com/vue/2.4.1/vue.js"></script>
    <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/vuex/2.3.1/vuex.js"></script>

</head>

<body class="container">
    <div id="app">
       <todo-container></todo-container>
    </div>
    <script src="./todolist.js"></script>
</body>

</html>

注意要在html頁面引入vuex的路徑,咱們這裏直接使用cdn上的庫,開始咱們的重構vuex

第一步:建立全局storebootstrap

在vuex中,store是全局惟一的,咱們在上一篇文章中也介紹了其基本建立方式,修改todolist.js,添加以下代碼:app

  var store=new Vuex.Store({
        state:{
            //TODO:狀態
        },
        mutations:{
            //TODO:改變狀態的方法
        }
    })

    var app = new Vue({
        store:store,
        el: '#app',
        components: {
            'todo-container': TodoContainer
        }
    });

上述代碼表示store已經建立,而且注入到註冊組件中了,在任何組件中均可以經過this.store來訪問state和提交mutation,這裏再簡單說一下mutaiton,其實咱們能夠把mutation當成事件來理解,在store定義的時候,建立mutation,咱們能夠認爲是mutation的註冊,就如咱們去註冊普通的事件同樣,內容都是key和value,其中key是事件的全局表示,value是事件的回調函數,類比mutation,定義是註冊,模式仍是func:function(){}的模式,在咱們作commit(「mutation」)的時候至關於觸發事件,這時候就會執行咱們註冊的回調函數。ide

第二步,咱們把共享狀態進行提取:函數

  var store=new Vuex.Store({
        state:{
            items:[] // todoContainer中items,
        
        initItem: {
          title: '',
          desc: '',
          id: ''
        },//初始化表單所用
        },
        mutations:{
            //TODO:改變狀態的方法
        }
    })

就下來,咱們把全部改變state的方法,都經過註冊mutation的方式來重構,在vuex中,必定要經過mutation來改變狀態:學習

mutations: {
            search: function (state, payload) {
                state.items = list.filter(v => v.title.indexOf(payload.title) !== -1);
            },
            save: function (state, payload) {
                if (state.initItem.id) {
                    var o = list.filter(v => v.id === payload.id);
                    o.title = payload.title;
                    o.desc = payload.desc;
                    state.items=state.items.map(v=>{
                        if(v.id==payload.id){
                            return payload;
                        }
                        return v;
                    });
                    
                } else {
                    state.items.push(new Todo(payload.title, payload.desc));
                }

                list = state.items;
            },
            remove: function (state, payload) {
                state.items = state.items.filter(v => v.id !== payload.id);
            },
            edit: function (state, payload) {
                state.initItem = state.items.filter(v => v.id === payload.id)[0];
            }
        }

咱們添加的上述幾個mutations,包括search、save、remove、edit,因爲在每個組件中均可以訪問到this.$store,那麼咱們就不用對事件一層一層的傳遞啦,咱們只須要在須要調用的地方,commit對應的mutation便可,好比search操做就是在searchbar組件中,那麼咱們不必傳遞到父組件中來觸發,基於此,咱們修改SearchBar組件:ui

 /**
     * 搜索組件
     */
    var SearchBar = {
        template: `
        <div class="row toolbar">
            <div class="col-md-8">
                keyword:
                <input type="text" v-model="keyword" />
                <input type="button" @click="search()" value="search" class="btn btn-primary"  />
            </div>
        </div>
    `,
        data: function () {
            return {
                keyword: ''
            }
        },
        methods: {
            search: function () {
                this.$store.commit("search", {
                    title: this.keyword
                });
            }
        }

    }

這裏看起來沒有簡單好多,可是咱們至少不用把咱們的事件往上級送了,能夠對比最初代碼,同理對咱們的其它組件都進行重構:

 /**
     * 表單組件
     */
    var TodoForm = {
        template: `
     <div class="col-md-6">
        <div class="form-inline">
            <label for="title" class="control-label col-md-4">title:</label>
            <input type="hidden" v-bind:value="todo.id" />
            <input type="text" v-model="todo.title" class="form-control col-md-8">
        </div>
        <div class="form-inline">
            <label for="desc" class="control-label col-md-4">desc</label>
            <input type="text" v-model="todo.desc" class="form-control col-md-8">
        </div>
        <div class="form-inline">
            <input type="button" value="OK" v-on:click="ok()" class="btn btn-primary offset-md-10"  />
        </div>
    </div>
    `,
        props: ['initItem'],

        computed: {
            todo: function () {
                return { id: this.initItem.id, title: this.initItem.title, desc: this.initItem.desc };
            }
        },

        methods: {
            ok: function () {
                this.$store.commit("save",this.todo);
            }
        }

    }
    /**
     * 列表項組件
     */
    var TodoItem = {
        template: `
     <tr>
        <th>{{todo.id}}</th>
        <td>{{todo.title}}</td>
        <td>{{todo.desc}}</td>
        <td>
            <input type="button" value="remove" @click="remove()" class="btn btn-danger" />
            <input type="button" value="edit" @click="edit()" class="btn btn-info" />
        </td>
    </tr>
    `,
        props: ['todo'],
        methods: {
            edit: function () {
               this.$store.commit('edit',this.todo);
            },
            remove: function () {
                this.$store.commit('remove',{id:this.todo.id});
            }
        }
    }
    /**
     * 列表組件
     */
    var TodoList = {
        template: `
    <div class="col-md-6">
        <table class="table table-bordered">
            <tr>
                <th></th>
                <th>title</th>
                <th>desc</th>
                <th></th>
            </tr>
            <todo-item  v-for="item in items" :todo="item" :key="item.id" ></todo-item>
        </table>
    </div>
    `,
        props: ['items'],
        components: {
            'todo-item': TodoItem
        }
        
    }
    /**
     * 容器組件
     * 說明:容器組件包括三個字組件
     */
    var TodoContainer = {
        template: `
        <div class="container">
            <search-bar></search-bar>
            <div class="row">
                <todo-list :items="items" ></todo-list>            
                <todo-form :init-item="initItem" ></todo-form>
            </div>
        </div>
    `,

        components: {
            'search-bar': SearchBar,/**SearchBar組件註冊 */
            'todo-form': TodoForm,/**TodoForm組件註冊 */
            'todo-list': TodoList/**TodoList組件註冊 */
        },
        computed: {
            initItem: function () {
                return this.$store.state.initItem;
            },
            items: function () {
                return this.$store.state.items;
            }
        }
    }

首先看一下咱們的TodoContainer組件,裏面已經清爽了好多,原來全部的邏輯,全部的屬性,都聚集在這裏,如今每一個組件的邏輯都是它本身負責,表單組件負責保存操做,因此在其中提交commit(「save」);todo組件負責編輯和刪除,因此在其方法中封裝了remove和edit的mutaiton的訪問。至此,咱們的代碼能夠正常運行,因爲只是對前文demo的重構,這裏再也不貼出運行效果圖。

小結,在store中定義的狀態,是響應式的,對其中狀態的改變會致使view的從新渲染,改變狀態只能經過提交mutation。因爲其狀態的響應式,因此咱們在訪問時通常定義成計算屬性,如TodoContainer組件中的initItem和items;通常來講,不是全部狀態都要定義到vuex的store中,每一個組件都會有本身私有狀態,只有全局或者共享狀態才適合定義在store中,因此在實際開發中,須要好好斟酌;本篇就到此爲止,其實算是上篇的一個延伸,下一篇介紹Actions,會繼續在本篇demo的基礎上進行延伸,敬請期待。

相關文章
相關標籤/搜索