字节跳动面试官:请说一下vuex工作原理(重点就几行代码而已啦)
不知为何掘金的文章最近都流行以 “字节跳动面试官” 作为开头,不蹭一波都不好心思说逛过掘金了。23333
最近是真到了面试的季节,那么就说一下 Vuex 的源码吧。看完你会发现,Vue和Vuex的实现原理主要就那么几行代码。
Vue双向绑定
要说 Vuex 的双向绑定那么必需先从 Vue 的双向绑定开始
Vue 的双向绑定大部分文章都说的很详细,这里精简点说一下,由于重点还是讲 Vuex
从Vue的源码来看,Vue的双向绑定主要做了2件事
- 数据劫持
- 增加观察者
数据劫持实现:(源码精简)
// 老版本通过 Object.defineProperty 递归可以实现// src/core/observer/index.jsObject.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { const value = getter ? getter.call(obj) : val if (Dep.target) { dep.depend() if (childOb) { childOb.dep.depend() } if (Array.isArray(value)) { dependArray(value) } } return value }, set: function reactiveSetter (newVal) { const value = getter ? getter.call(obj) : val if (newVal === value || (newVal !== newVal && value !== value)) { return } if (setter) { setter.call(obj, newVal) } else { val = newVal } childOb = !shallow && observe(newVal) dep.notify() }})这里无非就是劫持了对象的get和set方法。在所代理商的属性的get方法中,当dep.Target存在的时候会调用 dep.depend(),
划重点:2行代码
- Object.defineProperty
- dep.depend()
// 最新版可以通过 Proxy 实现Proxy(data, { get(target, key) { return target[key]; }, set(target, key, value) { let val = Reflect.set(target, key, value); _that.$dep[key].forEach(item => item.update()); return val; }})从上面的代码看出,无非就劫持了对象的get和set方法。在数据劫持之外最重要的部分就是 Dep 和 Watcher,这其实是一个观察者模式。用最简单的代码实现以下 Vue 的观察者模式。
观察者模式实现:(源码精简)
// 观察者 class Dep { constructor() { this.subs = [] } addSub(sub) { this.subs.push(sub) } depend() { if (Dep.target) { Dep.target.addDep(this); } } notify() { this.subs.forEach(sub => sub.update()) } } // 被观察者 class Watcher { constructor(vm, expOrFn) { this.vm = vm; this.getter = expOrFn; this.value; } get() { Dep.target = this; var vm = this.vm; var value = this.getter.call(vm, vm); return value; } evaluate() { this.value = this.get(); } addDep(dep) { dep.addSub(this); } update() { console.log('升级, value:', this.value) } } // 观察者实例 var dep = new Dep(); // 被观察者实例 var watcher = new Watcher({x: 1}, (val) => val); watcher.evaluate(); // 观察者监听被观察对象 dep.depend() dep.notify()划重点:3件事
- 通过
watcher.evaluate()将自身实例赋值给Dep.target - 调用
dep.depend()将dep实例将 watcher 实例 push 到 dep.subs中 - 通过数据劫持,在调用被劫持的对象的 set 方法时,调用 dep.subs 中所有的
watcher.update()
从此。双向绑定完成。
vuex插件
有了上文作为铺垫,我们即可以很轻松的来解释vuex的原理了。
Vuex仅仅是Vue的一个插件。Vuex只能使用在vue上,由于其高度依赖于Vue的双向绑定和插件系统。
Vuex的注入代码比较简单,调用了一下applyMixin方法,现在的版本其实就是调用了Vue.mixin,在所有组件的 beforeCreate生命周期注入了设置 this.$store这样一个对象。
// src/store.jsexport function install (_Vue) { if (Vue && _Vue === Vue) { return } Vue = _Vue applyMixin(Vue)}// src/mixins.jsexport default function (Vue) { const version = Number(Vue.version.split('.')[0]) if (version >= 2) { Vue.mixin({ beforeCreate: vuexInit }) } else { const _init = Vue.prototype._init Vue.prototype._init = function (options = {}) { options.init = options.init ? [vuexInit].concat(options.init) : vuexInit _init.call(this, options) } } function vuexInit () { const options = this.$options // store injection if (options.store) { this.$store = typeof options.store === 'function' ? options.store() : options.store } else if (options.parent && options.parent.$store) { this.$store = options.parent.$store } }}划重点:1行代码 Vue.mixin
那么 Vuex.Store 是如何实现的呢?
// src/store.jsconstructor (options = {}) { const { plugins = [], strict = false } = options // store internal state this._committing = false this._actions = Object.create(null) this._actionSubscribers = [] this._mutations = Object.create(null) this._wrappedGetters = Object.create(null) this._modules = new ModuleCollection(options) this._modulesNamespaceMap = Object.create(null) this._subscribers = [] this._watcherVM = new Vue() 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)} // strict mode this.strict = strict const state = this._modules.root.state // init root module. // this also recursively registers all sub-modules // and collects all module getters inside this._wrappedGetters installModule(this, state, [], this._modules.root) resetStoreVM(this, state) // apply plugins plugins.forEach(plugin => plugin(this))}划重点:其实上面的代码绝大部分都不需要关注的 - -。其实重点就是一行代码resetStoreVM(this, state)。
那么 resetStoreVM 里面是什么呢?
// src/store.jsfunction resetStoreVM (store, state, hot) { Vue.config.silent = true store._vm = new Vue({ data: { $$state: state }, computed })}划重点:还是一行代码:new Vue。通过 Vue自己的双向绑定而后注入给
你是不是以为就这样结束了呢?NoNoNo,当你再Vue中通过 this 假如调用 store的数据呢?
// 当获取state时,返回以双向绑定的$$satevar prototypeAccessors$1 = { state: { configurable: true } };prototypeAccessors$1.state.get = function () { return this._vm._data.$$state};// 将state定义在原型中Object.defineProperties( Store.prototype, prototypeAccessors$1 );其实就是获取 this._vm._data.$$state 而已啦。
### 最后欢迎关注公众号「前台进阶课」认真学前台,一起进阶。1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 字节跳动面试官:请说一下vuex工作原理(重点就几行代码而已啦)