當面試被問vuex的實現(xiàn)原理,你要怎么回答?下面本篇文章就來帶大家深入了解一下vuex的實現(xiàn)原理,希望對大家有所幫助!
關(guān)于vuex就不再贅述,簡單回顧一下:當應(yīng)用碰到多個組件共享狀態(tài)時,簡單的單向數(shù)據(jù)流很容易被破壞:第一,多個視圖依賴于同一狀態(tài);第二,來自不同視圖的行為需要變更同一狀態(tài)。
(資料圖片)
若解決前者使用傳參的方式,則不適用于多層嵌套的組件以及兄弟組件;若解決后者使用父子組件直接引用或事件變更和同步狀態(tài)的多份拷貝,則不利于代碼維護。
所以,最好的辦法是:把組件的共享狀態(tài)抽取出,以一個全局單例模式管理!這也正是vuex背后的基本思想?!鞠嚓P(guān)推薦:vuejs視頻教程、web前端開發(fā)】
所以,vuex的大致框架如下:
class Store { constructor() { // state // getters // mutations // actions } // commit // dipatch}接下來,就寫寫看。
vue create vue2-vuex//創(chuàng)建vue2項目yarn add vuex@next --save//安裝vuexyarn serve//啟動項目
1、State
(1)使用
//store.js// 倉庫import Vue from "vue"import Vuex from "vuex"import extra from "./extra.js"Vue.use(Vuex) //引入vuex的方式,說明Store需要install方法export default new Vuex.Store({ // 倉庫數(shù)據(jù)源 state: { count: 1, dowhat: "addCount" },}//app.vue<template> <div class="testState"> <p>{{mycount}}</p> <p>{{dowhat}}:{{count}}</p> </div></template><script>export default { import { mapState } from "vuex" // 推薦方式 computed: mapState()({ mycount: state => state.count }), // 推薦方式的簡寫方式 computed: { // 解構(gòu)的是getters ...mapState(["count", "dowhat"]) }, }</script>(2)注意
由于 Vuex 的狀態(tài)存儲是響應(yīng)式的,從 store 實例中讀取狀態(tài)最簡單的方法就是在計算屬性中返回某個狀態(tài),這種模式導(dǎo)致組件依賴全局狀態(tài)單例。在模塊化的構(gòu)建系統(tǒng)中,在每個需要使用 state 的組件中需要頻繁地導(dǎo)入,并且在測試組件時需要模擬狀態(tài)
Vuex 通過 store選項,提供了一種機制將狀態(tài)從根組件“注入”到每一個子組件中(需調(diào)用 Vue.use(Vuex))
(3)實現(xiàn)
所以除了Store內(nèi)部的五大屬性以外,還需要考慮插件的一個install方法,所以大致框架如下:
class Store { constructor() { // state // getters // mutations // actions //modules } // commit // dipatch}let Vuex = { Store, Install}export default Vuex所以,接下來就可以具體實現(xiàn)了,
class Store { constructor(options) { // state this.state = options.state }}let install = function(_Vue) { _Vue.mixin({ beforeCreate() { //在組件創(chuàng)建之前自動調(diào)用,每個組件都有這個鉤子 if (this.$options && this.$options.store) { //this.$options讀取根組件 this.$store = this.$options.store } else { this.$store = this.$parent && this.$parent.$store } } })}然而,上述的state的實現(xiàn)有一個缺點:當改變數(shù)據(jù)的時候,state的數(shù)據(jù)不能動態(tài)的渲染。所以如何把state里的數(shù)據(jù)成為響應(yīng)式成為關(guān)鍵問題?實際上,類似vue里的data,也可以通過這種方式讓其成為響應(yīng)式。那么就得從install方法中傳入Vue,所以改變后:
let Vue=nullclass Store { constructor(options) { // state this.vm = new _Vue({ data: { state: options.state//data中的數(shù)據(jù)才是響應(yīng)式 } }) } get state() { return this.vm.state }}let install = function(_Vue) {//用于Vue.use(plugin) Vue=_Vue _Vue.mixin({ onBeforeCreate() { //在組件創(chuàng)建之前自動調(diào)用,每個組件都有這個鉤子 if (this.$options && this.$options.store) { //this.$options讀取根組件 this.$store = this.$options.store } else { this.$store = this.$parent && this.$parent.$store } } })}2、getters
(1)使用
//store.jsexport default new Vuex.Store({ // 計算屬性 getters: { // 這里的函數(shù)不需要調(diào)用,可以直接使用,官方默認前面有g(shù)et getCount(state) {//接受 state 作為其第一個參數(shù) return state.count * 100; } },}(2)注意
有時候我們需要從 store 中的 state 中派生出一些狀態(tài)(比如增加,刪除,過濾等等),Vuex 允許我們在 store 中定義“getter”(可以認為是 store 的計算屬性)。就像計算屬性一樣,getter 的返回值會根據(jù)它的依賴被緩存起來,且只有當它的依賴值發(fā)生了改變才會被重新計算,Getter 接受 state 作為其第一個參數(shù),getter 在通過方法訪問時,每次都會去進行調(diào)用,而不會緩存結(jié)果
(3)實現(xiàn)
// getters let getters = options.getters || {} this.getters = {} Object.keys(getters).forEach(getterName => { Object.defineProperty(this.getters, getterName, { get: () => { return getters[getterName](this.state) } }) })3、mutations
(1)使用
//store.jsexport default new Vuex.Store({ // 相當于methods mutations: { // mutations內(nèi)部的函數(shù),天生具備一個形參 add(state, n) { state.count += n; }, decrease(state, n) { state.count -= n; } },}methods: { submit() { console.log("success"); }, // 解構(gòu)倉庫mutations里面的方法,要啥解構(gòu)啥 ...mapMutations(["add", "decrease"]), // this.$store.commit("add"), ...mapActions(["addAction", "decreaseAction"]), // this.addAction()調(diào)用actions里面的方法 // this.$store.dispatch("add"),}(2)注意
更改 Vuex 的 store 中的狀態(tài)的唯一方法是提交 mutation。Vuex 中的 mutation 類似于事件:每個 mutation 都有一個字符串的 事件類型 (type)和 一個 回調(diào)函數(shù) (handler)。這個回調(diào)函數(shù)就是進行狀態(tài)更改的地方,并且它會接受 state 作為第一個參數(shù),不能直接調(diào)用一個 mutation handler。這個選項更像是事件注冊:“當觸發(fā)一個類型為 increment的 mutation 時,調(diào)用此函數(shù)?!币獑拘岩粋€ mutation handler,你需要以相應(yīng)的 type 調(diào)用 store.commit方法
可以向 store.commit傳入額外的參數(shù),即 mutation 的 載荷(payload),在大多數(shù)情況下,載荷應(yīng)該是一個對象,這樣可以包含多個字段并且記錄的 mutation 會更易讀
(3)實現(xiàn)
// mutations let mutations = options.mutations || {} this.mutations = {} Object.keys(mutations).forEach(mutationName => { this.mutations[mutationName] = (arg) => {//保證多個(第二個)參數(shù)的傳入 mutations[mutationName](this.state, arg) } }) commit = (method, arg) => {//使用箭頭函數(shù)改變被調(diào)用的this的指向 // console.log(this); this.mutations[method](arg) }4、actions
(1)使用
//store.jsexport default new Vuex.Store({ actions: { addAction(context) { // 在這里調(diào)用add方法 context.commit("add", 10); }, decreaseAction({ commit }) { commit("decreaseAction", 5) } },}(2)注意
Action 提交的是 mutation,而不是直接變更狀態(tài)。Action 可以包含任意異步操作Action 函數(shù)接受一個與 store 實例具有相同方法和屬性的 context 對象Action 通過store.dispatch方法觸發(fā)Action 通常是異步的,store.dispatch可以處理被觸發(fā)的 action 的處理函數(shù)返回的 Promise,并且 store.dispatch仍舊返回 Promise一個 store.dispatch在不同模塊中可以觸發(fā)多個 action 函數(shù)。在這種情況下,只有當所有觸發(fā)函數(shù)完成后,返回的 Promise 才會執(zhí)行(3)實現(xiàn)
// actions let actions = options.actions || {} this.actions = {} Object.keys(actions).forEach(actionName => { this.actions[actionName] = (arg) => { actions[actionName](this, arg) } }) dispatch=(method, arg) =>{ this.actions[method](arg) }5、modules
(1)使用
// actions let actions = options.actions || {} this.actions = {} Object.keys(actions).forEach(actionName => { this.actions[actionName] = (arg) => { actions[actionName](this, arg) } }) dispatch=(method, arg) =>{ this.actions[method](arg) }//store.js modules: { extra: extra }(2)注意
由于使用單一狀態(tài)樹,應(yīng)用的所有狀態(tài)會集中到一個比較大的對象。當應(yīng)用變得非常復(fù)雜時,store 對象就有可能變得相當臃腫,Vuex 允許我們將 store 分割成模塊(module)。每個模塊擁有自己的 state、mutation、action、getter、甚至是嵌套子模塊——從上至下進行同樣方式的分割對于模塊內(nèi)部的 mutation 和 getter,接收的第一個參數(shù)是模塊的局部狀態(tài)對象對于模塊內(nèi)部的 action,局部狀態(tài)通過context.state暴露出來,根節(jié)點狀態(tài)則為 context.rootState對于模塊內(nèi)部的 getter,根節(jié)點狀態(tài)(rootState)會作為第三個參數(shù)暴露出來
let Vue = null//全局的_Vueclass Store { constructor (options) { // state //this.state = options.state 寫法不完美,當改變數(shù)據(jù)的時候,不能動態(tài)的渲染,所以需要把data中的數(shù)據(jù)做成響應(yīng)式的 this.vm = new _Vue({ data: { state: options.state//data中的數(shù)據(jù)才是響應(yīng)式 } }) // getters let getters = options.getters || {} this.getters = {} Object.keys(getters).forEach(getterName => { Object.defineProperty(this.getters, getterName, { get: () => { return getters[getterName](this.state) } }) }) // mutations let mutations = options.mutations || {} this.mutations = {} Object.keys(mutations).forEach(mutationName => { this.mutations[mutationName] = (arg) => {//保證多個(第二個)參數(shù)的傳入 mutations[mutationName](this.state, arg) } }) // actions let actions = options.actions || {} this.actions = {} Object.keys(actions).forEach(actionName => { this.actions[actionName] = (arg) => { actions[actionName](this, arg) } }) } dispatch=(method, arg) =>{ this.actions[method](arg) } commit = (method, arg) => { // console.log(this); this.mutations[method](arg) } get state() { return this.vm.state }}let install = function(_Vue) { Vue = _Vue Vue.mixin({ beforeCreate() {//在組件創(chuàng)建之前自動調(diào)用,每個組件都有這個鉤子 if (this.$options && this.$options.store) { // this.$options讀取到根組件 this.$store = this.$options.store } else { // //如果不是根組件的話,也把$store掛到上面,因為是樹狀組件 this.$store = this.$parent && this.$parent.$store } } })}let Vuex = { Store, install}export default Vuex(學(xué)習視頻分享:vuejs入門教程、編程基礎(chǔ)視頻)
以上就是深入了解vuex的實現(xiàn)原理的詳細內(nèi)容,更多請關(guān)注php中文網(wǎng)其它相關(guān)文章!
關(guān)鍵詞: