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

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

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

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

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

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

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

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

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

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

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

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

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

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

<div id="1">  

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

#### 1.1. HTML

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

```html

<div id="app">

    <h1>html - это просто.</h1>

</div>

```

#### 1.2. HTML + JAVASCRIPT

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

```html

<div id="app">

    <h1>Заголовок</h1>

</div>

<script>

    var h1 = document.querySelector('#app > h1');

    h1.innerText = 'html + js - это гибко.'

</script>

```

#### 1.3. JAVASCRIPT FRAMEWORK

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

```html

<div id="app">

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

</div>

<script src="https://unpkg.com/vue"></script>

<script>

    const app = new Vue({

        el: '#app',

        data: {

            framework: 'Vue.js'

        }

    })

</script>

```

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

```html

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

```

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

```javascript

app.framework = 'Vue js';

```

<div id="2">  

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

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

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

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

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

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

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

<div id="3">  

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

<div id="3.1">  

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

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

index.html

```html

<div id="app">

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

</div>

```  

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. Это будет заметно на более сложных примерах.  

<div id="3.2">  

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

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

main.ts  

```typescript

var form: IForm = <IForm>{

    type:'IForm',

    name:'form',

    flex: Flex.fixed,

    grouping: Grouping.vertical

    elements: [

        <IDataElement>{

            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

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

```

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

<div id="4">  

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

<div id="4.1">  

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

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

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

```html

<div id="list-rendering">

  <ol>

    <li v-for="todo in todos">

      {{ todo.text }}

    </li>

  </ol>

  <button v-on:click="reverseMessage" @click="setFlag">Перевернуть сообщение</button>

</div>

```  

В файле ```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<ITodo>,

      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')

```

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

<div id="4.2">  

#### 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 = <IForm>{

    type: 'IForm',

    name: 'simpleForm',

    flex: Flex.flexible,

    grouping: Grouping.vertical,

    elements: [

        <IMyList>{

            type: 'IList',

            name: 'list',

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

            itemsElement: <IStr>{

                type: 'IStr',

                name: 'str',

                bindingProperty: 'elem',

            },

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

                this.items.forEach(item => {

                    var str = <string>item.element.value;

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

                        .reverse()

                        .join('')  

                });

                this.isReversed = !this.isReversed;

            }

        },

        <IButton>{

            type: 'IButton',

            name: 'reverse',

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

            onClick: async function (form) {

                (<IMyList>form.getElement('list')).reverseText();

                return true;

            }

        }

    ]

}

AppManager.add([form]);

AppManager.open('simpleForm', [

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

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

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

], undefined);

```

<div id="5">  

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

<div id="5.1">  

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

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

```javascript

Vue.component('todo-item', {

  props: ['todo'],

  template: '<li>{{ todo.text }}</li>'

})

```

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

```html

<div id="app">

  <ol>

    <todo-item

      v-for="item in groceryList"

      v-bind:todo="item"

      v-bind:key="item.id"

    ></todo-item>

  </ol>

</div>

```  

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

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

```javascript

var app = new Vue({

  el: '#app',

  data: {

    groceryList: [

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

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

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

    ]

  }

})

```

<div id="5.2">  

#### 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 = <IStr>{

        type: 'IStr',

        name: 'str',

        bindingProperty: _bindingProperty

      };

    }

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

        this.items.forEach(item => {

            var str = <string>item.element.value;

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

                .reverse()

                .join('')  

        });

        this.isReversed = !this.isReversed;

    }

}

var form: IForm = <IForm>{

    type: 'IForm',

    name: 'simpleForm',

    flex: Flex.flexible,

    grouping: Grouping.vertical,

    elements: [

        <IList>(new MyList('list', 'elem')),

        <IButton>{

            type: 'IButton',

            name: 'reverse',

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

            onClick: async function (form) {

                (<MyList>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, данные и поведение в виде методов.

<div id="6">  

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

<div id="6.1">  

#### 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. Конечно, служебных свойств и методов гораздо больше.  

<div id="6.2">  

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

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

```typescript

var user = {

  firstName: 'Иван'

}

var form: IForm = <IForm>{

    type:'IForm',

    name:'form',

    grouping: Grouping.vertical

    elements: [

        <IDataElement>{

            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 и другие.

<div id="7">  

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

<div id="7.1">  

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

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

Описываем html:  

```html

<div

  class="element"

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

></div>

```

```typescript

data: {

  isReadonly: true,

  hasError: false

}

```

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

```html

<div class="element readonly"></div>

```  

 

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

```html

<div v-bind:style="{ position: userPosition, font-size: fontSize + 'em' }"></div>

```

```typescript

data: {

  userPosition: 'relative',

  fontSize: 1.5

}

```

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

<div id="7.2">  

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

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

```typescript

var form: IForm = <IForm>{

    type:'IForm',

    name:'form',

    grouping: Grouping.vertical,

    stylesheets: 'custom.css',

    elements: [

        <IDataElement>{

            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);

```

<div id="8">  

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

<div id="8.1">  

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

```html

<div id="app">

  <button v-on:click="warn('Что ты наделал???')">Нажми!</button>

</div>

```

```typescript

new Vue({

  el: '#app',

  methods: {

    warn: function (message) {

      alert(message)

    }

  }

})

```

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

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

```typescript

this.$emit('myevent');

```

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

```html

<my-component v-on:myevent="doSomething"></my-component>

```  

<div id="8.2">  

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

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

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

```typescript

export var baseElem: IElement = <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<T=ISomeElementEvents> extends IElement<T>  {

    someProperty: string;

    setSomeProperty(val: string): void;

}

export interface ISomeElementEvents {

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

}

export var elem: ISomeElement = <ISomeElement>{

    type: 'IElement',

    name: 'elem',

    someProperty: undefined,

    setSomeProperty: function (val: string) {

        someProperty = val;

        this.emit("someSetted", this.form, this);

    }

}

```

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

<div id="9">  

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

<div id="9.1">  

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

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

```html

<div id="app">

    <input v-model="userText" placeholder="текст">

    <p>Введённое сообщение: {{ userText }}</p>

</div>

```

```typescript

new Vue({

  el: '#app',

  data: {

    userText: 'Текст'

  }

})

```

<div id="9.2">  

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

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

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

```typescript

var form: IForm = <IForm>{

    type:'IForm',

    name:'form',

    grouping: Grouping.vertical

    elements: [

        <IProperty>{

            type: 'IProperty',

            name: 'text',

            bindingProperty: 'userText',

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

        },

        <IStr>{

                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);

```

<div id="10">  

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

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

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

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

### Выводы

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

Источник: https://habr.com/ru/articles/771586/


Интересные статьи

Интересные статьи

В идеальном мире дизайнеры, бэки и фронты – все из одной команды. Но наш мир далек от совершенства, поэтому частенько приходится работать с тем, что дают клиенты.После очередного "чужеродного" дизайна...
Так как вся логика сейчас, весь функционал находится в компонентах, то компонент является основной клеточкой нашего приложения. Эти простые клеточки можно объединять в "ткани" и составлять из них весь...
У нас в команде есть пару проектов, для которых есть старые frontend. Написаны все они на разных технологиях, но объединяет их одно: нежелание кого-либо туда лезть и что-то править. Команде там кажетс...
Типовое решение онлайн-платформы для продажи B2B и B2C eCommerce-продуктов имеет набор определенных страниц, каждая из которых преследует цель формирования пользовательского опыта (Customer eXper...
Эта история личное мнение, основанное на наблюдениях, во время работы в разных проектах.Вход в командную разработкуВо времена моей работы на фрилансе проблем по выбору стека технологий не...