Интеграция с Google Closure Library
Google Closure Compiler содержит ряд специальных возможностей для интеграции с Google Closure Library.
Здесь важны две вещи.
- Для их использования возможно использовать минимум от Google Closure Library. Это значит - всего несколько лишних строчек кода от всей библиотеки.
- Можно расширить компилятор аналогичным образом для интеграции с другими инструментами и фреймворками.
Интеграция подключается флагом --process_closure_primitives , который по умолчанию установлен в true . То есть, она включена по умолчанию.
Этот флаг запускает специальный проход компилятора, описанный классом ProcessClosurePrimitives и подключает дополнительную проверку типов ClosureReverseAbstractInterpreter .
Мы рассмотрим все действия, которые при этом происходят, а также некоторые опции, которые безопасным образом используют символы Google Closure Library без объявления флага.
Следующие действия описаны в классе ProcessClosurePrimitives .
В Google Closure Library есть переменная:
/**
* @define {boolean} ...
*/
var COMPILED = false;
Проход ProcessClosurePrimitives переопределяет ее в true и использует это при оптимизациях, удаляя ветки кода, не предназначены для запуска на production.
Такие функции существуют, например, в ядре Google Closure Library. К ним в первую очередь относятся вызовы, предназначенные для сборки и проверки зависимостей. Они содержат код, обрамленный проверкой COMPILED , например:
goog.require = function(rule) {
// ...
if (!COMPILED) {
// основное тело функции
}
}
Аналогично может поступить и любой скрипт, даже без использования Google Closure Library:
Пример: my.js
/** @define {boolean} */
var COMPILED = false
Framework = { }
Framework.sayCompiled = function() {
if (!COMPILED) {
alert("Not compressed")
} else {
alert("Compressed")
}
}
После компиляции в продвинутом режиме:
Framework = {};
Framework.sayCompiled = Framework.a = function() {
alert("Compressed")
};
Компилятор переопределил COMPILED в true и произвел соответствующие оптимизации.
В Google Closure Compiler есть внутренняя опция locale
Эта опция переопределяет переменную goog.LOCALE на установленную при компиляции.
Для использования опции locale , на момент написания статьи, ее нужно задать в Java коде компилятора, т.к. соответствующего флага нет.
Как и COMPILED , константу goog.LOCALE можно и использовать в своем коде без библиотеки Google Closure Library.
Директивы goog.provide , goog.require , goog.addDependency обрабатываются особым образом.
Все зависимости проверяются, а сами директивы проверки - удаляются из сжатого файла.
Экспорты, заданные при помощи goog.exportSymbol , записываются во вспомогательный список, чтобы избежать перезаписи.
Если подробнее, то код goog.exportSymbol('a',myVar) защитит символ a от перезаписи: никакая другая переменная не будет переименована в a .
Google Closure Library умеет преобразовывать классы CSS на более короткие по заданному списку.
Например, следующая функция задает такой список.
goog.setCssNameMapping({
"goog-menu": "a",
"goog-menu-disabled": "a-b",
"CSS_LOGO": "b",
"hidden": "c"
});
Тогда следующий вызов преобразуется в "a a-b":
goog.getCssName('goog-menu') + ' ' + goog.getCssName('goog-menu', 'disabled')
Google Closure Compiler производит соответствующие преобразования в сжатом файле и удаляет вызов setCssNameMapping из кода.
На мой взгляд, это делается больше из соображений обфускации, нежели оптимизации.
При объявлении внутренней опции externExportsPath , содержащей путь к файлу, в этот файл будут записаны все экспорты, описанные через goog.exportSymbol /goog.exportProperty .
В дальнейшем этот файл может быть использован как список экстернов для компиляции.
Эта опция может быть полезна для создания внешних библиотек, распространяемых со списком экстернов.
Соответствующий проход компилятора описан в классе ExternExportsPass .
В Google Closure Library есть ряд функций для проверки типов. Например: goog.isArray , goog.isString , goog.isNumber , goog.isDef и т.п.
Компилятор использует их для проверки типов.
Например:
function removeNode(id) {
var node
if (goog.isNumber(id)) {
node = document.getElementById(id) // (!)
}
node.parentNode.removeChild(node)
}
document.onclick = function() { removeNode(123) }
При компиляции с опцией --check_types такой код приведет к предупреждению:
file.js:4: WARNING - actual parameter 1 of Document.prototype.getElementById does not match formal parameter
found : number
required: string
node = document.getElementById(id)
Компилятор увидел вызов goog.isNumber и вывел из этого тип переменной number внутри блока if .
Функция getElementById описана во внутреннем списке стандартных экстернов и принимает строку, а ей передали число id . Поэтому компилятор генерирует предупреждение.
В реальных скриптах обычно используются строковые префиксы, поэтому будет код такого вида:
...
if (goog.isNumber(id)) {
id = 'node-'+id // (ok)
node = document.getElementById(id)
}
...
При этом компилятор, увидев присвоение id='node'+id , обновит тип id в string и никакого предупреждения сгенерировано не будет. Если, конечно, вы используете id там, где допустим именно тип string .
Эта логика описана в классе ClosureReverseAbstractInterpreter .
Названия функций, определяющих типы, жестко прописаны в Java-коде, поменять их на свои без модификации исходников нельзя.
Для этого в Google Closure Compiler есть внутренняя опция generateExports .
Эта недокументированная опция добавляет проход компилятора, описанный классом GenerateExports .
Он читает аннотации @export и создает из них экспортирующие вызовы goog.exportSymbol/exportProperty . Название экспортирующих функций находится в классе соглашений кодирования, каким по умолчанию является GoogleCodingConvention .
Например:
/** @export */
function Widget() {
}
/** @export */
Widget.prototype.hide = function() {
this.elem.style.display = 'none'
}
После компиляции с продвинутом режиме:
function a() {
}
goog.d("Widget", a);
a.prototype.a = function() {
this.b.style.display = "none"
};
goog.c(a.prototype, "hide", a.prototype.a);
Свойства благополучно экспортированы. Удобно.
Google Closure Compiler содержит дополнительные фичи, облегчающие интеграцию с Google Closure Library. Некоторые из них весьма полезны.
При этом никто не заставляет вас использовать библиотеку Google целиком - интеграция завязана лишь на конкретные описанные возможности библиотеки.
При обработке символов компилятор не смотрит, подключена ли библиотека, он находит обрабатывает их просто по именам. Поэтому вы можете использовать свою реализацию соответствующих функций, или самостоятельно объявить символ, как в примере с COMPILED .
Для использования некоторых внутренних опций, их необходимо определить внутри компилятора. Различные опции описаны в статьях раздела Google Closure Compiler в деталях.
|
спасибо за интересную информацию
Отправить комментарий
Приветствуются комментарии:Для остальных вопросов и обсуждений есть форум.