摘要:定義調(diào)用更改的中的狀態(tài)的唯一方法是提交中的非常類(lèi)似于事件每個(gè)都有一個(gè)字符串的事件類(lèi)型和一個(gè)回調(diào)函數(shù),參數(shù)。注意點(diǎn)必須是同步函數(shù)原因當(dāng)觸發(fā)的時(shí)候,回調(diào)函數(shù)還沒(méi)有被調(diào)用。實(shí)質(zhì)上任何在回調(diào)函數(shù)中進(jìn)行的狀態(tài)的改變都是不可追蹤的。
Vuex
集中式狀態(tài)管理
使用時(shí)機(jī):
每一個(gè)組件都擁有當(dāng)前應(yīng)用狀態(tài)的一部分,整個(gè)應(yīng)用的狀態(tài)是分散在各個(gè)角落的。然而經(jīng)常會(huì)需要把把狀態(tài)的一部分共享給多個(gè)組件。
Vuex:一個(gè)專(zhuān)門(mén)為Vue.js 應(yīng)用設(shè)計(jì)的狀態(tài)管理架構(gòu).
狀態(tài)管理:統(tǒng)一管理和維護(hù)各個(gè)vue組件的可變化狀態(tài)(可以理解成vue組件里的某些data數(shù)據(jù),全局變量)
出現(xiàn)背景:
追蹤自定義事件NaN,這個(gè)事件由那個(gè)組件觸發(fā)的,誰(shuí)在監(jiān)聽(tīng)它。
業(yè)務(wù)邏輯遍布各個(gè)組件,導(dǎo)致各種意想不到的問(wèn)題。
由于要顯式的分發(fā)和監(jiān)聽(tīng),父組件和子組件強(qiáng)耦合
Vuex 核心概念:
狀態(tài)樹(shù):包含所有應(yīng)用層級(jí)狀態(tài)。意味著,每個(gè)應(yīng)用將僅僅包含一個(gè)store實(shí)例。單狀態(tài)樹(shù)能夠直接定位任意特定的狀態(tài)片段。
Getters:在Vue組件內(nèi)部獲取stroe中狀態(tài)/數(shù)據(jù)的函數(shù)
Mutations:通過(guò)事件回調(diào)函數(shù)來(lái)修改store中的狀態(tài)的變化.
Actions:在組件內(nèi)部使用函數(shù),分發(fā)mutations事件的函數(shù).
為什么需要有action
每一個(gè)web應(yīng)用都至少對(duì)應(yīng)一個(gè)數(shù)據(jù)結(jié)構(gòu),而導(dǎo)致這個(gè)數(shù)據(jù)結(jié)構(gòu)狀態(tài)更新的來(lái)源很豐富;光是用戶對(duì)view的操作(dom事件)就有幾十種,此外還有ajax獲取數(shù)據(jù)、路由/hash狀態(tài)變化的記錄和跟蹤。
來(lái)源豐富不是最可怕的,更可怕的是每個(gè)來(lái)源提供的數(shù)據(jù)結(jié)構(gòu)并不統(tǒng)一。DOM事件還好,前端可以自主控制與設(shè)計(jì);ajax獲取的數(shù)據(jù),其結(jié)構(gòu)常常是服務(wù)端開(kāi)發(fā)人員說(shuō)了算,面對(duì)業(yè)務(wù)場(chǎng)景跟前端并不相同,往往會(huì)為了自己的便利,給出在前端看來(lái)很隨意的數(shù)據(jù)結(jié)構(gòu)。
web應(yīng)對(duì)中所有的數(shù)據(jù)與狀態(tài)的變化,幾乎都來(lái)自[事件],DOM事件,AJAX成功或失敗事件,路由change事件,setTimeout定時(shí)器事件,以及自定義事件。任意時(shí)間都可能產(chǎn)生需要合并全局?jǐn)?shù)據(jù)對(duì)象里的新數(shù)據(jù)或者線索。
是event響應(yīng)函數(shù)里主動(dòng)調(diào)用了action函數(shù),并且傳入它需要的數(shù)據(jù)。
action 作用:
action 是專(zhuān)門(mén)用來(lái)被component調(diào)用的函數(shù)
action函數(shù)能夠通過(guò)分發(fā)相應(yīng)的mutation函數(shù), 來(lái)觸發(fā)對(duì)stroe的更新
action 可以先從HTTP后端 或 store 中讀取其它數(shù)據(jù)之后再分發(fā)更新事件
Vuex把狀態(tài)分為:
組件本地狀態(tài)
僅在一個(gè)組件內(nèi)使用的狀態(tài)(data字段)應(yīng)用層級(jí)狀態(tài)(應(yīng)用級(jí)的狀態(tài)不屬于任何特定的組件,但每一個(gè)組件仍然可以監(jiān)視其變化從而響應(yīng)式的更新DOM)
組件內(nèi)部使用的狀態(tài),通過(guò)配置選項(xiàng)傳入Vue組件內(nèi)部的意思。
應(yīng)用級(jí)別狀態(tài)
多個(gè)組件共用的狀態(tài)
同時(shí)被多個(gè)組件共享的狀態(tài)層級(jí)
Vuex 應(yīng)用的核心是store(倉(cāng)庫(kù))理解成項(xiàng)目中使用的數(shù)據(jù)的集合。 包含著大部分的狀態(tài)(即state)
Vuex和單純的全局對(duì)象:
Vuex的狀態(tài)存儲(chǔ)是響應(yīng)式的。當(dāng)Vue組件從store中讀取狀態(tài)的時(shí)候,若store中的狀態(tài)發(fā)生變化。
不能截至改變store中的狀態(tài)。改變store中的狀態(tài)的唯一途徑就是顯示地分發(fā) 狀態(tài)變更事件
作用:方便的跟蹤每一個(gè)狀態(tài)的變化。
vuex 把應(yīng)用的數(shù)據(jù)和修改的數(shù)據(jù)的方法,放在了一個(gè) sotre 對(duì)象里面統(tǒng)一管理,對(duì)數(shù)據(jù)的獲取和操作則分別通過(guò) vm新增的配置屬性 vuex 的 getters 和 actions 來(lái)進(jìn)行
整個(gè)APP的數(shù)據(jù)就是存放在state對(duì)象里,隨取隨用.
定義一個(gè)mutations對(duì)象??梢园?b>mutations理解為“用于改變state狀態(tài)的一系列方法”
在vuex的概念里,state僅能通過(guò)mutations修改。
好處:能夠更直觀清晰的集中管理應(yīng)用的狀態(tài)。
數(shù)據(jù)流動(dòng)是單向
組件可以調(diào)用actions
Actions是用來(lái)分發(fā) mutations
只有mutations可以修改狀態(tài)
store是反應(yīng)式(狀態(tài)的變化會(huì)在組件內(nèi)部得到反映)
sotre只需要在最頂層的組件聲明一次
在入口文件加入:
var store = new Vuex.Store({ state: { a: false, money: 0 }, mutations: { increment( state ) { store.state.a; } } });
組件中使用:
computed: { a: function() { return store.state.a; } }
使用Vuex遵守規(guī)則:
應(yīng)用層級(jí)的狀態(tài)應(yīng)該幾種到單個(gè)sotre對(duì)象中。
提交mutation是更改狀態(tài)的唯一方法,并且這個(gè)過(guò)程是同步的。
異步邏輯都應(yīng)該封裝到action里面。
State單一狀態(tài)樹(shù)
Vuex 使用 單一狀態(tài)樹(shù)。 使用一個(gè)對(duì)象包含全部的應(yīng)用層級(jí)狀態(tài)(數(shù)據(jù))。把它作為一個(gè)唯一數(shù)據(jù)源提供方存在(全局變量)。
每個(gè)應(yīng)用程序僅僅包含一個(gè)store實(shí)例。單狀態(tài)數(shù)能夠直接定位任意特定的狀態(tài)片段,在調(diào)試過(guò)程中能夠輕易的獲取整個(gè)當(dāng)前應(yīng)用狀態(tài)。(單狀態(tài)樹(shù)和模塊化并不沖突)
Vuex 中數(shù)據(jù)都是單向的,Vue組件只能從 store獲取。
如何在Vue組件中獲得Vuex狀態(tài)
由于 Vuex 的狀態(tài)存儲(chǔ)是響應(yīng)式的,從 store 實(shí)例中 讀取狀態(tài)在計(jì)算屬性computed 中返回某個(gè)狀態(tài)。
new Vue({ el: ".app", computed: { count: function () { return stroe.state.count; } } });
store.state特性:
每當(dāng)store.state.count變化的時(shí)候,都會(huì)重新求取計(jì)算屬性,并且觸發(fā)更新相關(guān)的DOM。
導(dǎo)致組件過(guò)度依賴(lài)全局狀態(tài)單例。
每個(gè)模塊化的組件中,需要頻繁導(dǎo)入,并且在測(cè)試組件時(shí)需要模擬狀態(tài)。
組件仍然保有局部狀態(tài)
使用Vuex并不意味著需要將所有的狀態(tài)放入Vuex。
優(yōu)點(diǎn):把所有狀態(tài)放入Vuex會(huì)是的狀態(tài)變化更顯式和易調(diào)試。
缺點(diǎn):代碼變得冗長(zhǎng)和不直觀。
如果有些狀態(tài)嚴(yán)格屬于單個(gè)組件,最好還是作為組件的局部狀態(tài)。
Getters需要對(duì)數(shù)據(jù)進(jìn)行第二次加工處理,全局函數(shù)處理,可以在 store中定義getters(可以認(rèn)為store的計(jì)算屬性)。
定義
const store = new Vue({ state: { list: [{id: 1, text: "a"}, {id: 2, text: "b"}] }, getters: { done: state => { return state.todos.filter(todo => todo.done); } } });
調(diào)用
store.getters.lenCountMutations
更改Vuex的store中的狀態(tài)的唯一方法是提交 mutation.
Vuex中的mutations非常類(lèi)似于事件:每個(gè)mutations都有一個(gè)字符串的 事件類(lèi)型(type) 和 一個(gè)回調(diào)函數(shù)(handler),參數(shù):state。
定義
const store = new Vue.store({ state: { a: 0 }, mutations: { heade( state ) { state.a += 10; } } });
調(diào)用
sotre.commit("heade");
傳入多個(gè)參數(shù)(提交載荷Payload) 多數(shù)情況下,另外的參數(shù),會(huì)是對(duì)象。
// 定義 mutations: { heade( state, n ) { state.a += n; } } // 調(diào)用 store.commit("heade", 100);
Mutations 需遵守 Vue 的響應(yīng)規(guī)則
Vuex中的store中的狀態(tài)是響應(yīng)式的,變更狀態(tài)時(shí),監(jiān)聽(tīng)狀態(tài)的Vue組件也會(huì)自動(dòng)更新。
注意:
最好提前在sotre中出初始化好所有所需屬性。
當(dāng)需要在對(duì)象上添加新屬性時(shí),應(yīng)該使用Vue.set(obj, "newProp", 123) 或者 以新對(duì)象替換老對(duì)象。
使用常量替代Mutation事件類(lèi)型
使用常量替代mutation事件類(lèi)型在各種 Flux 實(shí)現(xiàn)中常見(jiàn)模式.
結(jié)果:可以使linter之類(lèi)的工具發(fā)揮作用,同時(shí)把這些常量放在多帶帶的文件中可以讓代碼合作者對(duì)這個(gè)app包含的mutation清晰明了。
// mutation-types.js export const FETCH_MOVIE_BY_ID = "FETCH_MOVIE_BY_ID"; // store.js import Vuex form "vuex"; import {FETCH_MOVIE_BY_ID} from "./mutations-types.js"; const store = new Vuex.Store({ state: {}, mutations: { ["FETCH_MOVIE_BY_ID"]( state ) { // mutate state } } });
mutation注意點(diǎn):
mutation必須是同步函數(shù)
原因:當(dāng)mutation觸發(fā)的時(shí)候,回調(diào)函數(shù)還沒(méi)有被調(diào)用。 實(shí)質(zhì)上任何在回調(diào)函數(shù)中進(jìn)行的狀態(tài)的改變都是不可追蹤的。
在組件中提交Mutations
定義:
increment: function( context ) { context.a += 10; }
使用方式:
// 方式1 store.commit("increment"); // this.$store.commit("xxx") // 方式2 methods: { mapMutations(["increment"]) // // 映射 this.increment() 為 this.$store.commit("increment") }Actions
Actions類(lèi)似于mutations,不同點(diǎn):
Actions 提交的是mutation,而不是直接變更狀態(tài)。
Actinos 可以包含任意異步操作。
store = new Vuex.Store({ state: { a: 0 }, mutations: { add() { state.a += 10; } }, actions: { add( context ) { context.commit("add"); } } });
Action 函數(shù)參數(shù):store實(shí)例具有相同的方法和屬性的context對(duì)象。
可以調(diào)用context.commit(); 執(zhí)行一個(gè)mutation。 或者通過(guò)context.state 和 context.getters 來(lái)獲取 state和 getters。
分發(fā)Action
Action通過(guò)store.dispatch方法觸發(fā).
store.dispatch();
Action 能夠支持載荷方式和對(duì)象方式進(jìn)行分發(fā).
// 以載荷形式分發(fā) store.dispatch("add", { amount: 10 }) // 以對(duì)象形式分發(fā) store.dispatch({ type: "add", amount: 10 })
在組件中分發(fā)Action
定義:
increment: function( context ) { context.a += 10; }
使用方式:
// 方式1 store.dispatch("increment"); // this.$store.dispatch("xxx") // 方式2 methods: { mapActions(["increment"]) // // 映射 this.increment() 為 this.$store.commit("increment") }
組合Actions
Action 通常是異步,如何知道action 什么時(shí)候結(jié)束。如何組合多個(gè)action,以處理更加復(fù)雜的異步流程?
store.dispatch的返回的是被處罰的action函數(shù)的返回值,因?yàn)?,可以在action中返回Promise
actions: { actionA({commit}) { return new Promise((resolve, reject) => { setTimeout(() => { commit("someMutation"); resolve(); }, 1000) }) }, actionB({dispatch}) { return dispatch("actionA").then(()=>{ commit("someOhterMutation"); }) } }
一個(gè)sotre.dispatch()在不同模塊中可以出發(fā)多個(gè)action函數(shù)。在當(dāng)前情況下,只有當(dāng)所有觸發(fā)函數(shù)完成后,返回的Promise才會(huì)執(zhí)行。
案例:
import * as types from "../types"; import {fetchMoviesByType, fetchSearchMovies, fetchMovieById} from "../api"; const state = { movies: [], movieList: { title: "", total: 0, subjects: [], }, busy: false, movie: {}, }; const actions = { [types.FETCH_MOVIES](context,payload){ fetchMoviesByType(payload.type, payload.start, payload.count) .then(data=>{ data.type = payload.type; return context.commit([types.FETCH_MOVIES], data) }); }, [types.FETCH_MOVIE_LIST](context,payload){ fetchMoviesByType(payload.type, payload.start) .then(data=>context.commit([types.FETCH_MOVIE_LIST], data)); }, [types.FETCH_MOVIE_BY_ID](context, id){ fetchMovieById(id) .then(data => context.commit([types.FETCH_MOVIE_BY_ID], data)); }, [types.SET_INFINITE_BUSY](context, data){ context.commit([types.SET_INFINITE_BUSY], data); }, [types.CLEAN_MOVIE](context){ context.commit(types.CLEAN_MOVIE); }, [types.CLEAN_MOVIES](context){ context.commit([types.CLEAN_MOVIES]) }, [types.CLEAN_MOVIE_LIST](context){ context.commit([types.CLEAN_MOVIE_LIST]) } }; const mutations = { [types.FETCH_MOVIES](state, list){ state.movies.push(list); }, [types.FETCH_MOVIE_LIST](state, list){ state.movieList.title = list.title; state.movieList.total = list.total; state.movieList.subjects = state.movieList.subjects.concat(list.subjects); if(state.movieList.subjects.length < state.movieList.total){ state.busy = false; } }, [types.FETCH_MOVIE_BY_ID](state, movie){ state.movie = movie; }, [types.SET_INFINITE_BUSY](state, data){ state.busy = data; }, [types.CLEAN_MOVIE](state){ state.movie = {}; }, [types.CLEAN_MOVIES](state){ state.movies = []; }, [types.CLEAN_MOVIE_LIST](state){ state.movieList = {}; } }; export default { state, mutations, actions }
`api.js` 發(fā)送ajax // 定義ajax事件 `modules/movie.js` 中寫(xiě) state,mutations,actions // action 中執(zhí)行ajax定義事件, 回調(diào)函數(shù)中執(zhí)行mutations中定義的事件 `mutations` 定義事件, 第一個(gè)參數(shù)是`context`,當(dāng)前定義的store `types.js` 定義常量替代mutation事件類(lèi)型
// src/components/header.vue // 從vuex拿數(shù)據(jù),然后渲染到頁(yè)面上 // 如果需要修改可以調(diào)用setTitle import { setTitle } from "../vuex/actions"; export default { vuex: { // 獲取vuex狀態(tài)數(shù)據(jù) getters: { title: state => state.title, info: ({index}) => index.info }, // 狀態(tài)變更事件 actions: { setTitle } } }Modules
場(chǎng)景:使用單一狀態(tài)數(shù),導(dǎo)致應(yīng)用的所有狀態(tài)集中到一個(gè)很大的對(duì)象。但是,當(dāng)應(yīng)用變得很大時(shí),sotre對(duì)象會(huì)變得難以維護(hù)。
Vuex允許將sotre分割到模塊(module),每個(gè)模塊擁有自己的state,mutation,action,getters.
import Vuex from "vuex" import Vue from "vue" import movie from "./modules/movie" Vue.use(Vuex); export default new Vuex.Store({ modules: { movie } });Store配置項(xiàng)
new Vuex.Store({ state: {}, // 初始狀態(tài) actions: {}, // 執(zhí)行 mutation,異步. getters: {}, // 全局方法,二次加工數(shù)據(jù) mutations: {} // Store 與外界交互的入口 });
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/87920.html
閱讀 2808·2023-04-25 18:06
閱讀 2603·2021-11-22 09:34
閱讀 1697·2021-11-08 13:16
閱讀 1323·2021-09-24 09:47
閱讀 3059·2019-08-30 15:44
閱讀 2784·2019-08-29 17:24
閱讀 2597·2019-08-23 18:37
閱讀 2446·2019-08-23 16:55