Статья впервые опубликована вличный блог
содержание
- Proxy
- Использование Vue 2.0
Object.defineProperty()
Реализовать ответ данных - в Вью 3.0
Proxy
- Другие применения прокси
Proxy
Что такое прокси?Под ним можно понимать установку «перехвата» перед объектом.При обращении к объекту он должен пройти через этот уровень перехвата. Это означает, что вы можете выполнять различные операции в этом слое перехвата. Например, вы можете обработать исходный объект на этом уровне перехвата и вернуть нужную структуру данных.
ES6 изначально предоставляет конструктор Proxy, который объясняется на MDN следующим образом: Объекты Proxy используются для определения настраиваемого поведения для основных операций (таких как поиск свойств, назначение, перечисление, вызовы функций и т. д.).
Давайте сначала посмотрим, как его использовать.
const p = new Proxy(target, handler);
-
target
: целевой объект для перехвата (может быть объект любого типа, включая собственные массивы, функции или даже другой прокси). -
handler
: Объект, который определяет поведение, которое будет перехвачено
const p = new Proxy({}, {
get(target, propKey) {
return '哈哈,你被我拦截了';
}
});
console.log(p.name);
// 哈哈,你被我拦截了
Обратите внимание, что прокси используется для управления объектами. Цель прокси — расширить возможности объекта.
см. другой пример Мы можем реализовать функцию, которая не позволяет изменять свойство имени объекта извне.
const p = new Proxy({}, {
set(target, propKey, value) {
if (propKey === 'name') {
throw new TypeError('name属性不允许修改');
}
// 不是 name 属性,直接保存
target[propKey] = value;
}
});
p.name = 'proxy';
// TypeError: name属性不允许修改
p.a = 111;
console.log(p.a); // 111
Babel используется для преобразования синтаксиса, такого как новые API (например, Array.from, Array.prototype.includes). Нам необходимо установить дополнительные пакеты для поддержки, такие какcore-js/stableиregenerator-runtime/runtime(PS: @babel/polyfill устарел после babel 7.x), а также есть некоторые API (String#normalize, Proxy, fetch и т. д.)
core-js
На данный момент полифилл не предусмотрен, подробности можно найти в официальной документации.core-js#missing-polyfills.
Proxy
Всего поддерживается 13 операций перехвата, и вы можете просмотреть их в деталях.MDN.
Как vue2.x реализует ответ данных?
Для рекурсивного обхода данных в данных используйтеObject.defineProperty()Захватите геттер и сеттер, выполните обработку сбора зависимостей данных в геттере, отслеживайте изменения данных в сеттере и уведомляйте место для подписки на текущие данные.Часть исходного кода src/core/observer/index.js#L156-L193, версия 2.6.11 выглядит следующим образом
let childOb = !shallow && observe(val)
// 对 data中的数据进行深度遍历,给对象的每个属性添加响应式
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
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter()
}
if (getter && !setter) return
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
// 新的值需要重新进行observe,保证数据响应式
childOb = !shallow && observe(newVal)
// 将数据变化通知所有的观察者
dep.notify()
}
})
Что плохого в этом?
- Добавление и удаление свойств объекта не обнаруживается: при добавлении нового свойства к объекту
newProperty
, только что добавленное свойство не имеет механизма, позволяющего vue обнаруживать обновления данных (поскольку оно добавляется после инициализации).vue.$set
Это значит, что vue знает, что вы добавили атрибуты, и он выполнит обработку за вас,$set
Внутренне также по телефонуObject.defineProperty()
иметь дело с - Изменение нижнего индекса массива не может быть отслежено, в результате чего значение массива устанавливается непосредственно через нижний индекс массива, и не может реагировать в режиме реального времени.
- Когда в данных много данных и уровень глубокий, будут проблемы с производительностью, потому что все данные в данных нужно пройти и настроить на отзывчивость.
Возьмите массив в качестве примера (PS: ответ данных в реальном времени относится к отображаемому содержимому страницы, а не к данным самого значения vm.items):
<ul id="example">
<li v-for="item in items">
{{ item }}
</li>
</ul>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
const vm = new Vue({
el: '#example',
data: {
items: ['a', 'b', 'c']
},
})
// 直接使用下标修改数据不是实时响应
setTimeout(() => {
vm.items[1] = 'x';
vm.items[3] = 'd';
console.log(vm.items);
// 此时打印结果为 ['a', 'x', 'c', 'd'],但页面内容没有更新
}, 500);
// 使用 $set 修改数据是实时响应
setTimeout(() => {
vm.$set(vm.items, 1, 'x1')
vm.$set(vm.items, 3, 'd1')
console.log(vm.items);
// 此时打印结果为 ['a', 'x1', 'c', 'd1'],页面内容更新
}, 1000);
Вы можете нажать, чтобы просмотреть код напрямуюcodepen
vue3.0 использует прокси
vue3.0 еще официально не выпущен, ноvue-nextСоответствующий код находится в открытом доступе и в настоящее время находится в альфа-версии.
Зачем использовать прокси для решения вышеуказанной проблемы? В основном потому, что Proxy — это объект перехвата, верно?对象
Для осуществления «перехвата» доступ к объекту из внешнего мира должен сначала пройти через этот слой перехвата. К какому бы атрибуту объекта ни обращались, ранее определенному или вновь добавленному, он пойдет на перехват,
Возьми простой 🌰
Используйте следующееObject.defineProperty()
иProxy
Реализуйте простой ответ данных
использоватьObject.defineProperty()
выполнить:
class Observer {
constructor(data) {
// 遍历参数data的属性,给添加到this上
for(let key of Object.keys(data)) {
if(typeof data[key] === 'object') {
data[key] = new Observer(data[key]);
}
Object.defineProperty(this, key, {
enumerable: true,
configurable: true,
get() {
console.log('你访问了' + key);
return data[key]; // 中括号法可以用变量作为属性名,而点方法不可以;
},
set(newVal) {
console.log('你设置了' + key);
console.log('新的' + key + '=' + newVal);
if(newVal === data[key]) {
return;
}
data[key] = newVal;
}
})
}
}
}
const obj = {
name: 'app',
age: '18',
a: {
b: 1,
c: 2,
},
}
const app = new Observer(obj);
app.age = 20;
console.log(app.age);
app.newPropKey = '新属性';
console.log(app.newPropKey);
Результатом выполнения приведенного выше кода является
// 修改 obj原有的属性 age的输出
你设置了age
新的age=20
你访问了age
20
// 设置新属性的输出
新属性
Видно, что добавление атрибута в объект не контролируется внутри, и вновь добавленный атрибут нужно использовать снова вручнуюObject.defineProperty()
контролировать.
Вот почемуvue 2.x
Средний Причина добавления и удаления свойств объекта не обнаружена, внутренне установлено$set
позвонивObject.defineProperty()
иметь дело с.
Ниже мы используемProxy
альтернативаObject.defineProperty()
выполнить
const obj = {
name: 'app',
age: '18',
a: {
b: 1,
c: 2,
},
}
const p = new Proxy(obj, {
get(target, propKey, receiver) {
console.log('你访问了' + propKey);
return Reflect.get(target, propKey, receiver);
},
set(target, propKey, value, receiver) {
console.log('你设置了' + propKey);
console.log('新的' + propKey + '=' + value);
Reflect.set(target, propKey, value, receiver);
}
});
p.age = '20';
console.log(p.age);
p.newPropKey = '新属性';
console.log(p.newPropKey);
p.a.d = '这是obj中a的属性';
console.log(p.a.d);
Вы можете увидеть вывод ниже
// 修改原对象的age属性
你设置了age
新的age=20
你访问了age
20
// 设置新的属性
你设置了newPropKey
新的newPropKey=新属性
你访问了newPropKey
新属性
// 给obj的a属性(是个对象)设置属性d
你访问了a
你访问了a
这是obj中a的属性
// 备注:如果对象的属性是对象,需要返回一个新的Proxy
// 稍后会补充一下, 大家也可以先自己考虑一下, 欢迎讨论
PS: Добавьте пример использования Proxy для обработки многоуровневых объектов:How to create a Deep Proxy?
Видно, что вновь добавленные свойства не нуждаются в повторном добавлении адаптивной обработки, т.к.Proxy
Это операция над объектом, пока вы получаете доступ к объекту, вы перейдете кProxy
в логике.
Reflect (представленный в ES6) — это встроенный объект, предоставляющий методы для перехвата операций JavaScript. Объект Object, очевидно, является частью внутреннего метода языка (например,
Object.defineProperty()
) вставитьReflect
на объекте. Измените возвращаемые результаты некоторых методов объекта, чтобы сделать их более разумными. Пусть операции с объектами станут функциональным поведением. Просмотр определенного контентаMDN
Другие применения прокси
За исключением предстоящегоvue 3.0
Кроме того, какие еще библиотеки используютсяProxy
Шерстяная ткань?
- dobjs/dobЭто решение переписать mobx с помощью прокси.
- immerРеализуйте неизменяемые типы данных. Подход Immer заключается в том, чтобы поддерживать состояние внутри, перехватывать все операции и внутренне оценивать, есть ли изменения, и, наконец, решать, как вернуться. Вы можете увидеть конкретное содержимое.Введение и анализ исходного кода immer.jsЭта статья.
Все используют перехват чтения и записи на объекте и делают некоторые дополнительные суждения и операции при чтении и записи.
Суммировать
-
Proxy
используется для манипулирования объектами,Object.defineProperty()
Он используется для управления свойствами объектов. -
vue2.x
использоватьObject.defineProperty()
Реализует отзывчивость данных, но из-заObject.defineProperty()
Это операция над свойствами объекта, поэтому необходимо подробно изучить объект, чтобы работать со свойствами. -
vue3.0
использоватьProxy
Это перехват объекта, независимо от того, какая операция выполняется с объектом, он перейдет к логике обработки прокси. -
vue3.0
,dobjs/dob
,immer
В настоящее время библиотека используетсяProxy
, выполните перехват чтения и записи объекта и выполните некоторую дополнительную обработку.
Ссылаться на
- Углубленные принципы реагирования
- Рендеринг списка
- Начало работы с ECMAScript 6 — прокси
- MDN-proxy
- Интервьюер: Каковы преимущества и недостатки реализации прокси с двусторонней привязкой по сравнению с определением свойства?
- Извините, изучение прокси действительно может делать все, что вы хотите
- Введение и анализ исходного кода immer.js
разное
Недавно был запущен 100-дневный расширенный план, в основном для того, чтобы углубиться в принципы, лежащие в основе каждой точки знаний. Добро пожаловать в общедоступный аккаунт WeChat «Звезда Мусимы». Давайте учиться вместе и пробиться в течение 100 дней.