Provide/inject 真的可以取代 Vuex 吗?
Hello,各位小伙伴,接下来的一段时间里,我会把我的课程《Vue.js 3.0 核心源码解析》中问题的答案陆续在我的公众号发布,由于课程的问题大多数都是开放性的问题,所以我的答案也不一定是标准的,仅供你参考喔。
本期的问题:如果你想利用依赖注入让整个应用下组件都能共享某个数据,你会怎么做?为什么?
这个问题本身并不难,因为你只要知道了依赖注入的实现原理,你就可以轻松回答出:只要在应用的根实例上 provide
某个数据,然后在子组件 inject
使用,就相当于整个应用的组件共享该数据了。
看上去,使用 provide/inject
就可以实现全局数据共享,这个能力似乎和 Vuex
提供的能力类似,那么它可以替代 Vuex
吗?
Vuex 的核心概念
Vuex
是什么,官方的解释是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex
本质上是一种全局单例模式的方式来管理组件的共享状态。在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为。
在 Vuex
中,有四个核心的概念,我们来简单过一下。
State
state
是 Vuex
中最基础的概念,它用于数据的 存储,举个例子:
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.provide
把 store
实例 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.provide
把 store
实例 provide
到根实例中,所以在 app
内部的任意组件中都可以 inject store
实例并访问了。
除了 Composition API,Vue.js 3.0 依然支持 Options API 的方式去编写组件,显然在 Options API 组件中我们依然可以通过 this.$store
访问到 store
实例,因为实例的查找最终会找到全局 globalProperties
中的属性。
所以我们看到 provide/inject
在 Vuex
中的作用就是让组件可以访问到 store
实例。
Vuex 的其它能力
Vuex
除了管理组件的共享状态,还有一些其他好用的特性,这里我介绍三个常用的特性。
模块
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store
对象就有可能变得相当臃肿。
为了解决以上问题,Vuex
允许我们将 store
分割成模块(module
)。每个模块拥有自己的 state
、getter
、mutation
、action
、甚至是嵌套子模块——从上至下进行同样方式的分割:
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'], {// ...
})
插件
Vuex
的 store
接受 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
有哪些应用场景呢?
其实这个在课程中已经说了,我比较推荐在组件库的开发中使用,因为对于一个特定组件,它和其嵌套的子组件上下文联系很紧密。
我出这个题主要是希望你能做到以下两点:
从源码层面探索,了解
provide/inject
的实现原理。延伸思考
provide/inject
在实现全局数据共享需求与Vuex
的相同与差异。
要记住,分析和思考的过程远比答案重要。
Provide/inject 真的可以取代 Vuex 吗?相关推荐
- 用provide/inject来实现简单的vuex状态管理功能
在开发的时候,经常会涉及到组件之间的通信.简单的有父子组件的通信,兄弟组件的通信通常可以借助Bus来进行.当然也可以用vuex来进行状态管理,但是,有时候用vuex未免有把简单的问题复杂化. 如果要进 ...
- vue3使用provide/inject实现全局变量功能,部分摆脱vuex
vue3新的provide/inject功能可以穿透多层组件,实现数据从父组件传递到子组件. 这时将所有需要使用的全局变量在根组件就provide,这样,所有的组件都能使用到这个变量. 如果需要变量是 ...
- export default用法vue_Vue组件通信—provide/inject
前言: 之前在 Vue 中进行组件通信一般都会使用 props,开始使用 provide/inject 是非常偶然的一次尝试. 当时在开发中需要实现祖孙组件,甚至祖祖祖祖孙组件之间的通信,在这种多层级 ...
- vue 中provide的用法_聊聊Vue中provide/inject的应用详解
众所周知,在组件式开发中,最大的痛点就在于组件之间的通信.在 Vue 中,Vue 提供了各种各样的组件通信方式,从基础的 props/$emit 到用于兄弟组件通信的 EventBus,再到用于全局数 ...
- 一文读懂vuex4源码,原来provide/inject就是妙用了原型链?
1. 前言 你好,我是若川,欢迎加我微信ruochuan12,加群长期交流学习. 这是学习源码整体架构系列 之 vuex4 源码(第十篇).学习源码整体架构系列文章(有哪些必看的JS库):jQuery ...
- Vue 组件化通信 provide inject ,dispatch ,boardcast
入门 作为前端最容易上手的框架,Vue入门其实没啥说的,我放一段清单的代码,大家能看懂就说明能上手了 <template><div id="app">< ...
- 聊聊 Vue 中 provide/inject 的应用
众所周知,在组件式开发中,最大的痛点就在于组件之间的通信.在 Vue 中,Vue 提供了各种各样的组件通信方式,从基础的 props/$emit 到用于兄弟组件通信的 EventBus,再到用于全局数 ...
- Vue组件通信原理剖析(三)provide/inject原理分析
首先我们先从一个面试题入手. 面试官问: "Vue中组件通信的常用方式有哪些?" 我答: 1. props 2. 自定义事件 3. eventbus 4. vuex 5. 还有常见 ...
- 【vue】— provide/inject的原理
系列文章目录 [VUE]- diff算法原理 [VUE]- watch侦听器原理 文章目录 系列文章目录 一.provide/inject的作用及使用方式 二.顺道复习一下组件间的通讯方式吧 三.源码 ...
最新文章
- 汇编: loop指令
- Py之pyttsx:pyttsx/pyttsx3​​​​​​​的简介、安装、使用方法之详细攻略
- Eclipse快捷键生成语句
- 阿里腾讯前端一面小结
- CCF201503-3 节日(100分)
- ideal如何快速导入import_Vue性能优化:如何实现延迟加载和代码拆分?
- Django新手需要注意的10个要点
- Titanium快速开发app
- FreeBSD NetBSD OpenBSD DragonFlyBSD 操作系统
- 504 Gateway Time-out 错误处理记录
- 《BGP设计与实现》一2.11 总结
- idea2017 破解版使用
- 怎么把zip转换html,如何压缩为rar格式 怎样把rar格式变成zip格式
- 解决linux下无法连接为wifi热点上网
- 年轻人要对自己狠一点
- Android 动画方案
- 再见!永远的21号!马刺退役邓肯21号球衣
- 《电脑报2014年》更新至第51期
- 我的企业使用虚拟信用卡支付有什么优势
- HTML5 从入门到精通读书笔记
热门文章
- 【Unity】文字游戏制作插件Fungus教程(6)碰撞触发和消息的发送和接收
- 如何避免使用过多的 if else?
- pyecharts导入html数据,数据可视化之pyecharts
- Google Docs 介绍
- 笔记本外接USB显卡闪屏的解决方法
- 每天读一遍,防止颓废
- pyppeteer Chrome无响应问题解决
- java 两个类互相包含_如何在Java中互相使用两个类中的方法?
- 绘图;BeginPaint;PAINTSTRUCT(ps);SetArcDirection函数改变弧线的方向;LineDDA函数
- 基于JavaWeb的失物招领平台设计与实现