Pull to refresh

Создание сложных приложений в ExtJS.

Reading time 8 min
Views 4.8K
Original author: Jozef Sakalos
Автор: Jozef Sakalos, aka Saki
Статья в оригинале: Создание сложного приложение в Ext на blog.extjs.eu

Предисловие


Я решил написать эту статью для тех пользователей Ext 2.x, которые уже переросли одну единственную HTML-страницу со встроенными скриптом, создающим простое окно или форму, для тех, которые уже решили, что Ext — это их путь и для тех, которые превозмогая трудности связанные с большими объемами кода, понимают, что нуждаются в его структурировании.
Сколько людей, столько и мнений. И поэтому способ, который я опишу ниже, не является одним единственным возможным. Также хотелось бы отметить, что не каждое приложение, написанное с применением этого подхода, является гарантированно хорошим. Ничего подобного.
Замечу, что описываемый подход является работоспособным, четко структурированным, без труда поддерживаемым и, одним словом: рабочим!

Что значит сложное приложение?


Когда вы оперируете объектом Viewport с BorderLayout, grid-таблицей и формой в одном файле, это, конечно же, не сложное приложение, правда? Если же у вас десятки окон, в каждом из которых grid-таблицы, формы или BorderLayout и все это добро раскидано по десяткам файлов, то это уже сложное и большое приложение, верно?
В немецком языке имеется одно милое словечко: Jein = Ja + Nein
Ответом на оба приведенных утверждения будет Jein. Вопрос заключается в следующем: когда приложение становится большим и сложным? Ответ прост: в тот самый момент как вы начинаете его таковым ощущать. Это момент когда становится сложно ориентироваться в большом количестве файлов или начинаются проблемы при поиске определенного места в случае попытки понять отношение компонентов, например, и так далее.
Можно с уверенностью говорить, что каждое приложение сложно столь же насколько простое достойно быть хорошо написанным и может стать по-настоящему большим, как только мы начнем добавлять новый функционал, наращивать объем кода, добавлять правила CSS и т.д.
Лучшей и самой безопасной установкой при создании нового приложения является — «Я начинаю сложное приложение!»

Файлы и директории


То, что необходимо организовать в первую очередь. В нашем распоряжении всегда имеется каталог DocumentRoot так что все подкаталоги, затронутые ниже, будут приводиться относительно него.
Рекомендуемая структура каталогов:
  • ./css (опциональная link)
  • ./ext (ссылка)
  • ./img (ссылка)
  • ./js
  • index.html

Слово «ссылка» означает, что каталог является мягкой ссылкой на реальный каталог, где лежат файлы. Преимущество этого приема в том, что, например, вы можете загрузить новую версию Ext в любой реальный каталог и заставить ссылку указывать на него, что избавит от редактирования путей в вашем коде. Можно проводить тестирование новой версии и если все в порядке, оставить ссылку указывать на новую версию, а если нет, то просто вернуть ее назад.

  • css — хранилище всех ваших стилей. Если у вас имеются глобальные стили, вроде цветов компании или оформления шрифтов, то вы также можете создать этот каталог как ссылку;
  • ext — ссылка на используемую вами версию Ext, как это было описано ранее;
  • img — каталог с вашими изображениями. Также может содержать подкаталоги для иконок и т.п.;
  • js — будет содержать все JavaScript файлы приложения, а также их скомпонованную версию;
  • index.html — HTML-файл являющийся входной точкой вашего приложения. Вы можете назвать его как угодно, да и быть html-файлов может несколько, например, для операции аутентификации. Но в любом случае файл входной точки (с обработчиком onReady) должен быть один;
  • дополнительно вы можете создать директорию или ссылку на серверную часть приложения (./classes в моем случае). Можно дать ей какое угодно имя, но следует выбрать согласующееся со всеми приложениями, которые вы разрабатываете (имена ./server, ./php будут неплохими вариантами).

index.html


Минимальное содержимое index.html может быть следующим:
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <link rel="stylesheet" type="text/css" href="./ext/resources/css/ext-all.css">
  <link rel="stylesheet" type="text/css" href="./css/application.css">
  <script type="text/javascript" src="./ext/adapter/ext/ext-base.js"></script>
  <script type="text/javascript" src="./ext/ext-all-debug.js"></script>
  <script type="text/javascript" src="./application.js"></script>
  <title>A Big Application</title>
</head>
<body></body>
</html>

Хотя вы и можете работать с файлом подобным приведенному выше, я рекомендую добавлять заголовочную информацию как к этому типу файлов, так и ко всем иным. Маркер конца файла также имеет значение. См. примеры такого рода заголовков.

js/application.js


Нам понадобится файл, где мы сможем поместить onReady обработчик. Пусть его имя будет application.js. Минимальное содержимое такого файла приведено ниже:
// vim: sw=4:ts=4:nu:nospell:fdc=4
/**
 * An Application
 *
 * @author    Ing. Jozef Sakalos
 * @copyright (c) 2008, by Ing. Jozef Sakalos
 * @date      2. April 2008
 * @version   $Id$
 *
 * @license application.js is licensed under the terms of the Open Source
 * LGPL 3.0 license. Commercial use is permitted to the extent that the
 * code/component(s) do NOT become part of another Open Source or Commercially
 * licensed development library or toolkit without explicit permission.
 *
 * License details: http://www.gnu.org/licenses/lgpl.html
 */

/*global Ext, Application */

Ext.BLANK_IMAGE_URL = './ext/resources/images/default/s.gif';
Ext.ns('Application');

// application main entry point
Ext.onReady(function() {

    Ext.QuickTips.init();

    // code here

}); // eo function onReady

// eof

Ваши код может иным, но обязательным шагом будет установка Ext.BLANK_IMAGE_URL в значение, ссылающееся на ваш сервер. Это путь к прозрачному 1х1 изображению, которое используется Ext в качестве плэйсхолдера и если он ведет в пустоту, вы можете столкнуться с различными проблемами отрисовки такими как: отсутствие изображения стрелки в Ext.form.ComboBox, иконок и т.п.
Возможно, что вам также понадобится создать глобальную переменную для вашего приложения (в данном случае это Application).
В чем вы должны быть уверенны, так это в том, что onReady обработчик наличествует у вас лишь однажды — в точке входа в приложение.

css/application.css


Поместите в этот файл все свои стили, если они у вас есть. В случае если вам необходимо лишь малое число таковых, то, возможно, не имеет смысла создавать для них отдельный файл. Можно записать их непосредственно в документ, используя .
Наоборот, помните, что вы создаете сложное приложение, стало быть, всему свое место. Если вы будете писать стили в заголовок документа, то вам рано или поздно придется решать проблемы с отрисовкой и вы не будете знать какие стили побудили их.

Как делать не надо


Что обычно следует за тем как был получен некий базис, такой, какой имеется у нас на данном этапе? Начинаем писать код. Итак, мы погрузились в кресло и начали творить:
var vp = new Ext.Viewport({
     layout:'border'
    ,items:[
        new Ext.grid.GridPanel({
            store:new Ext.data.Store({
                 proxy:new Ext.data.HttpProxy({ ...

Одну минуточку. Развивая подобное, мы очень скоро будем иметь все 10000 строк кода в application.js, а это последнее что нам необходимо. Очевидно, был пропущен какой-то этап. Если мы создали такой огромный файл, почему бы нам не вставить его код непосредственно в index.html?

Правильный путь: разделяй и властвуй


Любое целое, вне зависимости от его размера, состоит из более малых систем, которые, в свою очередь, состоят из еще более малых частей, содержащих некоторые элементы. Ваше разрабатываемое сложное приложение не исключение. И вот сейчас как раз время чтобы определить для себя эти части, компоненты и связи между ними.
Итак, еще более удобно усядьтесь, хорошо подумайте, нарисуйте эскиз, составьте список, не принципиально, что вы будете именно делать, главное чтобы в результате у вас на руках был перечень компонент, из которых будет состоять ваше приложение. По крайней мере, главных из них.

Преднастроенные классы


Ну вот, как только вы покончили с анализом и определением составных частей своего приложения можно приступить к написанию одного из них. Как лучше всего это сделать? Наилучшим решением будет написание классов расширяющих стандартные компоненты Ext, ввиду того, что последние уже имеют все настройки, перезаписываемые переданными в их конструкторы значениями. Я называю такие расширения преднастроенными классами т.к. они редко вносят новый функционал и служат в основном для конфигурирования. Примером может служить grid-таблица «Персонал» со своею моделью колонок, хранилищем, настройками сортировки, редакторами и т.д.
В таком случае конфигурация нашего окна могла бы выглядеть следующим образом:
var win = new Ext.Window({
     title:'Personnel'
    ,widht:600
    ,height:400
    ,items:{xtype:'personnelgrid'}
});
win.show();

Написание преднастроенного класса


Разберемся на примере:
Application.PersonnelGrid = Ext.extend(Ext.grid.GridPanel, {
     border:false
    ,initComponent:function() {
        Ext.apply(this, {
             store:new Ext.data.Store({...})
            ,columns:[{...}, {...}]
            ,plugins:[...]
            ,viewConfig:{forceFit:true}
            ,tbar:[...]
            ,bbar:[...]
        });

        Application.PersonnelGrid.superclass.initComponent.apply(this, arguments);
    } // eo function initComponent

    ,onRender:function() {
        this.store.load();

        Application.PersonnelGrid.superclass.onRender.apply(this, arguments);
    } // eo function onRender
});
Ext.reg('personnelgrid', Application.PersonnelGrid);

Что у нас тут происходит? Мы расширяем Ext.grid.GridPanel, создавая новый класс-расширение Application.PersonnelGrid и регистрируем для него новый xtype с именем personnelgrid.
По сути, мы передаем обычной grid-таблице все настройки достаточные для превращения ее в специализированную grid-таблицу «Персонала». Начиная с этого момента, у нас имеется новый компонент, строительный блок нашего приложения, который мы можем использовать где угодно (в окне, на панели, самостоятельно) для отображения списка сотрудников. Создать его можно следующим образом:
var pg = new Application.PersonnelGrid();

или используя xtype (т.н. ленивое создание):
var win = new Ext.Window({
     items:{xtype:'personnelgrid'}
    ,....
});

Организация и хранение преднастроенных классов


Код приведенный выше не нуждается, да и не будет запускаться в обработчике onReady — он не оперирует DOM, а просто создает JavaScript объект. Следовательно, он может и должен быть помещен в отдельный файл (js/Application.PersonnelGrid.js) и будет включаться в хедер index.html.
Что ж, пока все идет хорошо — у нас почти все готово и все (почти), что нам нужно так это продолжать описывать преднастроенный классы, класть их в ./js, включать в index.html и собирать наше приложение из их экземпляров как кусочки головоломки.
Выглядит неплохо, а?

Межкомпонентное сообщение


Представьте себе, что вам необходимо рамочный макет (border layout) со списком ссылок слева (west region) и панелью закладок посередине (center region). Щелчок по ссылке должен создавать новую закладку в центре. Где вы поместите логику всего происходящего, обработчик события и код создания? Слева или в центре?
Нигде. Почему? Если у нас есть преднастроенный класс, который создает и отображает список слева, а мы помещаем логику туда, то его существование теряет смысл без центрального региона. Мы просто не сможем использовать список без панели закладок.
Если же мы поместим логику в центральную область, итог будет схожим: панель закладок не может существовать без списка ссылок.
Есть только один компонент осознающий существование левой и центральной панелей — это их контейнер с рамочным макетированием, единственное правильное место для размещения логики межкомпонентного сообщения.

Производственная система


Ввиду соглашения об организации нашего приложения очень рано мы столкнемся с большим числом JavaScript файлов (в моем случае порядка 80 и их число растет с каждым днем), что может негативно сказаться на производительности реально работающей, не тестовой, системы.

Наилучшим решением является слияние (конкатенация) всех JavaScript файлов в нужном порядке следования итогом чего станет один большой файл, который затем необходимо сжать одним из инструментов минификации или сжатия.
Производственная система будет подключать:
  • ext-all.js
  • app-all.js
  • application.js

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

Заключение


В общем-то это все… Существуют специализированные техники для некоторых классов Ext, много иных серверных и клиентских фишек, но то, что было изложено выше — общая концепция.
Счастливого кодинга!


На правах переводчика:
  • Статья зацепила меня тем, что описывает общую концепцию работы со сложным приложением в конкретном случае разработки в ExtJS;
  • далее перевод планируется поместить в официальный мануал проекта рядом с оригиналом, поэтому просьба реагировать не только плюсами и минусами, но и в текстовом виде, пожалуйста;
  • кажущийся странным танец вокруг ссылок при описании файловой организации обусловлен тем, что сами разработчики ExtJS интенсивно используют построение собранных из множества файлов билдов. В таком случае новая версия того же ExtJS это как все исходное дерево, так и билд фреймворка. Гораздо удобнее, конечно, изменить ссылку, чем копировать туда-сюда много файлов.
Tags:
Hubs:
+44
Comments 17
Comments Comments 17

Articles