Hello,各位小伙伴,接下来的一段时间里,我会把我的课程《Vue.js 3.0 核心源码解析》中问题的答案陆续在我的公众号发布,由于课程的问题大多数都是开放性的问题,所以我的答案也不一定是标准的,仅供你参考喔。

本期的问题:如果你想利用依赖注入让整个应用下组件都能共享某个数据,你会怎么做?为什么?

这个问题本身并不难,因为你只要知道了依赖注入的实现原理,你就可以轻松回答出:只要在应用的根实例上 provide 某个数据,然后在子组件 inject 使用,就相当于整个应用的组件共享该数据了。

看上去,使用 provide/inject 就可以实现全局数据共享,这个能力似乎和 Vuex 提供的能力类似,那么它可以替代 Vuex 吗?

Vuex 的核心概念

Vuex 是什么,官方的解释是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

Vuex 本质上是一种全局单例模式的方式来管理组件的共享状态。在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为。

Vuex 中,有四个核心的概念,我们来简单过一下。

  • State

stateVuex 中最基础的概念,它用于数据的 存储,举个例子:

import { createStore } from 'vuex'
const store = createStore({state: {todos: [{ id: 1, text: '...', done: true },{ id: 2, text: '...', done: false }]}
})

我们可以通过 store.state.todos 来访问到其中的 todos 数据。

  • Getter

有些时候,我们希望获取的数据可能不是单一的在 state 中的数据,可能需要做一些逻辑运算,我们可以使用 getter,它就是 store 的计算属性。延续前一个例子:

import { createStore } from 'vuex'
const store = createStore({state: {todos: [{ id: 1, text: '...', done: true },{ id: 2, text: '...', done: false }]},getters: {doneTodos: state => {return state.todos.filter(todo => todo.done)}}
})

我们可以通过 store.getters.doneTodos 访问到所有已完成的 todos 数据。

  • Mutation

数据有读就会有写,为了确保数据的改变可追踪,更改 state 数据的唯一的方式是提交 mutation。延续前一个例子:

import { createStore } from 'vuex'
const store = createStore({state: {todos: [{ id: 1, text: '...', done: true },{ id: 2, text: '...', done: false }]},getters: {doneTodos: state => {return state.todos.filter(todo => todo.done)}},mutations: {finishToDo(state, index) {state.todos[index].done = true}}
})

我们可以通过 store.commit('finishTodo', 1) 来修改第二个 todo 的完成状态。

  • Action

action 类似 mutation,不同在于在 action 内部并不直接修改数据,还是通过提交 mutation 来更改数据,此外 action 内部还能包含任意的异步操作。延续前一个例子:

import { createStore } from 'vuex'
const store = createStore({state: {todos: [{ id: 1, text: '...', done: true },{ id: 2, text: '...', done: false }]},getters: {doneTodos: state => {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) 延时 1s 后修改第二个 todo 的完成状态。

至此,我们了解了 Vuex 的四个最核心的概念,目前为止,我们都是通过原生 JavaScript 去操作 store 实例,并没有和组件关联,那么我们如何在组件中访问到 store 实例呢?

在组件中访问 store

在 Vue.js 3.0 中,我们通过 createStore 创建了 store 实例后,会在创建 App 对象的时候注入进去。

import { createApp } from 'vue'
import App from './App.vue'
import store from './store'createApp(App).use(store).mount('#app')

当执行 createApp(App).use(store) 的时候,相当于注册了 store 的插件,会执行到 store 提供的 install 方法,来看看 4.0 版本的 Vuex 是如何实现 install 方法的:

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

在注册插件的时候,内部通过 app.providestore 实例 provide 到了根实例中,此外,store 实例也被添加到了全局属性的 app.config.globalProperties.$store 中。

这么做之后,我们就可以在组件中轻松访问到 store 实例了。其中 app.provide 是给 Composition API 方式编写的组件用的,因为一旦使用了 Composition API ,我们在组件中想访问 store 的话会在 setup 函数中通过 useStore API 拿到,如下:

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

useStore 的实现如下:

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

原来 Vuex 就是利用了 provide/inject 依赖注入的 API 实现了在组件中访问到 store,由于是通过 app.providestore 实例 provide 到根实例中,所以在 app 内部的任意组件中都可以 inject store 实例并访问了。

除了 Composition API,Vue.js 3.0 依然支持 Options API 的方式去编写组件,显然在 Options API 组件中我们依然可以通过 this.$store 访问到 store 实例,因为实例的查找最终会找到全局 globalProperties 中的属性。

所以我们看到 provide/injectVuex 中的作用就是让组件可以访问到 store 实例。

Vuex 的其它能力

Vuex 除了管理组件的共享状态,还有一些其他好用的特性,这里我介绍三个常用的特性。

  • 模块

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 stategettermutationaction、甚至是嵌套子模块——从上至下进行同样方式的分割:

const moduleA = {state: () => ({ ... }),mutations: { ... },actions: { ... },getters: { ... }
}const moduleB = {state: () => ({ ... }),mutations: { ... },actions: { ... }
}const store = createStore({modules: {a: moduleA,b: moduleB}
})store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

另外,在 store 创建之后,你可以使用 store.registerModule 方法动态注册模块:

import { createStore } from 'vuex'const store = createStore({ /* options */ })// 注册模块 `myModule`
store.registerModule('myModule', {// ...
})// 注册嵌套模块 `nested/myModule`
store.registerModule(['nested', 'myModule'], {// ...
})
  • 插件

Vuexstore 接受 plugins 选项,这个选项暴露出每次 mutation 的钩子。Vuex 插件就是一个函数,它接收 store 作为唯一参数:

const myPlugin = (store) => {// 在 store 初始化的时候调用store.subscribe((mutation, state) => {// 每次提交 mutation 的时候调用})
}

然后像这样使用:

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

官方内置了 Logger 插件用于一般的调试:

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

通常我们会在开发环境中使用它,用来输出提交的 mutation 和生成状态快照。

  • 严格模式

为了保证数据的变化可追踪,我们要求所有状态的更改都应该通过提交 mutation 来触发,因此在严格模式下,一旦发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。

我们可以在创建 store 的时候开启:

const store = createStore({// ...strict: true
})

由于开启严格模式会有一定的性能损耗,我们也只会在开发环境中开启它。

总结

综上,我们发现 Vuex 提供的能力还是很丰富的,而仅仅用 provide/inject 是不能替代 Vuex 的,那么 provide/inject 有哪些应用场景呢?

其实这个在课程中已经说了,我比较推荐在组件库的开发中使用,因为对于一个特定组件,它和其嵌套的子组件上下文联系很紧密。

我出这个题主要是希望你能做到以下两点:

  1. 从源码层面探索,了解 provide/inject 的实现原理。

  2. 延伸思考 provide/inject 在实现全局数据共享需求与 Vuex 的相同与差异。

要记住,分析和思考的过程远比答案重要。

Provide/inject 真的可以取代 Vuex 吗?相关推荐

  1. 用provide/inject来实现简单的vuex状态管理功能

    在开发的时候,经常会涉及到组件之间的通信.简单的有父子组件的通信,兄弟组件的通信通常可以借助Bus来进行.当然也可以用vuex来进行状态管理,但是,有时候用vuex未免有把简单的问题复杂化. 如果要进 ...

  2. vue3使用provide/inject实现全局变量功能,部分摆脱vuex

    vue3新的provide/inject功能可以穿透多层组件,实现数据从父组件传递到子组件. 这时将所有需要使用的全局变量在根组件就provide,这样,所有的组件都能使用到这个变量. 如果需要变量是 ...

  3. export default用法vue_Vue组件通信—provide/inject

    前言: 之前在 Vue 中进行组件通信一般都会使用 props,开始使用 provide/inject 是非常偶然的一次尝试. 当时在开发中需要实现祖孙组件,甚至祖祖祖祖孙组件之间的通信,在这种多层级 ...

  4. vue 中provide的用法_聊聊Vue中provide/inject的应用详解

    众所周知,在组件式开发中,最大的痛点就在于组件之间的通信.在 Vue 中,Vue 提供了各种各样的组件通信方式,从基础的 props/$emit 到用于兄弟组件通信的 EventBus,再到用于全局数 ...

  5. 一文读懂vuex4源码,原来provide/inject就是妙用了原型链?

    1. 前言 你好,我是若川,欢迎加我微信ruochuan12,加群长期交流学习. 这是学习源码整体架构系列 之 vuex4 源码(第十篇).学习源码整体架构系列文章(有哪些必看的JS库):jQuery ...

  6. Vue 组件化通信 provide inject ,dispatch ,boardcast

    入门 作为前端最容易上手的框架,Vue入门其实没啥说的,我放一段清单的代码,大家能看懂就说明能上手了 <template><div id="app">< ...

  7. 聊聊 Vue 中 provide/inject 的应用

    众所周知,在组件式开发中,最大的痛点就在于组件之间的通信.在 Vue 中,Vue 提供了各种各样的组件通信方式,从基础的 props/$emit 到用于兄弟组件通信的 EventBus,再到用于全局数 ...

  8. Vue组件通信原理剖析(三)provide/inject原理分析

    首先我们先从一个面试题入手. 面试官问: "Vue中组件通信的常用方式有哪些?" 我答: 1. props 2. 自定义事件 3. eventbus 4. vuex 5. 还有常见 ...

  9. 【vue】— provide/inject的原理

    系列文章目录 [VUE]- diff算法原理 [VUE]- watch侦听器原理 文章目录 系列文章目录 一.provide/inject的作用及使用方式 二.顺道复习一下组件间的通讯方式吧 三.源码 ...

最新文章

  1. 汇编: loop指令
  2. Py之pyttsx:pyttsx/pyttsx3​​​​​​​的简介、安装、使用方法之详细攻略
  3. Eclipse快捷键生成语句
  4. 阿里腾讯前端一面小结
  5. CCF201503-3 节日(100分)
  6. ideal如何快速导入import_Vue性能优化:如何实现延迟加载和代码拆分?
  7. Django新手需要注意的10个要点
  8. Titanium快速开发app
  9. FreeBSD NetBSD OpenBSD DragonFlyBSD 操作系统
  10. 504 Gateway Time-out 错误处理记录
  11. 《BGP设计与实现》一2.11 总结
  12. idea2017 破解版使用
  13. 怎么把zip转换html,如何压缩为rar格式 怎样把rar格式变成zip格式
  14. 解决linux下无法连接为wifi热点上网
  15. 年轻人要对自己狠一点
  16. Android 动画方案
  17. 再见!永远的21号!马刺退役邓肯21号球衣
  18. 《电脑报2014年》更新至第51期
  19. 我的企业使用虚拟信用卡支付有什么优势
  20. HTML5 从入门到精通读书笔记

热门文章

  1. 【Unity】文字游戏制作插件Fungus教程(6)碰撞触发和消息的发送和接收
  2. 如何避免使用过多的 if else?
  3. pyecharts导入html数据,数据可视化之pyecharts
  4. Google Docs 介绍
  5. 笔记本外接USB显卡闪屏的解决方法
  6. 每天读一遍,防止颓废
  7. pyppeteer Chrome无响应问题解决
  8. java 两个类互相包含_如何在Java中互相使用两个类中的方法?
  9. 绘图;BeginPaint;PAINTSTRUCT(ps);SetArcDirection函数改变弧线的方向;LineDDA函数
  10. 基于JavaWeb的失物招领平台设计与实现