Я не знаю, почему статьи «Наггетса» недавно популярны с «Bytedance интервьюеру» как начало, и мне смущается, чтобы сказать, что я посетил наггетс без волны. 23333.
Это действительно сезон интервью, так что давайте поговорим об этомVuex
исходный код. Прочитав его, вы обнаружите, что принцип реализации Vue и Vuex в основном состоит всего из нескольких строк кода.
Двусторонняя привязка Vue
сказатьVuex
Двусторонняя привязка должна начинаться сVue
Двусторонняя привязка начинается с
Vue
Говорят, что большинство двусторонних креплений очень детализированы, здесь есть тонкий момент из-за фокуса илиVuex
Из исходного кода Vue, двусторонняя привязка Vue в основном делает две вещи
- захват данных
- добавить наблюдателя
Реализация захвата данных: (упрощен исходный код)
// 老版本通过 Object.defineProperty 递归可以实现
// src/core/observer/index.js
Object.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 Observer с максимально простым кодом.
Реализация режима наблюдателя: (сокращение исходного кода)
// 观察者
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 и экземпляр наблюдателя в dep.subs - При перехвате данных, когда вызывается метод set захваченного объекта, вызываются все dep.subs
watcher.update()
Впредь. Двусторонняя привязка завершена.
плагин vuex
С учетом вышеизложенного в качестве предзнаменования мы можем легко объяснить принцип работы vuex.
Vuex — это просто плагин для Vue. Vuex можно использовать только на vue, потому что он сильно зависит от двусторонней привязки Vue и системы плагинов.
Код внедрения Vuex относительно прост, назовем егоapplyMixin
метод, текущая версия фактически вызываетVue.mixin
, во всех компонентахbeforeCreateЖизненный цикл внедряет настройкиthis.$storeтакой объект.
// src/store.js
export function install (_Vue) {
if (Vue && _Vue === Vue) {
return
}
Vue = _Vue
applyMixin(Vue)
}
// src/mixins.js
export 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.js
constructor (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.js
function resetStoreVM (store, state, hot) {
Vue.config.silent = true
store._vm = new Vue({
data: {
$$state: state
},
computed
})
}
Фокус: еще одна строка кода:new Vue
. Через собственную двустороннюю привязку Via, а затем внедрить в
Вы думали, что это конец? Нет-нет-нет, а что, если вы вызовете данные магазина через это во Vue?
// 当获取state时,返回以双向绑定的$$sate
var 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
Вот и все.
Суммировать
Двусторонняя привязка Vuex достигается вызовом нового Vue, затем внедрением его в жизненный цикл компонента Vue через Vue.mixin, а затем помещением данных в компонент путем захвата state.get.
наконец
- Нелегко любить оригинал
- Добро пожаловать, чтобы обратить внимание на общедоступную учетную запись «Продвинутый курс по интерфейсу», чтобы серьезно изучить интерфейс и продвигаться вместе.