你好,我是若川,微信搜索「若川视野」关注我,专一前端技术分享,一个愿景是帮助5年内前端开阔视野走向前列的公众号。欢迎加我微信
ruochuan12
,长期交流学习。html这是
学习源码总体架构系列
之 vuex4 源码(第十篇)。学习源码总体架构系列文章(有哪些必看的JS库):jQuery、underscore、lodash、sentry、vuex、axios、koa、redux、vue-devtools 直接打开文件功能揭秘。前端10篇源码系列文章小成就达成,从19年7月开始写,19年写了6篇,20年写了2篇,今年写了2篇。算是一个完结吧。短期内应该暂时不更新这个系列了。主要是投入的时间和精力比较多,看的人不多,获得的反馈也比较少。以后先写其余文章吧。欢迎持续关注我(若川)。vue
本文仓库地址:
git clone https://github.com/lxchuan12/vuex4-analysis.git
,本文最佳阅读方式,克隆仓库本身动手调试,容易吸取消化。node要是有人说到怎么读源码,正在读文章的你能推荐个人源码系列文章,那真是无觉得报啊。react
个人文章,尽可能写得让想看源码又不知道怎么看的读者能看懂。我都是推荐使用搭建环境断点调试源码学习,哪里不会点哪里,边调试边看,而不是硬看。正所谓:授人与鱼不如授人予渔。webpack
阅读本文后你将学到:ios
git subtree
管理子仓库Vuex 4
源码、理解Vuex
原理Vuex 4
和 Vuex 3
的异同Vuex 4
composition API
如何使用Vue.provide / Vue.inject
API 使用和原理Vue3
插件若是对于谷歌浏览器调试还不是很熟悉的读者,能够看这篇文章chrome devtools source面板,写的很详细。顺带提一下,我打开的设置,source面板中支持展开搜索代码块(默认不支持),一图胜千言。git
谷歌浏览器是咱们前端经常使用的工具,因此建议你们深刻学习,毕竟工欲善其事,必先利其器。github
以前写过Vuex 3
的源码文章学习 vuex 源码总体架构,打造属于本身的状态管理库、若川的博客Vuex源码,仓库有很详细的注释和看源码方法,因此本文不会过多赘述与Vuex 3
源码相同的地方。web
把个人vuex4
源码仓库 git clone https://github.com/lxchuan12/vuex4-analysis.git
克隆下来,顺便star一下个人vuex4源码学习仓库^_^。跟着文章节奏调试和示例代码调试,用chrome动手调试印象更加深入。文章长段代码不用细看,能够调试时再细看。看这类源码文章百遍,可能不如本身多调试几遍,大胆猜想,当心求证。也欢迎加我微信交流ruochuan12
。
结论先行:Vuex
原理能够拆解为三个关键点。
第一点、其实就是每一个组件实例里都注入了Store
实例。
第二点、Store
实例中的各类方法都是为Store
中的属性服务的。
第三点、Store
中的属性变动触发视图更新。
本文主要讲解第一点。第二点在个人上一篇文章学习 vuex 源码总体架构,打造属于本身的状态管理库详细讲了,本文就不赘述了。第三点两篇文章都没有详细讲述。
如下是一段简短的代码说明Vuex
原理的。
// 简版 class Store{ constructor(){ this._state = 'Store 实例'; } dispatch(val){ this.__state = val; } commit(){} // 省略 } const store = new Store(); var rootInstance = { parent: null, provides: { store: store, }, }; var parentInstance = { parent: rootInstance, provides: { store: store, } }; var childInstance1 = { parent: parentInstance, provides: { store: store, } }; var childInstance2 = { parent: parentInstance, provides: { store: store, } }; store.dispatch('我被修改了'); // store Store {_state: "我被修改了"} // rootInstance、parentInstance、childInstance一、childInstance2 这些对象中的provides.store都改了。 // 由于共享着同一个store对象。
看了上面的官方文档中的图,大概知道是用provide
父级组件中提供Store
实例,用inject
来获取到Store
实例。
那么接下来,带着问题:
一、为何修改了实例store
里的属性,变动后会触发视图更新。
二、Vuex4
做为Vue
的插件如何实现和Vue
结合的。
三、provide
、inject
的如何实现的,每一个组件如何获取到组件实例中的Store
的。
四、为何每一个组件对象里都有Store
实例对象了(渲染组件对象过程)。
五、为何在组件中写的provide
提供的数据,能被子级组件获取到。
TODO:
那么每一个组件如何获取组件实例中的Store
实例,composition API
中本质上则是使用inject
函数。
全局的Store
实例对象。经过Vue.reactive()
监测数据。
在看源码以前,先来看下Vuex 4
发布的release
和官方文档迁移提到的重大改变,Vuex 4 release。
Vuex 4
的重点是兼容性。Vuex 4
支持使用Vue 3
开发,而且直接提供了和Vuex 3
彻底相同的API
,所以用户能够在Vue 3
项目中复用现有的Vuex
代码。
相比Vuex 3
版本。主要有以下重大改变(其余的在上方连接中):
Vuex 3
是Vue.use(Vuex)
Vuex 4
则是app.use(store)
import { createStore } from 'vuex' export const store = createStore({ state() { return { count: 1 } } })
import { createApp } from 'vue' import { store } from './store' import App from './App.vue' const app = createApp(App) app.use(store) app.mount('#app')
createLogger
函数import { createLogger } from 'vuex'
接下来咱们从源码的角度来看这些重大改变。
git subtree add --prefix=vuex https://github.com/vuejs/vuex.git 4.0
这种方式保留了vuex4
仓库的git
记录信息。更多git subtree
使用方式能够查看这篇文章用 Git Subtree 在多个 Git 项目间双向同步子项目,附简明使用手册。
做为读者朋友的你,只需克隆个人Vuex 4
源码仓库 https://github.com/lxchuan12/vuex4-analysis.git
便可,也欢迎star
一下。
把vuex/examples/webpack.config.js
,加个devtool: 'source-map'
,这样就能开启sourcemap
调试源码了。
咱们使用项目中的购物车的例子调试,贯穿全文。
git clone https://github.com/lxchuan12/vuex4-analysis.git cd vuex npm i npm run dev # 打开 http://localhost:8080/ # 选择 composition 购物车的例子 shopping-cart # 打开 http://localhost:8080/composition/shopping-cart/ # 按 F12 打开调试工具,source面板 => page => webpack:// => .
听说一图胜千言,这时简单截个调试的图。
找到 createStore
函数打上断点。
// webpack:///./examples/composition/shopping-cart/store/index.js import { createStore, createLogger } from 'vuex' import cart from './modules/cart' import products from './modules/products' const debug = process.env.NODE_ENV !== 'production' export default createStore({ modules: { cart, products }, strict: debug, plugins: debug ? [createLogger()] : [] })
找到app.js
入口,在app.use(store)
、app.mount('#app')
等打上断点。
// webpack:///./examples/composition/shopping-cart/app.js import { createApp } from 'vue' import App from './components/App.vue' import store from './store' import { currency } from './currency' const app = createApp(App) app.use(store) app.mount('#app')
接下来,咱们从createApp({})
、app.use(Store)
两个方面发散开来说解。
相比 Vuex 3
中,new Vuex.Store
,实际上是同样的。只不过为了和Vue 3
统一,Vuex 4
额外多了一个 createStore
函数。
export function createStore (options) { return new Store(options) } class Store{ constructor (options = {}){ // 省略若干代码... this._modules = new ModuleCollection(options) const state = this._modules.root.state resetStoreState(this, state) // 省略若干代码... } } function resetStoreState (store, state, hot) { // 省略若干代码... store._state = reactive({ data: state }) // 省略若干代码... }
监测数据
和Vuex 3
不一样的是,监听数据再也不是用new Vue()
,而是Vue 3
提供的reactive
方法。
Vue.reactive
函数方法,本文就不展开讲解了。由于展开来说,又能够写篇新的文章了。只须要知道主要功能是监测数据改变,变动视图便可。
这也就算解答了开头提出的第一个问题。
跟着断点咱们继续看app.use()
方法,Vue
提供的插件机制。
use
作的事情提及来也算简单,把传递过来的插件添加插件集合中,到防止重复。
执行插件,若是是对象,install
是函数,则把参数app和其余参数传递给install函数执行。若是是函数直接执行。
// webpack:///./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js function createAppAPI(render, hydrate) { return function createApp(rootComponent, rootProps = null) { // 代码有删减 const installedPlugins = new Set(); const app = (context.app = { use(plugin, ...options) { // 已经有插件,而且 不是生产环境,报警告。 if (installedPlugins.has(plugin)) { (process.env.NODE_ENV !== 'production') && warn(`Plugin has already been applied to target app.`); } // 插件的install 是函数,则添加插件,并执行 install 函数 else if (plugin && isFunction(plugin.install)) { installedPlugins.add(plugin); // 断点 plugin.install(app, ...options); } // 插件自己 是函数,则添加插件,并执行 插件自己函数 else if (isFunction(plugin)) { installedPlugins.add(plugin); plugin(app, ...options); } // 若是都不是报警告 else if ((process.env.NODE_ENV !== 'production')) { warn(`A plugin must either be a function or an object with an "install" ` + `function.`); } // 支持链式调用 return app; }, provide(){ // 省略... 后文再讲 } }); } }
上面代码中,断点这行plugin.install(app, ...options);
跟着断点走到下一步,install
函数。
export class Store{ // 省略若干代码... install (app, injectKey) { // 为 composition API 中使用 // 能够传入 injectKey 若是没传取默认的 storeKey 也就是 store app.provide(injectKey || storeKey, this) // 为 option API 中使用 app.config.globalProperties.$store = this } // 省略若干代码... }
Vuex4
中的install
函数相对比Vuex3
中简单了许多。
第一句是给Composition API
提供的。注入到根实例对象中。
第二句则是为option API
提供的。
接着断点这两句,按F11
来看app.provide
实现。
简单来讲就是给context
的provides
属性中加了store
= Store实例对象。
provide(key, value) { // 若是已经有值了警告 if ((process.env.NODE_ENV !== 'production') && key in context.provides) { warn(`App already provides property with key "${String(key)}". ` + `It will be overwritten with the new value.`); } // TypeScript doesn't allow symbols as index type // https://github.com/Microsoft/TypeScript/issues/24587 context.provides[key] = value; return app; }
接着从上方代码中搜索context
,能够发现这一句代码:
const context = createAppContext();
接着咱们来看函数 createAppContext
。context
为上下文
// webpack:///./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js function createAppContext() { return { app: null, config: { isNativeTag: NO, performance: false, globalProperties: {}, optionMergeStrategies: {}, isCustomElement: NO, errorHandler: undefined, warnHandler: undefined }, mixins: [], components: {}, directives: {}, provides: Object.create(null) }; }
app.config.globalProperties 官方文档
用法:
app.config.globalProperties.$store = {} app.component('child-component', { mounted() { console.log(this.$store) // '{}' } })
也就能解释为何每一个组件均可以使用 this.$store.xxx
访问 vuex
中的方法和属性了。
也就是说在appContext.provides
中注入了一个Store实例对象。这时也就是至关于根组件实例和config
全局配置globalProperties
中有了Store实例对象。
至此咱们就看完,createStore(store)
,app.use(store)
两个API
。
app.provide
实际上是用于composition API
使用的。
但这只是文档中这样说的,为何就每一个组件实例都能访问的呢,咱们继续深刻探究下原理。
接下来,咱们看下源码具体实现,为何每一个组件实例中都能获取到的。
这以前先来看下组合式API
中,咱们如何使用Vuex4
,这是线索。
接着咱们找到以下文件,useStore
是咱们断点的对象。
// webpack:///./examples/composition/shopping-cart/components/ShoppingCart.vue import { computed } from 'vue' import { useStore } from 'vuex' import { currency } from '../currency' export default { setup () { const store = useStore() // 我加的这行代码 window.ShoppingCartStore = store; // 省略了若干代码 } }
接着断点按F11
,单步调试,会发现最终是使用了Vue.inject
方法。
// vuex/src/injectKey.js import { inject } from 'vue' export const storeKey = 'store' export function useStore (key = null) { return inject(key !== null ? key : storeKey) }
接着看inject
函数,看着代码不少,其实原理很简单,就是要找到咱们用provide
提供的值。
若是没有父级,也就是根实例,就取实例对象中的vnode.appContext.provides
。
不然就取父级中的instance.parent.provides
的值。
在Vuex4
源码里则是:Store
实例对象。
// webpack:///./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js function inject(key, defaultValue, treatDefaultAsFactory = false) { // fallback to `currentRenderingInstance` so that this can be called in // a functional component // 若是是被一个函数式组件调用则取 currentRenderingInstance const instance = currentInstance || currentRenderingInstance; if (instance) { // #2400 // to support `app.use` plugins, // fallback to appContext's `provides` if the intance is at root const provides = instance.parent == null ? instance.vnode.appContext && instance.vnode.appContext.provides : instance.parent.provides; if (provides && key in provides) { // TS doesn't allow symbol as index type return provides[key]; } // 若是参数大于1个 第二个则是默认值 ,第三个参数是 true,而且第二个值是函数则执行函数。 else if (arguments.length > 1) { return treatDefaultAsFactory && isFunction(defaultValue) ? defaultValue() : defaultValue; } // 警告没找到 else if ((process.env.NODE_ENV !== 'production')) { warn(`injection "${String(key)}" not found.`); } } // 若是没有当前实例则说明则报警告。 // 也就是是说inject必须在setup中调用或者在函数式组件中使用 else if ((process.env.NODE_ENV !== 'production')) { warn(`inject() can only be used inside setup() or functional components.`); } }
接着咱们继续来看inject
的相对应的provide
。
provide
函数做用其实也算简单,一、也就是给当前组件实例上的provides
对象属性,添加键值对key/value
。
二、还有一个做用是当当前组件和父级组件的provides
相同时,在当前组件实例中的provides
对象和父级,则创建连接,也就是原型[[prototype]]
,(__proto__
)。
// webpack:///./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js function provide(key, value) { if (!currentInstance) { if ((process.env.NODE_ENV !== 'production')) { warn(`provide() can only be used inside setup().`); } } else { let provides = currentInstance.provides; // by default an instance inherits its parent's provides object // but when it needs to provide values of its own, it creates its // own provides object using parent provides object as prototype. // this way in `inject` we can simply look up injections from direct // parent and let the prototype chain do the work. const parentProvides = currentInstance.parent && currentInstance.parent.provides; if (parentProvides === provides) { provides = currentInstance.provides = Object.create(parentProvides); } // TS doesn't allow symbol as index type provides[key] = value; } }
provide
函数中的这段,可能不是那么好理解。
if (parentProvides === provides) { provides = currentInstance.provides = Object.create(parentProvides); }
咱们来举个例子消化一下。
var currentInstance = { provides: { store: { __state: 'Store实例' } } }; var provides = currentInstance.provides; // 这句是我手动加的,在后文中则是建立实例时就是写的同一个对象,固然就会相等了。 var parentProvides = provides; if(parentProvides === provides){ provides = currentInstance.provides = Object.create(parentProvides); }
通过一次执行这个后,currentInstance
就变成了这样。
{ provides: { // 能够容纳其余属性,好比用户本身写的 __proto__ : { store: { __state: 'Store实例' } } } }
执行第二次时,currentInstance
则是:
{ provides: { // 能够容纳其余属性,好比用户本身写的 __proto__: { // 能够容纳其余属性,好比用户本身写的 __proto__ : { store: { __state: 'Store实例' } } } } }
以此类推,多执行provide
几回,原型链就越长。
上文inject
、provide
函数中都有个变量currentInstance
当前实例,那么当前实例对象是怎么来的呢。
为何每一个组件就能访问到,依赖注入的思想。
有一个讨巧的方法,就是在文件runtime-core.esm-bundler.js
中搜索provides
,则能搜索到createComponentInstance
函数
接下来咱们createComponentInstance
函数如何建立组件实例。
能够禁用其余断点,单专断点这里,
好比:const appContext = (parent ? parent.appContext : vnode.appContext) || emptyAppContext;
来看具体实现。
// webpack:///./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js const emptyAppContext = createAppContext(); let uid$1 = 0; function createComponentInstance(vnode, parent, suspense) { const type = vnode.type; const appContext = (parent ? parent.appContext : vnode.appContext) || emptyAppContext; const instance = { uid: uid$1++, vnode, type, parent, appContext, root: null, next: null, subTree: null, // ... provides: parent ? parent.provides : Object.create(appContext.provides), // ... } instance.root = parent ? parent.root : instance; // ... return instance; }
断点时会发现,根组件实例时vnode
已经生成,至因而何时生成的,我整理了下简化版。
// 把上文中的 appContext 赋值给了 `appContext` mount(rootContainer, isHydrate) { if (!isMounted) { const vnode = createVNode(rootComponent, rootProps); // store app context on the root VNode. // this will be set on the root instance on initial mount. vnode.appContext = context; } },
其中 Object.create
其实就是创建原型关系。这时放一张图,一图胜千言。
出自黄轶老师拉勾专栏,本想本身画一张图,但以为这张挺好的。
这时,也有一个讨巧的方法,在runtime-core.esm-bundler.js
文件中,搜索 provide(
能够搜到以下代码:
这段代码其实看起来很复杂的样式,实际上主要就是把用户在组件中写的provides
对象或者函数返回值遍历, 生成相似这样的实例对象:
// 当前组件实例 { parent: '父级的实例', provides: { // 能够容纳其余属性,好比用户本身写的 __proto__: { // 能够容纳其余属性,好比用户本身写的 __proto__ : { store: { __state: 'Store实例' } } } } }
// webpack:///./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js function applyOptions(instance, options, deferredData = [], deferredWatch = [], deferredProvide = [], asMixin = false) { // ... if (provideOptions) { deferredProvide.push(provideOptions); } if (!asMixin && deferredProvide.length) { deferredProvide.forEach(provideOptions => { // 组件中写 provides 能够是对象或者是函数 const provides = isFunction(provideOptions) ? provideOptions.call(publicThis) : provideOptions; Reflect.ownKeys(provides).forEach(key => { provide(key, provides[key]); }); }); } // ... }
这样一来就从上到下app.provide
提供的对象,被注入到每个组件实例中了。同时组件自己提供的provides
也被注入到实例中了。
接着咱们跟着项目来验证下,上文中的表述。翻看Vue3
文档能够发现有一个API
能够获取当前组件实例。
getCurrentInstance
支持访问内部组件实例,用于高阶用法或库的开发。
import { getCurrentInstance } from 'vue' const MyComponent = { setup() { const internalInstance = getCurrentInstance() internalInstance.appContext.config.globalProperties // 访问 globalProperties } }
知道这个API后,咱们能够在购物车例子的代码中添加一些代码。便于咱们理解。
// vuex/examples/composition/shopping-cart/components/App.vue import { getCurrentInstance, provide } from 'vue' import { useStore } from 'vuex'; setup () { const store = useStore() provide('ruochuan12', '微信搜索「若川视野」关注我,专一前端技术分享。') window.AppStore = store; window.AppCurrentInstance = getCurrentInstance(); },
// vuex/examples/composition/shopping-cart/components/ProductList.vue setup(){ const store = useStore() // 若川加入的调试代码--start window.ProductListStore = store; window.ProductListCurrentInstance = getCurrentInstance(); provide('weixin-2', 'ruochuan12'); provide('weixin-3', 'ruochuan12'); provide('weixin-4', 'ruochuan12'); const mp = inject('ruochuan12'); console.log(mp, '介绍-ProductList'); // 微信搜索「若川视野」关注我,专一前端技术分享。 // 若川加入的调试代码---end }
// vuex/examples/composition/shopping-cart/components/ShoppingCart.vue setup () { const store = useStore() // 若川加入的调试代码--start window.ShoppingCartStore = store; window.ShoppingCartCurrentInstance = getCurrentInstance(); provide('weixin', 'ruochuan12'); provide('weixin1', 'ruochuan12'); provide('weixin2', 'ruochuan12'); const mp = inject('ruochuan12'); console.log(mp, '介绍-ShoppingList'); // 微信搜索「若川视野」关注我,专一前端技术分享。 // 若川加入的调试代码--start }
在控制台输出这些值
AppCurrentInstance AppCurrentInstance.provides ShoppingCartCurrentInstance.parent === AppCurrentInstance // true ShoppingCartCurrentInstance.provides ShoppingCartStore === AppStore // true ProductListStore === AppStore // true AppStore // store实例对象
看控制台截图输出的例子,其实跟上文写的相似。这时若是写了顺手本身注入了一个provide('store': '空字符串'),那么顺着原型链,确定是先找到用户写的store
,这时Vuex没法正常使用,就报错了。
固然vuex4
提供了注入的key
能够不是store
的写法,这时就不和用户的冲突了。
export class Store{ // 省略若干代码... install (app, injectKey) { // 为 composition API 中使用 // 能够传入 injectKey 若是没传取默认的 storeKey 也就是 store app.provide(injectKey || storeKey, this) // 为 option API 中使用 app.config.globalProperties.$store = this } // 省略若干代码... }
export function useStore (key = null) { return inject(key !== null ? key : storeKey) }
统一解答下开头提出的5个问题:
一、为何修改了实例store
里的属性,变动后会触发视图更新。
答:使用Vue
中的 reactive
方法监测数据变化的。
class Store{ constructor (options = {}){ // 省略若干代码... this._modules = new ModuleCollection(options) const state = this._modules.root.state resetStoreState(this, state) // 省略若干代码... } } function resetStoreState (store, state, hot) { // 省略若干代码... store._state = reactive({ data: state }) // 省略若干代码... }
二、Vuex4
做为Vue
的插件如何实现和Vue
结合的。
答:app.use(store)
时会执行Store
中的install
方法,一句是为 composition API
中使用,提供Store
实例对象到根实例中。一句则是注入到根实例的全局属性中,为 option API
中使用。它们都会在组件生成时,注入到每一个组件实例中。
export class Store{ // 省略若干代码... install (app, injectKey) { // 为 composition API 中使用 // 能够传入 injectKey 若是没传取默认的 storeKey 也就是 store app.provide(injectKey || storeKey, this) // 为 option API 中使用 app.config.globalProperties.$store = this } // 省略若干代码... }
三、provide
、inject
的如何实现的,每一个组件如何获取到组件实例中的Store
的。
五、为何在组件中写的provide
提供的数据,能被子级组件获取到。
答:provide
函数创建原型链区分出组件实例用户本身写的属性和系统注入的属性。inject
函数则是经过原型链找父级实例中的provides
对象中的属性。
// 有删减 function provide(){ let provides = currentInstance.provides; const parentProvides = currentInstance.parent && currentInstance.parent.provides; if (parentProvides === provides) { provides = currentInstance.provides = Object.create(parentProvides); } provides[key] = value; }
// 有删减 function inject(){ const provides = instance.parent == null ? instance.vnode.appContext && instance.vnode.appContext.provides : instance.parent.provides; if (provides && key in provides) { return provides[key]; } }
也就是相似这样的实例:
// 当前组件实例 { parent: '父级的实例', provides: { // 能够容纳其余属性,好比用户本身写的 __proto__: { // 能够容纳其余属性,好比用户本身写的 __proto__ : { store: { __state: 'Store实例' } } } } }
四、为何每一个组件对象里都有Store
实例对象了(渲染组件对象过程)。
答:渲染生成组件实例时,调用createComponentInstance
,注入到组件实例的provides
中。
function createComponentInstance(vnode, parent, suspense) { const type = vnode.type; const appContext = (parent ? parent.appContext : vnode.appContext) || emptyAppContext; const instance = { parent, appContext, // ... provides: parent ? parent.provides : Object.create(appContext.provides), // ... } // ... return instance; }
答:由于社区有人写了Vue4
源码文章。
本文主要讲述了Vuex4
把Store
实例注入到各个组件中的原理,展开讲述了Vuex4
相对与Vuex3
安装方式的改变Vuex.createStore
、app.use(store)
,深刻源码分析Vue.inject
、Vue.provide
实现原理。
Vuex4
除了安装方式和监测数据变化方式使用了Vue.reactive
,其余基本和Vuex3.x
版本没什么区别。
最后回顾下文章开头的图,能够说就是原型链的妙用。
是否是以为豁然开朗。
Vuex
其实也是Vue
的一个插件,知晓了Vuex
原理,对于本身给Vue
写插件也是会游刃有余。
若是读者朋友发现有不妥或可改善之处,再或者哪里没写明白的地方,欢迎评论指出,也欢迎加我微信
ruochuan12
交流。另外以为写得不错,对您有些许帮助,能够点赞、评论、转发分享,也是对个人一种支持,万分感谢。若是能关注个人前端公众号:
「若川视野」,就更好啦。
你好,我是 若川,微信搜索 「若川视野」关注我,专一前端技术分享,一个愿景是帮助5年内前端开阔视野走向前列的公众号。欢迎加我微信ruochuan12
,长期交流学习。
主要有如下系列文章: 学习源码总体架构系列、 年度总结、 JS基础系列
官网文档 Provide / Inject
github 仓库 provide/inject 源码
github 仓库 provide/inject 测试
Vuex 4 官方中文文档