vue+vue-router+vuex地址管理思路

破殼之痕發表於2019-03-04

一、場景描述

如何精簡頁面目錄,在實現功能要求的同時,提高頁面可維護性成為前端發展趨勢。收貨地址管理是電商交易必不可少模組,本篇我將通過實現地址管理的部分功能分享下構建思路。

本篇會涉及到:es6、vuejs、vue-router、vuex基礎知識,在講解思路過程中,我會穿插說明,但是還是希望讀者對相關知識有些瞭解。

實現功能:

1、個人中心與訂單確認頁均可以進入地址列表頁;

2、個人中心進入地址列表頁,點選地址列表中地址進入地址編輯頁,並帶出地址相關資訊;

3、訂單確認頁進入地址列頁,點選地址列表中地址,選中地址並返回到訂單確認頁,並帶出選中地址資訊;

二、實現功能圖解

vue+vue-router+vuex地址管理思路

三、解決該問題思路及程式碼

注:該篇思路分析專案Demo是使用vue-cli 3.x版本搭建。

1、Demo目錄

  1. 在元件庫中建立了appHeader.vue,用於頭部的名稱展示。
  2. “頁面”庫中新建了四個“頁面”Home.vue(個人中心)、OrderConfirm.vue(訂單確認)、AddressList.vue(地址列表)、AddressEdit.vue(地址編輯)。專案初始化時的“頁面”About.vue刪除。
  3. App.vue中路由連結改造,會在後邊頁面介紹中說明。
  4. 路由檔案:Demo建立中,路由數量有限,且主要目的在功能實現,因此本次路由資訊均放在了router.js檔案中。
  5. 狀態管理器:同路由檔案類似,我們將狀態、方法均放在store.js檔案中。

vue+vue-router+vuex地址管理思路

2、路由匹配

在router.js檔案中匹配好所有的路由資訊,將本次Demo演示的四個頁面的路由資訊均匹配好。

import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
import OrderConfirm from './views/OrderConfirm.vue'
import AddressList from './views/AddressList.vue'
import AddressEdit from './views/AddressEdit.vue'

Vue.use(Router)

export default new Router({
    routes: [{
            path: '/',
            name: 'home',
            component: Home
        },
        {
            path: '/orderConfirm',
            name: 'orderConfirm',
            component: OrderConfirm
        },
        {
            path: '/addressList',
            name: 'addressList',
            component: AddressList
        },
        {
            path: '/addressEdit',
            name: 'addressEdit',
            component: AddressEdit
        }
    ]
})複製程式碼

3、“頁面”建立及對應路由與狀態管理說明

App.vue:初始化時的頁面,系統入口。此處做了點修改:template內容中路由連結資訊。

<template>
    <div id="app">
        <div id="nav">
            <router-link to="/">Home</router-link> ||
            <router-link to="/orderConfirm">OrderConfirm</router-link>
        </div>
        <router-view />
    </div>
</template>複製程式碼

Home.vue:個人中心頁,作為編輯地址列表的入口。

<template>
    <div class="home">
        <router-link to='/addressList'>管理地址</router-link>
    </div>
</template>
<style scoped>
a {
    text-decoration: none;
    color: black;
}
</style>複製程式碼
說明:
  • Home頁面在本次專案Demo就是一個連結跳轉的路徑,僅僅使用了我們在router.js中設定的地址列表路由/addressList
store.js:狀態管理檔案,為方便下邊“頁面”說明,提前將檔案內容貼出來,後續涉及到時會進行簡要說明。
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
        addressList: [{
                id: 1,
                username: "小明",
                phone: '15612345678',
                addressDetail: "某某省 某某市 某某區 110"
            },
            {
                id: 2,
                username: "小紅",
                phone: '13812345678',
                addressDetail: "某某省 某某市 某某區 110"
            },
            {
                id: 3,
                username: "小花",
                phone: '18612345678',
                addressDetail: "某某省 某某市 某某區 110"
            }
        ]
    },
    getters: {
        getAddress: (state, getters) => (id) => {
            return state.addressList.find(address => address.id === id)
        }
    },
    mutations: {
        addAddress(state, address) {
            return state.addressList.push(address)
        }
    }
})複製程式碼

OrderConfirm.vue:訂單確認頁,是選擇收貨地址入口,正是由於存在跟Home兩個入口,AddressList.vue中地址列表對於地址進行同一個操作,存在不同的反饋。

<template>
    <div>
        <router-link to='/addressList'>
            <div v-if='isAddress' class="address">
                <div><span>姓名:{{username}}</span></div>
                <div><span>電話:{{phone}}</span></div>
                <div><span>地址:{{addressDetail}}</span></div>
            </div>
            <div v-else>
                <span>選擇地址</span>
            </div>
        </router-link>
    </div>
</template>
<script>
export default {
    name: "orderConfirm",
    data() {
        return {
            isAddress: false,
            username: "",
            phone: "",
            addressDetail: ""
        }
    },
    created() {
        if (this.$route.query.id) {
            let userAddress = this.$store.getters.getAddress(this.$route.query.id)
            this.isAddress = true
            this.username = userAddress.username;
            this.phone = userAddress.phone;
            this.addressDetail = userAddress.addressDetail;
        }
    }
}
</script>
<style>
a {
    text-decoration: none;
    color: black;
}

.address {
    text-align: left;
    margin-left: 20PX;
}
</style>複製程式碼

說明:

  • 使用isAddress確認是否有地址,預設為false。當地址不存在時,顯示為“選擇地址”,當地址存在時,或者選擇地址後,顯示選中地址資訊。
  • this.$route.query.id是vue-router中query引數,該使用是元件中獲取引數query.id方式,通過這個欄位值去判斷是否存在地址資訊。如果存在顯示地址資訊,並設定isAddresstrue,如果不存在則顯示為選擇地址。
  • getters是vuex中計算屬性,類似於vue中的computed,我們可以通過getters獲取vuex經過“計算”的資料。
  • 視角轉向store.js檔案,getters中使用es6的箭頭函式,通過輸入值,獲取state中經過“計算”的資料。
  • this.$store.getters.getAddress(this.$route.query.id)是為獲取vuex中傳參的getters資料。在返回選中地址時,通過向getters傳遞this.$route.query.id獲取選中的資料,將返回結果賦值到初始設定欄位即可。
AddressList.vue:地址列表頁。該頁面需要通過判別路由後初始化頁面。
<template>
    <div>
        <app-header headTitle="地址列表"></app-header>
        <ul>
            <li v-for="address in addressList" :key="address.id">
                <router-link :id='address.id' :to="{path:url,query:{id:address.id}}">
                    <div>
                        <div>
                            <span>姓名:{{address.username}}</span>
                        </div>
                        <div>
                            <span>電話:{{address.phone}}</span>
                        </div>
                        <div>
                            <span>地址:{{address.addressDetail}}</span>
                        </div>
                    </div>
                </router-link>
            </li>
        </ul>
        <div>
            <router-link to='/addressEdit'><button>新增地址</button></router-link>
        </div>
    </div>
</template>
<script>
import appHeader from '@/components/appHeader.vue'
import { mapState } from 'vuex'
export default {
    name: "addressList",
    components: {
        appHeader: appHeader,
    },
    data() {
        return {
            url: ""
        }
    },
    computed: {
        ...mapState(["addressList"])
    },
    beforeRouteEnter(to, from, next) {
        if (from.name == 'orderConfirm') {
            next(vm => {
                vm.url = "/orderConfirm"
            })
        } else {
            next(vm => {
                vm.url = "/addressEdit"
            })
        }

    }
}
</script>
<style scoped>
ul li {
    list-style: none;
    text-align: left;
    margin-bottom: 10px;
    padding: 20px;
}

ul li:nth-child(even) {
    background-color: lightgrey;
}

a {
    text-decoration: none;
    color: black;
}

button {
    outline: none;
    border: none;
    width: 100px;
    height: 40px;
    border-radius: 10px;
    background-color: green;
    color: white;
}
</style>複製程式碼
說明:
  • 頁面初始化時,地址列表通過vuex輔助函式mapState獲取state中地址列表資料,並通過v-for指令進行列表渲染。
  • 該頁面進行初始渲染的關鍵是獲取路由導航來源,通過元件內路由導航守衛beforeRouteEnter實現,因為無法獲取元件例項this,因此在next方法中進行例項獲取。
  • 動態繫結url,可根據路由導航來源決定導航跳轉去向,另外在url中動態繫結query引數。
  • 在跳轉過程中,帶著query.id引數,即在本次Demo中很多‘頁面’中均存在this.$route.query.id驗證。
  • 另外,本頁中使用了元件app-header,對於頁面頭進行簡單設計。
  • 最後,在頁面底部新增了一個新增地址按鈕,是為在演示如何在一個頁面實現新增與修改地址功能。
appHeader.vue:頭部樣式元件,用在了地址列表頁面和地址編輯頁面。
<template>
    <div class='header'>
        <span>{{headTitle}}</span>
    </div>
</template>
<script>
export default {
    name: "appHeader",
    props: ['headTitle'],
}
</script>
<style>
.header {
    width: 100%;
    height: 60px;
    text-align: center;
    color: white;
    background-color: lightblue;
    font-size: 20px;
    line-height: 60px;
}
</style>複製程式碼

說明:

  • 這個元件沒有太多難點,僅僅涉及到props的使用。
AddressEdit.vue:地址編輯頁面。
<template>
    <div>
        <appHeader :headTitle="addressEdit"></appHeader>
        <div class='name'>
            <span>姓名:</span><input type="text" v-model='username'>
        </div>
        <div class='phone'>
            <span>電話:</span><input type="text" v-model='phone'>
        </div>
        <div class='addressDetail'>
            <span>地址:</span><input type="text" v-model='addressDetail'>
        </div>
        <router-link to='/addressList'><button @click='editAddress'>提交</button></router-link>
    </div>
</template>
<script>
import appHeader from '@/components/appHeader.vue'
import { mapGetters } from 'vuex'
export default {
    name: "addressEdit",
    data() {
        return {
            addressEdit: "",
            username: '',
            phone: '',
            addressDetail: ''
        }
    },
    components: {
        appHeader: appHeader
    },
    computed: {
        useraddress() {
            return this.$store.getters.getAddress(this.$route.query.id)
        }
    },
    methods: {
        editAddress() {
            let length = this.$store.state.addressList.length
            let address = {}
            address.id = length + 1
            address.username = this.username
            address.phone = this.phone
            address.addressDetail = this.addressDetail
            this.$store.commit("addAddress", address)
        }
    },
    created() {
        this.$route.query.id ? this.addressEdit = '編輯地址' : this.addressEdit = '新增地址';
        if (this.$route.query.id) {
            let userAddress = this.$store.getters.getAddress(this.$route.query.id)
            this.username = userAddress.username || '';
            this.phone = userAddress.phone || '';
            this.addressDetail = userAddress.addressDetail || '';
        }

    }

}
</script>
<style>
input {
    border: 1px solid lightblue;
    outline: none;
    width: 300px;
    height: 30px;
    border-radius: 5px;
}

.name {
    margin-top: 10px;
}

.phone {
    margin: 10px 0;
}

button {
    margin-top: 20px;
    outline: none;
    border: none;
    width: 100px;
    height: 40px;
    border-radius: 10px;
    background-color: green;
    color: white;
}
</style>複製程式碼

說明:

  • 頭部元件app-headerprops動態賦值,點選新增地址按鈕,進入地址編輯頁,頁面頭部展示“新增地址”,點選地址列表中地址,進入地址編輯頁,頁面頭部展示為“編輯地址”。
  • 地址修改時,對於輸入框內容根據this.$route.query.id從vuex中getters賦值並展示。
  • mutations方法類似vue中methods
  • 資訊填寫完畢後,通過mutations方法向vuex中state中值進行修改。在元件中methods中通過this.$store.commit("addAddress", address)提交到mutations。
  • 注意在本次的Demo展示中,我針對地址修改與新增,均向state中地址列表中新增地址,另外,我們不同入口進行地址列表頁面時,新增地址提交返回路徑地址也應該不一樣,有興趣的同學們可以嘗試修改的方法。鑑於是個本地展示,本次展示資料修使用的mutations,真正與後臺互動中,建議使用actions.

四、寫在後面

實際我們在做產品時,涉及到使用者體驗的東西很多,比如頁面樣式、預設地址、輸入驗證、驗證提示、地址級聯選擇等等,作為一個功能實現,暫不予考慮。

以上是作為一名喜歡前端技術的產品經理實現該功能的思路及方法,相信大神們會有更簡潔便利的方式。

最後,針對文中有不對的地方,或者可以再優化的點,請多多指教。


相關文章