日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > vue >内容正文

vue

如何优雅使用 vuex

發(fā)布時間:2023/11/16 vue 226 coder
生活随笔 收集整理的這篇文章主要介紹了 如何优雅使用 vuex 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

大綱

本文內(nèi)容更多的是講講使用 vuex 的一些心得想法,所以大概會講述下面這些點:

Q1:我為什么會想使用 vuex 來管理數(shù)據(jù)狀態(tài)交互?

Q2:使用 vuex 框架有哪些缺點或者說副作用?

Q3:我是如何在項目里使用 vuex 的?

初識 vuex

對于 vuex,有人喜歡,有人反感

喜歡的人覺得它可以很好的解決復(fù)雜的數(shù)據(jù)交互場景

反感的人覺得它有各種缺點:繁瑣冗余的代碼編寫、維護性差的字符串形式變量注入、過于依賴 vue 框架導(dǎo)致異步擴展場景差

這其中,有一個很模糊的點,復(fù)雜的數(shù)據(jù)交互場景并沒有一個衡量標(biāo)準(zhǔn),每個人都有自己的見解

再加上不同人有著不同的項目經(jīng)歷,這就造成了經(jīng)常會出現(xiàn)有趣的現(xiàn)象:你體會不到我為什么非要使用 vuex,他理解不了這種場景何須使用 vuex,我也講不明白選擇 vuex 的緣由

借用官網(wǎng)文檔一句話:

您自會知道什么時候需要它

很玄乎,更通俗來講就是,多踩點坑,多遭遇些痛點,當(dāng)你的最后一根稻草被壓垮時,自然就會去尋找更好的方案解決

我一直都不喜歡 vuex,因為我覺得它的 mapMutations 或者 mapState 注入到 vue 里的變量和方法都是字符串,極大的破壞了代碼的可讀性和維護性,沒辦法通過 idea 快速的跳轉(zhuǎn)到變量定義的地方

當(dāng)然,你也可以定義一些靜態(tài)變量來替換這些字符串就可以解決跳轉(zhuǎn)問題,但代價就是代碼更繁瑣了,本來使用 vuex 時就需要寫一堆繁瑣的代碼,這下更麻煩

還有一個不想使用 vuex 的原因是因為我的項目業(yè)務(wù)邏輯挺復(fù)雜,除了 vue 單文件外,項目里還劃分了來負(fù)責(zé)業(yè)務(wù)邏輯或異步任務(wù)的 js 層代碼,而 vuex 是為 vue 框架而設(shè)計的,存放在 vuex 數(shù)據(jù)中心的變量可以通過它的一些工具方法來注入到 vue 組件的 computed 計算屬性里方便直接使用,比如

import { mapState } from 'vuex'

export default {
  // 映射 this.count 為 store.state.count
  computed: mapState({
    count: state => state.count
  })
}

但如果想在 js 文件里使用 vuex 里的數(shù)據(jù),就會比較繁瑣:

import store from 'store'

console.log(store.state.count);

基于以上種種原因,我遲遲未在項目里使用 vuex

那么,我最后為什么又選擇使用了 vuex 呢?

一,項目的一些數(shù)據(jù)交互場景使用 vue 原生的輸入輸出方案已經(jīng)忍不下去了

二,我想辦法解決了我沒法忍受的 vuex 的幾個缺點了

三,這是個新項目,并沒有復(fù)雜的業(yè)務(wù)場景,項目基本由 vue 單文件來書寫即可

簡單來說,就是有個新項目符合適用 vuex 的場景,而且一些組件交互場景使用原生方案過于繁瑣,vuex 剛好能夠解決這個問題,雖然 vuex 有一定的使用成本,但這些缺點恰好又被我想了一些法子解決或簡化掉

這樣一來,引入 vuex 既能解決我的訴求,又不會引入太多我無法接受的缺點,那自然可以來玩一玩

背景

vue 框架是基于組件機制來組裝成頁面,所以頁面數(shù)據(jù)是分散到各個組件間,而組件間的數(shù)據(jù)交互使用的是 vue 自帶的輸入(props)輸出($emit)機制

這種數(shù)據(jù)交互方案有個特點,數(shù)據(jù)對象都存儲在組件內(nèi)部,交互需要通過輸入和輸出

而輸入輸出機制有個缺點:頁面復(fù)雜時,經(jīng)常需要層層傳遞數(shù)據(jù),因為非父子組件間的交互,只能尋找最近的祖先組件來做中轉(zhuǎn)

同時,輸入輸出機制還有一個局限性:當(dāng)不同頁面的組件需要進行數(shù)據(jù)交互時,它就無能為力了

平常開發(fā),這種輸入輸出的方案也基本滿足了

但如果有需要跨頁面的數(shù)據(jù)交互或者說,有需要將數(shù)據(jù)做持久化處理的場景時;以及如果頁面組件層次復(fù)雜,存在props 和 $emit 層層傳遞時,那這時候如果忍不了輸入輸出方案的用法,那么就可以研究新方案了

解決這種場景的方案很多,但從本質(zhì)上來講,都可以統(tǒng)歸為:數(shù)據(jù)中心方案

這種方案思路就是將數(shù)據(jù)對象從組件內(nèi)部移出到外部存儲維護,需要使用哪個數(shù)據(jù)變量的組件自行去數(shù)據(jù)中心取

vue 其實也有機制可以達到這種效果,如:依賴注入,但慎用,太破壞數(shù)據(jù)流的走向了

我們也可以自己用 js 來實現(xiàn)一個數(shù)據(jù)中心,專門建個 js 文件來存儲維護數(shù)據(jù)模型,需要數(shù)據(jù)變量的組件引入 js 文件來讀取

但每個數(shù)據(jù)中心都必須解決兩個問題:數(shù)據(jù)復(fù)用和數(shù)據(jù)污染,通俗來講就是數(shù)據(jù)初始化和重置,也就是數(shù)據(jù)的生命周期

數(shù)據(jù)復(fù)用是為了確保不同組件間從數(shù)據(jù)中心里讀取時,是同一份數(shù)據(jù)副本,這才能達到數(shù)據(jù)交互目的

而數(shù)據(jù)污染是指不同模塊間使用同個數(shù)據(jù)中心時,數(shù)據(jù)模型是否可以達到相互獨立,互不影響的效果,這通常是某個功能在不同模塊間被復(fù)用時會出現(xiàn)的場景;如果這種場景不好理解,那么也可以想想這種場景:再次加載該頁面,組件再次被創(chuàng)建后,從數(shù)據(jù)中心里讀取的數(shù)據(jù)副本是否是相互獨立的

如果數(shù)據(jù)存儲在 vue 組件內(nèi)部,那數(shù)據(jù)的生命周期就是跟隨著組件的創(chuàng)建和銷毀,這也是為什么 data 是一個返回對象的函數(shù),因為這樣可以借助 js 的函數(shù)作用域機制來解決數(shù)據(jù)的復(fù)用和污染問題

但數(shù)據(jù)從 vue 組件內(nèi)部移出,存儲到數(shù)據(jù)中心,那么這些處理就需要自己來實現(xiàn)

所以,數(shù)據(jù)中心并不是簡單建個 js 類,里面聲明下數(shù)據(jù)對象就完事了的

基于此,我選擇使用 vuex

vuex 副作用

先看個使用 vuex 的簡單例子:

// 聲明
const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 變更狀態(tài)
      state.count++
    }
  }
})

// vue 里使用
import { mapMutations } from 'vuex'
import { mapState } from 'vuex'

export default {
  // ...
  computed: {
     ...mapState({
         // 將 `this.count` 映射為 `this.$store.state.count`
         count: state => state.count
     })   
  },
  methods: {
    ...mapMutations([
      'increment', // 將 `this.increment()` 映射為 `this.$store.commit('increment')`
    ])
  }
}

僅僅簡單定義個數(shù)據(jù)對象,就需要聲明對象模型 state,聲明對象操作方法 mutation,然后在相應(yīng)的 vue 組件內(nèi)先通過 mapState 注入變量的讀方法,再通過 mapMutations 注入變量的寫方法

而以上這么多繁瑣的代碼,在原本的 vue 機制里,就是簡單的在 data 里聲明下變量就完事,這一對比,vuex 的使用上,復(fù)雜度和繁瑣度很大,有一定的使用成本

所以很多人不喜歡用它,官方也說簡單的頁面也沒有必要去使用它

這是我覺得 vuex 的第一個缺點,或者說副作用:繁瑣冗余的代碼編寫

第二個我覺得 vuex 的缺點就是,mapState 或 mapMutation 注入的變量,都是字符串的

字符串就意味著,你在 vue 單文件內(nèi)其他地方通過 this.xxx 使用這些變量時,當(dāng)你想查看變量的聲明時,idea 無法識別!

這是我特別無法接受的一點,降低我的維護、開發(fā)效率

不過這點因人而異,有人覺得它不是個問題,或者使用個靜態(tài)變量來替換字符串也可以解決,但這些我個人是沒辦法接受

然而 vue 原生輸入輸出的數(shù)據(jù)交互又不足夠支撐我的一些需求場景,自己用 js 實現(xiàn)個數(shù)據(jù)中心吧,又擔(dān)心沒強制規(guī)范,沒處理好,后期跑偏掉更難維護,那就想想辦法搞定 vuex 的這兩個缺點吧

如何更簡易的使用 vuex

先說下,我雖然用了些方法,讓我使用 vuex 可以達到我的預(yù)期,既滿足我的需求場景,又不至于引入太多副作用

但實際上,這種方式也許就偏離了 vuex 官方的推薦方式了,別人不一定能接受我的這種用法

所以,這篇更多的是分享我的一些思路和想法,有一說一,并不通用,歡迎拍磚

就我個人對于 vuex 的缺點,我所不能接受的就兩點:

  • 繁瑣冗余的代碼編寫
  • 維護性差、可讀性差的字符串變量注入

那么,就是得想辦法解決這兩個問題,先來說第一個

封裝自動生成代碼解決 vuex 使用繁瑣問題

用 vuex 需要編寫很多繁瑣的代碼,這些代碼是少不了的,既然少不了,那換個思路,不用我來編寫不就好了

想辦法提取共性,封裝個工具方法,讓它來生成每次使用 vuex 的那些繁瑣代碼,這樣一來,使用就方便了

state 里聲明的數(shù)據(jù)對象模型,這些代碼是沒辦法自動生成的,畢竟數(shù)據(jù)模型都不大一樣

而修改數(shù)據(jù)變量的 mutation 代碼就可以想辦法來自動生成了

/**
 * 根據(jù) state 對象屬性自動生成 mutations 更新屬性的方法,如:
 * state: {
 *  projectId: '',
 *  searchParams: {
 *      batchId: ''
 *  }
 * }
 *
 * ===>
 *
 * {
 *  updateProjectId: (state, payload) => { state.projectId = payLoad }
 *  updateSearchParams: (state, payload) => { state.searchParams = {...state.searchParams, ...payload} }
 *  updateBatchId: (state, payload) => { state.searchParams.batchId = payload }
 * }
 *
 * 非對像類型的屬性直接生成賦值操作,對象類型屬性會通過擴展運算符重新生成對象
 * 且會遞歸處理內(nèi)部對象的屬性,扁平化的生成 updateXXX 方法掛載到 mutations 對象上
 * @param {Object} stateTemplate
 */
export function generateMutationsByState(stateTemplate) {
  let handleInnerObjState = (parentKeyPath, innerState, obj) => {
    Object.keys(innerState).forEach(key => {
      let value = innerState[key];
      let updateKey = `update${key[0].toUpperCase()}${key.substr(1)}`;
      if (typeof value === 'object' && value != null && !Array.isArray(value)) {
        obj[updateKey] = (state, payload) => {
          let target = state;
          for (let i = 0; i < parentKeyPath.length; i++) {
            target = target[parentKeyPath[i]];
          }
          target[key] = { ...target[key], ...payload };
        };
        handleInnerObjState([...parentKeyPath, key], value, obj);
      } else {
        obj[updateKey] = (state, payload) => {
          let target = state;
          for (let i = 0; i < parentKeyPath.length; i++) {
            target = target[parentKeyPath[i]];
          }
          target[key] = payload;
        };
      }
    });
  };
  let mutations = {};
  Object.keys(stateTemplate).forEach(key => {
    let obj = {};
    let value = stateTemplate[key];
    let updateKey = `update${key[0].toUpperCase()}${key.substr(1)}`;
    if (typeof value === 'object' && value != null && !Array.isArray(value)) {
      obj[updateKey] = (state, payload) => {
        state[key] = { ...state[key], ...payload };
      };
      handleInnerObjState([key], value, obj);
    } else {
      obj[updateKey] = (state, payload) => {
        state[key] = payload;
      };
    }
    Object.assign(mutations, obj);
  });
  return mutations;
}

然后是 mapState 和 mapMutation 注入到 vue 組件的這些代碼也可以通過 computed 計算屬性的特性來自動生成,這樣使用上更加方便,畢竟使用 computed 計算屬性的方式就跟使用 data 里聲明的變量一樣,沒有什么區(qū)別

import store from './index';
/**
 * 將 store 里指定的 state 轉(zhuǎn)成計算屬性 computed 的 set() get()
 * vue 里就可以直接類似操作 data 屬性一樣使用 state
 *
 * @param {String} moduleName state 所屬的 store 的 module 名
 * @param {Array} states 待處理的 states e.g: ['project', 'searchParams.projectName'] 其中,
 * 掛載在 computed 上的屬性名,默認(rèn)等于 state,當(dāng) state 結(jié)構(gòu)多層時,取最后一層的屬性名
 *
 * ps: state 對應(yīng)的 mutation 必須以 updateXXX 方式命名
 */
export function storeToComputed(moduleName, states) {
  if (!store) {
    throw new TypeError('store is null');
  }
  if (!moduleName) {
    throw new TypeError("state's module name is null");
  }
  if (!states || !Array.isArray(states) || states.length === 0) {
    throw new TypeError('states is null or not array');
  }
  let computed = {};
  states.forEach(state => {
    if (state.indexOf('.') !== -1) {
      let _states = state.split('.');
      let _key = _states[_states.length - 1];
      computed[_key] = {
        get() {
          let res = store.state[moduleName];
          for (let i = 0; i < _states.length; i++) {
            res = res[_states[i]];
          }
          return res;
        },
        set(value) {
          store.commit(
            `${moduleName}/update${_key[0].toUpperCase()}${_key.substr(1)}`,
            value
          );
        },
      };
    } else {
      computed[state] = {
        get() {
          return store.state[moduleName][state];
        },
        set(value) {
          store.commit(
            `${moduleName}/update${state[0].toUpperCase()}${state.substr(1)}`,
            value
          );
        },
      };
    }
  });

  return computed;
}

那么最終可以達到的效果就是:

  • 只需在 store 文件里聲明 state 數(shù)據(jù)變量
  • 然后再需要注入的 vue 組件里注入即可
// 聲明
import { generateMutationsByState } from './helper';

const global = {
    state: {
        count: 0
    }
}
global.mutations = generateMutationsByState(global.state);

const store = new Vuex.Store({
    modules: {
        global
    }
})
// vue里使用
import { storeToComputed } from '@/store/storeToComputed';

export default {
  // ...
  computed: {
      // 將 this.$store.state.global.count 映射成 this.count
     ...storeToComputed('global', ['count'])
  },
}

我的這種用法,其實就只是單純將 vuex 拿來作為數(shù)據(jù)中心使用而已,在 store 文件里不編寫邏輯代碼,也不使用 action

這種用法的好處,我是覺得,會跟原本在 vue 的 data 里聲明變量后的用法比較類似。因為就是將原本定義在 data 里的變量換成定義在專門的 store 文件里,然后再多一步將變量通過工具方法注入到 vue 的 computed 里,接下去的使用變量的任何場景,在哪賦值,在哪取值,哪里處理異步請求等等的代碼,原本怎么寫,現(xiàn)在還是怎么寫,完全不影響

這就意味著,這種方案后續(xù)如果有缺陷,或者用不習(xí)慣,那么想切換到 vue 原生的輸入輸出方案非常方便,影響點、改動點都會比較少,就是將 storeToComputed 注入到 computed 的變量換到 data 就完事了

甚至說,后續(xù)想換掉 vuex 也會比較方便,畢竟只是單純用它當(dāng)做數(shù)據(jù)中心而已

然后再配合上 vuex 的動態(tài)掛載和卸載的用法,這個數(shù)據(jù)中心就可以像 angular 框架那樣做到精確控制數(shù)據(jù)對象的作用域和生命周期,全局共享、模塊間共享、頁面內(nèi)共享、組件內(nèi)共享等都可以很方便做到,這樣一來,數(shù)據(jù)交互就不怕復(fù)雜場景了

這是我之所以會這么使用 vuex 的考慮

自定義 vscode 插件解決字符串變量的跳轉(zhuǎn)問題

繁瑣的代碼編寫問題搞定了,接下去就是看看怎么解決字符串變量注入的跳轉(zhuǎn)問題了

先來說說,我為什么會在意變量支不支持利用 idea 直接跳轉(zhuǎn)到聲明的地方

這是因為,有些頁面比較復(fù)雜,數(shù)據(jù)變量比較多,或者時間久了,很容易忘記一些變量的命名、含義

而我們通常都只會在聲明的地方加上一些注釋

所以利用 idea 直接快速跳轉(zhuǎn)到聲明的地方,第一,有注釋可以快速幫助回憶、理清變量含義;第二,忘記變量命名全稱可以快速復(fù)制使用;第三,方便我查看其它數(shù)據(jù)變量

那么,怎么解決這個問題呢?

自然就是自己擴展開發(fā)個 vscode 插件來支持了,面向百度的話,vscode 插件開發(fā)并不困難,看幾篇教程,清楚插件的生命周期和一些 API 就可以上手了

關(guān)鍵是,如何識別 vuex 注入的這些變量?如何跳轉(zhuǎn)到 store 文件里聲明數(shù)據(jù)變量的 state 位置?

如果想做成通用的插件,那可能需要多花點精力

但如果只是基于自己當(dāng)前的項目來解決這個問題,那就簡單多了,因為項目有一定的規(guī)范,比如 vuex 的 store 文件存放的目錄地址,比如注入到 vue 組件里的使用方式,這些都是有規(guī)范和規(guī)律的

簡單說下我的思路:

  1. 先掃描項目 store 目錄下文件,識別出有數(shù)據(jù)模型 (state) 的文件,解析并存儲數(shù)據(jù)模型各個變量名和位置
  2. 注冊 vscode 的變量操作響應(yīng),當(dāng)按住 ctrl 并將鼠標(biāo)移到變量上時,響應(yīng)我們的插件程序
  3. 判斷當(dāng)前聚焦操作的變量是否是通過在 computed 里注入的變量,是則繼續(xù)下一步尋找變量聲明的文件位置
  4. 通過變量名和模塊名到 store 里匹配變量,匹配到后,記錄變量的聲明信息和文件位置,當(dāng)點擊左鍵時,響應(yīng)跳轉(zhuǎn)行為

github 地址:vuex-peek

總結(jié)

最后簡單總結(jié)下,項目里并不是必須要使用 vuex,vuex 所解決的場景,用 vue 原生的輸入輸出機制想想辦法也能解決,區(qū)別可能就是代碼的可讀性、維護性上的區(qū)別,數(shù)據(jù)流走向是否清晰等

vuex 作為三方庫,自然就是一個可選的服務(wù),用不用,怎么用,都因人而異;考慮好自己的訴求,對比好引入前后的影響點,權(quán)衡好自己能接受的點就好

比如我,使用 vuex 的方式上說得難聽點,也有點不倫不類,畢竟并沒有按照官方示例來使用,反而是自己搞了套使用規(guī)范,這也增加了別人的上手成本

所以寫這篇,不在于強推使用 vuex,只是從自己的一些經(jīng)歷分享自己使用一些三方庫的心路歷程,所思所想

很多時候,當(dāng)你開始吐槽某某方案、當(dāng)你開始無法接受某某用法時,這其實意味著,這是一次絕佳的探索機會

吐槽完就想辦法去優(yōu)化、去尋找新方案;接受不了時,就想辦法去研究看能否解決這些痛點

人嘛,總是在一次次的踩進坑里,再爬出來

總結(jié)

以上是生活随笔為你收集整理的如何优雅使用 vuex的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。