Обновление: Спасибо за вашу поддержку.Недавно был выброшен официальный сайт блога, чтобы каждый мог его систематически читать.В будущем будет больше контента и больше оптимизации.Нажмите здесь, чтобы просмотреть
------ Далее идет текст ------
Продвинутая серия Vue резюмируется следующим образом, добро пожаловать на чтение.
Принцип отзывчивости и реализация расширенной серии Vue (1)
Принцип и реализация плагина Vue advanced series (2)
Принцип и реализация функции рендеринга расширенной серии Vue (3)
Что такое реактивная реактивность
Реактивность указывает, как динамически изменять всю систему после изменения состояния, а в реальных сценариях приложений проекта, то есть как данные динамически изменяют Dom.
нужно
Теперь есть требование, есть две переменные a и b, причем b всегда в 10 раз больше a, как это сделать?
Простая попытка 1:
let a = 3;
let b = a * 10;
console.log(b); // 30
На первый взгляд кажется, что оно удовлетворяет требованиям, но значение b в это время фиксировано, как бы ни изменялось a, b не будет меняться вместе с ним. То есть b не поддерживает синхронизацию данных с a. b изменится только в том случае, если значение b будет переопределено после изменения a.
a = 4;
console.log(a); // 4
console.log(b); // 30
b = a * 10;
console.log(b); // 40
Простая попытка 2:
Определите отношение между a и b в функции, затем выполните эту функцию после изменения a, и значение b изменится. Псевдокод выглядит следующим образом.
onAChanged(() => {
b = a * 10;
})
Таким образом, теперь возникает вопрос, как достичьonAChanged
функция, которая автоматически выполняется при измененииonAChanged
, см. продолжение.
Объедините слой просмотра
Теперь объедините a, b и страницы просмотра, где a соответствует данным, а b соответствует странице. Бизнес-сценарий очень прост: после изменения данных а измените страницу б.
<span class="cell b"></span>
document
.querySelector('.cell.b')
.textContent = state.a * 10
Теперь установите связь между данными a и страницей b и оберните их функцией, чтобы установить следующую связь.
<span class="cell b"></span>
onStateChanged(() => {
document
.querySelector(‘.cell.b’)
.textContent = state.a * 10
})
После повторного абстрагирования это выглядит так.
<span class="cell b">
{{ state.a * 10 }}
</span>
onStateChanged(() => {
view = render(state)
})
view = render(state)
является высокоуровневой абстракцией для рендеринга всех страниц. Здесь не рассматриваетсяview = render(state)
, потому что он должен включать в себя ряд технических деталей, таких как структура DOM и ее реализация. Что здесь нужно, так этоonStateChanged
реализация.
выполнить
Это достигается за счетObject.defineProperty
серединаgetter
а такжеsetter
метод. Обратитесь к ссылке ниже для конкретного использования.
должен быть в курсеget
а такжеset
Функции — это дескрипторы доступа,value
а такжеwritable
Функции — это дескрипторы данных. Дескриптор должен быть одной из этих двух форм, но они не могут сосуществовать, иначе возникнет исключение.
Пример 1: Реализацияconvert()
функция
Требования следующие:
- 1. Входящий объект
obj
как параметр - 2. Используйте
Object.defineProperty
Преобразование всех свойств объекта - 3. Преобразованный объект сохраняет исходное поведение, но журналы выводятся во время операций получения или установки.
Пример:
const obj = { foo: 123 }
convert(obj)
obj.foo // 输出 getting key "foo": 123
obj.foo = 234 // 输出 setting key "foo" to 234
obj.foo // 输出 getting key "foo": 234
в пониманииObject.defineProperty
серединаgetter
а такжеsetter
После использования метода, изменивget
а такжеset
функция может быть реализованаonAChanged
а такжеonStateChanged
.
выполнить:
function convert (obj) {
// 迭代对象的所有属性
// 并使用Object.defineProperty()转换成getter/setters
Object.keys(obj).forEach(key => {
// 保存原始值
let internalValue = obj[key]
Object.defineProperty(obj, key, {
get () {
console.log(`getting key "${key}": ${internalValue}`)
return internalValue
},
set (newValue) {
console.log(`setting key "${key}" to: ${newValue}`)
internalValue = newValue
}
})
})
}
Пример 2: РеализацияDep
Добрый
Требования следующие:
- 1. Создайте
Dep
класс с двумя методами:depend
а такжеnotify
- 2. Создайте
autorun
функция, переходящая вupdate
функция как параметр - 3. В
update
вызов функцииdep.depend()
, который явно зависит отDep
пример - 4. Звонок
dep.notify()
вызыватьupdate
повторный запуск функции
Пример:
const dep = new Dep()
autorun(() => {
dep.depend()
console.log('updated')
})
// 注册订阅者,输出 updated
dep.notify()
// 通知改变,输出 updated
Сначала нужно определитьautorun
функционировать, получатьupdate
функционировать как параметр. потому что звонюautorun
когдаDep
Регистрируйте абонентов и звонитеdep.notify()
повторно выполнитьupdate
функция, поэтомуDep
должен иметьupdate
Здесь используются ссылки, переменныеactiveUpdate
Выражается как функция пакета обновления.
Код реализации выглядит следующим образом.
let activeUpdate = null
function autorun (update) {
const wrappedUpdate = () => {
activeUpdate = wrappedUpdate // 引用赋值给activeUpdate
update() // 调用update,即调用内部的dep.depend
activeUpdate = null // 绑定成功之后清除引用
}
wrappedUpdate() // 调用
}
wrappedUpdate
Суть в замыкании,update
можно получить внутри функцииactiveUpdate
переменная, то жеdep.depend()
Также доступен внутриactiveUpdate
переменная, поэтомуDep
Реализация очень проста.
Код реализации выглядит следующим образом.
class Dep {
// 初始化
constructor () {
this.subscribers = new Set()
}
// 订阅update函数列表
depend () {
if (activeUpdate) {
this.subscribers.add(activeUpdate)
}
}
// 所有update函数重新运行
notify () {
this.subscribers.forEach(sub => sub())
}
}
Объединение двух вышеуказанных частей является полной реализацией.
Пример 3: Реализация реактивной системы
Требования следующие:
- 1. Объединив два приведенных выше примера,
convert()
переименовать в наблюдателяobserve()
- 2,
observe()
Преобразует свойства объекта в реактивные, каждому преобразованному свойству присваиваетсяDep
экземпляр, который отслеживает подпискиupdate
список функций, а при вызовеsetter
вызвать их повторный запуск - 3.
autorun()
перениматьupdate
функционировать как параметр, а вupdate
Выполняется повторно при изменении свойств подписки на функцию.
Пример:
const state = {
count: 0
}
observe(state)
autorun(() => {
console.log(state.count)
})
// 输出 count is: 0
state.count++
// 输出 count is: 1
После объединения примера 1 и примера 2 вышеуказанные требования могут быть достигнуты,observe
изменено вobj
Одновременное присвоение свойствDep
экземпляр и вget
Зарегистрируйте подписчиков вset
Уведомлять об изменениях.autorun
Функция остается неизменной.
Реализация выглядит следующим образом:
class Dep {
// 初始化
constructor () {
this.subscribers = new Set()
}
// 订阅update函数列表
depend () {
if (activeUpdate) {
this.subscribers.add(activeUpdate)
}
}
// 所有update函数重新运行
notify () {
this.subscribers.forEach(sub => sub())
}
}
function observe (obj) {
// 迭代对象的所有属性
// 并使用Object.defineProperty()转换成getter/setters
Object.keys(obj).forEach(key => {
let internalValue = obj[key]
// 每个属性分配一个Dep实例
const dep = new Dep()
Object.defineProperty(obj, key, {
// getter负责注册订阅者
get () {
dep.depend()
return internalValue
},
// setter负责通知改变
set (newVal) {
const changed = internalValue !== newVal
internalValue = newVal
// 触发后重新计算
if (changed) {
dep.notify()
}
}
})
})
return obj
}
let activeUpdate = null
function autorun (update) {
// 包裹update函数到"wrappedUpdate"函数中,
// "wrappedUpdate"函数执行时注册和注销自身
const wrappedUpdate = () => {
activeUpdate = wrappedUpdate
update()
activeUpdate = null
}
wrappedUpdate()
}
В сочетании с блок-схемой в документе Vue это становится еще понятнее.
Работа выполнена! ! !
Содержание этой статьи взято из платного видео автора VUE Youda.
общаться с
Моя ссылка на Github выглядит следующим образом, добро пожаловать в Star
GitHub.com/100 миллионов акционеров/Нет...
Я деревянный Ян И, старший инженер по интерфейсу NetEase, и мое интервью было сосредоточено на том, чтобы запечатлеть тяжелую и трудную неделю переднего плана. Позвольте мне ввести вас в мир продвинутого фронтенда, продвинутых в пути, подбадривайте друг друга!