Два frontend фреймворка. Два подхода

ed4fac280bc0ad0b1019306cda6d76d1

# Содержание:

1. [Эволюция Веб приложений в двух словах](#1)  

2. [Два подхода](#2)  

3. [Разработка простой компоненты](#3)  

4. [Разработка компоненты посложнее](#4)  

5. [Повторное использование](#5)  

6. [Данные и методы](#6)  

7. [CSS и стилевое оформление](#7)  

8. [Обработка событий](#8)  

9. [Работа с формами](#9)  

10. [Отладка](#10)  

В статье сравниваются два подхода к созданию веб интерфейса пользователя. Один подход — это современные фреймворки с компонентным подходом, который инкапсулирует HTML, js, css. Второй подход к разработке веб-интерфейса аналогичен разработке интерфейса пользователя десктопного приложения.

 

### 1. Эволюция Веб приложений

#### 1.1. HTML

Сначала нам было достаточно статических страниц, написанных с помощью языка разметки html.

```html

   

html — это просто.

```

#### 1.2. HTML + JAVASCRIPT

После, к статичным страницам html добавили javascript.

```html

   

Заголовок

```

#### 1.3. JAVASCRIPT FRAMEWORK

Потом, объединили html и javascript и появились frontend framework. Вот, на примере одного из популярных фреймворков Vue.js.

```html

   

Фреймворк {{framework}} один из лучших.

```

Удобство современных фреймворков — это реактивность, т.е. связанность данных, когда данные на странице

```html

Фреймворк {{framework}} один из лучших.

```

связаны с данными компонента

```javascript

app.framework = 'Vue js';

```

 

### 2. Два подхода

Несмотря на развитие технологий, все равно, на первом месте идет язык разметки — html, а javascript так и остался на подхвате, ведь так изначально применен подход в веб сайтам. Такой подход несомненно очень гибкий, и позволяет создавать немыслимые веб интерфейсы как по красоте, так и по функциональности, но для этого frontend framework предлагает свой, так называемый, декларативный язык. Представляете, вы пишите программу на html, пусть доработанном, но, все же, языке разметки.  

А что, если использовать другой подход? А что если на первое место поставить javascript? Конечно, мы потеряем все то многообразие вариантов красочного и анимированного оформления веб-форм, но приобретем лаконичность и привычную атмосферу разработки десктопного приложения. Этот подход позволит создавать веб приложения на основе готовых библиотек элементов управления. А почему бы и нет, ведь десктопные приложения обходятся же ограниченным набором компонент (это не означает небольшим). И созданные приложения будут не менее красивыми и функциональными, ведь можно использовать множество тем оформления.  

Попытаемся сравнить два подхода.  

Первый подход будет представлять один из популярнейших фреймворков [vue.js](https://ru.vuejs.org/).  

Второй подход будет представлять один новый фреймворк [ui-organizer.ru](https://ui-organizer.ru). Собственно, это прототип библиотеки, но вполне рабочий прототип.

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

 

### 3. Разработка простой компоненты

 

#### 3.1. Простейший компонент Vue.js

В Vue.js компоненты необходимо разрабатывать самому, либо использовать сторонние библиотеки. Для тех, кто знает html и javascript, очень просто создать нужный компонент.  

index.html

```html

   

Фреймворк {{framework}} один из лучших.

```  

index.js

```javascript

const App = {

  data () {

    return {

      framework: 'Vue.js'

    }

  }

}

Vue.createApp (App).mount ('#app');

```

Как видно из примера, программный код написан на html в файле index.html и на javascript в файле index.js. Здесь опускаются вопросы компиляции и сборки приложения, так как в Vue.js процесс сборки отличается в зависимости используете ли вы typescript или javascript. В примере программный код в index.js написан на javascript, поэтому его не надо компилировать и, вообще, вы можете поместить его прямо на страницу index.html вместе с программным кодом, написанном на html. Да, программный код пишем прямо на html. Это будет заметно на более сложных примерах.  

 

#### 3.2 Простейший компонент ui-organizer

ui-organizer не предназначен для создания компонентов. ui-organizer — это библиотека готовых элементов управления (компонентов), с помощью которых вы можете сделать нечто подобное.  

main.ts  

```typescript

var form: IForm = {

    type:'IForm',

    name:'form',

    flex: Flex.fixed,

    grouping: Grouping.vertical

    elements: [

        {

            type: 'IDataElement',

            name: 'dataElement',

            bindingProperty: 'el',

            onAfterSetData: async function (form: IForm, elem: IDataElement, data: any){

                elem.value = `Библиотека ${data} — другой фреймворк.`;

                return true;

            }

        }

    ]

}

AppManager.add ([form]);

AppManager.open ('form', {«el»: «ui-organizer»}, undefined);

```

Как видите здесь html вообще не трогаем. И нам пришлось создать форму и добавить на нее элемент IDataElement. Html будет построен автоматически. Программа построит следующий html:  

```html

Библиотека ui-organizer — другой фреймворк.

```

Обращаем внимание на то, что программный код написан на ```typescript```. Это требует компиляции программы, чтобы получить чистый ```javascript```. Реализовать второй подход, наверное, невозможно без использования ```typescript``` — это снижает риски ошибок, ведь нам важно не написать очень хитрый код, с использованием всех возможностей ```javascript```, а создать надежное, простое в сопровождении приложение.  

 

### 4. Разработка компоненты посложнее

 

#### 4.1. Компонента «Список задач» Vue.js

Сделаем список задач с кнопкой.  

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

```html

 

       

  1.       {{ todo.text }}

       

  2.  

 

```  

В файле ```typescript``` добавляем данные и реализуем обработчики событий ```reverseMessage``` и ```setFlag```. В этом примере мы уже используем typescript.

```typescript

interface ITodo {

    text: string

}

const ListRendering = defineComponent ({

  data () {

    return {

      todos: [

        { text: 'Learn JavaScript' },

        { text: 'Learn Vue' },

        { text: 'Build something awesome' }

      ] as Array,

      isReversed: false

    }

  },

  methods: {

    reverseMessage (): void {

      this.todos.forEach (item =>{

          item.split ('')

            .reverse ()

            .join ('')

      })

    },

    setFlag (event): void {

      this.isReversed = ! this.isReversed;

    }

  }

})

Vue.createApp (ListRendering).mount ('#list-rendering')

```

Безусловно, данные тесно связаны с компонентом и с ними достаточно удобно работать. Еще плюс в том, что это очень гибкий инструмент и мы можем создать любой компонент (элемент управления на форме пользователя). По сути мы только что создали элемент управления, который можем использовать отдельно, а можем встроить в любой другой компонент.  

 

#### 4.2 Форма со «Списком задач» ui-organizer

Отличие второго подхода (ui-organizer) в том, что мы не создаем новый элемент управления, а используем существующие, чтобы решить задачу. В данном случае используем два элемента управления IList и IButton, причем элемент управления IList мы доработали добавив свойство ```isReversed``` и метод ```reverseText```. У элемента IButton добавили реализацию метода ```onClick``` .  

На самом деле IList и IButton это интерфейсы, а мы создаем объекты, реализующие эти интерфейсы.  В тексте упростили и написали просто: «элементы управления IList и IButton».  

AppManager открывает форму 'simpleForm' и передает ей массив элементов в формате JSON.

```typescript

interface IMyList extends IList {

    isReversed: boolean;

    reverseText: ()=>void;

}

var form: IForm = {

    type: 'IForm',

    name: 'simpleForm',

    flex: Flex.flexible,

    grouping: Grouping.vertical,

    elements: [

        {

            type: 'IList',

            name: 'list',

            isReversed: false, // Добавленное свойство

            itemsElement: {

                type: 'IStr',

                name: 'str',

                bindingProperty: 'elem',

            },

            reverseText (): void {// Добавленный метод

                this.items.forEach (item => {

                    var str = item.element.value;

                    item.element.value = str.split ('')

                        .reverse ()

                        .join ('')  

                });

                this.isReversed = ! this.isReversed;

            }

        },

        {

            type: 'IButton',

            name: 'reverse',

            caption: 'Перевернуть текст',

            onClick: async function (form) {

                (form.getElement ('list')).reverseText ();

                return true;

            }

        }

    ]

}

AppManager.add ([form]);

AppManager.open ('simpleForm', [

    { elem: «второй подход» },

    { elem: «новый фреймворк» },

    { elem: «еще один компонент» },

], undefined);

```

 

### 5. Компоненты и повторное использование

 

#### 5.1. Повторное использование в Vue.js

В Vue компоненты — это части приложения, которые можно использовать повторно. Вот например:  

```javascript

Vue.component ('todo-item', {

  props: ['todo'],

  template: '

  • {{ todo.text }}
  • '

    })

    ```

    На Html странице укажите, где хотите вставить компонент:  

    ```html

     

         

            v-for=«item in groceryList»

            v-bind: todo=«item»

            v-bind: key=«item.id»

          >

       

    ```  

    Здесь мы указали, что для каждого item в groceryList выводим на экран элемент todo-item.  

    В приложении передаем в данные этот самый groceryList.

    ```javascript

    var app = new Vue ({

      el: '#app',

      data: {

        groceryList: [

          { id: 0, text: 'Овощи' },

          { id: 1, text: 'Сыр' },

          { id: 2, text: 'Что там ещё люди едят?' }

        ]

      }

    })

    ```

     

    #### 5.2. Повторное использование в ui-organizer

    Здесь повторное использование — это класс элемента, мы можем создать сколько угодно объектов этого класса. Перепишем предыдущий пример из раздела [4.2](#4.2).  

    ```typescript

    class MyList extends UIList {

        constructor (_name: string, _bindingProperty: string){

          this.name = _name;

          this.isReversed = false;

          this.itemsElement = {

            type: 'IStr',

            name: 'str',

            bindingProperty: _bindingProperty

          };

        }

        reverseText (): void {// Добавленный метод

            this.items.forEach (item => {

                var str = item.element.value;

                item.element.value = str.split ('')

                    .reverse ()

                    .join ('')  

            });

            this.isReversed = ! this.isReversed;

        }

    }

    var form: IForm = {

        type: 'IForm',

        name: 'simpleForm',

        flex: Flex.flexible,

        grouping: Grouping.vertical,

        elements: [

            (new MyList ('list', 'elem')),

            {

                type: 'IButton',

                name: 'reverse',

                caption: 'Перевернуть текст',

                onClick: async function (form) {

                    (form.getElement ('list')).reverseText ();

                    return true;

                }

            }

        ]

    }

    AppManager.add ([form]);

    AppManager.open ('simpleForm', [

        { elem: «второй подход» },

        { elem: «новый фреймворк» },

        { elem: «еще один компонент» },

    ], undefined);

    ```

    Здесь мы создали класс MyList и наследовали его от предопределенного класса UIList, который, в свою очередь, реализует интерфейс IList. При описании формы мы просто создали экземпляр MyList, где в конструктор передали название элемента 'list' и название свойства 'elem', из которого будут подставляться данные.  

    Как вы могли заметить, в библиотеке ui-organizer не выделено понятие «компонент», так как, в объекте уже инкапсулированы html, данные и поведение в виде методов.

     

    ### 6. Данные и методы

     

    #### 6.1. Данные и методы компонента Vue.js

    В экземпляре Vue доступны данные и некоторые предопределенные методы:  

    ```typescript

    var user = { name: 'Иван' };

    var vm = new Vue ({

      el: '#app',

      data: user

    })

    vm.name === user.name; // => true

    vm.name = 'Дмитрий';

    user.name // => Дмитрий

    vm.$data === user // => true

    vm.$el === document.getElementById ('app') // => true

    vm.$watch ('name', function (newValue, oldValue) {

      // Этот коллбэк будет вызван, когда изменится `vm.name`

    })

    ```

    Здесь компоненту ```vm```  мы передали объект user. И теперь имеем доступ к свойствам этого объекта через компонент ```vm.name```. Это, так называемая система, реактивности, — наверно, ключевое преимущество современных фреймворков.  

    В примере также показано использование служебных свойств $data и $el и служебного метода $watch. Конечно, служебных свойств и методов гораздо больше.  

     

    #### 6.2. Данные и методы элемента управления ui-organizer

    Здесь основной подход следующий: элементу управления передаются данные, которые каким-то образом могут измениться. Чтобы получить измененные данные обратно, нужно запросить эти данные. Например, при открытии формы, ей передаются первоначальные данные. Пользователь может изменить эти данные. При нажатии кнопки submit, мы можем запросить измененные данные формы и отправить их на сервер.  

    ```typescript

    var user = {

      firstName: 'Иван'

    }

    var form: IForm = {

        type:'IForm',

        name:'form',

        grouping: Grouping.vertical

        elements: [

            {

                type: 'IDataElement',

                name: 'dataElement',

                bindingProperty: 'firstName',

            }

        ]

    }

    AppManager.add ([form]);

    AppManager.open ('form', user, undefined);

    AppManager.activeForm.getData ({}).firstName; //=>'Иван'

    var dataElem = AppManager.activeForm.getElement ('dataElement');

    /*№1*/AppManager.activeForm.setData ({firstName:'Дмитрий'}); //=>'Дмитрий'

    /*№2*/dataElem.getData ({}).firstName == 'Дмитрий'; //=>true

    /*№3*/dataElem.setData ({firstName:'Михаил'});

    /*№4*/AppManager.activeForm.collectData ({}).firstName; //=>'Михаил'

    AppManager.activeForm.dom; //=> HTMLElement

    ```

    В библиотеке ui-organizer для элемента управления можем установить bindingProperty. В нашем примере bindingProperty элемента управления dataElement установлено в firstName. Это значит, что значение свойства firstName в передаваемых данных будет установлено этому элементу (см. №№1–2). Верно, также обратное, когда мы меняем данные элемента функция collectData формы возвращает новые данные (см.№№3–4).  

    Здесь первоначальный объект остается неизменным. Для того, чтобы получить изменения в первоначальном объекте, необходимо функции getData передать этот первоначальный объект:  

    ```typescript

    AppManager.activeForm.getData (user);

    user.firstName; //=>'Михаил'

    ```

    Служебное свойство dom возвращает HTMLElement, связанный с этим элементом управления. У каждого элемента управления на форме свой DOMHTMLElement. У каждого элемента управления доступны служебные методы, такие как addClass, getElement и другие.

     

    ### 7. CSS и стилевое оформление

     

    #### 7.1. Работа с классами и стилями Vue.js

    Для стилевого оформления можем добавлять классы css. Конечно описание этих css классов нужно настроить в соответствующем css файле, который подключен к html.  

    Описываем html:  

    ```html

      class=«element»

      v-bind: class=»{ readonly: isReadonly, error: hasError }»

    >

    ```

    ```typescript

    data: {

      isReadonly: true,

      hasError: false

    }

    ```

    В описании компонента задаем данные, от изменения которых будет зависеть добавление классов к HTML элементу. Например, так как isReadonly = true, то класс readonly будет добавлен, а так как hasError = false, то класс error добавлен не будет. Таким образом, имеем:  

    ```html

    ```  

    То же самое с inline стилями:  

    ```html

    ```

    ```typescript

    data: {

      userPosition: 'relative',

      fontSize: 1.5

    }

    ```

    Здесь значение стилей pozition и font-size определяются соответственно переменными userPosition и fontSize, описанными в секции data.  

     

    #### 7.2. сcs и стилевое оформление ui-organizer

    Библиотека включает в поставку два файла: page.css, который определяет компоновку элементов управления и style.css, который определяет стилевое оформление. Вы можете заменить style.css. Кроме этого, можно для элемента IGroup указать файл .css, который будет применен для этой группы элементов. Кроме того каждый элемент имеет два метода addClass и removeClass, которые добавляют и удаляют классы HTML элемента.

    ```typescript

    var form: IForm = {

        type:'IForm',

        name:'form',

        grouping: Grouping.vertical,

        stylesheets: 'custom.css',

        elements: [

            {

                type: 'IDataElement',

                name: 'dataElement',

                bindingProperty: 'firstName',

                isSomeClass: false;

                onClick: async function (){

                    this.isSomeClass = ! this.isSomeClass;

                    if (this.isSomeClass) elem.addClass («someClass»);

                    else elem.removeClass («someClass»);

                }

            }

        ]

    }

    AppManager.add ([form]);

    AppManager.open ('form', undefined, undefined);

    ```

     

    ### 8. Обработка событий

     

    #### 8.1. Обработка событий vue js

    ```html

     

    ```

    ```typescript

    new Vue ({

      el: '#app',

      methods: {

        warn: function (message) {

          alert (message)

        }

      }

    })

    ```

    В vue js обработчики событий указываются прямо в html. И есть возможность подписываться на все события, возникающие в HTML элементе. В примере мы подписались на событие click html элемента button.  

    Вы также можете генерировать свое событие:  

    ```typescript

    this.$emit ('myevent');

    ```

    и подписываться на него  

    ```html

    ```  

     

    #### 8.2. Обработка событий ui-organizer

    Подход в ui-organizer другой: вы не имеете доступа к событиям html элементов, но можете обрабатывать предопределенные для каждого элемента управления события и генерировать свои.  

    Например, ниже написан обработчик события onAfterLoad, в котором задаются обработчики событий show и hide элемента управления IElement:  

    ```typescript

    export var baseElem: IElement = {

        type: 'IElement',

        name: 'baseElem',

        caption: 'Базовый элемент',

        onAfterLoad: async function (form: IForm, elem: IElement, data: any) {

            elem.on («show», () => {

                elem.addClass ('visible');

            });

            elem.on («hide», () => {

                elem.removeClass ('visible');

            })

            return true;

        }

    }

    ```

    Можете генерировать свои события и подписываться на них:  

    ```typescript

    export interface ISomeElement extends IElement  {

        someProperty: string;

        setSomeProperty (val: string): void;

    }

    export interface ISomeElementEvents {

        someSetted (form: IForm, element: IElement): void,

    }

    export var elem: ISomeElement = {

        type: 'IElement',

        name: 'elem',

        someProperty: undefined,

        setSomeProperty: function (val: string) {

            someProperty = val;

            this.emit («someSetted», this.form, this);

        }

    }

    ```

    В последнем примере определены два интерфейса для облегчения написания программы на typescript.

     

    ### 9. Работа с формами

     

    #### 9.1. Работа с формами vue js

    Для связи компонента vue js c элементом формы input имользуется директива v-model, которая указывается в html. Эта директива связывает данные компонента vue js с элементом ввода input.

    ```html

       

       

    Введённое сообщение: {{ userText }}

    ```

    ```typescript

    new Vue ({

      el: '#app',

      data: {

        userText: 'Текст'

      }

    })

    ```

     

    #### 9.2. Работа с формами ui-organizer

    Здесь для пользователя одинаково, что работать c простым элеметом IDataElement, со списком IList или полем ввода IProperty. Все является элементом формы. Программист не работает напрямую с элементом input или textArea, а использует элементы управления. Которым передаются данные как было показано в разделе [6.2](#6.2).  

    Форма с полем ввода и текстом (эхо, дублирующее поле ввода) будет выглядеть следующим образом:  

    ```typescript

    var form: IForm = {

        type:'IForm',

        name:'form',

        grouping: Grouping.vertical

        elements: [

            {

                type: 'IProperty',

                name: 'text',

                bindingProperty: 'userText',

                placeHoleder: 'Введите текст'

            },

            {

                    type: 'IStr',

                    name: 'str',

                    bindingProperty: 'elem',

            }

        ],

        onAfterLoad: async function (){

          let input: IProperty = this.getElement ('text') as IProperty;

          let srt: IStr = this.getElement ('str') as IStr;

          input.on ('change', (form, elem, val)=>{

            str.value = val;

          })

        }

    }

    AppManager.add ([form]);

    AppManager.open ('form', {«userText»: «Просто текст»}, undefined);

    ```

     

    ### 10. Отладка

    Как и vue js, так и ui-organizer имеют возможность отладки в VSCode и в браузере.  

    Для отладки в VSCode нужно установить соответствующее расширение отладчика, например Debugger Chrome.  

    Для отладки в браузере необходимо настроить конфигурацию сборщика, например webpack.  

    ### Выводы

    Конечно же, выводы делать читателям. Авторы заинтересованная сторона и им больше по душе второй подход, т.к. они используют его в своих разработках. Во втором подходе нам нравится простота, однотипность элементов управления, что кнопка, что поле ввода, что список. Одни и те же атрибуты, одни и те же события, методы. А самое главное, — подсказки. Попробуйте создать элемент управления, и вы будете приятно удивлены, как редактор (в нашем случае VSCode) указывает на ошибки и подсказывает в трудной ситуации.

    © Habrahabr.ru