Примечание редактора: время от времени мы будем приглашать инженеров для обсуждения интересных технических деталей и надеемся, что знание причин поможет вам лучше справляться с собеседованиями.
Это также дает интервьюеру больше идей.
Несмотря на то, что текущий стек технологий был перенесен из Vue в React, он по-прежнему очень приятен из фактического опыта нескольких проектов, разработанных с помощью Vue ранее.Документы Vue понятны и стандартизированы, дизайн API прост и эффективен, удобен для фронтенд-разработчиков. , и быстрый в использовании. Я даже думаю, что использование Vue более эффективно, чем React во многих сценариях. Раньше я периодически читал исходный код Vue, но я не разобрался в нем, поэтому я сделаю здесь некоторое техническое резюме и углублюсь мое понимание Vue, поэтому я напишу сегодня. является одним из наиболее часто используемых API в Vuecomputed
принцип реализации.
основное введение
Без дальнейших церемоний, основной пример выглядит следующим образом:
<div id="app">
<p>{{fullName}}</p>
</div>
new Vue({
data: {
firstName: 'Xiao',
lastName: 'Ming'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
В Vue нам не нужно вычислять прямо в шаблоне{{this.firstName + ' ' + this.lastName}}
, потому что слишком много декларативной логики в шаблоне сделает сам шаблон слишком тяжелым, особенно когда на странице используется большое количество сложных логических выражений для обработки данных, это сильно повлияет на удобство сопровождения страницы, иcomputed
Первоначальный замысел дизайна также заключается в решении таких проблем.
Контрастный слушательwatch
Конечно, мы часто используемcomputed
Он часто связан с другим API во Vue, который является слушателем.watch
Для сравнения, поскольку в некотором смысле они одинаковы, они являются зависимыми механизмами отслеживания на основе Vue, когда зависимые данные изменяются, все соответствующие данные или полагаться на эту функцию будут автоматически изменять данные или вызовы.
Хотя вычисляемые свойства в большинстве случаев являются более подходящими, иногда также требуется пользовательский прослушиватель. Вот почему Vue проходит
watch
Опции обеспечивают более общий подход к реагированию на изменения данных. Это наиболее полезно, когда вам нужно выполнять асинхронные или дорогостоящие операции при изменении данных.
Из официальных документов Vuewatch
Из объяснения мы можем узнать, что с помощьюwatch
Параметры позволяют нам выполнять асинхронные операции (доступ к API) или дорогостоящие операции, ограничивать частоту выполнения операции и устанавливать промежуточное состояние до тех пор, пока мы не получим окончательный результат, что невозможно с вычисляемыми свойствами.
Ниже приведены некоторые дополнительные сведения оcomputed
а такжеwatch
Разница:
-
computed
заключается в вычислении нового свойства и монтировании свойства в vm (экземпляр Vue) иwatch
заключается в том, что прослушиватель уже существует и подключен кvm
данные, поэтому используйтеwatch
также может контролироватьcomputed
Вычисленные изменения свойств (другиеdata
,props
) -
computed
Суть в ленивом оцениваемом наблюдателе, с кешируемостью, только при изменении зависимости первый доступcomputed
свойство, будет вычислено новое значение, иwatch
При изменении данных будет вызвана функция исполнения - Что касается сценариев использования,
computed
Применяется, что на одни данные влияет несколько данных, иwatch
Применимо к данным влияния на несколько данных;
Выше мы понимаемcomputed
а такжеwatch
Некоторые различия между ними и различия в сценариях использования, конечно, иногда между ними есть не такие четкие и строгие ограничения.В конце концов, необходимо анализировать разные бизнесы.
Принципиальный анализ
Ближе к дому, вернемся к теме статьиcomputed
Чтобы глубже понять внутренний механизм вычисляемых свойств, давайте шаг за шагом рассмотрим принцип их реализации в исходном коде Vue.
в анализеcomputed
Перед исходным кодом мы должны иметь общее представление об адаптивной системе Vue. Vue называет ее ненавязчивой адаптивной системой. Модель данных — это обычные объекты JavaScript, и когда вы их изменяете, представление автоматически обновляется.
Когда вы передаете простой объект JavaScript экземпляру Vue
data
вариант, Vue будет перебирать все свойства этого объекта и использоватьObject.defineProperty
Преобразуйте все эти свойства вgetter/setter
,Этиgetter/setter
Невидимые для пользователя, но внутри они позволяют Vue отслеживать зависимости, уведомлять об изменениях при доступе к свойствам и их изменении, а каждый экземпляр компонента имеет соответствующийwatcher
Экземпляр объекта, который будет записывать свойства как зависимости во время рендеринга компонента, а затем использовать их как зависимостиsetter
При вызове он уведомитwatcher
Пересчитать, что приведет к обновлению связанных с ним компонентов.
Система ответов Vue имеет три основных момента:observe
,watcher
,dep
:
-
observe
: траверсdata
свойства в , использованиеObject.definePropertyизget/set
метод захвата данных; -
dep
: у каждого свойства есть собственный подписчик сообщений.dep
, используемый для хранения всех объектов-наблюдателей, подписанных на это свойство; -
watcher
: Наблюдатель (объект), черезdep
Реализуйте мониторинг атрибута ответа и, прослушав результат, активируйте собственный обратный вызов для ответа.
Теперь, когда у нас есть начальное представление о реактивных системах, давайте проанализируем вычисляемые свойства.
Сначала мы находим, что инициализация вычисляемого свойства находится вsrc/core/instance/state.js
в файлеinitState
сделано в функции
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props)
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
// computed初始化
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
называетсяinitComputed
функция (которая также инициализируется до и послеinitData
а такжеinitWatch
) и передать два параметраvm
экземпляр иopt.computed
определено разработчикомcomputed
варианты, перейдите кinitComputed
функция:
const computedWatcherOptions = { computed: true }
function initComputed (vm: Component, computed: Object) {
// $flow-disable-line
const watchers = vm._computedWatchers = Object.create(null)
// computed properties are just getters during SSR
const isSSR = isServerRendering()
for (const key in computed) {
const userDef = computed[key]
const getter = typeof userDef === 'function' ? userDef : userDef.get
if (process.env.NODE_ENV !== 'production' && getter == null) {
warn(
'Getter is missing for computed property "${key}".',
vm
)
}
if (!isSSR) {
// create internal watcher for the computed property.
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
computedWatcherOptions
)
}
// component-defined computed properties are already defined on the
// component prototype. We only need to define computed properties defined
// at instantiation here.
if (!(key in vm)) {
defineComputed(vm, key, userDef)
} else if (process.env.NODE_ENV !== 'production') {
if (key in vm.$data) {
warn('The computed property "${key}" is already defined in data.', vm)
} else if (vm.$options.props && key in vm.$options.props) {
warn('The computed property "${key}" is already defined as a prop.', vm)
}
}
}
}
Из этого кода давайте рассмотрим эти части:
-
Получить определение вычисляемого свойства
userDef
а такжеgetter
Функция оценкиconst userDef = computed[key] const getter = typeof userDef === 'function' ? userDef : userDef.get
Есть два способа определить вычисляемое свойство: один — непосредственно следовать за функцией, другой — добавить
set
а такжеget
Объектная форма метода, поэтому сначала получите определение вычисляемого свойстваuserDef
, то согласноuserDef
введите, чтобы получить соответствующийgetter
Оценить функцию. -
Наблюдатели для вычисляемых свойств
watcher
и подписчики сообщенийdep
watchers[key] = new Watcher( vm, getter || noop, noop, computedWatcherOptions )
здесь
watchers
то естьvm._computedWatchers
Ссылка на объект, содержащий наблюдателя для каждого вычисляемого свойства.watcher
Экземпляры (Примечание: «наблюдатели вычисляемых свойств», «подписчики» иwatcher
Оба относятся к одному и тому же значению, но обратите внимание, чтоWatcher
отличие конструктора),Watcher
Конструктор передал 4 параметра при создании экземпляра:vm
пример,getter
функция оценки,noop
пустая функция,computedWatcherOptions
постоянный объект (предоставленный здесь дляWatcher
личность{computed:true}
элемент, указывающий, что это наблюдатель для вычисляемого свойства, а не для невычисляемого свойства, мы приходим кWatcher
Определение конструктора:class Watcher { constructor ( vm: Component, expOrFn: string | Function, cb: Function, options?: ?Object, isRenderWatcher?: boolean ) { if (options) { this.computed = !!options.computed } if (this.computed) { this.value = undefined this.dep = new Dep() } else { this.value = this.get() } } get () { pushTarget(this) let value const vm = this.vm try { value = this.getter.call(vm, vm) } catch (e) { } finally { popTarget() } return value } update () { if (this.computed) { if (this.dep.subs.length === 0) { this.dirty = true } else { this.getAndInvoke(() => { this.dep.notify() }) } } else if (this.sync) { this.run() } else { queueWatcher(this) } } evaluate () { if (this.dirty) { this.value = this.get() this.dirty = false } return this.value } depend () { if (this.dep && Dep.target) { this.dep.depend() } } }
Для краткости и выразительности здесь я вручную удалил фрагменты кода, которые нам пока не нужны. Наблюдаемый
Watcher
изconstructor
, в сочетании с только что сказаннымnew Watcher
Четвертый параметр передается{computed:true}
Да, для вычисляемых свойствwatcher
будет выполнятьif
условный кодthis.dep = new Dep()
,а такжеdep
То есть подписчик сообщений, создавший это свойство.export default class Dep { static target: ?Watcher; subs: Array<Watcher>; constructor () { this.id = uid++ this.subs = [] } addSub (sub: Watcher) { this.subs.push(sub) } depend () { if (Dep.target) { Dep.target.addDep(this) } } notify () { const subs = this.subs.slice() for (let i = 0, l = subs.length; i < l; i++) { subs[i].update() } } } Dep.target = null
Dep
Также упростим часть кода, наблюдаемWatcher
а такжеDep
отношения, выраженные в одном предложенииwatcher
экземпляр вdep
и кdep.subs
добавил подписчиков вdep
пройти черезnotify
пройденныйdep.subs
уведомить каждогоwatcher
возобновить. -
defineComputed
Определение вычисляемых свойствif (!(key in vm)) { defineComputed(vm, key, userDef) } else if (process.env.NODE_ENV !== 'production') { if (key in vm.$data) { warn('The computed property "${key}" is already defined in data.', vm) } else if (vm.$options.props && key in vm.$options.props) { warn('The computed property "${key}" is already defined as a prop.', vm) } }
потому что
computed
Атрибут монтируется непосредственно в экземпляр объекта, поэтому перед определением необходимо определить, есть ли переименование в объекте.defineComputed
Передаются три параметра:vm
экземпляр, вычисляемое свойствоkey
так же какuserDef
Определение вычисляемого свойства (объекта или функции). затем продолжайте искатьdefineComputed
Определение:export function defineComputed ( target: any, key: string, userDef: Object | Function ) { const shouldCache = !isServerRendering() if (typeof userDef === 'function') { sharedPropertyDefinition.get = shouldCache ? createComputedGetter(key) : userDef sharedPropertyDefinition.set = noop } else { sharedPropertyDefinition.get = userDef.get ? shouldCache && userDef.cache !== false ? createComputedGetter(key) : userDef.get : noop sharedPropertyDefinition.set = userDef.set ? userDef.set : noop } if (process.env.NODE_ENV !== 'production' && sharedPropertyDefinition.set === noop) { sharedPropertyDefinition.set = function () { warn( 'Computed property "${key}" was assigned to but it has no setter.', this ) } } Object.defineProperty(target, key, sharedPropertyDefinition) }
В конце этого кода вызывается натив
Object.defineProperty
метод, где третий передаваемый параметр — это дескриптор свойстваsharedPropertyDefinition
, инициализированный как:const sharedPropertyDefinition = { enumerable: true, configurable: true, get: noop, set: noop }
Тогда согласно
Object.defineProperty
Предыдущий код можно увидетьsharedPropertyDefinition
изget/set
метод послеuserDef
а такжеshouldCache
Например, многократное переписывание после суждения, когда рендеринг не служебной стороны,sharedPropertyDefinition
изget
функцияcreateComputedGetter(key)
В результате находимcreateComputedGetter
результат вызова функции и в конечном итоге перезаписатьsharedPropertyDefinition
Примерно представлено следующим образом:sharedPropertyDefinition = { enumerable: true, configurable: true, get: function computedGetter () { const watcher = this._computedWatchers && this._computedWatchers[key] if (watcher) { watcher.depend() return watcher.evaluate() } }, set: userDef.set || noop }
Выполняется при вызове вычисляемого свойства
get
Функции доступа для связывания объектов-наблюдателейwatcher
затем выполнитьwather.depend()
Соберите зависимости иwatcher.evaluate()
Вычислить оценку.
Проанализировав все шаги, давайте подытожим весь процесс:
- Когда компонент инициализируется,
computed
а такжеdata
создадут свои собственные системы реагирования,Observer
траверсdata
Каждое свойство, установленное вget/set
перехват данных - инициализация
computed
позвонюinitComputed
функция- Зарегистрировать
watcher
экземпляр, и внутри этого экземпляраDep
Подписчик сообщений используется для последующих зависимостей коллекции (таких как функция рендеринга).watcher
или другие объекты, которые наблюдают за изменением вычисляемого свойстваwatcher
) - Когда вызывается вычисляемое свойство, его
Object.defineProperty
изget
функция доступа - передача
watcher.depend()
метод своему собственному подписчику сообщенийdep
изsubs
добавить другие свойстваwatcher
- передача
watcher
изevaluate
метод (который, в свою очередь, вызываетwatcher
изget
метод) сделать себя другимwatcher
Подписчики сообщений подписчики, в первую очередьwatcher
назначатьDep.target
, затем выполнитеgetter
Функция оценки при доступе к свойствам внутри функции оценки (например, изdata
,props
или другиеcomputed
), также вызовет ихget
функция доступа, чтобы вычисляемое свойствоwatcher
добавлено к свойству в функции оценкиwatcher
подписчик сообщенияdep
, когда эти операции завершены, и, наконец, закрытьDep.target
назначить какnull
и возвращает результат функции оценки.
- Зарегистрировать
- Запускается при изменении свойства
set
Перехватите функцию, затем вызовите ее собственного подписчика сообщенийdep
изnotify
метод прохождения токаdep
сохраняет всех подписчиков вwathcer
изsubs
массив и вызывать один за другимwatcher
изupdate
способ завершения обновления ответа.
текст / также
Кодировщик, жаждущий поэзии и дистанции
Монтаж/Флуоресценция
Автор разрешил опубликовать эту статью, и авторские права принадлежат Chuangyu Frontend. Пожалуйста, укажите источник для перепечатки этой статьи. Ссылка на эту статью:известно Sec-Fed.com/2018-09-12-…
Если вы хотите подписаться на другие сообщения с передовой линии KnownsecFED, выполните поиск и подпишитесь на нашу общедоступную учетную запись WeChat: KnownsecFED. Добро пожаловать, чтобы оставить сообщение для обсуждения, мы ответим как можно скорее.
Спасибо за прочтение.