前端筆記之Vue(四)UI組件庫&Vuex&虛擬服務器初識

1、日曆組件

new Date()的月份是從0開始的。 javascript

下面表達式是:2018年6月1日
new Date(2018, 5, 1);

下面表達式是:2018年5月1日
new Date(2018, 4, 1);
或
new Date(2018, 5-1, 1);

下面表達式是:2018年5月31日(獲得上個月的最後一天)
new Date(2018, 5 , 0);
日的參數能夠是0,也能夠是負數,表示上個月底的那一天。

下面表達式是:2018年7月01日
new Date(2018, 5, 31);

 

App.vue父組件:css

<template>
    <div>
        <MonthView :year="year" :month="month"></MonthView>
    </div>
</template>
<script>
    import MonthView from "./components/MonthView.vue";
    export default {
        data(){
            return {
                year : 2018 ,
                month : 8 ,
            }
        },
        components : {
            MonthView
        },
        methods : {
        }
    }
</script>
示例代碼

MonthView.vue子組件html

<template>
<div>
    月視圖{{year}} {{month}}
        {{arr}}
    </div>
</template>
<script>
    export default {
        props : ["year" , "month"],
        computed : {
            arr(){
                //計算日曆的數組:三要素
                //本月1號星期幾
                var this1DayWeek = new Date(this.year, this.month - 1, 1).getDay();
                // 本月有幾天
                var thisMonthDay = new Date(this.year, this.month, 0).getDate();
                // 上月有多少天
                var prevMonthDay = new Date(this.year, this.month - 1, 0).getDate();

                console.log(benyue1haoxingqiji)
                console.log(benyueyoujitian)
                console.log(shangyueduoshaotian)
            }
        }
    }
</script>
示例代碼

顯示在頁面:前端

<template>
    <div>
        <table>
            <tr v-for="i in 6">
                <td v-for="j in arr.slice((i-1) * 7, i * 7)">
                    {{j}}
                </td>
            </tr>
        </table>
    </div>
</template>
<script>
    export default {
         props:["year","month"],
         computed : {
            arr(){
                var _arr = []; //存儲42天的數組

                // 計算日曆的數組:三要素
                //本月1號星期幾,根據星期幾獲得上個月剩餘天數
                var this1DayWeek = new Date(this.year, this.month-1, 1).getDay();
                //上個月有多少天
                var prevMonthDay =  new Date(this.year, this.month-1, 0).getDate();
                //本月有幾天
                var thisMonthDay =  new Date(this.year, this.month, 0).getDate();

                //用本月1號星期幾,推斷出上月的剩餘天數
                for(var i = 0; i < this1DayWeek;i++){
                    _arr.unshift(prevMonthDay - i)
                }
                
                //循環本月天數(累加),從數組末尾插入
                for(var i = 1; i <= thisMonthDay;i++){
                    _arr.push(i)
                }

                //補充下月的天數(滿42天爲止)
                var i = 1;
                while(_arr.length != 42){
                    _arr.push(i++);
                }
                
                return _arr;
            }
         }
    }
</script>
示例代碼

顯示農曆,安裝插件:vue

npm install solarlunar --save
<template>
    <div>
        <h1>月視圖 {{year}}年{{month}}月</h1>
        <table>
            <tr>
                <th>日</th>
                <th>一</th>
                <th>二</th>
                <th>三</th>
                <th>四</th>
                <th>五</th>
                <th>六</th>
            </tr>
            <tr v-for="i in 6">
                <td v-for="j in arr.slice((i-1) * 7, i * 7)">
                    <p class="p1">{{j.d}}</p>
                    <p class="p2">{{j.n}}</p>
                </td>
            </tr>
        </table>
    </div>
</template>
<script>
    import solarLunar from 'solarLunar';
    export default {
         props:["year","month"],
         computed : {
            arr(){
                var _arr = []; //存儲42天的數組

                // 計算日曆的數組:三要素
                //本月1號星期幾,根據星期幾獲得上個月剩餘天數
                var this1DayWeek = new Date(this.year, this.month-1, 1).getDay();
                //上個月有多少天
                var prevMonthDay =  new Date(this.year, this.month-1, 0).getDate();
                //本月有幾天
                var thisMonthDay =  new Date(this.year, this.month, 0).getDate();

                //用本月1號星期幾,推斷出上月的剩餘天數
                for(var i = 0; i < this1DayWeek;i++){
                    _arr.unshift({
                        d: prevMonthDay - i,
                        n: solarLunar.solar2lunar(this.year, this.month-1, prevMonthDay - i).dayCn
                    })
                }
                
                //循環本月天數,累加,從數組末尾插入
                for(var i = 1; i <= thisMonthDay;i++){
                    _arr.push({
                        d: i,
                        n: solarLunar.solar2lunar(this.year, this.month, i).dayCn
                    })
                }

                //補充下個月的天數(滿42天爲止)
                var i = 1;
                while(_arr.length != 42){
                    _arr.push({
                        d : i,
                        n : solarLunar.solar2lunar(this.year, this.month+1, i).dayCn
                    });
                    i++;
                }
                console.log(_arr)
                return _arr;
            }
         }
    }
</script>
示例代碼

 

下面作「換月換年」業務:java

App.vue父組件node

<template>
    <div>
        <MonthChooser 
            :year="year" 
            :month="month" 
            :setYear="setYear" 
            :setMonth="setMonth"
        >
        </MonthChooser>
        <MonthView :year="year" :month="month"></MonthView>
    </div>
</template>
<script>
    import MonthView from "./components/MonthView.vue";
    import MonthChooser from "./components/MonthChooser.vue";
    export default {
        data(){
            return{
                year :2018,
                month:8,
            }
        },
        components :{
            MonthView,
            MonthChooser
        },
        methods : {
            setYear(year){
                this.year = year; //設置年
            },
            setMonth(month){
                this.month = month; //設置月
            }
        }
    }
</script>
示例代碼

MonthChooser.vue切換年月組件webpack

<template>
    <div>
        <h1>
            <button @click="goPrev()">-</button>
            <a href="###">{{year}}</a> 年{{month}}月
            <button @click="goNext()">+</button>
        </h1>
    </div>
</template>
<script>
    export default {
         props:["year","month","setYear","setMonth"],
         methods :{
             goNext(){
                 if(this.month < 12){
                    // 若是月份小於12,能夠加月
                     this.setMonth(this.month + 1)
                 }else{
                    //  不然就加年,而且重設下年爲1月
                     this.setMonth(1)
                     this.setYear(this.year + 1)
                 }
             },
             goPrev(){
                 if(this.month > 1){
                    // 若是月份大於1月,能夠減月
                     this.setMonth(this.month - 1)
                 }else{
                    //  不然就減年,而且重設上年爲12月
                     this.setMonth(12); //重設爲12月
                     this.setYear(this.year - 1); //減年
                 }
             }
         }
    }
</script>
示例代碼

 

切換年代視圖組件:git

l App.vue父組件github

<template>
    <div>
        <MonthChooser
            :year="year"
            :month="month"
            :setYear="setYear"
            :setMonth="setMonth"
        ></MonthChooser>
        <MonthView :year="year" :month="month"></MonthView>
        <DecadeView :year="year" :setYear="setYear"></DecadeView>
    </div>
</template>
<script>
    import MonthView from "./components/MonthView.vue";
    import MonthChooser from "./components/MonthChooser.vue";
    import DecadeView from "./components/DecadeView.vue";
    export default {
        data(){
            return {
               ...
            }
        },
        components : {
            MonthView,
            MonthChooser,
            DecadeView
        },
        methods : {
            ...
        }
    }
</script>
示例代碼

DecadeView.vue子組件

<template>
    <div>
        <table>
            <tr v-for="i in 10">
            <!-- <td v-for="j in arr.slice((i-1) * 3, i * 3)"> -->
                <td v-for="j in 3" :class="{'cur':year == showYear(i, j)}" 
@click="setYear(showYear(i, j))"
>
                   {{showYear(i, j)}}
                </td>
            </tr>
        </table>
    </div>
</template>
<script>
    export default {
        props : ["year"],
        computed : {
            arr(){
                var _arr = [];
                //計算年份的頭
                var tou = this.year - this.year % 10 - 10;
                //從獲得的年份的頭開始循環 + 30
                for(var i = tou ; i < tou + 30;i++){
                    _arr.push(i); 
                }
                return _arr;
            }
        },
        methods : {
            showYear(i , j){
                return this.arr[(j - 1) * 10 + (i - 1)]
            }
        }
    }
</script>
<style>
    .cur{color:red;font-weight:bold;}
</style>
示例代碼

 

如下開始完善整個項目】:

切換視圖:App.vue父組件

<template>
    <div>
        <MonthChooser
            :year="year"
            :month="month"
            :setYear="setYear"
            :setMonth="setMonth"
            :setView="setView"
            v-if="view == 'month'"
        ></MonthChooser>
    <MonthView :year="year" :month="month" v-if="view == 'month'"></MonthView>

        <DecadeChooser
            :year="year"
            :month="month"
            :setYear="setYear"
            :setMonth="setMonth"
            :setView="setView"
            v-if="view=='decade'"
        ></DecadeChooser>
        <DecadeView
            :year="year"
            :setYear="setYear"
            v-if="view == 'decade'"
            :setView="setView"
        ></DecadeView>
    </div>
</template>
<script>
    import MonthView from "./components/MonthView.vue";
    import MonthChooser from "./components/MonthChooser.vue";
    import DecadeChooser from "./components/DecadeChooser.vue";
    import DecadeView from "./components/DecadeView.vue";
    export default {
        data(){
            return {
                year : 2018 ,
                month : 5 ,
                view : "month"
            }
        },
        components : {
            MonthView,
            MonthChooser,
            DecadeView,
            DecadeChooser
        },
        methods : {
            ...
            setView(view){
                this.view = view; //設置視圖切換
            }
        }
    }
</script>
示例代碼

DecadeChooser.vue年視圖按鈕組件:

<template>
    <div>
        <h1>
            <button @click="goPrev()">-</button>
            {{year}}年<a href="javascript:;" @click="setView('month')">{{month}}月</a>
            <button @click="goNext()">+</button>
        </h1>
    </div>
</template>
<script>
    export default{
        props : ["year", "month" , "setYear","setView"],
        methods : {
            goNext(){
                this.setYear(this.year + 1)
            },
            goPrev(){
                if(this.year <= 1970) return;
                this.setYear(this.year - 1)
            }
        }
    }
</script>
示例代碼

MonthChooser.vue月視圖按鈕組件:

<template>
    <div>
        <h1>
            <button @click="goPrev()">-</button>
            <a href="javascript:;" @click="setView('decade')">{{year}}</a>年{{month}}月
            <button @click="goNext()">+</button>
        </h1>
    </div>
</template>
<script>
    export default{
        props : ["year", "month" , "setYear", "setMonth","setView"],
        methods : {
            goNext(){
                ...
            },
            goPrev(){
                ...
            }
        }
    }
</script>
示例代碼

DecadeView.vue年份視圖組件:

<template>
    <div>
        <table>
            <tr v-for="i in 10">
                <td
                    v-for="j in 3"
                    :class="{'cur' : year == showYear(i , j)}"
                    @click="tdClick(i,j)"
                >
                   {{showYear(i , j)}}
                </td>
            </tr>
        </table>
    </div>
</template>
<script>
    export default {
        props : ["year","setYear","setView"],
        computed : {
            arr(){
                ...
            }
        },
        methods : {
            showYear(i , j){
                return this.arr[(j - 1) * 10 + (i - 1)]
            },
            tdClick(i , j){
                this.setYear(this.showYear(i , j)); //切換年份
                this.setView("month"); //切換年份後,回到月視圖
            }
        }
    }
</script>
示例代碼

MonthView.vue月視圖早已完善。


 

2、UI組件庫

餓了麼UIhttp://element-cn.eleme.io/#/zh-CN

iviewUI https://www.iviewui.com/

 

2.1餓了麼UI

以餓了麼UI爲例

安裝依賴:

npm install --save element-ui

 

main.js中配置eleUI組件:

在引入 Element 時,能夠傳入一個全局配置對象。該對象目前僅支持 size 字段,用於改變組件的默認尺寸。按照引入 Element 的方式,具體操做以下:

import Vue from "vue";
import App from "./App.vue";
import ElementUI from 'element-ui';
//import 'element-ui/lib/theme-chalk/index.css'; //樣式在index.html頁面引入
// Vue.use(ElementUI);
Vue.use(ElementUI, { size: 'small' });

new Vue({
    el : "#app" ,
    render: (h) => h(App)
});

而後就能夠在.vue組件中直接使用了。


 

2.2 iviewui

npm install iview --save

3、傳統數據管理的問題

如下是一個表示「單向數據流」理念的極簡示意:

 

當咱們的應用遇到多個組件共享狀態時,單向數據流的簡潔性很容易被破壞:

多個視圖依賴於同一狀態。

來自不一樣視圖的行爲須要變動同一狀態。

對於問題1:傳參的方法對於多層嵌套的組件將會很是繁瑣,而且對於兄弟組件間的狀態傳遞無能爲力。

對於問題2:咱們常常會採用父子組件直接引用或者經過事件來變動和同步狀態的多份拷貝。以上的這些模式很是脆弱,一般會致使沒法維護的代碼。

 

所以,咱們爲何不把組件的共享狀態抽取出來,以一個全局單例模式管理呢?在這種模式下,咱們的組件樹構成了一個巨大的「視圖」,無論在樹的哪一個位置,任何組件都能獲取狀態或者觸發行爲!

 

另外,經過定義和隔離狀態管理中的各類概念並強制遵照必定的規則,咱們的代碼將會變得更結構化且易維護。

 

這就是 Vuex 背後的基本思想,借鑑了 FluxRedux、和 The Elm Architecture。與其餘模式不一樣的是,Vuex 是專門爲 Vue.js 設計的狀態管理庫,以利用 Vue.js 的細粒度數據響應機制來進行高效的狀態更新。

 

以前咱們作的日曆組件將數據放到了App.vue最大父組件上,而且還有:year:month、:setYear:setMonth等一系列的參數傳遞操做。

 

可是vuex就是將數據放到了全局store中。

不用vuex也能作項目,只不過數據管理起來很不方便。


4、Vuex

4.1 Vuex配置

官網:https://vuex.vuejs.org/zh-cn/

安裝vuex

npm install --save vuex

什麼是vuex

Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的全部組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。

簡單說:vuex就是一個狀態管理容器,說白了就是將數據單獨存放出去。


4.2 state(全局倉庫)

什麼是「狀態管理模式」?

每一個Vuex應用的核心就是store(倉庫)。store就是一個容器,它包含着你項目中大部分的狀態 (state)

 

Vuex 和單純的全局對象有如下兩點不一樣:

Vuex的狀態存儲是響應式的。當 Vue 組件從 store 中讀取狀態的時候,若 store 中的狀態發生變化,那麼相應的組件也會相應地獲得高效更新變化。

你不能直接改變store中的狀態。改變store中的狀態的惟一途徑就是經過commit提交mutation。這樣使得咱們能夠方便地跟蹤每個狀態的變化,從而讓咱們可以實現一些工具幫助咱們更好地瞭解咱們的應用。

 

main.js建立一個全局倉庫(store),讓咱們從一個簡單的Vue計數器開始:

import Vue from 'vue';
import Vuex from 'vuex'; //引入Vuex包
import App from './App.vue';
Vue.use(Vuex); //將vuex安裝到vue中

//建立一個倉庫,而且存放一些全局數據(存放四大天王選項)
const store = new Vuex.Store({
    state : {
        a : 100
    }
})

new Vue({
    el : "#app",
    store, //將store注入到全局中
    render : (h)=> h(App)
})

Vuex經過store選項,提供了一種機制將狀態從根組件「注入」到每個子組件中(須要調用Vue.use(Vuex)

經過在根實例中註冊store選項,該store實例會注入到根組件下的全部子組件中,而且組件能經過this.$store訪問。

 

App.vue中的生命週期中輸出this,能看到vue實例化對象的全局有$store這個對象

<template>
    <div>
        <h1>全局倉庫state對象的a值:{{$store.state.a}}</h1>
    </div>
</template>
<script>
    export default {
        created(){
            console.log(this) console.log(this.$store.state.a)
        }
    }
</script>

以前作的日曆組件將數據放到了App.vue最大父組件上,而且還有:year:month、:setYear:setMonth等一系列的參數傳遞操做。

可是Vuex就是將數據放到了全局store中,注意:

無論項目有多大,store只有一個

只要配置正確,組件內部可使用$store便可訪問store的全局數據

改變 store 中的狀態(數據)的惟一途徑就是經過commit()函數提交 mutation

 

如下的描述來自於官方:https://vuex.vuejs.org/zh/guide/

再次強調,咱們經過提交 mutation 的方式,而非直接改變 store.state.a,是由於咱們想要更明確地追蹤到狀態的變化。這個簡單的約定可以讓你的意圖更加明顯,這樣你在閱讀代碼的時候能更容易地解讀應用內部的狀態改變。此外,這樣也讓咱們有機會去實現一些能記錄每次狀態改變,保存狀態快照的調試工具。有了它,咱們甚至能夠實現如時間穿梭般的調試體驗。

 

因爲 store 中的狀態是響應式的,在組件中調用 store 中的狀態簡單到僅須要在計算屬性中返回便可。觸發變化也僅僅是在組件的 methods 中提交 mutation


4.3 mutations

更改 Vuex store 中的狀態的惟一方法是提交 mutationVuex中的mutation很是相似於事件:每一個 mutation都有一個字符串的 事件類型(type) 和一個回調函數 (handler)。這個回調函數就是咱們實際進行狀態更改的地方,而且它會接受 state 做爲第一個參數:

你不能直接調用一個mutation handler。這個選項更像是事件註冊:當觸發一個type類型爲JIAmutation時,調用此函數。要喚醒一個mutation函數,你須要以相應的type調用store.commit()方法

 

main.js

import Vue from 'vue';
import Vuex from 'vuex'; //引入Vuex包
import App from './App.vue';
Vue.use(Vuex); //將vuex安裝到vue中

//建立一個倉庫,而且存放一些全局數據(存放四大選項)
const store = new Vuex.Store({
    state : {
        a : 100
    },
    mutations : { // 這裏的函數只能經過commit()觸發,其餘操做是沒法影響的
        JIA(state){
            state.a++
        },
        MINUS(state){
            state.a-- } }
})

 

App.vue

<template>
    <div>
        <h1>全局倉庫state對象的a:{{$store.state.a}}</h1>
        <button @click="add()">+</button>
        <button @click="minus()">-</button>
    </div>
</template>
<script>
export default {
    methods:{
        add(){
            // this.$store.state.a++ //不容許直接改全局的state數據
            this.$store.commit("JIA");
        },
        minus(){
            this.$store.commit("MINUS");
        }
    }
};
</script>

 

Vuex自帶一個日誌插件(vue-logger)用於調試:

import createLogger from 'vuex/dist/logger'

const store = new Vuex.Store({
  plugins: [createLogger()]
})

總結:

只有mutations中能夠改變state,其餘任何方式都不能改state的值

組件想要改變store中的state,只能經過commit()發出一條命令。

 


 

提交載荷(Payload

你能夠向 store.commit() 傳入額外的參數,即 mutation 的 載荷(payload,在大多數狀況下,載荷應該是一個對象,這樣能夠包含多個字段而且記錄的 mutation 會更易讀:

main.js

import Vue from 'vue';
import Vuex from 'vuex'; //引入Vuex包
import App from './App.vue';
import createLogger from 'vuex/dist/logger';

Vue.use(Vuex); //將vuex安裝到vue中

//建立一個倉庫,而且存放一些全局數據(存放四大選項)
const store = new Vuex.Store({
    state : {
        a : 100
    },
    mutations : {
        // 這裏的函數只能經過commit()觸發,其餘操做是沒法影響的
        JIA(state,payload){
            state.a += payload.n
        },
        MINUS(state, payload){
            state.a -= payload.n
        }
    },
    plugins: [createLogger()]
})

 

App.vue

<template>
    <div>
        <h1>全局倉庫state對象的a:{{$store.state.a}}</h1>
        <button @click="add()">+</button>
        <button @click="minus()">-</button>
        <button @click="add(2)">+</button>
        <input type="text" ref="txt">
        <button @click="addUser()">加用戶輸入的數</button>
    </div>
</template>
<script>
export default {
    methods:{
        add(n=1){
            // this.$store.state.a++ //不容許直接改全局的state數據
            // this.$store.commit("JIA", 8);
            this.$store.commit("JIA", {n});
        },
        minus(){
            this.$store.commit("MINUS", {n : 10});
        },
        addUser(){
            this.$store.commit("JIA", {n : Number(this.$refs.txt.value)});
        }
    }
};
</script>

記住一條重要的原則:mutations必須是同步函數


4.4 actions

上面說過mutation 中不能寫異步語句,爲了處理異步操做,咱們來看一看action

action 相似於 mutation,不一樣在於:

action 提交的是 mutation,而不是直接變動狀態。

action 能夠包含任意異步操做。

action 要經過 store.dispatch() 方法觸發

 

注意:涉及到異步Ajax請求數據,案例必須運行在服務器端(127.0.0.1

新建一個data文件夾,建立txt文件。使用ajax 異步讀取文本文件中數據:

App.vue父組件:

<script>
    export default {
        methods:{
            add(){
                this.$store.dispatch("JIA");
            }
        }
    }
</script>

 

main.js

const store = new Vuex.Store({
    state : {
        a : 100
    },
    mutations : {
        JIA(state,payload){
            console.log("只有commit命令能觸發我")
            state.a += payload.n
        }
    },
    actions : {
        async JIA({commit}){
            // console.log("只有dispatch命令能觸發我,這裏能夠寫異步語句")
            var data = await fetch('../data/1.txt').then(data=>data.json())
            //action提交的是mutation,而不是直接更改狀態,
            //請求成功返回的數據須要經過commit命令mutations去修改state中的數據
       // context.commit("JIA", {n: data})
            // this.commit("JIA", {n: data})
            commit("JIA", {n:data})
        }
    },
    plugins: [createLogger()]
})

actions中的函數,天生自帶默認參數

 

一些概念:

action 函數接受一個與store實例具備相同方法和屬性context對象,所以你能夠調用 context.commit 提交一個 mutation,或者經過 context.state context.getters 來獲取 state getters。當咱們在以後介紹到 Modules 時,你就知道 context 對象爲何不是 store 實例自己了。

 

實踐中,會常常用到ES2015的 參數解構 來簡化代碼(特別是咱們須要調用commit不少次的時候):

actions:{
    async JIA({commit}){
        var data = await fetch("../data/1.txt").then(data=>data.json());
        commit("JIA",data)
    }
},

 

action 經過 store.dispatch() 方法觸發:

methods:{
    add(){
        this.$store.dispatch("JIA")
    }
}

乍一眼看上去感受畫蛇添足,咱們直接分發 mutation 豈不更方便?實際上並不是如此,還記得 mutation 必須同步執行這個限制麼?Action 就不受約束!咱們能夠在 action 內部執行異步操做:

actions支持一樣的載荷方式和對象方式進行分發:

vuex單向數據流動的圖示:

 

使用 Vuex 並不意味着你須要將全部的狀態放入Vuex。雖然將全部的狀態放到 Vuex 會使狀態變化更顯式和易調試,但也會使代碼變得冗長和不直觀。若是有些狀態嚴格屬於單個組件,最好仍是做爲組件的局部狀態。你應該根據你的應用開發須要進行權衡和肯定。


4.5 getters

有時候咱們須要從 store 中的 state 中派生出一些狀態,例如對列表進行過濾並計數:

computed: {
   arr() {
       return this.$store.state.todos.filter(todo => todo.done).length
   }
}

Vuex中,getter相似於組件中的computed,表示state的一些計算後的值。

 

若是有多個組件須要用到此屬性,咱們要麼複製這個函數,或者抽取到一個共享函數而後在多處導入它——不管哪一種方式都不是很理想。

Vuex 容許咱們在 store 中定義「getter」(能夠認爲是 store 的計算屬性)。就像computed計算屬性同樣,getter 的返回值會根據它的依賴被緩存起來,且只有當它的依賴值發生了改變纔會被從新計算。

 

Getter 接受 state 做爲其第一個參數:

main.js

import Vue from 'vue';
import Vuex from 'vuex'; //引入Vuex包
import App from './App.vue';
import createLogger from 'vuex/dist/logger';  //調試工具

Vue.use(Vuex);  //將Vuex安裝到全局

//建立一個倉庫,而且存放以一些全局數據(存放四大選項)
const store = new Vuex.Store({
    state:{
        a:100,
        students:[
            {name:"小明",sex:"男"},
            {name:"小紅",sex:"女"},
            {name:"小剛",sex:"男"},
            {name:"小花",sex:"女"},
            {name:"小黑",sex:"男"}
        ]
    },
    getters:{
        //獲得全部男生
        nan(state){
            return state.students.filter((item)=>{
                return item.sex == '男';
            })
        },
        //獲得全部女生
        nv(state){
            return state.students.filter((item)=>{
                return item.sex == '女';
            })
        },
        //獲得男生和女生的個數,getter也能夠接受getter做爲第二個參數
        nanCount(state,getters){
            return getters.nan.length;
        },
        nvCount(state,getters){
            return getters.nv.length;
        }
    },
    plugins: [createLogger()]
})

new Vue({
    el:"#app",
    store,  //將store注入到全局
    render:(h)=> h(App)
})

 

App.vue父組件:

<template>
    <div>
        <h1>{{students}}</h1>
        <h2>男生:{{$store.getters.nanCount}}個</h2>
        <h2>女生:{{$store.getters.nvCount}}個</h2>
        <button @click="nan">查看男生</button>
        <button @click="nv">查看女生</button>
        <button @click="all">查看所有</button>
    </div>
</template>
<script>
export default {
    data(){
        return {
            isState:'all'
        }
    },
    computed:{
        students(){
            if(this.isState == 'all'){
                return this.$store.state.students;
            }else if(this.isState == 'nan'){
                return this.$store.getters.nan;
            }else if(this.isState == 'nv'){
                return this.$store.getters.nv
            }
        }
    },
    methods:{
        nan(){
            this.isState = 'nan'
        },
        nv(){
            this.isState = 'nv'
        },
        all(){
            this.isState = 'all'
        }
    }

}
</script>

在介紹state中咱們瞭解到,在Store倉庫裏,state就是用來存放數據,如果對數據進行處理輸出,好比數據要過濾,通常咱們能夠寫到computed中。可是若是不少組件都使用這個過濾後的數據,好比餅狀圖組件和曲線圖組件,咱們是否能夠把這個數據抽提出來共享?這就是getters存在的意義。官網說的很清楚,gettersstore的計算屬性。

  1. getters 能夠對State進行計算操做。
  2. 雖然在組件內也能夠作,可是getters能夠在多組件之間複用
  3. 若是一個狀態只在一個組件內使用,是能夠不用getters

getters上簡單來講就是存放一些公共函數供組件調用。getters 會暴露爲 $store.getters 對象,也就是說能夠經過 $store.getters[屬性]來進行相應的調用。


4.6vuex的命名空間

目錄結構:

│  package.json
│  webpack.config.js
│
└─www
    │  index.html
    │
    └─app
        │  App.vue
        │  main.js
        │
        ├─components
        └─store
            │  index.js
            │
            ├─counter
            │      store.js
            │
            └─taobao
                    store.js
示例代碼

./package.json:

{
  "name": "vue_study",
  "version": "1.0.0",
  "description": "",
  "main": "webpack.config.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack-dev-server --content-base ./www --port 8080"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "vue": "^2.5.17",
    "vuex": "^3.0.1"
  },
  "devDependencies": {
    "css-loader": "^1.0.1",
    "style-loader": "^0.23.1",
    "vue-loader": "^15.4.2",
    "vue-style-loader": "^4.1.2",
    "vue-template-compiler": "^2.5.17",
    "webpack": "^4.9.1",
    "webpack-cli": "^3.1.2"
  }
}
示例代碼

./webpack.config.js:

const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');

module.exports = {
    //程序的入口文件
    entry: "./www/app/main.js",

    //程序的出口(打包的文件)
    output: {
        //打包文件輸出的路徑
        path: path.resolve(__dirname, "./www/dist"),
        //打包文件的名稱
        filename: 'all.js',
        publicPath: "/public" //這是對webpack-dev-server的配置,配置虛擬路徑
    },
    //監聽文件的變化(自動打包)
    watch: true,
    mode: "development",
    //配置webpack模塊插件
    module: {
        //關於模塊的配置規則
        rules: [{
                // 模塊規則(配置 loader、解析器等選項)
                test: /\.js?$/, //解析的時候匹配js文件
                //翻譯什麼文件夾中的文件
                include: [path.resolve(__dirname, "www/app")],
                //不翻譯什麼文件夾中的文件
                exclude: [path.resolve(__dirname, "node_modules")],
                // loader:"babel-loader",
                //配置翻譯語法
                // options:{
                //     presets:["es2015","es2016"]
                // }
            },
            {
                test: /\.vue$/,
                loader: 'vue-loader',
                include: [path.resolve(__dirname, "www/app")],
                exclude: [path.resolve(__dirname, "node_modules")],
                options: {
                    loaders: {
                        js: 'babel-loader!eslint-loader'
                    }
                }
            },
            {
                test: /\.css$/,
                include: [path.resolve(__dirname, "www/app")],
                exclude: [path.resolve(__dirname, "node_modules")],
                use: ['vue-style-loader', 'css-loader'],
            },
            {
              test: /\.styl(us)?$/,
              use: [
                'vue-style-loader',
                'css-loader',
                'stylus-loader'
              ]
            }
        ]
    },
    resolve: {
        alias: {
            'vue$': 'vue/dist/vue.esm.js' // 用 webpack 1 時需用 'vue/dist/vue.common.js'
        }
    },
    //最新版webpack須要引入此插件
    plugins: [
        new VueLoaderPlugin()
    ],
    //webpack設置代理跨越
    devServer: {
        proxy: {
            '/api': {
                target: 'http://127.0.0.1:3000', //設置你調用的接口域名和端口
                //這裏理解成/api代理target中的地址,後面組件中調用接口要使用/api代替
                pathRewrite: { '^/api': '' }
            }
        }
    }
}
示例代碼

./www/index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>Document</title>
</head>
<body>
    <div id="app"></div>
</body>
<script type="text/javascript" src="public/all.js"></script>
</html>
示例代碼

./www/app/App.vue:

<template>
    <div>
        <h1>counter的{{$store.state.counterStore.a}}</h1>
        <h1>taobao的{{$store.state.taobaoStore.a}}</h1>
        <button @click="add">觸發counter的ADD</button>
    </div>
</template>
<script>
     export default {
        methods:{
            add(){
                //根據命名空間發出異步
                this.$store.dispatch("counterStore/ADD")
            }
        }
     }
</script>

./www/app/main.js:

import Vue from "vue";
import App from "./App.vue";
import store from "./store";

new Vue({
    el: "#app",
    store,//引入store文件夾中的index.js
    render: (h) => h(App)
})

./www/app/store/index.js:

import Vue from "vue";
import Vuex from "vuex";
import counterStore from "./counter/store.js";//引入counter的store
import taobaoStore from "./taobao/store.js";//引入taobao的store
import createLogger from 'vuex/dist/logger';

Vue.use(Vuex);

export default new Vuex.Store({
    modules: {//放入modules
        counterStore,
        taobaoStore
    },
    plugins : [createLogger()]
})

./www/app/store/counter/store.js:

export default {
    namespaced: true,  //命名空間
    state : {
        a:100
    },
    mutations : {
        ADD(state,payload){
            state.a++//上面的state
        }
    },
    actions : {
        ADD({commit}){
            commit("ADD")//調用上面mutations的ADD
        }
    }
}

./www/app/store/taobao/store.js:

export default {
    namespaced: true,//開啓命名空間跟上面同樣的用法
    state : {
        a:200
    },
    mutations : {},
    actions : {}
}

5、配置虛擬服務器

通常狀況下,一個應用的數據都須要等待後端接口人員開發完對應的接口才能夠獲取到,這樣子的效率有點低。最好是咱們能夠本身模擬接口數據,進行頁面的數據填充,打通全部關節,以後等接口開發好了,改下接口地址就行了。

因此,做爲前端和客戶端開發人員,在後端尚未給出對應的api接口時,咱們沒法作測試。

這時,咱們可使用json-server快速搭建一個測試的api接口,能在幾十秒以內搭建好。

json-server 它能模擬「數據庫」,提供了一套簡單的APIRESTFUL)接口。

在開發過程當中,先後端不管是否分離,接口多半是滯後於頁面開發的。因此創建一個RESTFUL風格的API接口,給前端頁面提供虛擬的數據,是很是有必要的。

 

由於它足夠簡單,寫少許數據,便可使用。

也由於它足夠強大,支持CORSJSONP跨域請求,支持GET, POST, PUT, PATCH DELETE 方法,更提供了一系列的查詢方法,如limitorder等。下面將詳細介紹 json-server 的使用。

https://github.com/typicode/json-server

相關文章
相關標籤/搜索