字节跳动面试官:请说一下vuex工作原理(重点就几行代码而已啦)

作者 : 开心源码 本文共4247个字,预计阅读时间需要11分钟 发布时间: 2022-05-13 共176人阅读

不知为何掘金的文章最近都流行以 “字节跳动面试官” 作为开头,不蹭一波都不好心思说逛过掘金了。23333

最近是真到了面试的季节,那么就说一下 Vuex 的源码吧。看完你会发现,Vue和Vuex的实现原理主要就那么几行代码。

Vue双向绑定

要说 Vuex 的双向绑定那么必需先从 Vue 的双向绑定开始

Vue 的双向绑定大部分文章都说的很详细,这里精简点说一下,由于重点还是讲 Vuex

从Vue的源码来看,Vue的双向绑定主要做了2件事

  1. 数据劫持
  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行代码

  1. Object.defineProperty
  2. 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方法。在数据劫持之外最重要的部分就是 DepWatcher,这其实是一个观察者模式。用最简单的代码实现以下 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件事

  1. 通过 watcher.evaluate() 将自身实例赋值给 Dep.target
  2. 调用 dep.depend() 将dep实例将 watcher 实例 push 到 dep.subs中
  3. 通过数据劫持,在调用被劫持的对象的 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工作原理(重点就几行代码而已啦)

发表回复