Web前端码农是如何进入腾讯的,vuex 源码整体架构学习,零基础开发web前端

本文详细剖析了VuexStore的构造过程,涉及模块安装、状态注册、mutation、action、getter的处理,以及hotUpdate功能,展示了Vuex在状态管理和组件通信中的关键机制。
摘要由CSDN通过智能技术生成

this.state = (typeof rawState === ‘function’ ? rawState() : rawState) || {}

}

}

经过一系列的注册后,最后 this._modules = new ModuleCollection(options) this._modules 的值是这样的。笔者画了一张图表示:

installModule 函数

function installModule (store, rootState, path, module, hot) {

// 是根模块

const isRoot = !path.length

// 命名空间 字符串

const namespace = store._modules.getNamespace(path)

if (module.namespaced) {

// 省略代码:模块命名空间map对象中已经有了,开发环境报错提示重复

// module 赋值给 _modulesNamespaceMap[namespace]

store._modulesNamespaceMap[namespace] = module

}

// … 后续代码 移出来 待读解释

}

注册 state

// set state

// 不是根模块且不是热重载

if (!isRoot && !hot) {

// 获取父级的state

const parentState = getNestedState(rootState, path.slice(0, -1))

// 模块名称

// 比如 cart

const moduleName = path[path.length - 1]

// state 注册

store._withCommit(() => {

// 省略代码:非生产环境 报错 模块 state 重复设置

Vue.set(parentState, moduleName, module.state)

})

}

最后得到的是类似这样的结构且是响应式的数据 实例 Store.state 比如:

{

// 省略若干属性和方法

// 这里的 state 是只读属性 可搜索 get state 查看,上文写过

state: {

cart: {

checkoutStatus: null,

items: []

}

}

}

const local = module.context = makeLocalContext(store, namespace, path)

module.context 这个赋值主要是给 helpers 中 mapStatemapGettersmapMutationsmapActions四个辅助函数使用的。

生成本地的dispatch、commit、getters和state。

主要作用就是抹平差异化,不需要用户再传模块参数。

遍历注册 mutation

module.forEachMutation((mutation, key) => {

const namespacedType = namespace + key

registerMutation(store, namespacedType, mutation, local)

})

/**

  • 注册 mutation

  • @param {Object} store 对象

  • @param {String} type 类型

  • @param {Function} handler 用户自定义的函数

  • @param {Object} local local 对象

*/

function registerMutation (store, type, handler, local) {

// 收集的所有的mutations找对应的mutation函数,没有就赋值空数组

const entry = store._mutations[type] || (store._mutations[type] = [])

// 最后 mutation

entry.push(function wrappedMutationHandler (payload) {

/**

  • mutations: {

  • pushProductToCart (state, { id }) {

  •    console.log(state);
    
  • }

  • }

  • 也就是为什么用户定义的 mutation 第一个参数是state的原因,第二个参数是payload参数

*/

handler.call(store, local.state, payload)

})

}

遍历注册 action

module.forEachAction((action, key) => {

const type = action.root ? key : namespace + key

const handler = action.handler || action

registerAction(store, type, handler, local)

})

/**

  • 注册 mutation

  • @param {Object} store 对象

  • @param {String} type 类型

  • @param {Function} handler 用户自定义的函数

  • @param {Object} local local 对象

*/

function registerAction (store, type, handler, local) {

const entry = store._actions[type] || (store._actions[type] = [])

// payload 是actions函数的第二个参数

entry.push(function wrappedActionHandler (payload) {

/**

  • 也就是为什么用户定义的actions中的函数第一个参数有

  • { dispatch, commit, getters, state, rootGetters, rootState } 的原因

  • actions: {

  • checkout ({ commit, state }, products) {

  •    console.log(commit, state);
    
  • }

  • }

*/

let res = handler.call(store, {

dispatch: local.dispatch,

commit: local.commit,

getters: local.getters,

state: local.state,

rootGetters: store.getters,

rootState: store.state

}, payload)

/**

  • export function isPromise (val) {

return val && typeof val.then === ‘function’

}

  • 判断如果不是Promise Promise 化,也就是为啥 actions 中处理异步函数

也就是为什么构造函数中断言不支持promise报错的原因

vuex需要Promise polyfill

assert(typeof Promise !== ‘undefined’, vuex requires a Promise polyfill in this browser.)

*/

if (!isPromise(res)) {

res = Promise.resolve(res)

}

// devtool 工具触发 vuex:error

if (store._devtoolHook) {

// catch 捕获错误

return res.catch(err => {

store._devtoolHook.emit(‘vuex:error’, err)

// 抛出错误

throw err

})

} else {

// 然后函数执行结果

return res

}

})

}

遍历注册 getter

module.forEachGetter((getter, key) => {

const namespacedType = namespace + key

registerGetter(store, namespacedType, getter, local)

})

/**

  • 注册 getter

  • @param {Object} store Store实例

  • @param {String} type 类型

  • @param {Object} rawGetter 原始未加工的 getter 也就是用户定义的 getter 函数

  • @examples 比如 cartProducts: (state, getters, rootState, rootGetters) => {}

  • @param {Object} local 本地 local 对象

*/

function registerGetter (store, type, rawGetter, local) {

// 类型如果已经存在,报错:已经存在

if (store._wrappedGetters[type]) {

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

console.error([vuex] duplicate getter key: ${type})

}

return

}

// 否则:赋值

store._wrappedGetters[type] = function wrappedGetter (store) {

/**

  • 这也就是为啥 getters 中能获取到 (state, getters, rootState, rootGetters) 这些值的原因

  • getters = {

  •  cartProducts: (state, getters, rootState, rootGetters) => {
    
  •    console.log(state, getters, rootState, rootGetters);
    
  •  }
    
  • }

*/

return rawGetter(

local.state, // local state

local.getters, // local getters

store.state, // root state

store.getters // root getters

)

}

}

遍历注册 子模块

module.forEachChild((child, key) => {

installModule(store, rootState, path.concat(key), child, hot)

})

resetStoreVM 函数

resetStoreVM(this, state, hot)

初始化 store._vm 响应式的

并且注册 _wrappedGetters 作为 computed 的属性

function resetStoreVM (store, state, hot) {

// 存储一份老的Vue实例对象 _vm

const oldVm = store._vm

// bind store public getters

// 绑定 store.getter

store.getters = {}

// reset local getters cache

// 重置 本地getters的缓存

store._makeLocalGettersCache = Object.create(null)

// 注册时收集的处理后的用户自定义的 wrappedGetters

const wrappedGetters = store._wrappedGetters

// 声明 计算属性 computed 对象

const computed = {}

// 遍历 wrappedGetters 赋值到 computed 上

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.

/**

  • partial 函数

  • 执行函数 返回一个新函数

export function partial (fn, arg) {

return function () {

return fn(arg)

}

}

*/

computed[key] = partial(fn, store)

// getter 赋值 keys

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

// 使用一个 Vue 实例对象存储 state 树

// 阻止警告 用户添加的一些全局mixins

// 声明变量 silent 存储用户设置的静默模式配置

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

// 开启严格模式 执行这句

// 用 $watch 观测 state,只能使用 mutation 修改 也就是 _withCommit 函数

if (store.strict) {

enableStrictMode(store)

}

// 如果存在老的 _vm 实例

if (oldVm) {

// 热加载为 true

if (hot) {

// dispatch changes in all subscribed watchers

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

// 设置 oldVm._data.$$state = null

store._withCommit(() => {

oldVm._data.$$state = null

})

}

// 实例销毁

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

}

}

到此,构造函数源代码看完了,接下来看 Vuex.Store 的 一些 API 实现。

Vuex.Store 实例方法


Vuex API 文档

commit

提交 mutation

commit (_type, _payload, _options) {

// check object-style commit

// 统一成对象风格

const {

type,

payload,

options

} = unifyObjectStyle(_type, _payload, _options)

const mutation = { type, payload }

// 取出处理后的用户定义 mutation

const entry = this._mutations[type]

// 省略 非生产环境的警告代码 …

this._withCommit(() => {

// 遍历执行

entry.forEach(function commitIterator (handler) {

handler(payload)

})

})

// 订阅 mutation 执行

this._subscribers.forEach(sub => sub(mutation, this.state))

// 省略 非生产环境的警告代码 …

}

commit 支持多种方式。比如:

store.commit(‘increment’, {

count: 10

})

// 对象提交方式

store.commit({

type: ‘increment’,

count: 10

})

unifyObjectStyle函数将参数统一,返回 { type, payload, options }

dispatch

分发 action

dispatch (_type, _payload) {

// check object-style dispatch

// 获取到type和payload参数

const {

type,

payload

} = unifyObjectStyle(_type, _payload)

// 声明 action 变量 等于 type和payload参数

const action = { type, payload }

// 入口,也就是 _actions 集合

const entry = this._actions[type]

// 省略 非生产环境的警告代码 …

try {

this._actionSubscribers

.filter(sub => sub.before)

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

} catch (e) {

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

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

console.error(e)

}

}

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

})

}

replaceState

替换 store 的根状态,仅用状态合并或时光旅行调试。

replaceState (state) {

this._withCommit(() => {

this._vm._data.$$state = state

})

}

watch

响应式地侦听 fn 的返回值,当值改变时调用回调函数。

/**

  • 观测某个值

  • @param {Function} getter 函数

  • @param {Function} cb 回调

  • @param {Object} options 参数对象

*/

watch (getter, cb, options) {

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

assert(typeof getter === ‘function’, store.watch only accepts a function.)

}

return this._watcherVM.$watch(() => getter(this.state, this.getters), cb, options)

}

subscribe

订阅 store 的 mutation

subscribe (fn) {

return genericSubscribe(fn, this._subscribers)

}

// 收集订阅者

function genericSubscribe (fn, subs) {

if (subs.indexOf(fn) < 0) {

subs.push(fn)

}

return () => {

const i = subs.indexOf(fn)

if (i > -1) {

subs.splice(i, 1)

}

}

}

subscribeAction

订阅 store 的 action

subscribeAction (fn) {

const subs = typeof fn === ‘function’ ? { before: fn } : fn

return genericSubscribe(subs, this._actionSubscribers)

}

registerModule

注册一个动态模块。

/**

  • 动态注册模块

  • @param {Array|String} path 路径

  • @param {Object} rawModule 原始未加工的模块

  • @param {Object} options 参数选项

*/

registerModule (path, rawModule, options = {}) {

// 如果 path 是字符串,转成数组

if (typeof path === ‘string’) path = [path]

// 省略 非生产环境 报错代码

// 手动调用 模块注册的方法

this._modules.register(path, rawModule)

// 安装模块

installModule(this, this.state, path, this._modules.get(path), options.preserveState)

// reset store to update getters…

// 设置 resetStoreVM

resetStoreVM(this, this.state)

}

unregisterModule

卸载一个动态模块。

/**

  • 注销模块

  • @param {Array|String} path 路径

*/

unregisterModule (path) {

// 如果 path 是字符串,转成数组

if (typeof path === ‘string’) path = [path]

// 省略 非生产环境 报错代码 …

// 手动调用模块注销

this._modules.unregister(path)

this._withCommit(() => {

// 注销这个模块

const parentState = getNestedState(this.state, path.slice(0, -1))

Vue.delete(parentState, path[path.length - 1])

})

// 重置 Store

resetStore(this)

}

hotUpdate

热替换新的 action 和 mutation

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
img

Vue 面试题

1.Vue 双向绑定原理
2.描述下 vue 从初始化页面–修改数据–刷新页面 UI 的过程?
3.你是如何理解 Vue 的响应式系统的?
4.虚拟 DOM 实现原理
5.既然 Vue 通过数据劫持可以精准探测数据变化,为什么还需要虚拟 DOM 进行 diff 检测差异?
6.Vue 中 key 值的作用?
7.Vue 的生命周期
8.Vue 组件间通信有哪些方式?
9.watch、methods 和 computed 的区别?
10.vue 中怎么重置 data?
11.组件中写 name 选项有什么作用?
12.vue-router 有哪些钩子函数?
13.route 和 router 的区别是什么?
14.说一下 Vue 和 React 的认识,做一个简单的对比
15.Vue 的 nextTick 的原理是什么?
16.Vuex 有哪几种属性?
17.vue 首屏加载优化
18.Vue 3.0 有没有过了解?
19.vue-cli 替我们做了哪些工作?

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img

)]
[外链图片转存中…(img-R0tRw9iD-1712457481984)]
[外链图片转存中…(img-1ROqyVV2-1712457481984)]
[外链图片转存中…(img-jCQhkaRz-1712457481985)]
[外链图片转存中…(img-nnUNsxaT-1712457481985)]
[外链图片转存中…(img-XyrXxT8u-1712457481985)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-tROwBjMO-1712457481986)]

Vue 面试题

1.Vue 双向绑定原理
2.描述下 vue 从初始化页面–修改数据–刷新页面 UI 的过程?
3.你是如何理解 Vue 的响应式系统的?
4.虚拟 DOM 实现原理
5.既然 Vue 通过数据劫持可以精准探测数据变化,为什么还需要虚拟 DOM 进行 diff 检测差异?
6.Vue 中 key 值的作用?
7.Vue 的生命周期
8.Vue 组件间通信有哪些方式?
9.watch、methods 和 computed 的区别?
10.vue 中怎么重置 data?
11.组件中写 name 选项有什么作用?
12.vue-router 有哪些钩子函数?
13.route 和 router 的区别是什么?
14.说一下 Vue 和 React 的认识,做一个简单的对比
15.Vue 的 nextTick 的原理是什么?
16.Vuex 有哪几种属性?
17.vue 首屏加载优化
18.Vue 3.0 有没有过了解?
19.vue-cli 替我们做了哪些工作?
[外链图片转存中…(img-nMpTwLIt-1712457481986)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-9jOZdWIw-1712457481987)]

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值