1. 前言

你好,我是若川,微信搜寻「若川视线」关注我,专一前端技术分享,一个愿景是帮忙5年内前端开阔视野走向前列的公众号。欢送加我微信ruochuan12,长期交流学习。

这是学习源码整体架构系列 之 vuex4 源码(第十篇)。学习源码整体架构系列文章(有哪些必看的JS库):jQuery、underscore、lodash、sentry、vuex、axios、koa、redux、vue-devtools 间接关上文件性能揭秘。

10篇源码系列文章小成就达成,从19年7月开始写,19年写了6篇,20年写了2篇,往年写了2篇。算是一个完结吧。短时间内应该临时不更新这个系列了。次要是投入的工夫和精力比拟多,看的人很少,失去的反馈也比拟少。之后先写其余文章吧。欢送继续关注我(若川)。

本文仓库地址:git clone https://github.com/lxchuan12/vuex4-analysis.git,本文最佳浏览形式,克隆仓库本人入手调试,容易排汇消化。

要是有人说到怎么读源码,正在读文章的你能举荐我的源码系列文章,那真是无以为报啊

我的文章,尽量写得让想看源码又不晓得怎么看的读者能看懂。我都是举荐应用搭建环境断点调试源码学习哪里不会点哪里边调试边看,而不是硬看。正所谓:授人与鱼不如授人予渔

浏览本文后你将学到:

    1. git subtree 治理子仓库
    1. 如何学习Vuex 4源码、了解Vuex原理
    1. Vuex 4Vuex 3 的异同
    1. Vuex 4 composition API 如何应用
    1. Vue.provide / Vue.inject API 应用和原理
    1. 如何写一个 Vue3 插件
  • 等等

如果对于谷歌浏览器调试还不是很相熟的读者,能够看这篇文章chrome devtools source面板,写的很具体。顺带提一下,我关上的设置,source面板中反对开展搜寻代码块(默认不反对),一图胜千言。

谷歌浏览器是咱们前端罕用的工具,所以倡议大家深刻学习,毕竟工欲善其事,必先利其器

之前写过Vuex 3的源码文章学习 vuex 源码整体架构,打造属于本人的状态治理库、若川的博客Vuex源码,仓库有很具体的正文和看源码办法,所以本文不会过多赘述与Vuex 3源码雷同的中央。

1.1 本文浏览最佳形式

把我的vuex4源码仓库 git clone https://github.com/lxchuan12/vuex4-analysis.git克隆下来,顺便star一下我的vuex4源码学习仓库^_^。跟着文章节奏调试和示例代码调试,用chrome入手调试印象更加粗浅。文章长段代码不必细看,能够调试时再细看。看这类源码文章百遍,可能不如本人多调试几遍,大胆猜想,小心求证。也欢送加我微信交换ruochuan12

2. Vuex 原理简述

论断后行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、childInstance1、childInstance2 这些对象中的provides.store都改了。// 因为共享着同一个store对象。

看了下面的官网文档中的图,大略晓得是用provide父级组件中提供Store实例,用inject来获取到Store实例。

那么接下来,带着问题:

1、为什么批改了实例store里的属性,变更后会触发视图更新。

2、Vuex4作为Vue的插件如何实现和Vue联合的。

3、provideinject的如何实现的,每个组件如何获取到组件实例中的Store的。

4、为什么每个组件对象里都有Store实例对象了(渲染组件对象过程)。

5、为什么在组件中写的provide提供的数据,能被子级组件获取到。

TODO:
那么每个组件如何获取组件实例中的Store实例,composition API中实质上则是应用inject函数。

全局的Store 实例对象。通过Vue.reactive()监测数据。

3. Vuex 4 重大扭转

在看源码之前,先来看下Vuex 4公布的release和官网文档迁徙提到的重大扭转,Vuex 4 release。

从 3.x 迁徙到 4.0

Vuex 4的重点是兼容性。Vuex 4反对应用Vue 3开发,并且间接提供了和Vuex 3完全相同的API,因而用户能够在Vue 3我的项目中复用现有的Vuex代码。

相比Vuex 3版本。次要有如下重大扭转(其余的在上方链接中):

3.1 装置过程

Vuex 3Vue.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')

3.2 外围模块导出了 createLogger 函数

import { createLogger } from 'vuex'

接下来咱们从源码的角度来看这些重大扭转

4. 从源码角度看 Vuex 4 重大变动

4.1 chrome 调试 Vuex 4 源码筹备工作

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.gitcd vuexnpm inpm 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.jsimport { 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.jsimport { 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)两个方面发散开来解说。

4.2 Vuex.createStore 函数

相比 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提供的插件机制。

4.3 app.use() 办法

use做的事件说起来也算简略,把传递过去的插件增加插件汇合中,到避免反复。

执行插件,如果是对象,install是函数,则把参数app和其余参数传递给install函数执行。如果是函数间接执行。

// webpack:///./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.jsfunction 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函数。

4.4 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实现。

4.4.1 app.provide

简略来说就是给contextprovides属性中加了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.jsfunction createAppContext() {    return {        app: null,        config: {            isNativeTag: NO,            performance: false,            globalProperties: {},            optionMergeStrategies: {},            isCustomElement: NO,            errorHandler: undefined,            warnHandler: undefined        },        mixins: [],        components: {},        directives: {},        provides: Object.create(null)    };}

Vue3 文档利用配置(app.config)

4.4.2 app.config.globalProperties

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,这是线索。

4.5 composition API 中如何应用Vuex 4

接着咱们找到如下文件,useStore是咱们断点的对象。

// webpack:///./examples/composition/shopping-cart/components/ShoppingCart.vueimport { computed } from 'vue'import { useStore } from 'vuex'import { currency } from '../currency'export default {  setup () {    const store = useStore()    // 我加的这行代码    window.ShoppingCartStore = store;    // 省略了若干代码  }}

接着断点按F11,单步调试,会发现最终是应用了Vue.inject办法。

4.5.1 Vuex.useStore 源码实现

// vuex/src/injectKey.jsimport { inject } from 'vue'export const storeKey = 'store'export function useStore (key = null) {  return inject(key !== null ? key : storeKey)}

4.5.2 Vue.inject 源码实现

接着看inject函数,看着代码很多,其实原理很简略,就是要找到咱们用provide提供的值。

如果没有父级,也就是根实例,就取实例对象中的vnode.appContext.provides
否则就取父级中的instance.parent.provides的值。

Vuex4源码里则是:Store实例对象。

// webpack:///./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.jsfunction 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

4.5.3 Vue.provide 源码实现

provide函数作用其实也算简略,1、也就是给以后组件实例上的provides对象属性,增加键值对key/value

2、还有一个作用是当以后组件和父级组件的provides雷同时,在以后组件实例中的provides对象和父级,则建设链接,也就是原型[[prototype]],(__proto__)。

// webpack:///./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.jsfunction 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几次,原型链就越长。

上文injectprovide函数中都有个变量currentInstance以后实例,那么以后实例对象是怎么来的呢。

为什么每个组件就能拜访到,依赖注入的思维。
有一个讨巧的办法,就是在文件runtime-core.esm-bundler.js中搜寻provides,则能搜寻到createComponentInstance函数

接下来咱们createComponentInstance函数如何创立组件实例。

4.6 createComponentInstance 创立组件实例

能够禁用其余断点,独自断点这里,
比方:const appContext = (parent ? parent.appContext : vnode.appContext) || emptyAppContext;
来看具体实现。

// webpack:///./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.jsconst 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 其实就是建设原型关系。这时放一张图,一图胜千言。

出自黄轶老师拉勾专栏,本想本人画一张图,但感觉这张挺好的。

4.6.1 组件实例生成了,那怎么把它们联合呢

这时,也有一个讨巧的办法,在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.jsfunction 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能够获取以后组件实例。

4.7 getCurrentInstance 获取以后实例对象

getCurrentInstance 反对拜访外部组件实例,用于高阶用法或库的开发。

import { getCurrentInstance } from 'vue'const MyComponent = {  setup() {    const internalInstance = getCurrentInstance()    internalInstance.appContext.config.globalProperties // 拜访 globalProperties  }}

晓得这个API后,咱们能够在购物车例子的代码中增加一些代码。便于咱们了解。

// vuex/examples/composition/shopping-cart/components/App.vueimport { 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.vuesetup(){  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.vuesetup () {    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}

在控制台输入这些值

AppCurrentInstanceAppCurrentInstance.providesShoppingCartCurrentInstance.parent === AppCurrentInstance // trueShoppingCartCurrentInstance.providesShoppingCartStore === AppStore // trueProductListStore === AppStore // trueAppStore // 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. 解答下结尾提出的5个问题

对立解答下结尾提出的5个问题:

1、为什么批改了实例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  })  // 省略若干代码...}

2、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    }    // 省略若干代码...}

3、provideinject的如何实现的,每个组件如何获取到组件实例中的Store的。

5、为什么在组件中写的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实例' }  }    }  }}

4、为什么每个组件对象里都有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;}
  1. 你怎么晓得那么多的

答:因为社区有人写了Vue4源码文章。

6. 总结

本文次要讲述了Vuex4Store实例注入到各个组件中的原理,开展讲述了Vuex4绝对与Vuex3装置形式的扭转Vuex.createStoreapp.use(store) ,深刻源码剖析Vue.injectVue.provide实现原理。

Vuex4 除了装置形式和监测数据变动形式应用了Vue.reactive,其余根本和Vuex3.x版本没什么区别。

最初回顾下文章结尾的图,能够说就是原型链的妙用。

是不是感觉恍然大悟。

Vuex其实也是Vue的一个插件,通晓了Vuex原理,对于本人给Vue写插件也是会熟能生巧。

如果读者敌人发现有不妥或可改善之处,再或者哪里没写明确的中央,欢送评论指出,也欢送加我微信 ruochuan12 交换。另外感觉写得不错,对您有些许帮忙,能够点赞、评论、转发分享,也是对我的一种反对,万分感激。如果能关注我的前端公众号:「若川视线」,就更好啦。

对于

你好,我是若川,微信搜寻「若川视线」关注我,专一前端技术分享,一个愿景是帮忙5年内前端开阔视野走向前列的公众号。欢送加我微信ruochuan12,长期交流学习。
次要有以下系列文章:学习源码整体架构系列、年度总结、JS根底系列

参考链接

官网文档 Provide / Inject

github 仓库 provide/inject 源码

github 仓库 provide/inject 测试

Vuex 4 官网中文文档