Vue.js-狀態管理與Vuex

學習筆記: 狀態管理與Vuex

狀態管理與Vuex

非父子組件(跨級組件和兄弟組件)通訊時,使用了bus(中央事件總線)的一個方法,用來觸發和接收事件,進一步起到通訊的做用。css

一個組件能夠分爲數據(model)和視圖(view),數據更新時,視圖也會自動更新。在視圖中又能夠綁定一個事件,它們觸發methods裏指定的方法,從而能夠改變數據、更新視圖,這是一個組件基本的運行模式。html

const store = new Vuex.Store({});

倉庫store包含了應用的數據(狀態)和操做過程。Vuex裏的數據都是響應式的,任何組件使用同一store的數據時,只要store的數據變化,對應的組件也會當即更新。前端

數據保存在Vuex選項的state字段內。vue

const store = new Vuex.Store({
    state: {
        count: 0
    }
});

在任何組件內,能夠直接經過$store.state.count讀取。webpack

<template>
    <div>
        <h1>首頁</h1>
        {{$store.state.count}}
    </div>
</template>

直接卸載template裏顯得有點亂,能夠用一個計算屬性來顯示:git

<div>
    <h1>首頁</h1>
    {{count}}
</div>

export default {
    computed: {
        count() {
            return $store.state.count;
        }
    }
}

在組件內來自store的數據只能讀取,不能手動修改,修改store中數據的惟一途徑是顯式地提交mutationsgithub

mutations是Vuex的第二個選項,用來直接修改state裏的數據。web

在組件內,經過this.$store.commit方法來執行mutationsvue-router

mutations還能夠接受第二個參數,能夠是數字、字符串或對象等類型。數組

ES6語法

函數的參數能夠設定默認值,當沒有傳入該參數時,使用設置的值。

increment(state,n=1)等同於:
    increment(state,n){
        n=n||1;
    }

提交mutations的另外一種方式是直接使用包含type屬性的對象。

mutations裏儘可能不要異步操做數據,不然組件在 commit後數據不能當即改變,並且不知道何時會改變。

高級用法

Vuex還有其餘3個選項可使用:getteractionsmodules

getter能將computed的方法提取出來,也能夠依賴其餘的getter,把getter做爲第二個參數。

actionmutation很像,不一樣的是action裏面提交的是mutation,而且能夠一步操做業務邏輯。

action在組件內經過$store.dispatch觸發。

modules用來將store分割到不一樣模塊,當項目足夠大時,store裏的stategettersmutationsactions會很是多,使用modules能夠把它們寫到不一樣的文件中。

modulemutationgetter接收的第一個參數state是當前模塊的狀態。在actionsgetters中還能夠接收一個參數rootState,來訪問根節點的狀態。

實戰:中央事件總線插件vue-bus

中央事件總線bus做爲一個簡單的組件傳遞事件,用於解決跨級和兄弟組件通訊的問題。

vue-bus插件給Vue添加一個屬性$bus,並代理$emit$on$off三個方法。

ES6語法

emit(event,..args)中的 ...是函數參數的解構。由於不知道組件會傳遞多少個參數進來,使用 ...args能夠把從當前參數到最後的參數都獲取到。

使用vue-bus有兩點須要注意:

  1. 第一是$bus.on應該在created鉤子內使用,若是在mounted使用,它可能接收不到其餘組件來自created鉤子內發出的事件;
  2. 第二點是使用了$bus.onbeforeDestroy鉤子裏應該再使用$bus.off解除,由於組件銷燬後,就沒有必要把監聽的句柄存儲在vue-bus中。

Vue插件

註冊插件須要一個公開的方法install,它的第一個參數時Vue構造器,第二個參數是一個可選的選項對象。

<p data-height="350" data-theme-id="0" data-slug-hash="RJVOXd" data-default-tab="js" data-user="whjin" data-embed-version="2" data-pen-title="Vue插件" class="codepen">See the Pen Vue插件 by whjin (@whjin) on CodePen.</p>
<script async src="https://static.codepen.io/ass...;></script>

前端路由與vue-router

SPA的核心就是前端路由,對於一個網址,每次GET或POST等請求在服務端有一個專門的正則配置列表,而後匹配到具體的一條路徑後,分發到不一樣的Controller,進行各類操做,最終將html或數據返回給前端,這樣就完成了一次IO。

前端路由,即由前端來維護一個路由規則。實現方式有兩種;

  1. 一種是利用urlhash,就是常說的錨點(#),JavaScript經過hashChange事件來監聽url的改變;
  2. 另外一種就是HTML5的history模式,它使url看起來像普通網站那樣,以/分割,沒有#,但也沒並無跳轉,不過使用這種模式須要服務端支持,服務端在接收到全部的請求後,都指向同一個html文件,否則會出現404

所以,SPA只有一個html,整個網站全部的內容都在這個html裏,經過JavaScript來處理。

若是要獨立開發一個前端路由,須要考慮到頁面的可插拔、生命週期、內存管理等問題。

vue-router

vue-router的實現原理與經過is特性實現動態組件的方法相似,路由不一樣的頁面事實上就是動態加載不一樣的組件。

建立一個數組來指定路由匹配列表,每個路由映射一個組件:

const Routers = [
    {
        path: '/index',
        component: (resolve) => require(['./views/index.vue'], resolve)
    },
    {
        path: '/about',
        component: (resolve) => require(['./views/about.vue'], resolve)
    }
];

Routers裏每一項的path屬性就是指定當前匹配的路徑,component是映射的組件。

webpack會把每個路由都打包爲一個js文件,在請求道該頁面時,再去加載這個頁面的js,也就是異步實現的懶加載(按需加載)。這樣作的好處是不須要在打開首頁的時候就把全部的頁面內容所有加載進來,只在訪問時才加載。

使用了異步路由後,變移除的每一個頁面的 js都叫作 chunk(塊),它們命名默認是 0.main.js1.main.js...
能夠在 webpack配置的出口 output裏經過設置 chunkFilename字段修改 chunk命名。
output: {
    publicPath: "/dist/",
        filename: "[name].js",
        chunkFilename: "[name].chunk.js"
}

有了chunk後,在每一個頁面(.vue文件)裏寫的樣式也須要配置後纔會打包進main.css,不然仍然會經過JavaScript動態建立<style>標籤的形式寫入。

const RouterConfig = {
    //使用HTML5的History路由模式
    mode: 'history',
    routes: Routers
};

const router = new VueRouter(RouterConfig);

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

在RouterConfig裏設置modehistory會開啓HTML5的History路由模式,經過/設置路徑。若是不配置mode,就會使用#來設置路徑。

開啓History路由,在生產環境時必須進行配置,將全部路由都指向同一個html,或設置404頁面,不然刷新時頁面就會出現404

在路由列表裏,能夠在最後新加一項,當訪問的路徑不存在時,重定向到首頁:

{
    path: '*',
    redirect: '/index'
}

路由列表的path能夠帶參數,好比/user/123,其中用戶ID123是動態的,但它們路由到同一個頁面,在這個頁面裏,指望獲取這個ID,然互毆請求相關數據。

跳轉

vue-router有兩種跳轉頁面的方法,第一種是使用內置的<router-link>組件,它會被渲染爲一個<a>標籤。

<template>
    <div>
        <h1>首頁</h1>
        <router-link to="/about">跳轉到about</router-link>
    </div>
</template>

它的用法與通常的組件同樣,to是一個prop,指定須要跳轉的路徑,也能夠用v-bind動態設置。

使用<router-link>,在HTML5的History模式下會攔截點擊,避免瀏覽器從新加載頁面。

<router-view>還有其餘一些prop,經常使用的有:

  • tag 能夠指定渲染成什麼標籤,好比<router-link to="/about" tag="li">渲染的結果就是<li>,而不是<a>
  • replace 使用replace不會留下History記錄,因此導航後不能用後退鍵返回上一個頁面,如<router-link to="/about" replace>
  • active-class<router-link>對應的路由匹配成功時,會自定給當前元素設置一個名爲router-link-activeclass,設置prop:active-class能夠修改默認的名稱。在作相似導航欄時,可使用該功能高亮顯示當前頁面對應的導航欄單項,可是通常不會修改active-class,直接使用默認值router-link-active

有時候,跳轉頁面可能須要在JavaScript中進行,相似於window.location.href。這時可使用第二種跳轉方法,使用router實例的方法。

<template>
    <div>
        <h1>介紹頁</h1>
        <button @click="handleRouter">跳轉到user</button>
    </div>
</template>

<script>
    export default {
        methods: {
            handleRouter() {
                this.$router.push('/user/123');
            }
        }
    }
</script>

$router還有其餘一些方法:

  • replace 相似於<router-link>replace功能,它不會向history添加新紀錄,而是替換掉當前的history記錄,如this.$router.replace('/user/123')
  • go 相似於window.history.go(),在history記錄中向前或後退多少步,參數是整數

高級用法

在SPA項目中,如何修改網頁的標題?

在頁面發生路由變化時,統一設置。

vue-router提供了導航鉤子beforeEachafterEach,它們會在路由即將改變前和改變後觸發,因此設置標題能夠在beforeEach鉤子完成。

<p data-height="365" data-theme-id="0" data-slug-hash="gKRaLm" data-default-tab="js" data-user="whjin" data-embed-version="2" data-pen-title="vue-router導航鉤子" class="codepen">See the Pen vue-router導航鉤子 by whjin (@whjin) on CodePen.</p>
<script async src="https://static.codepen.io/ass...;></script>

導航鉤子有3個參數:

  • to 即將要進入的目標的路由對象
  • from 當前導航即將要離開的路由對象
  • next 調用該方法後,才能進入下一個鉤子

路由列表的meta字段能夠自定義一些信息,將每一個頁面的title寫入meta來統一維護,beforeEach鉤子能夠從路由對象to裏獲取meta信息,從而改變標題。

某些頁面須要校驗是否登陸,若是登陸就能夠訪問,不然跳轉到登陸頁。經過localStorage來簡單判斷是否登陸。

router.beforeEach((to, from, next) => {
    if (window.localStorage.getItem('token')) {
        next()
    } else {
        next('/login')
    }
});

next()的參數設置爲false,能夠取消導航,設置爲具體的路徑能夠導航到指定的頁面。

使用webpack構建

webpack的主要適用場景時單頁面富應用(SPA)。SPA經過是由一個html文件和一堆按需加載的js文件組成。

exportimport是用來導出和導入模塊的。一個模塊就是一個js文件,它擁有獨立的做用域,裏面定義的變量外部是沒法獲取的。

module對象的rules屬性中能夠指定一系列的loaders,每個loader都必須包含testuse兩個選項。

webpack編譯過程當中遇到require()import語句導入一個後綴名爲.css的文件時,先將它經過css-loader轉換,再經過style-loader轉換,而後繼續打包。use選項的值能夠是數組或字符串,若是是數組,它的編譯順序就是從後往前。

webpack的主要核心部分包括 入口(Entry)出口(Output)加載器(Loaders)插件(Plugin)

單文件組件與vue-loader

<style>標籤使用scoped屬性,表示當前的CSS只在這個組件有效,若是不加,namediv的樣式會應用到整個項目。

使用.vue文件須要先安裝vue-loadervue-style-loader等加載器並作配置。若是要使用ES6語法,還須要安裝babelbabel-loader等加載器。

<p data-height="465" data-theme-id="0" data-slug-hash="NzjNgp" data-default-tab="js" data-user="whjin" data-embed-version="2" data-pen-title="Vue-webpack.config.js" class="codepen">See the Pen Vue-webpack.config.js by whjin (@whjin) on CodePen.</p>
<script async src="https://static.codepen.io/ass...;></script>

新建.babelrc文件,並寫入babel的配置,webpack會依賴此配置文件來使用babel編譯ES6代碼。

{
  "presets": ["es2015"],
  "plugins": ["transform-runtime"],
  "comments": false
}

每一個.vue文件表明一個組件,組件之間能夠相互依賴。

ES語法:

=>是箭頭函數

render: h=>h(App)等同於
    render: function(h) {
        return h(App)
    }
    
也等同於:
    render: h=>{
        return h(App)
    }

箭頭函數裏的this指向與普通函數不同,箭頭函數體內的this對象就是定義時所在的對象,而不是使用時所在的對象。

相關文章
相關標籤/搜索