предисловие
Это третья статья из серии из десяти статей о Vue, в которой мы говорим об одной из основных функций Vue — принципе отзывчивости.
Как понять отзывчивость
Это можно понять так: при изменении состояния сразу меняются транзакции, связанные с этим состоянием, а с фронтенда связанный DOM также меняется после изменения состояния данных. Модели данных — это обычные объекты JavaScript. И когда вы их изменяете, представление обновляется.
бросить вопрос
Давайте сначала посмотрим на наше обычное письмо в Vue:
<div id="app" @click="changeNum">
{{ num }}
</div>
var app = new Vue({
el: '#app',
data: {
num: 1
},
methods: {
changeNum() {
this.num = 2
}
}
})
Этот способ написания очень распространен, но вы рассмотрели, почему он реализован
this.num = 2
Почему обновляется вид сзади? В этой статье я пытаюсь прояснить этот момент.
Как нам это сделать, не используя Vue?
Моей первой мыслью было реализовать это так:
let data = {
num: 1
};
Object.defineProperty(data, 'num',{
set: function( newVal ){
document.getElementById('app').value = newVal;
}
});
input.addEventListener('input', function(){
data.num = 2;
});
Примерно так можно добиться щелчка по элементу и автоматического обновления вида.
Здесь нам нужно манипулировать свойствами доступа объекта через Object.defineProperty. При прослушивании изменений данных управляйте соответствующим DOM.
И вот общий шаблон - шаблон публикации/подписки.
Я нарисовал грубую блок-схему, чтобы проиллюстрировать шаблон наблюдателя и шаблон публикации/подписки. следующее:
Внимательные студенты обнаружат, что разница между моим грубым процессом и использованием Vue заключается в том, что мне нужно манипулировать DOM для повторного рендеринга самостоятельно.
Если мы используем Vue, этот шаг обрабатывается кодом внутри Vue. Вот почему нам не нужно вручную манипулировать DOM при использовании Vue.
оObject.defineProperty
Я уже упоминал об этом в предыдущей статье, поэтому повторяться здесь не буду.
Насколько Vue реактивен
Мы знаем, что объекты можно передавать черезObject.defineProperty
Манипулировать его свойствами доступа, то есть объект имеетgetter
иsetter
метод. Это краеугольный камень отзывчивости.
Сначала взгляните на очень интуитивно понятную блок-схему:
метод initData
Когда Vue инициализируется, его_init()
метод будет вызван для выполненияinitState(vm)
метод.initState
Метод заключается в основном вprops
,methods
,data
,computed
иwathcer
и другие свойства были инициализированы.
Мы здесьdata
Сделайте более подробный анализ процесса инициализации.
function initData (vm: Component) {
let data = vm.$options.data
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {}
if (!isPlainObject(data)) {
......
}
// proxy data on instance
const keys = Object.keys(data)
const props = vm.$options.props
const methods = vm.$options.methods
let i = keys.length
while (i--) {
const key = keys[i]
...... // 省略部分兼容代码,但不影响理解
if (props && hasOwn(props, key)) {
......
} else if (!isReserved(key)) {
proxy(vm, `_data`, key)
}
}
// observe data
observe(data, true /* asRootData */)
}
initData
Основной процесс инициализации данных также выполняет две функции:
- пройти через
proxy
поставить каждое значениеvm._data.[key]
прокси дляvm.[key]
начальство; - перечислить
observe
Метод наблюдает за изменениями всех данных и превращает данные в отзывчивые (наблюдаемые), что можно сделать с помощьюvm._data.[key]
Доступ к соответствующему свойству в определенной функции возврата данных.
захват данных —Observe
Этот метод делает все свойства под данными реактивными (наблюдаемыми).
// 给对象的属性添加 getter 和 setter,用于依赖收集和发布更新
export class Observer {
value: any;
dep: Dep;
vmCount: number;
constructor (value: any) {
this.value = value
// 实例化 Dep 对象
this.dep = new Dep()
this.vmCount = 0
// 把自身实例添加到数据对象 value 的 __ob__ 属性上
def(value, '__ob__', this)
// value 是否为数组的不同调用
if (Array.isArray(value)) {
const augment = hasProto ? protoAugment : copyAugment
augment(value, arrayMethods, arrayKeys)
this.observeArray(value)
} else {
this.walk(value)
}
}
// 取出所有属性遍历
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}
observeArray (items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
}
def
инкапсулируется в функциюObject.defineProperty
, так что вы console.log(data) , вы найдете еще один__ob__
характеристики.
Метод defineReactive перебирает все свойства
// 定义一个响应式对象的具体实现
export function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
const dep = new Dep()
..... // 省略部分兼容代码,但不影响理解
let childOb = !shallow && observe(val)
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 (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
// 对新的值进行监听
childOb = !shallow && observe(newVal)
// 通知所有订阅者,内部调用 watcher 的 update 方法
dep.notify()
}
})
}
defineReactive
Метод изначально инициализирует экземпляр объекта Dep, а затем рекурсивно вызывает его, вызывая дочерние объектыobserve
способ сделать все подсвойства также реактивными объектами. И вObject.defineProperty
изgetter
иsetter
вызов методаdep
сопутствующие методы.
который:
-
getter
Работа, выполняемая методом, — это сбор зависимостей —dep.depend()
-
setter
Работа, выполняемая методом, заключается в публикации обновления -dep.notify()
Мы обнаружили, что существует существенная связь с объектом Dep. Далее мы смотрим на объект Dep. это отд.
Деп в роли диспетчерского центра
В предыдущей статье мы упомянули модель публикации/подписки, и перед издателем и подписчиком стоит диспетчерский центр. Роль Dep здесь играет центр планирования, а его основными функциями являются:
- Соберите подписчика Watcher и добавьте подписки в список Watcher
- Получайте события от издателей
- Уведомлять подписчиков о целевых обновлениях и позволять подписчикам выполнять собственный метод обновления.
Подробный код выглядит следующим образом:
// Dep 构造函数
export default class Dep {
static target: ?Watcher;
id: number;
subs: Array<Watcher>;
constructor () {
this.id = uid++
this.subs = []
}
// 向 dep 的观察者列表 subs 添加 Watcher
addSub (sub: Watcher) {
this.subs.push(sub)
}
// 从 dep 的观察者列表 subs 移除 Watcher
removeSub (sub: Watcher) {
remove(this.subs, sub)
}
// 进行依赖收集
depend () {
if (Dep.target) {
Dep.target.addDep(this)
}
}
// 通知所有订阅者,内部调用 watcher 的 update 方法
notify () {
const subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}
// Dep.target 是全局唯一的观察者,因为在任何时候只有一个观察者被处理。
Dep.target = null
// 待处理的观察者队列
const targetStack = []
export function pushTarget (_target: ?Watcher) {
if (Dep.target) targetStack.push(Dep.target)
Dep.target = _target
}
export function popTarget () {
Dep.target = targetStack.pop()
}
Деп может быть понят как правыйWatcher
Вид управления, Деп иWatcher
тесно связаны. Так что мы должны взглянутьWatcher
реализация.
Подписчики - Наблюдатель
Watcher
Существует много методов-прототипов, определенных в , здесь я лишь кратко расскажу о них.update
иget
эти три метода.
// 为了方便理解,部分兼容代码已被我省去
get () {
// 设置需要处理的观察者
pushTarget(this)
const vm = this.vm
let value = this.getter.call(vm, vm)
// deep 是否为 true 的处理逻辑
if (this.deep) {
traverse(value)
}
// 将 Dep.target 指向栈顶的观察者,并将他从待处理的观察者队列中移除
popTarget()
// 执行依赖清空动作
this.cleanupDeps()
return value
}
update () {
if (this.computed) {
...
} else if (this.sync) {
// 标记为同步
this.run()
} else {
// 一般都是走这里,即异步批量更新:nextTick
queueWatcher(this)
}
}
Реактивный процесс Vue, вероятно, такой. Если вам интересно, вы можете посмотреть исходный код.
Наконец, мы рассмотрим это с помощью этой блок-схемы:
План вывода статей, связанных с Vue
В последнее время друзья всегда задавали мне вопросы, связанные с Vue, поэтому я выпущу 10 статей, связанных с Vue, в надежде быть полезным для всех. Я буду обновлять один через 7-10 дней.
- [Внешний словарь] Процесс внедрения Vuex в жизненный цикл Vue (полный)
- [Внешний словарь] Необходимый запас знаний для изучения исходного кода Vue (полный)
- [Внешний словарь] Анализ принципа отзывчивости Vue (полный)
- [Внешний словарь] Процесс исправления новых и старых VNodes
- [Front-end словарь] Как разрабатывать функциональные компоненты и загружать в npm
- [Внешний словарь] Оптимизируйте свой проект Vue с учетом этих аспектов
- [Внешний словарь] Разработка внешнего интерфейса на основе дизайна Vue-Router
- [Front-end словарь] Как правильно использовать Webpack в проекте
- [Внешний словарь] Рендеринг Vue на стороне сервера
- [Внешний словарь] Как выбрать между Axios и Fetch
Предлагаю вам обратить внимание на мой официальный аккаунт и получать свежие статьи как можно скорее.
Если вы хотите добавить групповое общение, вы также можете добавить маленького умного робота, который будет автоматически подтягивать вас в группу: