// 模块 state
const rawState = rawModule.state
// Store the origin module’s state
// 原始Store 可能是函数,也可能是是对象,是假值,则赋值空对象。
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
中mapState
、mapGetters
、mapMutations
、mapActions
四个辅助函数使用的。
生成本地的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
。
// 热加载
hotUpdate (newOptions) {
// 调用的是 ModuleCollection 的 update 方法,最终调用对应的是每个 Module 的 update
this._modules.update(newOptions)
// 重置 Store
resetStore(this, true)
}
组件绑定的辅助函数
文件路径:vuex/src/helpers.js
mapState
为组件创建计算属性以返回 Vuex store
中的状态。
export const mapState = normalizeNamespace((namespace, states) => {
const res = {}
// 非生产环境 判断参数 states 必须是数组或者是对象
if (process.env.NODE_ENV !== ‘production’ && !isValidMap(states)) {
console.error(‘[vuex] mapState: mapper parameter must be either an Array or an Object’)
}
normalizeMap(states).forEach(({ key, val }) => {
res[key] = function mappedState () {
let state = this.$store.state
let getters = this.$store.getters
// 传了参数 namespace
if (namespace) {
// 用 namespace 从 store 中找一个模块。
const module = getModuleByNamespace(this.$store, ‘mapState’, namespace)
if (!module) {
return
}
state = module.context.state
getters = module.context.getters
}
return typeof val === ‘function’
-
? val.call(this, state, getters)
- state[val]
}
// 标记为 vuex 方便在 devtools 显示
// mark vuex getter for devtools
res[key].vuex = true
})
return res
})
normalizeNamespace 标准化统一命名空间
function normalizeNamespace (fn) {
return (namespace, map) => {
// 命名空间没传,交换参数,namespace 为空字符串
if (typeof namespace !== ‘string’) {
map = namespace
namespace = ‘’
} else if (namespace.charAt(namespace.length - 1) !== ‘/’) {
// 如果是字符串,最后一个字符不是 / 添加 /
// 因为 _modulesNamespaceMap 存储的是这样的结构。
/**
- _modulesNamespaceMap:
cart/: {}
products/: {}
}
- */
namespace += ‘/’
}
return fn(namespace, map)
}
}
// 校验是否是map 是数组或者是对象。
function isValidMap (map) {
return Array.isArray(map) || isObject(map)
}
/**
-
Normalize the map
-
标准化统一 map,最终返回的是数组
-
normalizeMap([1, 2, 3]) => [ { key: 1, val: 1 }, { key: 2, val: 2 }, { key: 3, val: 3 } ]
-
normalizeMap({a: 1, b: 2, c: 3}) => [ { key: ‘a’, val: 1 }, { key: ‘b’, val: 2 }, { key: ‘c’, val: 3 } ]
-
@param {Array|Object} map
-
@return {Object}
*/
function normalizeMap (map) {
if (!isValidMap(map)) {
return []
}
return Array.isArray(map)
-
? map.map(key => ({ key, val: key }))
- Object.keys(map).map(key => ({ key, val: map[key] }))
}
module.context
这个赋值主要是给 helpers
中 mapState
、mapGetters
、mapMutations
、mapActions
四个辅助函数使用的。
// 在构造函数中 installModule 中
const local = module.context = makeLocalContext(store, namespace, path)
这里就是抹平差异,不用用户传递命名空间,获取到对应的 commit、dispatch、state、和 getters
getModuleByNamespace
function getModuleByNamespace (store, helper, namespace) {
// _modulesNamespaceMap 这个变量在 class Store installModule 函数中赋值的
const module = store._modulesNamespaceMap[namespace]
if (process.env.NODE_ENV !== ‘production’ && !module) {
console.error([vuex] module namespace not found in ${helper}(): ${namespace}
)
}
return module
}
看完这些,最后举个例子: vuex/examples/shopping-cart/components/ShoppingCart.vue
computed: {
…mapState({
checkoutStatus: state => state.cart.checkoutStatus
}),
}
没有命名空间的情况下,最终会转换成这样
computed: {
checkoutStatus: this.$store.state.checkoutStatus
}
假设有命名空间’ruochuan’,
computed: {
…mapState(‘ruochuan’, {
checkoutStatus: state => state.cart.checkoutStatus
}),
}
则会转换成:
computed: {
checkoutStatus: this.$store._modulesNamespaceMap.[‘ruochuan/’].context.checkoutStatus
}
mapGetters
为组件创建计算属性以返回 getter
的返回值。
export const mapGetters = normalizeNamespace((namespace, getters) => {
const res = {}
// 省略代码:非生产环境 判断参数 getters 必须是数组或者是对象
normalizeMap(getters).forEach(({ key, val }) => {
// The namespace has been mutated by normalizeNamespace
val = namespace + val
res[key] = function mappedGetter () {
if (namespace && !getModuleByNamespace(this.$store, ‘mapGetters’, namespace)) {
return
}
// 省略代码:匹配不到 getter
return this.$store.getters[val]
}
// mark vuex getter for devtools
res[key].vuex = true
})
return res
})
举例:
computed: {
…mapGetters(‘cart’, {
products: ‘cartProducts’,
total: ‘cartTotalPrice’
})
},
最终转换成:
computed: {
products: this.$store.getters[‘cart/cartProducts’],
total: this.$store.getters[‘cart/cartTotalPrice’],
}
mapActions
创建组件方法分发 action
。
export const mapActions = normalizeNamespace((namespace, actions) => {
const res = {}
// 省略代码:非生产环境 判断参数 actions 必须是数组或者是对象
normalizeMap(actions).forEach(({ key, val }) => {
res[key] = function mappedAction (…args) {
// get dispatch function from store
let dispatch = this.$store.dispatch
if (namespace) {
const module = getModuleByNamespace(this.$store, ‘mapActions’, namespace)
if (!module) {
return
}
dispatch = module.context.dispatch
}
return typeof val === ‘function’
-
? val.apply(this, [dispatch].concat(args))
- dispatch.apply(this.$store, [val].concat(args))
}
})
return res
})
mapMutations
创建组件方法提交 mutation
。mapMutations 和 mapActions 类似,只是 dispatch 换成了 commit。
let commit = this.$store.commit
commit = module.context.commit
return typeof val === ‘function’
-
? val.apply(this, [commit].concat(args))
- commit.apply(this.$store, [val].concat(args))
vuex/src/helpers
mapMutations
、mapActions
举例:
{
methods: {
…mapMutations([‘inc’]),
…mapMutations(‘ruochuan’, [‘dec’]),
…mapActions([‘actionA’])
…mapActions(‘ruochuan’, [‘actionB’])
}
}
最终转换成
{
methods: {
inc(…args){
return this. s t o r e . d i s p a t c h . a p p l y ( t h i s . store.dispatch.apply(this. store.dispatch.apply(this.store, [‘inc’].concat(args))
},
dec(…args){
return this. s t o r e . m o d u l e s N a m e s p a c e M a p . [ ′ r u o c h u a n / ′ ] . c o n t e x t . d i s p a t c h . a p p l y ( t h i s . store._modulesNamespaceMap.['ruochuan/'].context.dispatch.apply(this. store.modulesNamespaceMap.[′ruochuan/′].context.dispatch.apply(this.store, [‘dec’].concat(args))
},
actionA(…args){
return this. s t o r e . c o m m i t . a p p l y ( t h i s . store.commit.apply(this. store.commit.apply(this.store, [‘actionA’].concat(args))
}
actionB(…args){
return this. s t o r e . m o d u l e s N a m e s p a c e M a p . [ ′ r u o c h u a n / ′ ] . c o n t e x t . c o m m i t . a p p l y ( t h i s . store._modulesNamespaceMap.['ruochuan/'].context.commit.apply(this. store.modulesNamespaceMap.[′ruochuan/′].context.commit.apply(this.store, [‘actionB’].concat(args))
}
}
}
由此可见:这些辅助函数极大地方便了开发者。
createNamespacedHelpers
创建基于命名空间的组件绑定辅助函数。
export const createNamespacedHelpers = (namespace) => ({
// bind(null) 严格模式下,napState等的函数 this 指向就是 null
mapState: mapState.bind(null, namespace),
mapGetters: mapGetters.bind(null, namespace),
mapMutations: mapMutations.bind(null, namespace),
mapActions: mapActions.bind(null, namespace)
})
就是把这些辅助函数放在一个对象中。
插件
–
插件部分文件路径是:
vuex/src/plugins/devtool
vuex/src/plugins/logger
文章比较长了,这部分就不再叙述。具体可以看笔者的仓库 vuex-analysis vuex/src/plugins/
的源码注释。
总结
–
文章比较详细的介绍了vuex
、vue
源码调试方法和 Vuex
原理。并且详细介绍了 Vuex.use
安装和 new Vuex.Store
初始化、Vuex.Store
的全部API
(如dispatch
、commit
等)的实现和辅助函数 mapState
、mapGetters
、 mapActions
、mapMutations
createNamespacedHelpers
。
文章注释,在vuex-analysis源码仓库里基本都有注释分析,求个star
。再次强烈建议要克隆代码下来。
git clone https://github.com/lxchuan12/vuex-analysis.git
先把 Store
实例打印出来,看具体结构,再结合实例断点调试,事半功倍。
Vuex
源码相对不多,打包后一千多行,非常值得学习,也比较容易看完。
如果读者发现有不妥或可改善之处,再或者哪里没写明白的地方,欢迎评论指出。另外觉得写得不错,对您有些许帮助,可以点赞、评论、转发分享,也是对笔者的一种支持,万分感谢。
推荐阅读
vuex 官方文档
vuex github 仓库
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

总结一下这三次面试下来我的经验是:
-
一定不要死记硬背,要理解原理,否则面试官一深入就会露馅!
-
代码能力一定要注重,尤其是很多原理性的代码(之前两次让我写过Node中间件,Promise.all,双向绑定原理,被虐的怀疑人生)!
-
尽量从面试官的问题中表现自己知识的深度与广度,让面试官发现你的闪光点!
-
多刷面经!
我把所有遇到的面试题都做了一个整理,并且阅读了很多大牛的博客之后写了解析,免费分享给大家,算是一个感恩回馈吧,有需要的朋友【点击我】免费获取。祝大家早日拿到自己心怡的工作!
篇幅有限,仅展示部分内容
commit等)的实现和辅助函数
mapState、
mapGetters、
mapActions、
mapMutations
createNamespacedHelpers`。
文章注释,在vuex-analysis源码仓库里基本都有注释分析,求个star
。再次强烈建议要克隆代码下来。
git clone https://github.com/lxchuan12/vuex-analysis.git
先把 Store
实例打印出来,看具体结构,再结合实例断点调试,事半功倍。
Vuex
源码相对不多,打包后一千多行,非常值得学习,也比较容易看完。
如果读者发现有不妥或可改善之处,再或者哪里没写明白的地方,欢迎评论指出。另外觉得写得不错,对您有些许帮助,可以点赞、评论、转发分享,也是对笔者的一种支持,万分感谢。
推荐阅读
vuex 官方文档
vuex github 仓库
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-eDUlu7Ro-1712222929818)]
[外链图片转存中…(img-GWrHnFjK-1712222929819)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
[外链图片转存中…(img-pTPN2naO-1712222929819)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

总结一下这三次面试下来我的经验是:
-
一定不要死记硬背,要理解原理,否则面试官一深入就会露馅!
-
代码能力一定要注重,尤其是很多原理性的代码(之前两次让我写过Node中间件,Promise.all,双向绑定原理,被虐的怀疑人生)!
-
尽量从面试官的问题中表现自己知识的深度与广度,让面试官发现你的闪光点!
-
多刷面经!
我把所有遇到的面试题都做了一个整理,并且阅读了很多大牛的博客之后写了解析,免费分享给大家,算是一个感恩回馈吧,有需要的朋友【点击我】免费获取。祝大家早日拿到自己心怡的工作!
篇幅有限,仅展示部分内容