VUEX 3.x源码分析——7. 模块的动态注册和卸载

这是对vuex3版本的源码分析。
本次分析会按以下方法进行:

  1. 按官网的使用文档顺序,围绕着某一功能点进行分析。这样不仅能学习优秀的项目源码,更能加深对项目的某个功能是如何实现的理解。这个对自己的技能提升,甚至面试时的回答都非常有帮助。
  2. 在围绕某个功能展开讲解时,所有不相干的内容都会暂时去掉,等后续涉及到对应的功能时再加上。这样最大的好处就是能循序渐进地学习,同时也不会被不相干的内容影响。省略的内容都会在代码中以…表示。
  3. 每段代码的开头都会说明它所在的文件目录,方便定位和查阅。如果一个函数内容有多个函数引用,这些都会放在同一个代码块中进行分析,不同路径的内容会在其头部加上所在的文件目录。

本章讲解vuex中模块的动态注册和卸载的相关内容。
想了解vuex中的其他源码分析,欢迎参考我发布的下列文章:
VUEX3 源码分析——1. 理解State
VUEX3 源码分析——2. 理解Getter
VUEX3 源码分析——3. 理解Mutations
VUEX3 源码分析——4. 理解Action
VUEX 3 源码分析——5. 理解Module
VUEX 3.x源码分析——6. 理解命名空间namespace

官网示例:

  • 在 store 创建之后,使用 store.registerModule 方法注册模块。之后就可以通过 store.state.myModule 和 store.state.nested.myModule 访问模块的状态。
  • 使用 store.unregisterModule(moduleName) 来动态卸载模块。注意,不能使用此方法卸载静态模块(即创建 store 时声明的模块)。
  • 通过 store.hasModule(moduleName) 方法检查该模块是否已经被注册到 store。
import Vuex from 'vuex'

const store = new Vuex.Store({ /* 选项 */ })

// 注册模块 `myModule`
store.registerModule('myModule', {
  // ...
})
// 注册嵌套模块 `nested/myModule`
store.registerModule(['nested', 'myModule'], {
  // ...
})

注册动态模块:registerModule

  • registerModule方法接受三个参数,模块名,模块内容,options。
  • 首先将path进行结构统一化,这样确保可以注册嵌套模块(但是要用户确保嵌套的父模块已存在)
  • 之后将模块注册到根模块上,嵌套模块会被处理,最终注册到上一级的模块中。例如上面示例的嵌套模块myModule会被注册到nested中。
  • 之后的逻辑和store初始化的内容一致,register和installModule等内容就不具体展开了,之前都有分析过,可以看我之前发布的文章。
  • 总得来说,registerModule方法就是在store创建之后,用户再次定义的某个模块挂载到根模块或者嵌套模块的父级中,再重新执行store的初始化工作(installModule,resetStoreState)
// store.js
export class Store {
	constructor(options = {}) {
        this._modules = new ModuleCollection(options)
        ...
    }
    ...
	registerModule(path, rawModule, options={}) {
		if (typeof path === 'string') path = [path] // 统一path结构
		this._modules.register(path, rawModule)
		//  options.preserveState 是用来跳过 installModule 中的 set state的代码逻辑
        installModule(this, this.state, path, this._modules.get(path), options.preserveState)
        // reset store to update getters...
        resetStoreState(this, this.state)   
	}
}

卸载动态模块:unregisterModule

  • unregisterModule方法接受一个参加,即需要卸载模块的模块名(或者嵌套名)
  • 调用unregister方法,将path对应的模块从父模块中删除。
// store.js
export class Store {
	constructor(options = {}) {
        this._modules = new ModuleCollection(options)
        ...
    }
    ...
	unregisterModule(path) {
		if (typeof path === 'string') path = [path]
		this._modules.unregister(path)
		this._withCommit(() => {
			const parentState = getNestedState(this.state, path.slice(0, -1))
			delete parentState[path[path.length - 1]]
		}
		resetStore(this)
	}
}

// ./module/module-collection.js
export default class ModuleCollection {
	...
	unregister(path) {
		const parent = this.get(path.slice(0, -1))
		const key = path[path.length-1]
		const child = parent.getChild(key)
		if (!child) {return}
		
		if (!child.runtime) {
			return
		}
		parent.removeChild(key)
	}
}
  • 不能删除静态模块(创建store时声明的模块)的原因在于unregister函数中对child.runtime的判断。从上面的代码可以看到,如果需要删除的模块的runtime为false,则不进行删除处理,直接返回。
  • 而在创建store时,这些静态模块的runtime都被设置成false,动态模块注册时又都是true,所以实现不不能删除静态模块的功能。
  • 所以如果你想实现静态模块也能删除,就可以对这里的源码内容进行修改
  • 相关代码如下:
// ./module/module-collection.js
export default class ModuleCollection {
	constructor(rawRootModule) {
		// 静态模块都在这里进行注册,runtime为false
		this.register([], rawRootModule, false)
	}
	...
	// 动态模块注册时没有runtime暴露外界,所以都会得到默认值true
	register(path, rawModule, runtime = true) { 
		const newModule = new Module(rawModule, runtime)
		...
	}
}

检查模块是否注册:hasModule

  • hasModule接受一个参数path,path会在内部进行格式统一化,确保可以检查嵌套模块。
// store.js
export class Store {
	constructor(options = {}) {
        this._modules = new ModuleCollection(options)
        ...
    }
    ...
	hasModule(path) {
		if (typeof path === 'string') path = [path]
		return this._modules.isRegistered(path)
	}
}


// ./module/module-collection.js
export default class ModuleCollection {
	...
	isRegistered(path) {
		const parent = this.get(path.slice(0, -1))
		const key = path[path.length-1]
		if (parent) {
			return parent.hasChild(key)
		}
		return false
	}
}

保留 state

  • 在注册一个新 module 时,你很有可能想保留过去的 state,例如从一个服务端渲染的应用保留 state。你可以通过 preserveState 选项将其归档:store.registerModule(‘a’, module, { preserveState: true })。
  • 当你设置 preserveState: true 时,该模块会被注册,action、mutation 和 getter 会被添加到 store 中,但是 state 不会。这里假设 store 的 state 已经包含了这个 module 的 state 并且你不希望将其覆写。
  • 以上是官网对保留state的解释,在源码实现层,是通过registerModule的第三个参数(布尔值)来实现的。
  • 在installModule函数中通过判断是否是根模块和hot的值来决定是否更新注册模块的state。由于这里hot = true,所以不会执行更新逻辑。但是这样要确保store 的 state 已经包含了这个 module 的 state。
// store.js
export class Store {
	constructor(options = {}) {
        this._modules = new ModuleCollection(options)
        ...
    }
    ...
	registerModule(path, rawModule, options={}) {
		if (typeof path === 'string') path = [path]
		this._modules.register(path, rawModule)
		//  这里的 options.preserveState = true,传入 installModule 函数
        installModule(this, this.state, path, this._modules.get(path), options.preserveState)
        resetStoreState(this, this.state)   
	}
}

// ./store.util.js
export function installModule(store, rootState, path, module, hot) {
	const isRoot = !path.length
	...
	// 由于hot=true,不会执行这里的逻辑,所以不会更新state
	if (!isRoot && !hot) {
		const parentState = getNestedState(rootState, path.slice(0, -1))
		const moduleName = path[path.length - 1]
		store._withCommit(() => {
			parentState[moduleName] = module.state
		}
	}
}
  • 11
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值