Javascript.RU

Система сборки и зависимостей Google Closure Library

В этой статье мы рассмотрим систему сборки и зависимостей Google Closure Library.

А также увидим, как применить ее к своему коду, даже если вместо Google Closure Library используется совсем другой фреймворк.

Система зависимостей Google Closure Library имеет два основных декларативных метода: goog.provide и goog.require.

В javascript-файлах при помощи goog.provide указывается, какие символы предоставляет этот файл.

Символ - это имя javascript-объекта, который тут же можно использовать. Например:

goog.provide('my.super.dom')
my.super.dom.createElement = function() { ... }

Как видите, здесь мы декларируем, что файл объявляет символ my.super.dom, и goog.provide тут же создает пустой объект, используя для этого код, наподобие следующего:

my = window.my || {}
my.super = my.super || {}
my.super.dom = my.super.dom || {}

Так что существующие объекты и свойства не перезаписываются.

При помощи goog.require указывается, какие символы нужны. Указывать require принято после provide для лучшей читабельности кода.

Например:

goog.provide('goog.Delay');
goog.provide('goog.async.Delay');

goog.require('goog.Disposable');
goog.require('goog.Timer');

Функция goog.require лишь декларирует требование объекта и проверяет его. Она также может подгрузить нужный файл, но только если найдет его во внутренней структуре зависимостей.

Для добавления информации о зависимостях используется функция goog.addDependency.

Эта функция для каждого файла перечисляет список символов, которые он дает (provide) и которые требует (require).

Например:

goog.addDependency('/tmp/my/bye.js', ['my.bye'], ['my.main']);

Этот вызов добавляет в структуру зависимостей информацию, что файл bye.js предоставляет символ my.bye и требует my.main.

После такого вызова можно смело использовать goog.require('my.bye') - библиотека подгрузит файл /tmp/my/bye.js.

C другой стороны, если информация не была добавлена при помощи goog.addDependency - вызов goog.require('my.bye') завершится с ошибкой.

Для символов, объявленных в Google Closure Library, зависимости хранятся в корневой директории библиотеки, в файле deps.js.

Для ваших собственных символов и файлов вы должны либо прописать зависимости goog.addDependency самостоятельно, либо использовать для этого скрипт calcdeps.py (см. далее).

Для автоматической сборки и генерации файла зависимостей из ваших модулей используется скрипт calcdeps.py, поставляемый вместе с библиотекой.

Для его запуска под Windows можно использовать ActivePython, под Linux/MacOS - обычный python.

Эта утилита получает три основные опции:

-i file.js
входной javascript-файл, можно указать несколько
-p path/to/dir
список путей
-o script/deps/list/compiled
тип вывода

Например:

calcdeps.py -i hello.js -p /tmp/my -p /tmp/gc/closure-library -o script

В нашем примере /tmp/my - место, где хранятся файлы main.js, bye.js, hello.js, а /tmp/gc/closure-library - каталог с google closure library, которая взята из SVN.

Скрипт calcdeps.py делает следующие простые шаги:

  1. Рекурсивно проходит по всем директориям из списка -p, находит все файлы с расширением .js
  2. Для каждого файла - регулярными выражениями выцепляет списки, что ему надо (goog.require) и что он дает (goog.provide).
  3. В зависимости от типа вывода:
    deps
    Выводит набор директив goog.addDependency.
    В нашем случае это будет длинная простыня:
// This file was autogenerated by calcdeps.py
goog.addDependency('/tmp/my/bye.js', ['my.bye'], ['my.main']);
goog.addDependency('/tmp/my/hello.js', ['my.hello'], ['my.main']);
goog.addDependency('/tmp/my/main.js', ['my.main'], []);
goog.addDependency('/tmp/gc/closure-library/alltests.js', [], []);
goog.addDependency('/tmp/gc/closure-library/closure/goog/base.js', [], []);
goog.addDependency('/tmp/gc/closure-library/closure/goog/deps.js', [], []);
goog.addDependency('/tmp/gc/closure-library/closure/goog/array/array.js', ['goog.array'], []);
// ...

Первые несколько зависимостей взяты из файлов bye.js, main.js, hello.js, а остальные - построены из файлов Google Closure Library.

Свой deps.js в Closure Library

Если вы используете Google Closure Library, то можете загружать новый файл в качестве deps.js вместо стандартного.
Для этого нужно установить флаг:

// до загрузки base.js
goog.global.CLOSURE_NO_DEPS=true

(base.js перестанет подгружать стандартный deps.js) и загружать deps.js через отдельный тег <script>, либо формировать список зависимостей goog.addDependency каким-либо другим путем.

list
выводит список нужных файлов в порядке зависимостей.
В нашем случае:
/tmp/gc/closure-library/closure/goog/base.js
/tmp/my/main.js
hello.js

Других файлов для сборки не нужно.

script
собирает все входные файлы (-i) и требуемые для них в один файл.
В нашем случае это будет javascript-файл следующего вида:
// Input 0
содержание файла closure-library/closure/goog/base.js

// Input 1
содержание файла main.js

// Input 2 
содержание файла hello.js
compiled
Компилирует файл, полученный в результате сборки. При использовании этой опции обязательно указание флага --compiler_jar с путем до jar-файла с компилятором.

Кроме того, как правило, указываются и флаги компиляции. Чтобы вывести откомпилированный файл в o.js, вызовем calcdeps.py вот так:

calcdeps.py -i hello.js -p /tmp/my -p /tmp/gc/closure-library -o compiled \
--compiler_jar compiler.jar --compiler_flags "--js_output_file o.js"

Кавычки для передачи флагов компилятора должны быть двойными.

Обратите внимание - в результате компиляции не будет директив goog.provide/require. Это потому, что компилятор использует специальный проход для проверки зависимостей и удаления этих директив из итогового файла.

С другой стороны, после компиляции с обычным уровнем оптимизации в файле остается много лишнего. Если добавить экспорт какой-нибудь функции и указать флаг "--js_output_file o.js --compilation_level ADVANCED_OPTIMIZATIONS", то почти вся base.js исчезнет. Таким образом, overhead от использования системы сборки google будет сведен к минимуму.

Вообще, никто не заставляет использовать Google Closure Library. Для использования системы зависимости хватит base.js.

Так как calcdeps.py основан на регэкспах, условные директивы provide/require не поддерживаются. Утилита выхватит из файла все provide/require без разбора блока и местоположения в файле.

У функции goog.require есть один недостаток. Если встроенные зависимости Google Closure Library она загружает автоматически, то для собственных символов такой возможности не предусмотрено: нужно для каждого символа/файла прописывать goog.addDependency.

Чтобы упростить процесс, мы немного расширим goog.require, добавив автоматический подхват символов.

Исходный вариант:

goog.require = function(rule) {

  // if the object already exists we do not need do do anything
  // TODO: ...
  if (!COMPILED) {
    // если объект уже есть - возврат
    if (goog.getObjectByName(rule)) {
      return;
    }
    // получить путь к файлу с символом rule 
    var path = goog.getPathFromDeps_(rule);
    if (path) {
      // _writeScripts пишет <script> только 1 раз для каждого файла
      goog.included_[path] = true;
      goog.writeScripts_();
    } else {
      // файл не нашли
      var errorMessage = 'goog.require could not find: ' + rule;
      if (goog.global.console) {
        goog.global.console['error'](errorMessage);
      }

        throw Error(errorMessage);
    }
  }
};

Введем правило - символ a.b.c находится в файле a/b/c.js, и модифицируем функцию goog.require, чтобы она при отсутствии зависимости в списке загружала файл по этому правилу.

А чтобы было еще удобнее - добавим префиксы, которые будут указывать путь к каждому пространству имен. Например:

goog.namespacePrefixes = {
  'my': '/js/my'
}

Это будет означать, что goog.require('my.file') загрузит символ из /js/my/file.js.

Вот версия goog.require с соответствующими модификациями:

goog.require = function(rule) {
  if (!COMPILED) {
    if (goog.getObjectByName(rule)) {
      return;
    }
    var path = goog.getPathFromDeps_(rule);
    if (path) {
      goog.included_[path] = true;
    } else {
      path = rule.replace(/\./g,'/')+'.js'
      for(var prefix in goog.namespacePrefixes) {
       path = path.replace(new RegExp("^"+prefix), goog.namespacePrefixes[prefix])
      }
      goog.included_[path] = true;
      setTimeout(function() {
        if (goog.getObjectByName(rule)) {
          return
        }
        var errorMessage = 'goog.require could not find: ' + rule;
        if (goog.global.console) {
          goog.global.console['error'](errorMessage);
        }
        throw Error(errorMessage);
      }, 0)
    }
    goog.writeScripts_();
    
  }
};

В функции выше также оставлен контроль ошибок. В отличие от простого require, мы можем узнать, появился ли символ, только после загрузки файла, поэтому проверка обернута в setTimeout(..., 0). Такой вызов сработает сразу после синхронной загрузки script.

Вы можете пропатчить base.js или, что гораздо лучше, загрузить свой файл с новым require и префиксами, который перезапишет определение в base.js.

После этого goog.require начнет подгружать зависимости аналогично системе зависимостей в dojo toolkit и других фреймворках.

Можно расширить эту функциональность, добавить подгрузку вида goog.require('my.*'), более сложную логику нахождения файла по символу и т.п., но нужно ли?

При сборке нескольких модулей Google Closure Compiler проверяет зависимости require/provide, но при этом многомодульные сборки можно делать и без них, как описано в соответствующей статье.

Для удобства примеры в этой статье собраны в архив gbuild.zip.

Он содержит модифицированный require и иллюстрирует, как использовать систему сборки Google, даже если вы не используете Google Closure Library.

Содержимое:

goog 
  директория с base.js из Google Closure Library
my
  директория с нашими js-файлами, base_require.js содержит модифицированный require 
calcdeps.py
  стандартный calcdeps.py из Google Closure Library
compiler.jar
  стандартный Google Closure Compiler
compile.bat
   файл для компиляции hello.js с ADVANCED оптимизациями и зависимостями
hello.html
  файл-пример, использующий hello.js

Автор: Andrew Kulinich (не зарегистрирован), дата: 25 ноября, 2009 - 10:48
#permalink

Кто-то разбирался как подцепить Google Closure Compiler к Ant'у?


Автор: prop (не зарегистрирован), дата: 25 ноября, 2009 - 12:29
#permalink

Автор: Timofey (не зарегистрирован), дата: 30 ноября, 2009 - 20:24
#permalink

Статья просто супер. Буду рекомендовать ее всем своим знакомым. Спасибо!!!


Автор: Baiblyartelay (не зарегистрирован), дата: 5 февраля, 2011 - 13:24
#permalink

да, наверно так и есть


Автор: zenitchik (не зарегистрирован), дата: 17 января, 2013 - 19:43
#permalink

Юзаю для этого steal из JavaScriptMVC.
Ихний сборщик нуждается в допиливании, но на выходе - удобная штука.


Автор: yifitvaz, дата: 2 апреля, 2019 - 06:32
#permalink

I felt very happy while reading this site. This was a really very informative site for me. I really liked it. This was really a cordial post. Thanks a lot!
tank trouble


Автор: mapquest driving directions (не зарегистрирован), дата: 16 октября, 2019 - 10:42
#permalink

The article is very easy to understand, detailed and meticulous! I had a lot of harvest after watching this article from you! I find it interesting, your article gave me a new perspective! I have read many other articles on the same topic, but your article convinced me! mapquest driving directions


Автор: osama shk (не зарегистрирован), дата: 27 января, 2020 - 18:44
#permalink

I just couldn't leave your website before telling you that I truly enjoyed the top quality info you present to your visitors? Will be back again frequently to check up on new posts.
rico chandra kusuma


Автор: osama shk (не зарегистрирован), дата: 30 января, 2020 - 16:40
#permalink

i read a lot of stuff and i found that the way of writing to clearifing that exactly want to say was very good so i am impressed and ilike to come again in future..
divorce lawyers colorado springs


Автор: osama shk (не зарегистрирован), дата: 2 февраля, 2020 - 14:27
#permalink

I am happy to find your distinguished way of writing the post. Now you make it easy for me to understand and implement the concept. Thank you for the post.
read the article


Автор: osama shk (не зарегистрирован), дата: 12 февраля, 2020 - 16:31
#permalink

i read a lot of stuff and i found that the way of writing to clearifing that exactly want to say was very good so i am impressed and ilike to come again in future..
https://sportstv.io/en/watch-live/all-sports/upcoming


Автор: Гость (не зарегистрирован), дата: 24 февраля, 2020 - 16:17
#permalink

I'm glad I found this web site, I couldn't find any knowledge on this matter prior to.Also operate a site and if you are ever interested in doing some visitor writing for me if possible feel free to let me know, i am always look for people to check out my web site.
death cleanup


Автор: mirieladera (не зарегистрирован), дата: 26 февраля, 2020 - 18:40
#permalink

Wonderful article, thanks for putting this together! This is obviously one great post. Thanks for the valuable information and insights you have so provided here.
salehoo review


Автор: osama shk (не зарегистрирован), дата: 26 февраля, 2020 - 20:13
#permalink

This article gives the light in which we can observe the reality. This is very nice one and gives indepth information. Thanks for this nice article.
psicoterapia en Madrid para adolescentes


Автор: happy wheels (не зарегистрирован), дата: 27 февраля, 2020 - 07:04
#permalink

The information is very special, I will have to follow you, the information you bring is very real, reflecting correctly and objectively, it is very useful for society to grow together


Автор: osama shk (не зарегистрирован), дата: 4 марта, 2020 - 18:36
#permalink

Very nice article, I enjoyed reading your post, very nice share, I want to twit this to my followers. Thanks!.
Webtalk app


Автор: osama shk (не зарегистрирован), дата: 7 марта, 2020 - 10:12
#permalink

i read a lot of stuff and i found that the way of writing to clearifing that exactly want to say was very good so i am impressed and ilike to come again in future..
結婚指輪 福岡


Автор: osama shk (не зарегистрирован), дата: 16 марта, 2020 - 13:03
#permalink

Nice to be visiting your blog once more, it has been months for me. Well this article that ive been waited for therefore long. i want this article to finish my assignment within the faculty, and it has same topic together with your article. Thanks, nice share.
fashion jewelry


Автор: osama shk (не зарегистрирован), дата: 19 марта, 2020 - 14:01
#permalink

Awesome and interesting article. Great things you've always shared with us. Thanks. Just continue composing this kind of post.
結婚指輪 猫


Автор: osama shk (не зарегистрирован), дата: 22 марта, 2020 - 20:26
#permalink

Thanks for another wonderful post. Where else could anybody get that type of info in such an ideal way of writing?
ハワイアンジュエリー


Автор: osama shk (не зарегистрирован), дата: 23 марта, 2020 - 22:54
#permalink

Great post, you have pointed out some fantastic points , I likewise think this s a very wonderful website.
結婚指輪 福岡


Автор: osama shk (не зарегистрирован), дата: 1 апреля, 2020 - 23:44
#permalink

That is the excellent mindset, nonetheless is just not help to make every sence whatsoever preaching about that mather. Virtually any method many thanks in addition to i had endeavor to promote your own article in to delicius nevertheless it is apparently a dilemma using your information sites can you please recheck the idea. thanks once more.
ivo ignatov


Автор: golu (не зарегистрирован), дата: 14 апреля, 2020 - 07:08
#permalink

It is wonderful to be here with everyone, I have a lot of knowledge from what you share, to say thank you, the information and knowledge here helps me a lot run 3


Автор: osama shk (не зарегистрирован), дата: 1 мая, 2020 - 21:24
#permalink

I would like to say that Elmhurst dentist blog really convinced me to do it! Thanks, very good post.
post free ads


Автор: uli (не зарегистрирован), дата: 14 июля, 2020 - 11:23
#permalink

This is a great thing, I think everyone feels this information is very valuable, thank you happy wheels


Отправить комментарий

Приветствуются комментарии:
  • Полезные.
  • Дополняющие прочитанное.
  • Вопросы по прочитанному. Именно по прочитанному, чтобы ответ на него помог другим разобраться в предмете статьи. Другие вопросы могут быть удалены.
    Для остальных вопросов и обсуждений есть форум.
P.S. Лучшее "спасибо" - не комментарий, как все здорово, а рекомендация или ссылка на статью.
Содержание этого поля является приватным и не предназначено к показу.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Разрешены HTML-таги: <strike> <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <u> <i> <b> <pre> <img> <abbr> <blockquote> <h1> <h2> <h3> <h4> <h5> <p> <div> <span> <sub> <sup>
  • Строки и параграфы переносятся автоматически.
  • Текстовые смайлы будут заменены на графические.

Подробнее о форматировании

CAPTCHA
Антиспам
8 + 3 =
Введите результат. Например, для 1+3, введите 4.
 
Текущий раздел
Поиск по сайту
Содержание

Учебник javascript

Основные элементы языка

Сундучок с инструментами

Интерфейсы

Все об AJAX

Оптимизация

Разное

Дерево всех статей

Последние комментарии
Последние темы на форуме
Forum