Vue уже составляет треть текущего отечественного веб-терминала, а также является одним из моих основных технологических стеков. Я знаю его в повседневном использовании, и мне это любопытно. Кроме того, большое количество исходного кода Vue Недавно в сообществе появились классы чтения. В статье я воспользовался этой возможностью, чтобы почерпнуть немного пищи из всех статей и обсуждений. В то же время я обобщил некоторые идеи при чтении исходного кода и подготовил несколько статей в качестве результатов своей работы. собственное мышление.мой уровень ограничен.добро пожаловать, чтобы оставить сообщение для обсуждения~
Целевая версия Vue:2.5.17-beta.0
Комментарии к исходному коду Vue:GitHub.com/Шерлок Эд9…
Отказ от ответственности: Синтаксис исходного кода в статье использует Flow, и исходный код сокращен по мере необходимости (чтобы не путать @_@), если вы хотите увидеть полную версию, пожалуйста, введите вышегитхаб-адрес, эта статья представляет собой серию статей, адрес статьи внизу ~
Заинтересованные студенты могут добавить группу WeChat в конце статьи для совместного обсуждения~
1. Отзывчивая система
Благодаря представлению на официальном веб-сайте мы знаем, что Vue.js — это инфраструктура MVVM, она заботится не об изменениях представлений, а об обновлениях представлений, управляемых данными, что делает наше управление состоянием очень простым и как этого добиться. Украдена картинка с официального сайта
Каждый экземпляр компонента имеет соответствующийWatcher
Экземпляр объекта, который будет записывать свойства как зависимости во время рендеринга компонента, а затем использовать их как зависимостиsetter
При вызове он уведомитwatcher
Пересчитать, что приведет к обновлению связанных с ним компонентов.
Здесь есть три важных понятияObserve
,Dep
,Watcher
соответственно находится вsrc/core/observer/index.js
,src/core/observer/dep.js
,src/core/observer/watcher.js
-
Observe
Класс в основном добавляется в свойства отзывчивого объектаgetter/setter
Используется для сбора зависимостей и распространения обновлений. -
Dep
Класс, используемый для сбора зависимостей текущего реактивного объекта. -
Watcher
Класс является наблюдателем, а экземпляры делятся на три типа: наблюдатель рендеринга, наблюдатель вычисляемых свойств и наблюдатель слушателя.
2. Реализация кода
2.1 initState
Отвечающая запись находится в src/core/instance/init.js.initState
середина:
// src/core/instance/state.js
export function initState(vm: Component) {
const opts = vm.$options
if (opts.props) initProps(vm, opts.props) // 初始化props
if (opts.methods) initMethods(vm, opts.methods) // 初始化methods
if (opts.data) initData(vm) // 初始化data
if (opts.computed) initComputed(vm, opts.computed) // 初始化computed
if (opts.watch) initWatch(vm, opts.watch) // 初始化watch
}
}
Он очень регулярно определяет несколько методов для инициализацииprops
,methods
,data
,computed
,wathcer
, посмотри сюдаinitData
метод, взгляните на леопарда
// src/core/instance/state.js
function initData(vm: Component) {
let data = vm.$options.data
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {}
observe(data, true /* asRootData */) // 给data做响应式处理
}
Сначала оцените, являются ли данные функцией, если да, возьмите возвращаемое значение, если нет, затем возьмите сами себя, а затемobserve
пара методовdata
Для обработки этот метод пытается создать экземпляр Observer.__ob__
, возвращает новый экземпляр Observer, если он успешно создан, или возвращает существующий экземпляр Observer, если экземпляр Observer уже существует.
2.2 Observer/defineReactive
// src/core/observer/index.js
export function observe (value: any, asRootData: ?boolean): Observer | void {
let ob: Observer | void
ob = new Observer(value)
return ob
}
Этот метод в основном используетdata
В качестве параметра для создания экземпляра объекта Observer Observer представляет собой класс, используемый для сбора зависимостей иnotify
Обновление, конструктор Observer используетdefineReactive
метод для реактивного форматирования ключей объекта и рекурсивного добавления к свойствам объектаgetter/setter
, срабатывает, когда данные оцениваютсяgetter
И собирать зависимости, запускать сначала при изменении значенияgetter
перезапускатьsetter
и отправлять обновления
// src/core/observer/index.js
export class Observer {
value: any;
dep: Dep;
constructor (value: any) {
value: any;
this.dep = new Dep()
def(value, '__ob__', this) // def方法保证不可枚举
this.walk(value)
}
// 遍历对象的每一个属性并将它们转换为getter/setter
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) { // 把所有可遍历的对象响应式化
defineReactive(obj, keys[i])
}
}
}
export function defineReactive ( obj: Object, key: string, val: any, customSetter?: ?Function, shallow?: boolean) {
const dep = new Dep() // 在每个响应式键值的闭包中定义一个dep对象
// 如果之前该对象已经预设了getter/setter则将其缓存,新定义的getter/setter中会将其执行
const getter = property && property.get
const setter = property && property.set
let childOb = !shallow && observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val // 如果原本对象拥有getter方法则执行
if (Dep.target) { // 如果当前有watcher在读取当前值
dep.depend() // 那么进行依赖收集,dep.addSub
}
return value
},
set: function reactiveSetter (newVal) {
const value = getter ? getter.call(obj) : val // 先getter
if (newVal === value || (newVal !== newVal && value !== value)) { // 如果跟原来值一样则不管
return
}
if (setter) { setter.call(obj, newVal) } // 如果原本对象拥有setter方法则执行
else { val = newVal }
dep.notify() // 如果发生变更,则通知更新,调用watcher.update()
}
})
}
getter
При сборе зависимостей обратите внимание на это, только когдаDep.target
Сбор зависимостей будет выполняться только при наличии значения, этоDep.target
находится в экземпляре Watcherget
когда вызывается методpushTarget
подтолкнет наблюдателя с текущим значением вDep.target
, исходный наблюдатель помещается в стекtargetStack
В стеке значение наблюдателя текущего значения извлекается из стека после того, как значение наблюдателя завершено, и исходное значение наблюдателя присваиваетсяDep.target
,cleanupDeps
Наконец-то поставил новыйnewDeps
Нет наблюдателя, который не был бы очищен, чтобы предотвратить бесполезные триггеры наблюдателя в представлении.
setter
когда первыйgetter
, и если в старом значении нет изменений, вернуть, если есть изменение, dep уведомляет все экземпляры Watcher, хранящиеся в подпрограммах, которые полагаются на эти данные.update
для обновления, здесьupdate
ЦКqueueWatcher( )
Асинхронно отправить в очередь наблюдателя планировщикаqueue
в, в nextTickflushSchedulerQueue( )
Выньте наблюдателя из очереди и выполните егоwatcher.run
И выполните соответствующую функцию ловушки
2.3 Dep
Ключевое слово, упомянутое много раз вышеDep
, который является контейнером для сбора зависимостей, илиСборщик зависимостей, он записал, какие Наблюдатели зависели от своих собственных изменений или какие Наблюдатели подписались на свои собственные изменения; вот цитата пользователя сети:
@liuhongyi0101: Проще говоря, это подсчет ссылок.Кто займет мои деньги, я запишу этого человека.Если моих денег меньше, я сообщу им, что у меня нет денег.
А вот маленькая книга, в которой записаны люди, которые заняли деньгиDep
сабвуферы в экземпляре
// src/core/observer/dep.js
let uid = 0 // Dep实例的id,为了方便去重
export default class Dep {
static target: ?Watcher // 当前是谁在进行依赖的收集
id: number
subs: Array<Watcher> // 观察者集合
constructor() {
this.id = uid++ // Dep实例的id,为了方便去重
this.subs = [] // 存储收集器中需要通知的Watcher
}
addSub(sub: Watcher) { ... } /* 添加一个观察者对象 */
removeSub(sub: Watcher) { ... } /* 移除一个观察者对象 */
depend() { ... } /* 依赖收集,当存在Dep.target的时候把自己添加观察者的依赖中 */
notify() { ... } /* 通知所有订阅者 */
}
const targetStack = [] // watcher栈
export function pushTarget(_target: ?Watcher) { ... } /* 将watcher观察者实例设置给Dep.target,用以依赖收集。同时将该实例存入target栈中 */
export function popTarget() { ... } /* 将观察者实例从target栈中取出并设置给Dep.target */
здесьDep
в случаеsubs
Собранная зависимость — это watcher, т.е.Watcher
Экземпляр , используемый для уведомления об обновлениях в будущем
2.4 Watcher
// src/core/observer/watcher.js
/* 一个解析表达式,进行依赖收集的观察者,同时在表达式数据变更时触发回调函数。它被用于$watch api以及指令 */
export default class Watcher {
constructor(
vm: Component,
expOrFn: string | Function,
cb: Function,
options?: ?Object,
isRenderWatcher?: boolean // 是否是渲染watcher的标志位
) {
this.getter = expOrFn // 在get方法中执行
if (this.computed) { // 是否是 计算属性
this.value = undefined
this.dep = new Dep() // 计算属性创建过程中并未求值
} else { // 不是计算属性会立刻求值
this.value = this.get()
}
}
/* 获得getter的值并且重新进行依赖收集 */
get() {
pushTarget(this) // 设置Dep.target = this
let value
value = this.getter.call(vm, vm)
popTarget() // 将观察者实例从target栈中取出并设置给Dep.target
this.cleanupDeps()
return value
}
addDep(dep: Dep) { ... } /* 添加一个依赖关系到Deps集合中 */
cleanupDeps() { ... } /* 清理newDeps里没有的无用watcher依赖 */
update() { ... } /* 调度者接口,当依赖发生改变的时候进行回调 */
run() { ... } /* 调度者工作接口,将被调度者回调 */
getAndInvoke(cb: Function) { ... }
evaluate() { ... } /* 收集该watcher的所有deps依赖 */
depend() { ... } /* 收集该watcher的所有deps依赖,只有计算属性使用 */
teardown() { ... } /* 将自身从所有依赖收集订阅列表删除 */
}
get
выполняется в методеgetter
Он передается, когда наблюдатель заново отображается в началеupdateComponent = () => { vm._update(vm._render(), hydrating) }
, этот метод первыйvm._render()
Сгенерируйте и визуализируйте дерево VNode, в процессе завершите текущий экземпляр Vue.vm
доступ к данным наgetter
,Потомvm._update()
идтиpatch
Обратите внимание здесьget
Метод наконец выполняетсяgetAndInvoke
, этот метод сначала просматривает сохраненные данные в наблюдателеdeps
, УдалитьnewDep
подписки, которые больше не существуют вdepIds = newDepIds; deps = newDeps
,ПучокnewDepIds
а такжеnewDeps
Пустой. Удаляйте старые подписки, которые больше не нужны после добавления новых подписок, чтобы в некоторых случаях, напримерv-if
Наблюдатель не будет уведомлен об изменении данных, для которых шаблон больше не требуется.update
охватывать
2.5 Резюме
Весь процесс сбора примерно такой, вы можете взглянуть на вышеприведенный процесс
Watcher имеет следующие сценарии использования:
-
render watcher
Наблюдатель рендеринга, наблюдатель, используемый для рендеринга представления. -
computed watcher
Наблюдатель за вычисляемыми свойствами, поскольку вычисляемые свойства зависят от других, а также зависят от них, поэтому они также содержатDep
пример -
watch watcher
слушатель наблюдатель
Пока другие наблюдатели (watchers
) зависимости, такие как данные, свойства данных, вычисляемые свойства, реквизиты, будут генерировать экземпляр Dep в замыканииdep
и называетсяgetter
когдаdep.depend
Соберите, кто от него зависит, и сохраните зависимого наблюдателя в своих собственных подпрограммах.this.subs.push(sub)
, чтобы уведомить, когда он изменитсяnotify
вставитьdep.subs
Зависит от себя в массивеwatchers
Я изменился, пожалуйста, будь вовремяupdate
~
Всякий раз, когда объект зависит от других реактивных объектов, будет сгенерирован наблюдательwatcher
, считать этоwatcher
От каких отзывчивых объектов зависит в этомwatcher
Прежде чем оценивать текущиеwatcher
установить глобальныйDep.target
, и во время, когда отзывчивый объект зависит от измененийupdate
Эта статья представляет собой серию статей, и более поздние части будут обновлены позже, чтобы добиться прогресса вместе ~
Большинство сообщений в Интернете имеют разную глубину и даже некоторые несоответствия. Следующие статьи являются кратким изложением процесса обучения. Если вы найдете какие-либо ошибки, пожалуйста, оставьте сообщение, чтобы указать ~
Ссылаться на:
PS: Всех приглашаю обратить внимание на мой публичный аккаунт [Front End Afternoon Tea], давайте работать вместе~
Кроме того, вы можете присоединиться к группе WeChat «Front-end Afternoon Tea Exchange Group», нажмите и удерживайте, чтобы определить QR-код ниже, чтобы добавить меня в друзья, обратите вниманиеДобавить группу, я заберу тебя в группу~