<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Provide/inject 真的可以取代 Vuex 嗎?(深度!)

          共 8642字,需瀏覽 18分鐘

           ·

          2021-07-10 00:13

          如果你想利用依賴注入讓整個應(yīng)用下組件都能共享某個數(shù)據(jù),你會怎么做?為什么?

          這個問題本身并不難,因?yàn)槟阒灰懒艘蕾囎⑷氲膶?shí)現(xiàn)原理,你就可以輕松回答出:只要在應(yīng)用的根實(shí)例上 provide 某個數(shù)據(jù),然后在子組件 inject 使用,就相當(dāng)于整個應(yīng)用的組件共享該數(shù)據(jù)了。

          看上去,使用 provide/inject 就可以實(shí)現(xiàn)全局?jǐn)?shù)據(jù)共享,這個能力似乎和 Vuex 提供的能力類似,那么它可以替代 Vuex 嗎?

          Vuex 的核心概念

          Vuex 是什么,官方的解釋是一個專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式。它采用集中式存儲管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測的方式發(fā)生變化。

          Vuex 本質(zhì)上是一種全局單例模式的方式來管理組件的共享狀態(tài)。在這種模式下,我們的組件樹構(gòu)成了一個巨大的“視圖”,不管在樹的哪個位置,任何組件都能獲取狀態(tài)或者觸發(fā)行為。

          Vuex 中,有四個核心的概念,我們來簡單過一下。

          • State

          stateVuex 中最基礎(chǔ)的概念,它用于數(shù)據(jù)的 存儲,舉個例子:

          import { createStore } from 'vuex'
          const store = createStore({
            state: {
              todos: [
                { id1text'...'donetrue },
                { id2text'...'donefalse }
              ]
            }
          })

          我們可以通過 store.state.todos 來訪問到其中的 todos 數(shù)據(jù)。

          • Getter

          有些時(shí)候,我們希望獲取的數(shù)據(jù)可能不是單一的在 state 中的數(shù)據(jù),可能需要做一些邏輯運(yùn)算,我們可以使用 getter,它就是 store 的計(jì)算屬性。延續(xù)前一個例子:

          import { createStore } from 'vuex'
          const store = createStore({
            state: {
              todos: [
                { id1text'...'donetrue },
                { id2text'...'donefalse }
              ]
            },
            getters: {
              doneTodosstate => {
                return state.todos.filter(todo => todo.done)
              }
            }
          })

          我們可以通過 store.getters.doneTodos 訪問到所有已完成的 todos 數(shù)據(jù)。

          • Mutation

          數(shù)據(jù)有讀就會有寫,為了確保數(shù)據(jù)的改變可追蹤,更改 state 數(shù)據(jù)的唯一的方式是提交 mutation。延續(xù)前一個例子:

          import { createStore } from 'vuex'
          const store = createStore({
            state: {
              todos: [
                { id1text'...'donetrue },
                { id2text'...'donefalse }
              ]
            },
            getters: {
              doneTodosstate => {
                return state.todos.filter(todo => todo.done)
              }
            },
            mutations: {
              finishToDo(state, index) {
                state.todos[index].done = true
              }
            }
          })

          我們可以通過 store.commit('finishTodo', 1) 來修改第二個 todo 的完成狀態(tài)。

          • Action

          action 類似 mutation,不同在于在 action 內(nèi)部并不直接修改數(shù)據(jù),還是通過提交 mutation 來更改數(shù)據(jù),此外 action 內(nèi)部還能包含任意的異步操作。延續(xù)前一個例子:

          import { createStore } from 'vuex'
          const store = createStore({
            state: {
              todos: [
                { id1text'...'donetrue },
                { id2text'...'donefalse }
              ]
            },
            getters: {
              doneTodosstate => {
                return state.todos.filter(todo => todo.done)
              }
            },
            mutations: {
              finishToDo(state, index) {
                state.todos[index].done = true
              }
            },
            actions: {
              delayFinishTodo({commit}, index) {
                setTimeout(() => {
                  commit('finishToDo', index)
                }, 1000)
              }
            }
          })

          我們可以通過 store.dispatch('delayFinishTodo', 1) 延時(shí) 1s 后修改第二個 todo 的完成狀態(tài)。

          至此,我們了解了 Vuex 的四個最核心的概念,目前為止,我們都是通過原生 JavaScript 去操作 store 實(shí)例,并沒有和組件關(guān)聯(lián),那么我們?nèi)绾卧诮M件中訪問到 store 實(shí)例呢?

          在組件中訪問 store

          在 Vue.js 3.0 中,我們通過 createStore 創(chuàng)建了 store 實(shí)例后,會在創(chuàng)建 App 對象的時(shí)候注入進(jìn)去。

          import { createApp } from 'vue'
          import App from './App.vue'
          import store from './store'

          createApp(App).use(store).mount('#app')

          當(dāng)執(zhí)行 createApp(App).use(store) 的時(shí)候,相當(dāng)于注冊了 store 的插件,會執(zhí)行到 store 提供的 install 方法,來看看 4.0 版本的 Vuex 是如何實(shí)現(xiàn) install 方法的:

          export class Store {
            install (app, injectKey) {
              app.provide(injectKey || storeKey, this)
              app.config.globalProperties.$store = this
            }
          }

          在注冊插件的時(shí)候,內(nèi)部通過 app.providestore 實(shí)例 provide 到了根實(shí)例中,此外,store 實(shí)例也被添加到了全局屬性的 app.config.globalProperties.$store 中。

          這么做之后,我們就可以在組件中輕松訪問到 store 實(shí)例了。其中 app.provide 是給 Composition API 方式編寫的組件用的,因?yàn)橐坏┦褂昧?Composition API ,我們在組件中想訪問 store 的話會在 setup 函數(shù)中通過 useStore API 拿到,如下:

          import { useStore } from 'vuex'
          export default {
            setup() {
              const store = useStore()
            }
          }

          useStore 的實(shí)現(xiàn)如下:

          import { inject } from 'vue'
          export const storeKey = 'store'
          export function useStore (key = null{
            return inject(key !== null ? key : storeKey)
          }

          原來 Vuex 就是利用了 provide/inject 依賴注入的 API 實(shí)現(xiàn)了在組件中訪問到 store,由于是通過 app.providestore 實(shí)例 provide 到根實(shí)例中,所以在 app 內(nèi)部的任意組件中都可以 inject store 實(shí)例并訪問了。

          除了 Composition API,Vue.js 3.0 依然支持 Options API 的方式去編寫組件,顯然在 Options API 組件中我們依然可以通過 this.$store 訪問到 store 實(shí)例,因?yàn)閷?shí)例的查找最終會找到全局 globalProperties 中的屬性。

          所以我們看到 provide/injectVuex 中的作用就是讓組件可以訪問到 store 實(shí)例。

          Vuex 的其它能力

          Vuex 除了管理組件的共享狀態(tài),還有一些其他好用的特性,這里我介紹三個常用的特性。

          • 模塊

          由于使用單一狀態(tài)樹,應(yīng)用的所有狀態(tài)會集中到一個比較大的對象。當(dāng)應(yīng)用變得非常復(fù)雜時(shí),store 對象就有可能變得相當(dāng)臃腫。

          為了解決以上問題,Vuex 允許我們將 store 分割成模塊(module)。每個模塊擁有自己的 stategetter、mutationaction、甚至是嵌套子模塊——從上至下進(jìn)行同樣方式的分割:

          const moduleA = {
            state() => ({ ... }),
            mutations: { ... },
            actions: { ... },
            getters: { ... }
          }

          const moduleB = {
            state() => ({ ... }),
            mutations: { ... },
            actions: { ... }
          }

          const store = createStore({
            modules: {
              a: moduleA,
              b: moduleB
            }
          })

          store.state.a // -> moduleA 的狀態(tài)
          store.state.b // -> moduleB 的狀態(tài)

          另外,在 store 創(chuàng)建之后,你可以使用 store.registerModule 方法動態(tài)注冊模塊:

          import { createStore } from 'vuex'

          const store = createStore({ /* options */ })

          // 注冊模塊 `myModule`
          store.registerModule('myModule', {
            // ...
          })

          // 注冊嵌套模塊 `nested/myModule`
          store.registerModule(['nested''myModule'], {
            // ...
          })
          • 插件

          Vuexstore 接受 plugins 選項(xiàng),這個選項(xiàng)暴露出每次 mutation 的鉤子。Vuex 插件就是一個函數(shù),它接收 store 作為唯一參數(shù):

          const myPlugin = (store) => {
            // 在 store 初始化的時(shí)候調(diào)用
            store.subscribe((mutation, state) => {
              // 每次提交 mutation 的時(shí)候調(diào)用
            })
          }

          然后像這樣使用:

          import { createStore } from 'vuex'
          const store = createStore({
            // ...
            plugins: [myPlugin]
          })

          官方內(nèi)置了 Logger 插件用于一般的調(diào)試:

          import { createStore, createLogger } from 'vuex'
          const store = createStore({
            // ...
            plugins: [createLogger()]
          })

          通常我們會在開發(fā)環(huán)境中使用它,用來輸出提交的 mutation 和生成狀態(tài)快照。

          • 嚴(yán)格模式

          為了保證數(shù)據(jù)的變化可追蹤,我們要求所有狀態(tài)的更改都應(yīng)該通過提交 mutation 來觸發(fā),因此在嚴(yán)格模式下,一旦發(fā)生了狀態(tài)變更且不是由 mutation 函數(shù)引起的,將會拋出錯誤。

          我們可以在創(chuàng)建 store 的時(shí)候開啟:

          const store = createStore({
            // ...
            stricttrue
          })

          由于開啟嚴(yán)格模式會有一定的性能損耗,我們也只會在開發(fā)環(huán)境中開啟它。

          總結(jié)

          綜上,我們發(fā)現(xiàn) Vuex 提供的能力還是很豐富的,而僅僅用 provide/inject 是不能替代 Vuex 的,那么 provide/inject 有哪些應(yīng)用場景呢?

          其實(shí)這個在課程中已經(jīng)說了,我比較推薦在組件庫的開發(fā)中使用,因?yàn)閷τ谝粋€特定組件,它和其嵌套的子組件上下文聯(lián)系很緊密。

          我出這個題主要是希望你能做到以下兩點(diǎn):

          1. 從源碼層面探索,了解 provide/inject 的實(shí)現(xiàn)原理。

          2. 延伸思考 provide/inject 在實(shí)現(xiàn)全局?jǐn)?shù)據(jù)共享需求與 Vuex 的相同與差異。

          要記住,分析和思考的過程遠(yuǎn)比答案重要。


          最后



          如果你覺得這篇內(nèi)容對你挺有啟發(fā),我想邀請你幫我三個小忙:

          1. 點(diǎn)個「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點(diǎn)在看,都是耍流氓 -_-)

          2. 歡迎加我微信「 sherlocked_93 」拉你進(jìn)技術(shù)群,長期交流學(xué)習(xí)...

          3. 關(guān)注公眾號「前端下午茶」,持續(xù)為你推送精選好文,也可以加我為好友,隨時(shí)聊騷。


          點(diǎn)個在看支持我吧,轉(zhuǎn)發(fā)就更好了



          瀏覽 49
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本精品视频在线播放 | 97热视频 | 国产欧美草莓视频 | 国产综合AV | 特级西西人体444WWw高清大胆 |