Мы знаем, Vue.js 2 через
Object.defineProperty()
функция реализации отзывчивости. 5 числа этого месяца Youda выпустил исходный код Vue.js 3, и в сообществе сразу же появилось множество статей о совместном использовании исходного кода. Всем давно известно, что новая версия отзывчивости реализована с помощью Proxy, и теперь мы будем использовать Proxy для реализации базового адаптивного каркаса.
Основание
оProxy
Базовые знания, вы можете пойти в MDN, чтобы узнатьПрямая ссылка.
Отзывчивое ядро
Готов к работе
В этой статье основная функция, реализующая отзывчивость, называетсяreactive
, эта функция возвращает прокси-результат, который можно активировать, манипулируя возвращенным результатом.
Во-первых, дайте тестовые данные:name
объект свойства obj, через который мы хотим пройтиreactive
Возвращает новый объект после обработки функцииproxyObj
, мы работаем какobj
Нравится операцияproxyObj
объект.
let obj = {
name: 'test name'
}
let proxyObj = reactive(obj);
Например: Reactive срабатывает при изменении proxyObj.name.
Второе: установите функцию для имитации реактивного процесса, здесь нет необходимости фактически обновлять DOM. Мы можем настроить функцию, которая может просто вывести строку, указывающую, что необходимо выполнить текущее обновление представления.
// 提示视图需要更新
function trigger() {
console.log('视图需要更新');
}
выполнитьreactive
функция
вспомогательная функция
Прежде всего понятно, что реактивная функция получает параметр, который нужно проксировать и результат после прокси возвращается. Прокси требуется только в том случае, если параметр является объектом, в противном случае он возвращается напрямую.
Здесь вам нужно создать вспомогательную функцию, чтобы определить, является ли переменная объектом:
function isObject(param) {
return typeof param === 'object' && param !== null;
}
основная функция
При использовании Proxy вам необходимо определить обработчик прокси-объекта для выполнения прокси-операций над целью Этот объект в основном имеет два метода, а именно get и set, которые запускаются при получении и установке значений свойств соответственно. В то же время внутренняя реализация должна использовать объект Reflect, подробности см. в приведенном ниже коде.
/* 返回一个被代理后的结果,通过操作这个结果可以来实现响应式, 例如视图更新 */
function reactive(target) {
// 如果是个对象,则返回被代理后的结果,如果不是则直接返回
if(!isObject(target)) {
return target;
}
// 需要定义一个代理对象来对 target 进行代理操作
// 这个对象主要有两个方法,即 get 和 set
const handler = {
get(target, key, receiver) {
return Reflect.get(target, key, receiver); // 相当于 return target[key]
},
set(target, key, value, receiver) {
trigger();
return Reflect.set(target, key, value, receiver); // 相当于 target[key] = value
}
};
// 利用 Proxy 来代理这个对象属性
let observed = new Proxy(target, handler);
return observed;
}
Теперь мы модифицируемproxyObj
Атрибут name обнаружил, что запускается самый простой ответ:
let obj = {
name: 'jjjs'
}
let proxyObj = reactive(obj);
// 修改 name 属性,发现可以监控到
proxyObj.name = 'new Name';
console.log(proxyObj.name);
Результат выводится:
И может быть отслеживается не иначе не существует атрибутов:
proxyObj.age = 6;
console.log(proxyObj.age);
результат:
Но когда мы хотим обработать массив, мы обнаруживаем, что запрос на обновление представления запускается дважды:
let array = [1,2,3];
let obArray = reactive(array);
obArray.push(4)
правильноreactive
выполнение кода, модификация, выводset
каждый раз в действииkey
, чтобы увидеть, кто инициировал его дважды:
function reactive(target) {
// ...
set(target, key, value, receiver) {
trigger();
console.log(key); // 输出变动的 key
return Reflect.set(target, key, value, receiver); // 相当于 target[key] = value
}
};
// ...
}
Из приведенного выше рисунка видно, что при мониторинге массива обновление индекса массива будет запущено один раз, а сам массивlength
Обновление также будет запущено, что является причиной вторичного триггера.
Но нам не нужноlength
Представление обновляется, когда оно обновляется, поэтому логика здесь должна быть изменена: только действие модификации частного свойства запускает обновление представления.
function reactive(target) {
const handler = {
// ...
set(target, key, value, receiver) {
// 只对私有属性的修改动作触发视图更新
if(!target.hasOwnProperty(key)) {
trigger();
console.log(key);
}
return Reflect.set(target, key, value, receiver); // 相当于 target[key] = value
}
};
// ...
}
Когда вам нужно получить вложенные объекты, например:
// 对于嵌套的对象
var obj = {
name: 'jjjs',
array: [1,2,3]
}
var proxyObj = reactive(obj);
proxyObj.array.push(4);
В этот момент будет обнаружено, что подсказка о необходимости обновления представления не будет срабатывать, что требует рекурсивной обработки объекта:
function reactive(target) {
// ...
const handler = {
get(target, key, receiver) {
const proxyTarget = Reflect.get(target, key, receiver); // 相当于获取 target[key]
if(isObject(target[key])) { // 对于对象进行递归
return reactive(proxyTarget); // 递归
}
return proxyTarget;
},
// ...
};
// ...
}
На данный момент установлено, что мониторинг может выполняться в обычном режиме:
Однако, когда время получит результат прокси, будут случаи нескольких триггеров агента:
function reactive(target) {
// ...
console.log('走代理');
// 利用 Proxy 来代理这个对象属性
let observed = new Proxy(target, handler);
return observed;
}
// 多次获取代理结果
var proxyObj = reactive(obj);
var proxyObj = reactive(obj);
var proxyObj = reactive(obj);
var proxyObj = reactive(obj);
результат:
走代理
走代理
走代理
走代理
Этой ситуации мы не хотим иметь, мы надеемся сделать один и тот же объект только один раз агентом. На этот раз мы должны действовать для кэширования объекта, один раз во время предыдущего кэширующего прокси-запроса судить, прошел ли агент, а не только через прокси-объект только один раз пройти через брокера.
Поскольку наиболее подходящим целевым контейнером кеша являетсяWeakMap
, 这是由于它对于对象的弱引用特性。 правильноWeakMap
здесьОзнакомьтесь с его функциями.
Теперь измените код, чтобы добавить объект кеша:
const toProxy = new WeakMap(); // 用来保存代理后的对象
function reactive(target) {
// ...
if(toProxy.get(target)) { // 判断对象是否已经被代理了
return toProxy.get(target);
}
// ...
console.log('走代理');
// 利用 Proxy 来代理这个对象属性
let observed = new Proxy(target, handler);
toProxy.set(target, observed); // 保存已经代理了的对象
return observed;
}
Теперь обратите внимание на результат приведенного выше кода:
走代理
Получается, что прокси передается только один раз для одного и того же объекта, чего мы и ожидали.
Суммировать
Вышеприведенное использует только десятки строк кода, чтобы сделать минималистскую реализацию прокси для отзывчивости.Хотя это просто, этого достаточно, чтобы понять идею.
Адрес исходного кода:Here
Если есть ошибки, спасибо за указание~