Первый взгляд на одну из основных особенностей Vue3.0 — прокси!

внешний интерфейс Vue.js

предисловие

Не так давно он проходил в Торонто с 14 по 16 ноября.VueConf TO 2018Конгресс, особенно дождь, речное издание под названиемVue3.0 Updatesпрограммная речь,Vue3.0План и направление обновления проработаны (заинтересованные партнеры могут ознакомиться с полнымPPT), что указывает на то, что он был заброшенObject.defineProperty, и решил использовать более быстрый нативныйProxy !!

Это удалит предыдущийVue2.xна основеObject.definePropertyЕсть много ограничений в реализации: не могу слушатьДобавление и удаление свойств,Изменения индекса и длины массиваи может поддерживатьMap,Set,WeakMapа такжеWeakSet!

Как "инженер фронтенда" надо иметь волну АмвейProxy !!

Что такое прокси?

MDNОписывается так-ProxyОбъекты используются для определения пользовательского поведения для основных операций (таких как поиск свойств, присвоение, перечисление, вызовы функций и т. д.).

Официальные описания всегда настолько лаконичны, что не имеют смысла...

По сути, он обеспечивает перехват перед операцией целевого объекта, который может фильтровать и переписывать внешние операции, а также модифицировать поведение некоторых операций по умолчанию, так что мы можем не напрямую оперировать самим объектом, а оперировать самим объектом.прокси-объектКосвенно управлять объектом для достижения ожидаемой цели ~

Какой? Все еще не ясно? Давайте посмотрим на пример, с первого взгляда все будет понятно~

    let obj = {
    	a : 1
    }
    let proxyObj = new Proxy(obj,{
        get : function (target,prop) {
            return prop in target ? target[prop] : 0
        },
        set : function (target,prop,value) {
            target[prop] = 888;
        }
    })
    
    console.log(proxyObj.a);        // 1
    console.log(proxyObj.b);        // 0

    proxyObj.a = 666;
    console.log(proxyObj.a)         // 888

В приведенном выше примере мы определили объект заранееobj, пройти черезProxyКонструктор генерируетproxyObjобъект, и егоset(написать) иget(Читать) поведение переработано.

Когда мы получаем доступ к свойству, которое изначально существовало в объекте, оно возвращает соответствующее значение в исходном свойстве.Если мы пытаемся получить доступ к свойству, которое не существует, оно возвращает0, то есть посещаемproxyObj.aкогда исходный объектaсвойство, поэтому он возвращает1, когда мы пытаемся получить доступ к объектам, которые не существуют вbсобственность, не вернетсяundefined, но вернулся0, когда мы пытаемся установить новое значение свойства, оно всегда возвращает888, так что даже если у нас естьproxyObj.aназначить как666, но не подействует, все равно вернется888!

грамматика

ES6изначально предоставленныйProxyСинтаксис очень прост, использование выглядит следующим образом:

let proxy = new Proxy(target, handler);

параметрtargetиспользуетсяProxyОбернутый целевой объект (может быть объект любого типа, включая собственные массивы, функции или даже другой прокси), параметрыhandlerЭто также объект, свойствами которого являются функции, определяющие поведение агента при выполнении операции, то есть пользовательское поведение.

ProxyОсновное использование такое же, как указано выше, разницаhandlerразные предметы,handlerможет быть пустым объектом{}, это означает, чтоproxyОперация над целевым объектомtargetоперации, то есть:

    let obj = {}
    
    let proxyObj = new Proxy(obj,{})
    
    proxyObj.a = 1;
    proxyObj.fn = function () {
        console.log('it is a function')
    }

    console.log(proxyObj.a); // 1
    console.log(obj.a);      // 1
    console.log(obj.fn())    // it is a function

Но следует отметить, что,handler не можемУстановить какnull, выдаст ошибку --Cannot create proxy with a non-object as target or handler!

хочуProxyЭто работает, мы не можем оперировать объектом исходного объекта, то есть целевым объектомtarget(Приведенный выше примерobjobject ), должен быть нацелен наProxyпример (приведенный выше примерproxyObjобъект) для работы, иначе ожидаемый эффект не будет достигнут.Согласно примеру в начале, мы установилиgetметод после попытки продолжить с исходного объектаobjчитать несуществующее свойство вb, результат все равно возвращаетсяundefined:

    console.log(proxyObj.b);     // 0
    console.log(obj.b);         // undefined

Для операций, которые могут быть установлены, но не перехвачены,proxyРезультат обработки объекта также будет воздействовать на исходный целевой объект.targetНа, как ты это понимаешь? Или возьмите пример в начале, мы переопределяемsetметод, возвращаются все настройки свойств888, не обладает никакими особыми свойствами (имеется в видуobjизaattribute ) для специального перехвата или обработки, затем передатьproxyObj.a = 666Результат операции также будет воздействовать на исходный целевой объект (objобъект), поэтомуobjобъектaЗначение также станет888 !

    proxyObj.a = 666;
    console.log( proxyObj.a);   // 888
    console.log( obj.a);        // 888

API

ES6серединаProxyВ настоящее время предусмотрено 13 видов прокси-операций, ниже приведены некоторые из наиболее часто используемых.apiСделайте некоторую индукцию и сортировку, студенты, которые хотят знать другие методы, могут пойти на официальный сайт, чтобы проверить:

--handler.get(target,property,receiver)

Используется для перехвата операции чтения свойства объекта,targetявляется целевым объектом,propertyПриобретенное имя атрибута,receiverдаProxyили унаследоватьProxyпредмет, как правилоProxyпример.

let proxy = new Proxy({},{
    get : function (target,prop) {
        console.log(`get ${prop}`);
        return 10;
    }
})
    
console.log(proxy.a)    // get a
                        // 10

Перехватываем чтение пустого объектаgetОперация, при получении своих внутренних свойств выведетget ${prop}, и возвращает 10 ;

let proxy = new Proxy({},{
    get : function (target,prop,receiver) {
            return receiver;
        }
    })

console.log(proxy.a)    // Proxy{}
console.log(proxy.a === proxy)  //true

вышесказанноеproxyобъектaсвойства сделаныproxyобъект предоставлен, поэтомуreceiverнаправлениеproxyобъект, поэтомуproxy.a === proxyто, что возвращаетсяtrue.

Следует отметить, что если целевое свойство, к которому осуществляется доступ, недоступно для записи или настройки, возвращаемое значение должно совпадать со значением целевого свойства, то есть оно не может быть изменено, иначе будет выдано исключение~

let obj = {};
Object.defineProperty(obj, "a", {
	configurable: false,
	enumerable: false,
	value: 10,
	writable: false
});

let proxy = new Proxy(obj,{
    get : function (target,prop) {
        return 20;
    }
})

console.log(proxy.a)    // Uncaught TypeError

вышесказанноеobjв объектеaСвойства недоступны для записи или настройки, мы передаемProxyсоздалproxyэкземпляр и перехватывает егоgetоперация, когда мы выводимproxy.aбудет генерировать исключение, в этот момент, если мы поместимgetКогда возвращаемое значение метода изменяется так, чтобы оно совпадало со значением целевого свойства, то есть 10 , Вы можете удалить исключение~

--handler.set(target, property, value, receiver)

Используется для перехвата операции установки значения свойства, параметры находятся вgetПо сравнению с методом еще одинvalue, то есть значение свойства, которое нужно установить~

существуетстрогий режимВниз,setМетод должен возвращать логическое значение, возвращатьtrueЭто означает, что на этот раз установка свойства прошла успешно.falseи операция установки свойства завершается ошибкой и выдает ошибкуTypeError.

let proxy = new Proxy({},{
    set : function (target,prop,value) {
        if( prop === 'count' ){
            if( typeof value === 'number'){
                console.log('success')
            	target[prop] = value;
            }else{
            	throw new Error('The variable is not an integer')
            }
        }
    }
})
    
 proxy.count = '10';    // The variable is not an integer
 
 proxy.count = 10;      // success

Мы модифицируем вышеsetметод на целевом объектеcountНазначение атрибутов ограничено, мы требуемcountНазначение имущества должно бытьnumberтип данных, если нет, вернуть ошибкуThe variable is not an integer, мы первыеcountстрока назначения'10', выдает исключение, второе присвоение - это число10, печатает успешно, поэтому мы можем использоватьsetметод для проверки данных!

Точно так же, если целевое свойство недоступно для записи и настройки, его значение не может быть изменено, то есть присвоение является недопустимым, как показано ниже:

let obj = {};
Object.defineProperty(obj, "count", {
    configurable: false,
    enumerable: false,
    value: 10,
    writable: false
});

let proxy = new Proxy(obj,{
    set : function (target,prop,value) {
        target[prop] = 20;
    }
})

proxy.count = 20 ;
console.log(proxy.count)   // 10

вышесказанноеobjв объектеcountсвойство, мы устанавливаем его как неизменяемое, и значение по умолчанию, которое мы даем как10, то даже если он задан как20, результат не изменился!

--handler.apply(target, thisArg, argumentsList)

Используется для перехвата вызовов функций, есть три параметра, которые являются целевым объектом (функцией)target, объект контекста при вызовеthisArgи массив аргументов при вызовеargumentsList, метод может возвращать любое значение.

targetДолжен быть функциональным объектом, иначе выдаетTypeError;

function sum(a, b) {
	return a + b;
}

const handler = {
    apply: function(target, thisArg, argumentsList) {
    	console.log(`Calculate sum: ${argumentsList}`); 
    	return target(argumentsList[0], argumentsList[1]) * 2;
    }
};

let proxy = new Proxy(sum, handler);

console.log(sum(1, 2));     // 3
console.log(proxy(1, 2));   // Calculate sum:1,2
                            // 6

Фактически,applyтакже перехватит цельFunction.prototype.apply()а такжеFunction.prototype.call(),так же какReflect.apply()операции следующим образом:

console.log(proxy.call(null, 3, 4));    // Calculate sum:3,4
                                        // 14

console.log(Reflect.apply(proxy, null, [5, 6]));    // Calculate sum: 5,6
                                                    // 22

--handler.construct(target, argumentsList, newTarget)

constructдля перехватаnewоператора, чтобы сделатьnewОператор генерируетProxyПрименительно к объекту целевой объект, используемый для инициализации прокси, сам должен иметь[[Construct]]Внутренний метод; он принимает три параметра, целевой объектtarget, список параметров конструктораargumentsListи когда объект изначально создается,newКонструктор действия команды, то есть в следующем примереp.

let p = new Proxy(function() {}, {
    construct: function(target, argumentsList, newTarget) {
    	console.log(newTarget === p );                          // true
    	console.log('called: ' + argumentsList.join(', '));     // called:1,2
    	return { value: ( argumentsList[0] + argumentsList[1] )* 10 };
    }
});

console.log(new p(1,2).value);      // 30

Также метод должен возвращать объект, иначе будет выброшено исключение!

var p = new Proxy(function() {}, {
    construct: function(target, argumentsList, newTarget) {
    	return 2
    }
});

console.log(new p(1,2));    // Uncaught TypeError

--handler.has(target,prop)

hasметод можно рассматривать как таргетингinХук операции. Когда мы решаем, имеет ли объект определенный атрибут, этот метод вступает в силу. Типичная операцияin, метод получает два параметра target objecttargetи свойство для проверкиpropи возвращаетbooleanстоимость.

let p = new Proxy({}, {
    has: function(target, prop) {
    	if( prop[0] === '_' ) {
    		console.log('it is a private property')
    		return false;
    	}
    	return true;
    }
});

console.log('a' in p);      // true
console.log('_a' in p )     // it is a private property
                            // false

В приведенном выше примере мы используемhasМетод скрывает подчеркивание свойства_Частная собственность в начале, так что она будет возвращена при судействеfalse, чтобы не былоinоператор найден ~

Следует отметить, что если свойство самого целевого объекта не может быть настроено, свойство не может быть скрыто прокси.Если целевой объект является нерасширяемым объектом, свойство объекта не может быть скрыто прокси, иначе оно будет бросатьTypeError.

let obj = { a : 1 };

Object.preventExtensions(obj); // 让一个对象变的不可扩展,也就是永远不能再添加新的属性

let p = new Proxy(obj, {
	has: function(target, prop) {
		return false;
	}
});

console.log('a' in p); // TypeError is thrown

привязка данных

Так много было введено выше, и это правильноProxyЕще одно предварительное понимание, тогда мы можем использоватьProxyВручную реализовать двустороннюю привязку предельно простых данных (Object.defineProperty()Метод реализации может обратиться ко мнеПредыдущая статьяВ конце упоминается)~

В основном это зависит от реализации функции, поэтому я просто махнул макетом~

Структура страницы следующая:

<!--html-->
<div id="app">
    <h3 id="paragraph"></h3>
    <input type="text" id="input"/>
</div>

В основном зависит от логической части:

//获取段落的节点
const paragraph = document.getElementById('paragraph');
//获取输入框节点
const input = document.getElementById('input');
    
//需要代理的数据对象
const data = {
	text: 'hello world'
}

const handler = {
	//监控 data 中的 text 属性变化
	set: function (target, prop, value) {
    	if ( prop === 'text' ) {
                //更新值
                target[prop] = value;
                //更新视图
                paragraph.innerHTML = value;
                input.value = value;
                return true;
    	} else {
    		return false;
    	}
	}
}

//添加input监听事件
input.addEventListener('input', function (e) {
    myText.text = e.target.value;   //更新 myText 的值
}, false)

//构造 proxy 对象
const myText = new Proxy(data,handler);

//初始化值
myText.text = data.text;    

выше мы проходимProxyсозданныйmyTextнапример, путем перехватаmyTextсерединаtextАтрибутыsetметод, чтобы обновить изменения представления и реализовать очень простую двустороннюю привязку данных ~

Суммировать

сказав так много,ProxyЭто, наконец, начинается. Хотя его синтаксис очень прост, на самом деле не так просто воспроизвести его значение, плюс его собственноеProxyНа мой взгляд, его можно использовать для вторичной обработки данных и проверки легальности данных, его даже можно использовать как прокси для функций, а для разработки вас ждут более полезные значения~

Помимо,Vue3.0Все готово к выпуску, вы не планируете дать ему поучиться?

Ну давай же!