Skip to content

Commit 0a6eb18

Browse files
committed
update 《Vuex源码解析》
1 parent 96a5c27 commit 0a6eb18

File tree

2 files changed

+120
-3
lines changed

2 files changed

+120
-3
lines changed

docs/Vuex源码解析.MarkDown

+119-2
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,123 @@ function registerAction (store, type, handler, local) {
493493
}
494494
```
495495

496-
dispatch
496+
因为registerAction的时候将push进_actions的action进行了一层封装(wrappedActionHandler),所以我们在进行dispatch的第一个参数中获取state、commit等方法。之后,执行结果res会被进行判断是否是Promise,不是则会进行一层封装,将其转化成Promise对象。dispatch时则从_actions中取出,只有一个的时候直接返回,否则用Promise.all处理再返回。
497497

498-
因为registerAction的时候将push进_actions的action进行了封装
498+
### watch
499+
500+
```javascript
501+
/* 观察一个getter方法 */
502+
watch (getter, cb, options) {
503+
if (process.env.NODE_ENV !== 'production') {
504+
assert(typeof getter === 'function', `store.watch only accepts a function.`)
505+
}
506+
return this._watcherVM.$watch(() => getter(this.state, this.getters), cb, options)
507+
}
508+
```
509+
510+
熟悉Vue的朋友应该很熟悉watch这个方法。这里采用了比较巧妙的设计,_watcherVM是一个Vue的实例,所以watch就可以直接采用了Vue内部的watch特性提供了一种观察数据getter变动的方法。
511+
512+
### registerModule
513+
514+
```javascript
515+
/* 注册一个动态module,当业务进行异步加载的时候,可以通过该接口进行注册动态module */
516+
registerModule (path, rawModule) {
517+
/* 转化称Array */
518+
if (typeof path === 'string') path = [path]
519+
520+
if (process.env.NODE_ENV !== 'production') {
521+
assert(Array.isArray(path), `module path must be a string or an Array.`)
522+
assert(path.length > 0, 'cannot register the root module by using registerModule.')
523+
}
524+
525+
/*注册*/
526+
this._modules.register(path, rawModule)
527+
/*初始化module*/
528+
installModule(this, this.state, path, this._modules.get(path))
529+
// reset store to update getters...
530+
/* 通过vm重设store,新建Vue对象使用Vue内部的响应式实现注册state以及computed */
531+
resetStoreVM(this, this.state)
532+
}
533+
```
534+
535+
registerModule用以注册一个动态模块,也就是在store创建以后再注册模块的时候用该接口。内部实现实际上也只有installModule与resetStoreVM两个步骤,前面已经讲过,这里不再累述。
536+
537+
### unregisterModule
538+
539+
```javascript
540+
/* 注销一个动态module */
541+
unregisterModule (path) {
542+
/* 转化称Array */
543+
if (typeof path === 'string') path = [path]
544+
545+
if (process.env.NODE_ENV !== 'production') {
546+
assert(Array.isArray(path), `module path must be a string or an Array.`)
547+
}
548+
549+
/*注销*/
550+
this._modules.unregister(path)
551+
this._withCommit(() => {
552+
/* 获取父级的state */
553+
const parentState = getNestedState(this.state, path.slice(0, -1))
554+
/* 从父级中删除 */
555+
Vue.delete(parentState, path[path.length - 1])
556+
})
557+
/* 重制store */
558+
resetStore(this)
559+
}
560+
```
561+
562+
同样,与registerModule对应的方法unregisterModule,动态注销模块。实现方法是先从state中删除模块,然后用resetStore来重制store。
563+
564+
### resetStore
565+
566+
```javascript
567+
/* 重制store */
568+
function resetStore (store, hot) {
569+
store._actions = Object.create(null)
570+
store._mutations = Object.create(null)
571+
store._wrappedGetters = Object.create(null)
572+
store._modulesNamespaceMap = Object.create(null)
573+
const state = store.state
574+
// init all modules
575+
installModule(store, state, [], store._modules.root, true)
576+
// reset vm
577+
resetStoreVM(store, state, hot)
578+
}
579+
```
580+
581+
这里的resetStore其实也就是将store中的_actions等进行初始化以后,重新执行installModule与resetStoreVM来初始化module以及用Vue特性使其“响应式化”,这跟构造函数中的是一致的。
582+
583+
## 插件
584+
585+
Vue提供了一个非常好用的插件[Vue.js devtools](https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd)
586+
587+
```javascript
588+
/* 从window对象的__VUE_DEVTOOLS_GLOBAL_HOOK__中获取devtool插件 */
589+
const devtoolHook =
590+
typeof window !== 'undefined' &&
591+
window.__VUE_DEVTOOLS_GLOBAL_HOOK__
592+
593+
export default function devtoolPlugin (store) {
594+
if (!devtoolHook) return
595+
596+
/* devtoll插件实例存储在store的_devtoolHook上 */
597+
store._devtoolHook = devtoolHook
598+
599+
/* 出发vuex的初始化事件,并将store的引用地址传给deltool插件,使插件获取store的实例 */
600+
devtoolHook.emit('vuex:init', store)
601+
602+
/* 监听travel-to-state事件 */
603+
devtoolHook.on('vuex:travel-to-state', targetState => {
604+
/* 重制state */
605+
store.replaceState(targetState)
606+
})
607+
608+
/* 订阅store的变化 */
609+
store.subscribe((mutation, state) => {
610+
devtoolHook.emit('vuex:mutation', mutation, state)
611+
})
612+
}
613+
```
614+
615+
如果已经安装了该插件,则会在windows对象上暴露一个__VUE_DEVTOOLS_GLOBAL_HOOK__。devtoolHook用在初始化的时候会触发“vuex:init”事件通知插件,然后通过on方法监听“vuex:travel-to-state”事件来重置state。最后通过Store的subscribe方法来添加一个订阅者,在触发commit方法修改mutation数据以后,该订阅者会被通知,从而触发“vuex:mutation”事件。

vuex-src/store.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ function registerAction (store, type, handler, local) {
481481
res = Promise.resolve(res)
482482
}
483483
if (store._devtoolHook) {
484-
/* 存在devtool插件的时候触发vuex的error给devtool */
484+
/* 存在devtool捕获的时候触发vuex的error给devtool */
485485
return res.catch(err => {
486486
store._devtoolHook.emit('vuex:error', err)
487487
throw err

0 commit comments

Comments
 (0)