Серия интервьюеров (5): Почему вы используете интерфейсный фреймворк?
прошлое
- Серия интервьюеров (1): Как реализовать глубокое клонирование
- Серия интервьюеров (2): Реализация шины событий
- Серия интервьюеров (3): Реализация внешней маршрутизации
- Серия интервьюеров (4): Преимущества двустороннего связывания на основе перехвата данных прокси
предисловие
Начиная с Backbone, front-end фреймворки росли как грибы после дождя.Мы привыкли разрабатывать на разных фреймворках, но каково значение front-end фреймворков?Почему мы выбираем front-end фреймворки для разработки?
Предварительное заявление:Мы не оценивали входящие параметры вовремя, чтобы избежать ошибок, а реализовывали только основные методы.
Каталог статей
- Фундаментальное значение front-end framework
- Что такое полный интерфейсный фреймворк
- Как реализовать адаптивную систему на основе прокси
1. Фундаментальное значение front-end framework
1.1 Преимущества интерфейсных фреймворков
Когда я впервые изучил интерфейсный фреймворк (мой первый фреймворк был React), я не понимал, что может дать фреймворк, просто потому, что все его использовали.Одним из наиболее практических применений является то, что почти все предприятия используют каркас, без каркаса.
По мере углубления использования я постепенно понимаю преимущества фреймворка:
- Компонентизация: среди них компонентизация React является наиболее тщательной, и она может даже достигать атомарных компонентов на функциональном уровне.Высокая степень компонентизации может сделать наши проекты простыми в обслуживании, а также легко комбинировать и расширять.
- Естественная многоуровневость: код в эпоху JQuery в основном представляет собой спагетти-код с серьезными связями.Современные фреймворки, будь то режим MVC, MVP или MVVM, могут помочь нам в многоуровневости, а развязку кода легче читать и писать.
- Экология: теперь основные интерфейсные фреймворки имеют свою собственную экологию, будь то архитектура управления потоками данных или библиотека пользовательского интерфейса, есть зрелые решения.
1.2 Фундаментальное значение интерфейсного фреймворка
В предыдущем разделе мы говорили только о преимуществах интерфейсных фреймворков, но не указывали на корень проблемы, пока я не увидел этостатья(Китайская версия).
Проще говоря, фундаментальное значение интерфейсного фреймворка состоит в том, чтобы решитьПроблемы с пользовательским интерфейсом и синхронизацией состояний.
В Vue, если мы хотимtodos
Добавьте один, просто нужноapp4.todos.push({ text: '新项目' })
, В настоящее время, поскольку встроенная адаптивная система Vue автоматически поможет нам синхронизировать пользовательский интерфейс и состояние.
<div id="app-4">
<ol>
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ol>
</div>
var app4 = new Vue({
el: '#app-4',
data: {
todos: [
{ text: '学习 JavaScript' },
{ text: '学习 Vue' },
{ text: '整个牛项目' }
]
}
})
Если мы работаем с JQuery или JS, у нас неизбежно будет многоli.appendChild
,document.createElement
При ожидании DOM-операций нам нужна длинная серия DOM-операций для обеспечения синхронизации состояния и UI.Ошибка в одной из ссылок приведет к BUG.Минусы ручной работы следующие:
- Частые манипуляции с DOM медленны.
- Слишком много промежуточных шагов, легко генерировать ошибки и сложно поддерживать, а также высокие умственные требования не способствуют эффективности разработки.
Будь то перехват данных в vue, грязное обнаружение в Angular или reRender на уровне компонентов в React, все они являются мощными инструментами, помогающими нам решить проблему синхронизации пользовательского интерфейса и состояния.
Это также объясняет причину, по которой Backbone, как создатель front-end фреймворка, в будущем оказался одиноким: Backbone только представил идею MVC, а не решил проблему синхронизации между View и Modal. Три основных современных фреймворка, напрямую работающие с Modal, могут синхронизировать характеристики пользовательского интерфейса. Backbone по-прежнему связан с JQuery, работающим с Dom in View для достижения цели синхронизации пользовательского интерфейса, что явно не соответствует требованиям к дизайну современной интерфейсной среды.
2. Как Vue обеспечивает синхронизацию пользовательского интерфейса с состоянием
UI относится к View в MVVM, state относится к Modal в MVVM, а View-Modal обеспечивает синхронизацию между View и Modal.
Vue проходит черезОтзывчивая системаДля обеспечения синхронизации между видом и модалом VUE решила быть совместимым с IE.Object.defineProperty
Как реализация адаптивной системы, но если не учитывать пользователей IE,Object.defineProperty
Не лучший выбор, пожалуйста, см.Серия интервьюеров (4): Преимущества двустороннего связывания на основе перехвата данных прокси.
Мы будем реализовывать реактивную систему с использованием прокси.
Перед прочтением рекомендуется прочитатьСерия интервьюеров (4): Преимущества двустороннего связывания на основе перехвата данных проксина основе
Object.defineProperty
приблизительная реализация.
2.1 Центр публикации и подписки
Реактивная система неотделима от модели публикации-подписки, потому что нам нуженDep
Сохраните подписчиков и уведомите подписчиков, сохраненных в Dep, при изменении наблюдателя, чтобы подписчики знали об изменениях и обновляли представление, чтобы обеспечить синхронизацию представления и состояния.
Пожалуйста, ознакомьтесь с моделью публикации-подпискиСерия интервьюеров (2): Реализация шины событий
/**
* [subs description] 订阅器,储存订阅者,通知订阅者
* @type {Map}
*/
export default class Dep {
constructor() {
// 我们用 hash 储存订阅者
this.subs = new Map();
}
// 添加订阅者
addSub(key, sub) {
// 取出键为 key 的订阅者
const currentSub = this.subs.get(key);
// 如果能取出说明有相同的 key 的订阅者已经存在,直接添加
if (currentSub) {
currentSub.add(sub);
} else {
// 用 Set 数据结构储存,保证唯一值
this.subs.set(key, new Set([sub]));
}
}
// 通知
notify(key) {
// 触发键为 key 的订阅者们
if (this.subs.get(key)) {
this.subs.get(key).forEach(sub => {
sub.update();
});
}
}
}
2.2 Реализация слушателя
мы в подписчикахDep
реализовалnotify
способ уведомить соответствующих подписчиков об этом, однакоnotify
Когда именно срабатывает метод?
Разумеется, уведомление срабатывает при изменении состояния, то есть при изменении Modal в MVVM, однакоDep
Очевидно, что можно узнать, изменился ли модаль, поэтому нам нужно создать слушатель.Observer
Чтобы отслеживать Modal, мы будем выполнять операции уведомления при изменении Modal.
на основе vueObject.defineProperty
Чтобы реализовать слушателя, мы используем Proxy для реализации слушателя.
а такжеObject.defineProperty
Свойства мониторинга разные.Прокси может контролировать (фактически проксировать) весь объект, поэтому нет необходимости проходить по очереди свойства объекта для мониторинга, но если свойство объекта все еще является объектом, то Прокси не может монитор, поэтому мы внедрилиobservify
Вы можете выполнять рекурсивный мониторинг.
/**
* [Observer description] 监听器,监听对象,触发后通知订阅
* @param {[type]} obj [description] 需要被监听的对象
*/
const Observer = obj => {
const dep = new Dep();
return new Proxy(obj, {
get: function(target, key, receiver) {
// 如果订阅者存在,直接添加订阅
if (Dep.target) {
dep.addSub(key, Dep.target);
}
return Reflect.get(target, key, receiver);
},
set: function(target, key, value, receiver) {
// 如果对象值没有变,那么不触发下面的操作直接返回
if (Reflect.get(receiver, key) === value) {
return;
}
const res = Reflect.set(target, key, observify(value), receiver);
// 当值被触发更改的时候,触发 Dep 的通知方法
dep.notify(key);
return res;
},
});
};
/**
* 将对象转为监听对象
* @param {*} obj 要监听的对象
*/
export default function observify(obj) {
if (!isObject(obj)) {
return obj;
}
// 深度监听
Object.keys(obj).forEach(key => {
obj[key] = observify(obj[key]);
});
return Observer(obj);
}
2.3 Реализация подписчика
На данный момент мы решили две проблемы: одна — узнать, что Modal изменился (используя прослушиватель Observer для мониторинга объекта Modal), другая — как собрать подписчиков и уведомить об их изменениях (используя подписчиков для сбора подписчиков и уведомления о подписках). с уведомлением By).
В настоящее время нам не хватает одного подписчика (Watcher)
// 订阅者
export default class Watcher {
constructor(vm, exp, cb) {
this.vm = vm; // vm 是 vue 的实例
this.exp = exp; // 被订阅的数据
this.cb = cb; // 触发更新后的回调
this.value = this.get(); // 获取老数据
}
get() {
const exp = this.exp;
let value;
Dep.target = this;
if (typeof exp === 'function') {
value = exp.call(this.vm);
} else if (typeof exp === 'string') {
value = this.vm[exp];
}
Dep.target = null;
return value;
}
// 将订阅者放入待更新队列等待批量更新
update() {
pushQueue(this);
}
// 触发真正的更新操作
run() {
const val = this.get(); // 获取新数据
this.cb.call(this.vm, val, this.value);
this.value = val;
}
}
2.4 Реализация пакетного обновления
Мы реализовали подписчика (Watcher) в предыдущем разделе, ноupdate
Метод состоит в том, чтобы поставить подписчик в очередь, чтобы обновить, а не запускать его напрямую, по следующим причинам:
Так что эта очередь должна сделать этоасинхронныйа такжедедупликация, поэтому мы используемSet
В качестве структуры данных для хранения Watcher и использованияPromise
Имитирует асинхронные обновления.
// 创建异步更新队列
let queue = new Set()
// 用Promise模拟nextTick
function nextTick(cb) {
Promise.resolve().then(cb)
}
// 执行刷新队列
function flushQueue(args) {
queue.forEach(watcher => {
watcher.run()
})
// 清空
queue = new Set()
}
// 添加到队列
export default function pushQueue(watcher) {
queue.add(watcher)
// 下一个循环调用
nextTick(flushQueue)
}
2.5 Резюме
Давайте разберемся в процессе: как адаптивная система синхронизирует UI (View) и состояние (Modal)?
Сначала нам нужно отслеживать модальный объект В этой статье мы используем прокси для мониторинга модального объекта, поэтому наш наблюдатель может знать, когда модальный объект изменяется.
Как мы уведомим View, когда узнаем, что Modal изменился? Вы должны знать, что модальное изменение может вызвать несколько обновлений пользовательского интерфейса.Например, если имя пользователя изменено, необходимо изменить имя пользователя в его компоненте личной информации, компоненте уведомления и т. д. В этой ситуации мы можем легко придумать с использованиемопубликовать подписатьсяРежим для решения, нам нужен подписчик (Dep) для хранения подписчиков (Watcher), при мониторинге модальных изменений нам нужно только уведомить соответствующих подписчиков об обновлении.
Так откуда берутся подписчики? На самом деле каждый экземпляр компонента соответствует подписчику (только потому, что экземпляр компонента соответствует подписчику, Dep можно использовать для уведомления соответствующего компонента, иначе будет беспорядок, а уведомление подписчика эквивалентно косвенному уведомлению компонента).
Когда подписчик узнает о конкретных изменениях, он будет обновлен соответствующим образом и отразит обновление в пользовательском интерфейсе (представлении), пока синхронизация между пользовательским интерфейсом и модальным завершена.
полный кодУже на github в настоящее время реализована только адаптивная система, а полная мини-версия mvvm framework будет внедряться постепенно, так что вы можете пометить или посмотреть, чтобы следить за прогрессом.
3. Адаптивные системы — это еще не все
Хотя адаптивная система является основной концепцией Vue, одной адаптивной системы недостаточно.
Хотя отзывчивая система знает об изменениях значений данных, когда значения не могут полностью отображать пользовательский интерфейс, нам все равно нужно выполнять повторную визуализацию на уровне компонентов, что неэффективно.Поэтому Vue представила виртуальный DOM в версии 2.0, а выполненный виртуальный DOM Дальнейшие операции сравнения позволяют выполнять более мелкие операции, которые могут гарантировать более низкую границу reReander (гарантированно менее медленную).
Кроме того, для удобства разработчиков в vue есть много встроенных инструкций, поэтому нам также понадобится парсер шаблонов vue.
следующее уведомление
Старомодный виртуальный DOM, реализация виртуального DOM и оптимизация алгоритма сравнения.