Vue组件通信原理剖析(二)全局状态管理Vuex,程序员的中年危机

install,

version: ‘VERSION’,

mapState,

mapMutations,

mapGetters,

mapActions,

createNamespacedHelpers

}

  • 其次是定义store,并且实现vue的Install方法

// src/store.js

let Vue // bind on install

export class Store {

}

// 实现的Install方法

export function install (_Vue) {

if (Vue && _Vue === Vue) {

if (process.env.NODE_ENV !== ‘production’) {

console.error(

‘[vuex] already installed. Vue.use(Vuex) should be called only once.’

)

}

return

}

Vue = _Vue

applyMixin(Vue)

}

问题2:state的数据响应式

看懂了Vuex的入口定义,下面我们就针对store的定义来一探究竟,先看看state的实现

// src/store.js

export class Store {

constructor(options = {}) {

// strict mode

this.strict = strict

const state = this._modules.root.state

// initialize the store vm, which is responsible for the reactivity

// (also registers _wrappedGetters as computed properties)

// 看上面的注释可以得知,resetStoreVM就是初始化store中负责响应式的vm的方法,而且还注册所有的gettersz作为vm的计算属性

resetStoreVM(this, state)

}

}

我们来看看resetStoreVM的具体实现

// src/store.js

function resetStoreVM (store, state, hot) {

const oldVm = store._vm

// bind store public getters

store.getters = {}

// reset local getters cache

store._makeLocalGettersCache = Object.create(null)

const wrappedGetters = store._wrappedGetters

const computed = {}

// 这里是实现getters的派生

forEachValue(wrappedGetters, (fn, key) => {

// use computed to leverage its lazy-caching mechanism

// direct inline function use will lead to closure preserving oldVm.

// using partial to return function with only arguments preserved in closure environment.

computed[key] = partial(fn, store)

Object.defineProperty(store.getters, key, {

get: () => store._vm[key],

enumerable: true // for local getters

})

})

// use a Vue instance to store the state tree

// suppress warnings just in case the user has added

// some funky global mixins

// 这是是通过new一个Vue实例,并将state作为实例的datas属性,那他自然而然就具有了响应式

const silent = Vue.config.silent

Vue.config.silent = true

store._vm = new Vue({

data: {

$$state: state

},

computed

})

Vue.config.silent = silent

// enable strict mode for new vm

if (store.strict) {

enableStrictMode(store)

}

if (oldVm) {

if (hot) {

// dispatch changes in all subscribed watchers

// to force getter re-evaluation for hot reloading.

store._withCommit(() => {

oldVm._data.$$state = null

})

}

Vue.nextTick(() => oldVm.$destroy())

}

}

问题3:getters实现state中的数据的派生

关于getters的实现,我们在上面也做了相应的解释,实际上就是将getters的方法包装一层后,收集到computed对象中,并使用Object.defineProperty注册store.getters,使得每次取值时,从store._vm中取。

关键的步骤就是创建一个Vue的实例

store._vm = new Vue({

data: {

$$state: state // 这是store中的所有state

},

computed // 这是store中的所有getters

})

问题4:mutations中同步commit

// src/store.js

// store的构造函数

constructor(options = {}) {

// 首先在构造方法中,把store中的commit和dispatch绑定到自己的实例上,

// 为什么要这么做呢?

// 是因为在commit或者dispatch时,尤其是dispatch,执行function时会调用实例this,而方法体内的this是具有作用域属性的,所以如果要保证每次this都代表store实例,就需要重新绑定一下。

const store = this

const { dispatch, commit } = this

this.dispatch = function boundDispatch (type, payload) {

return dispatch.call(store, type, payload)

}

this.commit = function boundCommit (type, payload, options) {

return commit.call(store, type, payload, options)

}

}

// commit 的实现

commit (_type, _payload, _options) {

// check object-style commit

const {

type,

payload,

options

} = unifyObjectStyle(_type, _payload, _options)

const mutation = { type, payload }

// 通过传入的类型,查找到mutations中的对应的入口函数

const entry = this._mutations[type]

// 这里是执行的主方法,通过遍历入口函数,并传参执行

this._withCommit(() => {

entry.forEach(function commitIterator (handler) {

handler(payload)

})

})

}

问题5:actions中的异步dispatch

上面说了在构造store时绑定dispatch的原因,下面我们就继续看看dispatch的具体实现。

// src/store.js

// dispatch 的实现

dispatch (_type, _payload) {

// check object-style dispatch

const {

type,

payload

} = unifyObjectStyle(_type, _payload)

const action = { type, payload }

// 同样的道理,通过type获取actions中的入口函数

const entry = this._actions[type]

······

// 由于action是异步函数的集合,这里就用到了Promise.all,来合并多个promise方法并执行

const result = entry.length > 1

? Promise.all(entry.map(handler => handler(payload)))
entry 0

return result.then(res => {

try {

this._actionSubscribers

.filter(sub => sub.after)

.forEach(sub => sub.after(action, this.state))

} catch (e) {

if (process.env.NODE_ENV !== ‘production’) {

console.warn([vuex] error in after action subscribers: )

console.error(e)

}

}

return res

})

}

到这里,我们就把整个store中状态存储和状态变更的流程系统的串联了一遍,让我们对Vuex内部的机智有个简单的认识,最后我们根据我们对Vuex的理解来实现一个简单的Vuex。

// store.js

let Vue

// 定义store类

class Store{

constructor(options = {}) {

this.$options = options

this._mutations = options.mutations

this._actions = options.actions

this._wrappedGetters = options.getters

// 定义computed

const computed = {}

this.getters = {}

const store = this

Object.keys(this._wrappedGetters).forEach(key => {

// 获取用户定义的getters

const fn = store._wrappedGetters[key]

// 转换为computed可以使用无参数形式

computed[key] = function() {

return fn(store.state)

}

// 为getters定义只读属性

Object.defineProperty(store.getters, key {

get:() => store._vm[key]

})

})

// state的响应式实现

this._vm = new Vue({

data: {

// 加两个$,Vue不做代理

$$state: options.state

},

computed // 添加计算属性

})

this.commit = this.commit.bind(this)

this.dispatch = this.dispatch.bind(this)

}

// 存取器,获取store.state ,只通过get形式获取,而不是直接this.xxx, 达到对state

get state() {

return this._vm._data.$$state

}

set state(v) {

// 如果用户不通过commit方式来改变state,就可以在这里做一控制

}

// commit的实现

commit(type, payload) {

const entry = this._mutations[type]

if (entry) {

entry(this.state, payload)

}

}

// dispatch的实现

dispatch(type, payload) {

const entry = this._actions[type]

if (entry) {

entry(this, payload)

}

}

}

// 实现install

function install(_Vue) {

Vue = _Vue

Vue.mixin({

beforeCreate() {

if (this.$options.store) {

Vue.prototype. S t o r e = t h i s . Store = this. Store=this.options.store // 这样就可以使用 this.$store

文末

js前端的重头戏,值得花大部分时间学习。

JavaScript知识

推荐通过书籍学习,《 JavaScript 高级程序设计(第 4 版)》你值得拥有。整本书内容质量都很高,尤其是前十章语言基础部分,建议多读几遍。

前端电子书

另外,大推一个网上教程 现代 JavaScript 教程 ,文章深入浅出,很容易理解,上面的内容几乎都是重点,而且充分发挥了网上教程的时效性和资料链接。

学习资料在精不在多,二者结合,定能构建你的 JavaScript 知识体系。

面试本质也是考试,面试题就起到很好的考纲作用。想要取得优秀的面试成绩,刷面试题是必须的,除非你样样精通。

这是288页的前端面试题

288页面试题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值