Создаём одинаковое приложение 5 раз

—

от автора

На написание этой статьи меня вдохновил YouTube-канал Fireship, записывающий отличные видео о веб-разработке, крайне рекомендую их посмотреть, если вам интересна эта тема.

Вот видео с канала, в котором в 10 фреймворках создают todo-приложение:

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

Создаём приложение

jQuery

Чтобы упростить создание приложения без фреймворка, я буду использовать jQuery. Начал я с создания базовой структуры файлов и открытия index.html. Если вам любопытно, структура файлов выглядит так:

По сути, у меня есть таблица стилей в SCSS, которую я скомпилирую в CSS, и на этом пока всё. Разметка html выглядит пока так, но в дальнейшем я её расширю:

<!DOCTYPE html> <html lang="en">  <head>     <meta charset="UTF-8">     <link rel="stylesheet" href="./css/styles.css" />     <meta http-equiv="X-UA-Compatible" content="IE=edge">     <meta name="viewport" content="width=device-width, initial-scale=1.0">     <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>     <title>Notes App</title> </head>  <body>     <div class="container">         <header>             <h1>Notes App</h1>         </header>         <main>             <div class="note">                 <form>                     <input required type="text" id="note-title" placeholder="Note Title" />                     <textarea id="note-body" placeholder="Note Body"></textarea>                     <input type="submit" id="note-submit" title="Add Note" />                 </form>             </div>         </main>     </div> </body>  </html>

Таблица стилей выглядит так:

body {     height: 100%;     width: 100%;     margin: 0; }  .container {     width: 100%;     height: auto;     margin: 0;     display: flex;     flex-direction: column;      header {         display: flex;         align-items: center;          width: 100%;         height: 56px;         background-color: #4e78b8;         color: white;          h1 {             margin-left: 6px;         }     }      main {         margin: 10px;         display: grid;         grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));         grid-gap: 1rem;         align-items: center;          .note {             display: flex;             flex-direction: column;              padding: 10px;             background-color: #a15fbb;             border-radius: 5px;              form {                 display: flex;                 flex-direction: column;                  textarea {                     resize: none;                 }             }         }     } }

Далее я компилирую код при помощи sass scss/styles.scss:css/styles.css, после чего мы готовы писать JavaScript. По сути, нам достаточно добавить в DOM новый div с парой дочерних элементов для отправки формы и сохранение в локальное хранилище. Вот что у меня в результате получилось:

let notes = [];  $(document).ready(function () {     if (localStorage.getItem("notes")) notes = JSON.parse(localStorage.getItem("notes"));     setNotes(); });  $("#note-submit").click(function (e) {      let noteTitle = $("#note-title").val();     let noteDesc = $("#note-body").val();     let note = {         title: noteTitle,         desc: noteDesc     }     notes.push(note);     console.log(notes);     localStorage.setItem("notes", JSON.stringify(notes));     setNotes(); });  function setNotes() {     notes.forEach((note) => {         $("main").prepend(`             <div class="note">                 <h4>${note.title}</h4>                 <span>${note.desc}</span>             </div>         `);     }); }

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

Angular

Забавен контраст между тем, сколько можно сделать с помощью Angular, и тем, что делаем мы. Вопреки впечатлению, которое могло сложиться по предыдущим постам, мне очень нравится Angular, просто я недолюбливают его недостаток модульности по сравнению с чем-то наподобие React. Впрочем, ладно, давайте сгенерируем проект:

$ ng new angular

И это буквально всё, что нужно для начала. Разве CLI Angular не великолепен? Для базовой структуры приложения я напишу, по сути, тот же код:

<div class="container">   <header>     <h1>Notes App</h1>   </header>   <main>     <div class="note" *ngFor="let note of [0, 1, 2, 3]">       <h4>Note Title</h4>       <span>Note Body</span>     </div>     <div class="note">       <form>         <input required type="text" #noteTitle placeholder="Note Title" ngModel />         <textarea #noteBody placeholder="Note Body" ngModel></textarea>         <input type="submit" #noteSubmit title="Add Note" />       </form>     </div>   </main> </div>

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

.container {   width: 100%;   height: auto;   margin: 0;   display: flex;   flex-direction: column;    header {       display: flex;       align-items: center;        width: 100%;       height: 56px;       background-color: #4e78b8;       color: white;        h1 {           margin-left: 6px;       }   }    main {       margin: 10px;       display: grid;       grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));       grid-gap: 1rem;       align-items: center;        .note {           display: flex;           flex-direction: column;            padding: 10px;           background-color: #a15fbb;           border-radius: 5px;            form {               display: flex;               flex-direction: column;                textarea {                   resize: none;               }           }       }   } }

Теперь мы просто можем написать код, похожий на тот, что писали раньше:

import { Component } from '@angular/core';  type Note = {   title: string;   desc: string; }  @Component({   selector: 'app-root',   templateUrl: './app.component.html',   styleUrls: ['./app.component.scss'] }) export class AppComponent {   notes: Array<Note> = [];   title!: string;   body?: string;    constructor() {     const data = localStorage.getItem("notes");     if (data) this.notes = JSON.parse(data);   }    submitForm() {     let note: Note = {       title: this.title,       desc: this.body || ""     }     this.notes.push(note);     localStorage.setItem("notes", JSON.stringify(this.notes));   } }

Сделав это, мы можем вернуться к шаблону и исправить логику заметок:

<div class="container">   <header>     <h1>Notes App</h1>   </header>   <main>     <div class="note" *ngFor="let note of notes">       <h4>{{note.title}}</h4>       <span>{{note.desc}}</span>     </div>     <div class="note">       <form #addNoteForm="ngForm">         <input required type="text" placeholder="Note Title" [(ngModel)]="title" name="Title" />         <textarea placeholder="Note Body" [(ngModel)]="body" name="Body"></textarea>         <input type="submit" #noteSubmit title="Add Note" (click)="submitForm()" />       </form>     </div>   </main> </div>

Вот и всё, ребята!

React

Думаю, здесь всё будет сложнее необходимого из-за природы React. React спроектирован так, чтобы быть более модульным и лёгким, чем другие фреймворки, но в каком-то смысле из-за своей структуры для мелких приложений он сложнее. Я начал генерацию своего react-приложения с собственного шаблона sammy-libraries:

$ yarn create react-app react-app --template sammy-libraries

У меня время от времени возникает баг: node sass (который я использую обычно, потому что dart sass, по моему опыту, имеет более медленное время компиляции для React) отказывается компилировать мой код sass, поэтому я просто удалил node_modules и yarn.lock, а затем снова запустил yarn, и всё заработало идеально. Вот что я сделал: я начал с создания index.scss, аналогичного styles.scss из первого приложения, а затем в своём компоненте App воссоздал базовую структуру приложения:

import React, { useEffect, useState } from "react"; import NotesList from "components/NotesList"; import { NoteType } from "components/Note"; //import "scss/App.scss";  function App() {     const [notesList, setNotesList] = useState<NoteType[]>([]);      const [noteTitle, setNoteTitle] = useState<string>("");     const [noteDesc, setNoteDesc] = useState<string>("");      useEffect(() => {         const data = localStorage.getItem("notes");         if (data) {             setNotesList(JSON.parse(data));         }     }, []);      useEffect(() => {         localStorage.setItem("notes", JSON.stringify(notesList));     }, [notesList])      const addNote = (event: React.FormEvent<HTMLFormElement>) => {         let note: NoteType = {             title: noteTitle,             desc: noteDesc,         };         setNotesList([...notesList, note]);         event.preventDefault();     };      const changeTitle = (event: React.ChangeEvent<HTMLInputElement>) => {         setNoteTitle(event.currentTarget.value);     };      const changeDesc = (event: React.ChangeEvent<HTMLTextAreaElement>) => {         setNoteDesc(event.currentTarget.value);     };      return (         <div className="container">             <header>                 <h1>Notes App</h1>             </header>             <NotesList addNote={addNote} changeTitle={changeTitle} changeDesc={changeDesc} notes={notesList} />         </div>     ); }  export default App;

Пока проект ничего не делает, поэтому давайте добавим другие компоненты. Я создал три компонента в отдельных папках компонентов и заполнил их соответствующим образом.

NotesList.tsx:

import React from "react"; import AddNote from "components/AddNote"; import Note, { NoteType } from "components/Note";  type NotesListProps = {     notes: NoteType[];     addNote: (event: React.FormEvent<HTMLFormElement>) => void;     changeTitle: (event: React.ChangeEvent<HTMLInputElement>) => void;     changeDesc: (event: React.ChangeEvent<HTMLTextAreaElement>) => void; };  function NotesList({ notes, addNote, changeTitle, changeDesc }: NotesListProps) {     return (         <main>             {notes.map((note) => {                 return (                     <Note                         note={{                             title: note.title,                             desc: note.desc,                         }}                     />                 );             })}             <AddNote addNote={addNote} changeTitle={changeTitle} changeDesc={changeDesc} />         </main>     ); }  export default NotesList;

Note.tsx:

import React from "react";  export type NoteType = {     title: string;     desc: string; }  interface NoteProps {     note: NoteType; }  function Note(props: NoteProps) {     return (         <div className="note">             <h4>{props.note.title}</h4>             <span>{props.note.desc}</span>         </div>     ); }  export default Note;

И AddNote.tsx:

import React from "react";  interface AddNoteProps {     changeTitle: (event: React.ChangeEvent<HTMLInputElement>) => void;     changeDesc: (event: React.ChangeEvent<HTMLTextAreaElement>) => void;     addNote: (event: React.FormEvent<HTMLFormElement>) => void; }  function AddNote(props: AddNoteProps) {     return(         <div className="note">             <form onSubmit={props.addNote}>                 <input type="text" placeholder="Note Title" onChange={props.changeTitle} />                 <textarea placeholder="Note Body" onChange={props.changeDesc}></textarea>                 <input type="submit" value="Add Note" />             </form>         </div>     ); }  export default AddNote;

Не самое сложное из того, что я делал, но определённо кажется более сложным, чем работа с jQuery или Angular, по крайней мере, мне. Я очень люблю React и считаю его любимым фреймворком, просто не знаю, что любить в нём, работая над подобными проектами. Если бы мне пришлось выбирать, то я бы сказать, что самый чистый из всех — это Angular, а JQuery кажется самым логичным (по крайней мере, для этого проекта). React — самый неуклюжий, с ним удобно работать, но он не кажется особо подходящим.

Vue

С этим фреймворком я работал один раз, и кому-то это может показаться оскорблением, но я действительно не видел смысла в возне с ним. Я могу использовать и Angular, и React. Мне кажется, что они покрывают почти все мои потребности (а оставшееся решают библиотеки), поэтому Vue никогда не казался мне полезным. Что ж, давайте создадим свой проект на Vue.

$ vue ui

По сути, я использовал всё по умолчанию, но с TypeScript и SCSS (в основном с dart sass, чтобы не ломать зависимости), потому что мне нравится применять их в своих проектах. Единственная реальная причина того, что я не использовал TypeScript в первом проекте, заключалась в том, что мне не хотелось возиться с обеспечением совместной работы jQuery и TS, но если вам интересно, то это возможно.

Как я подошёл к созданию приложения? Сначала я удалил почти всё, что было сгенерировано автоматически, и заменил код App вот на это:

<template>   <div class="container">     <header>       <h1>Notes App</h1>     </header>     <main>       <Note         v-for="(note, index) in notes"         :key="index"         :title="note.title"         :body="note.body"       />       <div class="note">         <form @submit="submitForm()">           <input type="text" placeholder="Note Title" v-model="title" />           <textarea placeholder="Note Body" v-model="body"></textarea>           <input type="submit" value="Add Note" />         </form>       </div>     </main>   </div> </template>  <script lang="ts"> import { Component, Vue } from "vue-property-decorator"; import Note from "./components/Note.vue";  type NoteType = {   title: string;   body: string; };  @Component({   components: {     Note,   }, }) export default class App extends Vue {   notes: Array<NoteType> = [];   title!: string;   body?: string;    constructor() {     super();     const data = localStorage.getItem("notes");     if (data) this.notes = JSON.parse(data);   }    submitForm(): void {     let note: NoteType = {       title: this.title,       body: this.body || "",     };     this.notes.push(note);     localStorage.setItem("notes", JSON.stringify(this.notes));   } } </script>  <style lang="scss"> body {   height: 100%;   width: 100%;   margin: 0; }  .container {   width: 100%;   height: auto;   margin: 0;   display: flex;   flex-direction: column;    header {     display: flex;     align-items: center;      width: 100%;     height: 56px;     background-color: #4e78b8;     color: white;      h1 {       margin-left: 6px;     }   }    main {     margin: 10px;     display: grid;     grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));     grid-gap: 1rem;     align-items: center;      .note {       display: flex;       flex-direction: column;        padding: 10px;       background-color: #a15fbb;       border-radius: 5px;        form {         display: flex;         flex-direction: column;          textarea {           resize: none;         }       }     }   } } </style>

А компонент Note выглядел вот так:

<template>   <div class="note">     <h4>{{ this.title }}</h4>     <span>{{ this.body }}</span>   </div> </template>  <script lang="ts"> import { Component, Prop, Vue } from "vue-property-decorator";  @Component({   components: {}, }) export default class App extends Vue {   @Prop() title!: string;   @Prop() body?: string; } </script>

И на этом всё.

Svelte

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

Потратив десять минут на поиск несуществующего CLI для Svelte yarn create-*, я решил настроить проект с бойлерплейтом фреймворка. Я преобразовал проект в TypeScript, потому что я фанат языков со строгой типизацией, а потом приступил к работе. Занявшись стилями, я пошёл на принцип и отказался от SCSS. Под этим я подразумеваю, что не мог возиться с настройкой SCSS, какой бы лёгкой она ни была, поэтому просто скомпилировал его вручную. Вот с таким компонентом я начал работу:

<script lang="ts"> import Note from "./components/Note.svelte";  type NoteType = {     title: string;     body: string; };  let notes: Array<NoteType> = [];  const data = localStorage.getItem("notes"); if (data) notes = JSON.parse(data);  let title: string = ""; let body: string = "";  function onSubmit() {     let note: NoteType = {         title: title,         body: body     };     notes.push(note);     localStorage.setItem("notes", JSON.stringify(notes)); } </script>  <div class="container">     <header>         <h1>Notes App</h1>     </header>     <main>         {#each notes as note}             <Note title={note.title} body={note.body} />         {/each}         <div class="note">             <form on:submit={onSubmit}>                 <input type="text" placeholder="Note Title" bind:value={title} />                 <textarea placeholder="Note Body" bind:value={body}></textarea>                 <input type="submit" value="Add Note" />             </form>         </div>     </main> </div>  <style> body {   height: 100%;   width: 100%;   margin: 0; }  .container {   width: 100%;   height: auto;   margin: 0;   display: flex;   flex-direction: column; } .container header {   display: flex;   align-items: center;   width: 100%;   height: 56px;   background-color: #4e78b8;   color: white; } .container header h1 {   margin-left: 6px; } .container main {   margin: 10px;   display: grid;   grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));   grid-gap: 1rem;   align-items: center; } .container main .note {   display: flex;   flex-direction: column;   padding: 10px;   background-color: #a15fbb;   border-radius: 5px; } .container main .note form {   display: flex;   flex-direction: column; } .container main .note form textarea {   resize: none; } </style>

А вот компонент Note:

<script lang="ts">     export var title: string;     export var body: string; </script>  <div class="note">     <h4>{title}</h4>     <span>{body}</span> </div>

Я обнаружил проблему, решения которой не знаю, и на данном этапе не хочу её решать: стили работают, только если вставить их в bundle.css, который затем сбрасывается при обновлении кода и ресурсов. Это не будет проблемой в полностью собранном приложении, но очень раздражает при тестировании. Не думаю, что буду решать эту проблему в ближайшее время, но, вероятно, со временем устраню её.

Заключение

Выше я говорил, что буду пробовать создавать при помощи Svelte и другие приложения, но не знаю, насколько решительно я готов выполнять это обещание. Хотя мне понравились многие аспекты Svelte, в нём слишком много препятствий, чтобы использовать его чаще. Учитывая проект, над которым я работал, React, по моему мнению, подвергся несправедливой критике. Angular по-прежнему кажется мне самым чистым, Vue — самым интересным, а jQuery — самым лучшим, что меня сильно удивило. Думаю, если бы мне пришлось выбирать фреймворк для будущих проектов, то это определённо бы зависело от проекта, но сам я вполне могу представить, что использую все пять, даже с учётом сложностей Svelte. Тем не менее, вероятно, основную свою работу я буду выполнять в Angular и React, а jQuery и Vue будут следующими по приоритету. Наверно, я дам Svelte ещё один шанс, но не думаю, что буду создавать в нём особо много проектов, вне зависимости от того, была ли справедлива критика в этом проекте. В любом случае, мне кажется, что любой из этих фреймворков станет отличным выбором для множества способов применения. Теперь я вижу, почему людям нравится Vue, но не могу сказать, что моё мнение очень сильно изменилось.

Код

Весь код выложен на github

— — — —
Дата-центр ITSOFT – размещение и аренда серверов и стоек в двух ЦОДах в Москве; colocation GPU-ферм и ASIC-майнеров, аренда GPU-серверов. Лицензии связи, SSL-сертификаты. Администрирование серверов и поддержка сайтов. UPTIME за последние годы составляет 100%.


ссылка на оригинал статьи https://habr.com/ru/articles/575416/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *