Node JS и React - как создать фулстек приложение. Полное руководство

2 года назад·3 мин. на чтение

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

Необходимые инструменты

Перед началом нужно убедиться, что на компьютере установлены все необходимые библиотеки, IDE и ПО, а именно:
  • NodeJS и npm. Их можно скачать с официального сайта nodejs.org. npm установится автоматически вместе с NodeJS.
  • Предпочитаемый IDE, например, Visual Studio Code.
  • Опционально, установить git для удобной работы с кодом.

О приложении

В этой статье напишем приложение, которое будет получать и отображать список дел. Структура папок будет выглядеть следующим образом.
app/
  frontend/
  backend/

Создание бэкэнда на NodeJS

Запустим команду в папке app/backend для инициализации проекта:
npm init -y
Эта команда создаст файл package.json. Этот файл содержит как общую информацию о проекте (название, версия, описание и т.д.), так и информацию о зависимостях, скрипты для запуска, сборки и тестирования. Для создания сервера будем использовать express. Установим его с помощью команды:
npm i express
Создадим файл index.js, который будет содержать код для запуска сервера. Этот код запускает веб сервер на порту 3010, если он не задан в переменных среды.
// backend/index.js
const express = require('express');
 
const PORT = process.env.PORT || 3010;
const app = express();
 
app.use((req, res, next) => {
  res.setHeader('Access-Control-Allow-Origin', '*');
  next();
});
 
app.listen(PORT, () => {
  console.log(`Server listening on ${PORT}`);
});
Добавим команду для запуска сервера в package.json. В результате сможем запускать наш сервер с помощью команды npm start.
// backend/package.json
...
"scripts": {
  "start": "node ./index.js"
},
...
Из директории app/backend запустим команду npm start. Если ошибок нет, получим сообщение, что сервер прослушивает порт 3010.
PS C:\tutorials-coding\nodejs-react-app\backend> npm start        

> backend@1.0.0 start
> node ./index.js

Server listening on 3010

Создание API

API это интерфейс, с помощью которого React приложение будет общаться с веб-сервером, т.е. запрашивать, изменять или удалять данные. В нашем случае мы создадим API для получения списка дел в формате JSON. Создадим файл todo-items.json c объектами todo. Этот массив будем отдавать по запросу /api/todo-items.
[
  {
    "id": 1,
    "text": "Изучить NodeJS",
    "done": true
  },
  {
    "id": 2,
    "text": "Изучить ReactJS",
    "done": true
  },
  {
    "id": 3,
    "text": "Написать приложение",
    "done": false
  }
]
Следующий код создает эндпоинт /api/todo-items. React приложение будет отправлять GET запрос на этот эндпоинт.
// backend/index.js

// ...

const todoItems = require('./todo-items.json');
app.get('/api/todo-items', (req, res) => {
  res.json({ data: todoItems });
});

app.listen(PORT, () => {
  console.log(`Server listening on ${PORT}`);
});
Для того чтобы изменения вступили в силу, нужно перезапустить NodeJS сервер. Для остановки скрипта - в терминале, в котором запущен npm start, нужно нажать Ctrl + C (Command + C). Далее снова запускаем npm start. Для проверки эндпоинта, в браузере перейдем по адресу http://localhost:3010/api/todo-items. В результате получим, такой ответ. Ответ от Node JS сервера

Создание фронтенда на React

В папке app/ откроем новый терминал и запустим команду для создания React приложения, где frontend имя нашего приложения.
npx create-react-app@latest frontend
Дождемся установки всех зависимостей. В терминале перейдем в папку frontend.
cd ./frontend
Установим библиотеку bootstrap для дальнейшего использования готовых компонентов.
npm install react-bootstrap bootstrap
Заимпортируем bootstrap.min.css в файле frontend/src/index.js.
import 'bootstrap/dist/css/bootstrap.min.css';
Запустим приложение командой npm start.
npm start
Получим следующее сообщение. Перейдем по указанному адресу в браузере.
Compiled successfully!
 
You can now view frontend in the browser.        
 
  Local:            http://localhost:3003        
  On Your Network:  http://192.168.99.1:3003     
 
Note that the development build is not optimized.
To create a production build, use npm run build. 

Отправка HTTP запроса из React в NodeJS

К этому моменту у нас уже есть рабочий сервер, который умеет принимать запросы и отдавать данные. Сделаем запрос на /api/todo-items из React приложения. Для этого вызовем функцию fetch из хука useEffect в файле App.js.
// frontend/src/App.js
import { useState, useEffect } from 'react';
import Form from 'react-bootstrap/Form';
import './App.css';
 
function App() {
  const [todoItems, setTodoItems] = useState([]);
 
  useEffect(() => {
    fetch('http://localhost:3010/api/todo-items')
      .then((res) => res.json())
      .then((result) => setTodoItems(result.data));
  }, []);
 
  return (
    <div>
      {todoItems.map((item) => (
        <Form.Group key={item.id} className="app__todo-item">
          <Form.Check type="checkbox" checked={item.done} />
          <Form.Control type="text" value={item.text} />
        </Form.Group>
      ))}
    </div>
  );
}
 
export default App;
Открыв приложение в браузере, получим такой результат. React приложение со списком дел Исходный код

Как использовать Proxy в JavaScript?

год назад·6 мин. на чтение

Раскрываем возможности прокси (Proxy) JavaScript, погружаемся в кастомизацию операций с объектами

Введение

В мире JavaScript разработчики постоянно ищут способы повысить мощность и гибкость своего кода. Прокси в JavaScript, представленный в ECMAScript 6 (ES6), стал замечательной фичей, позволяющей разработчикам перехватывать и настраивать основные операции над объектами. Выступая в качестве посредника, прокси открывают множество возможностей, позволяя разработчикам с легкостью изменять поведение объектов, проверять входные данные и защищать их. В этой статье мы углубимся во внутреннюю работу JavaScript Proxy. Мы рассмотрим его фундаментальное использование, концепцию ловушек, определяющих поведение прокси, и различные типы доступных ловушек. Кроме того, мы узнаем, как прокси можно использовать для валидации значений, проверки безопасности и защиты данных, а также для устранения ограничений, совместимости браузеров и соображений производительности. Итак, давайте отправимся в это путешествие открытий и разгадаем секреты JavaScript Proxy!

Базовое использование прокси JavaScript

При работе с JavaScript Proxy первым шагом является создание прокси-объекта с использованием нового синтаксиса new Proxy() Этот синтаксис позволяет определить целевой объект и объект обработчика, содержащий ловушки. Эти ловушки представляют собой специальные методы, которые перехватывают операции на прокси и позволяют нам настраивать их поведение. Например, get перехватывает доступ к свойству прокси-объекта. Определив эту ловушку, мы можем настроить реакцию прокси при доступе к свойству. Точно так же set перехватывает присвоение свойства, позволяя нам проверить или изменить присвоенное значение. Таким образом, ловушки в контексте прокси JavaScript — это специальные методы, которые действуют как посредники, перехватывая и позволяя настраивать операции объекта, такие как доступ к свойствам и назначение прокси-объекта. Они предоставляют разработчикам детальный контроль над поведением прокси, позволяя настраивать и манипулировать по мере необходимости.
Давайте посмотрим на пример:
const target = {
  name: 'John',
  age: 20
};

const handler = {
  get(target, property) {
    console.log(`Getting property: ${property}`);
    return target[property];
  },
  set(target, property, value) {
    console.log(`Setting property: ${property} = ${value}`);
    target[property] = value;
  }
};

const proxy = new Proxy(target, handler);

console.log(proxy.name); // Output: Getting property: name, John
proxy.age = 30; // Output: Setting property: age = 20
console.log(proxy.age); // Output: Getting property: age, 30
В приведенном выше примере мы создаем прокси-объект, который является оболочкой target объекта. get логирует доступное свойство и возвращает соответствующее значение из target объекта. Аналогичным образом, set логирует заданное свойство и присваивает значение target объекту. Используя эти ловушки, мы можем беспрепятственно перехватывать и изменять операции с объектами в соответствии с нашими требованиями.

Общие сведения о прокси-ловушках

Прокси JavaScript предоставляет несколько ловушек, каждая из которых соответствует отдельной операции с прокси-объектом. Некоторые из часто используемых ловушек включают get, set, has, apply, construct и другие. Эти ловушки позволяют нам перехватывать и настраивать такие операции, как доступ к свойствам, назначение, вызов функций, создание экземпляров объектов и многое другое. Давайте рассмотрим некоторые из этих ловушек на примерах:

get

const target = {
  name: 'John',
  age: 20
};

const handler = {
  get(target, property) {
    console.log(`Getting property: ${property}`);
    return target[property];
  }
};

const proxy = new Proxy(target, handler);

console.log(proxy.name); // Output: Getting property: name, John
console.log(proxy.age); // Output: Getting property: age, 20
В этом примере get перехватывает доступ к свойству на прокси и логирует доступ к свойству перед возвратом его значения.

set

const target = {
  name: 'John',
  age: 20
};

const handler = {
  set(target, property, value) {
    console.log(`Setting property: ${property} = ${value}`);
    target[property] = value;
  }
};

const proxy = new Proxy(target, handler);

proxy.name = 'Jerry'; // Output: Setting property: name = Jerry
console.log(proxy.name); // Output: Jerry
Здесь set перехватывает назначение свойства на прокси и логирует назначенное свойство и значение, прежде чем изменять их в целевом объекте. Используя эти и другие ловушки, мы можем эффективно настраивать и контролировать поведение прокси-объектов в соответствии с нашими конкретными потребностями.

Перехват операций с объектами с помощью прокси

JavaScript Proxy позволяет нам перехватывать и настраивать различные операции с объектами. Давайте рассмотрим несколько примеров:

Удаление свойства

const target = {
  name: 'John',
  age: 20
};

const handler = {
  deleteProperty(target, property) {
    console.log(`Deleting property: ${property}`);
    delete target[property];
  }
};

const proxy = new Proxy(target, handler);

delete proxy.age; // Output: Deleting property: age
console.log(proxy); // Output: { name: 'John' }
В этом примере ловушка deleteProperty перехватывает удаление свойства прокси-объекта. Мы можем настроить поведение, чтобы логировать удаленное свойство, а затем удалить его из целевого объекта.

Перехват вызова функции

const target = {
  sum: (a, b) => a + b
};

const handler = {
  apply(target, thisArg, argumentsList) {
    console.log(`Calling function: ${target.name}`);
    return target.apply(thisArg, argumentsList);
  }
};

const proxy = new Proxy(target.sum, handler);

console.log(proxy(2, 3)); // Output: Calling function: sum, 5
В этом случае ловушка apply перехватывает вызов функции в прокси-объекте. Мы можем настроить поведение для логирования имени функции, прежде чем вызывать ее с предоставленными аргументами.

Перехват проверки наличия свойства

Ловушка has срабатывает, когда оператор in используется для проверки наличия свойства в прокси-объекте. Определив эту ловушку, можно настроить реакцию прокси-объекта на проверки существования свойства. Это полезно, если требуется динамически управлять наличием свойств или реализовать механизмы управления доступом.
const target = {
  name: 'John',
  age: 20
};

const handler = {
  has(target, property) {
    console.log(`Checking property existence for "${property}"`);
    return property in target;
  }
};

const proxy = new Proxy(target, handler);

console.log('name' in proxy); // Output: Checking property existence for "name", true
console.log('email' in proxy); // Output: Checking property existence for "email", false
В приведенном примере кода мы создаем объект handler с ловушкой has. Внутри ловушки has мы логируем сообщение, указывающее, что выполняется проверка существования свойства. Затем мы используем оператор in, чтобы проверить, существует ли свойство в прокси-объекте. Ловушка перехватывает эту операцию и возвращает true или false в зависимости от того, существует ли свойство в базовом целевом объекте.

Перехват создания объекта

Ловушка construct в JavaScript Proxy перехватывает создание объекта, когда прокси-объект используется в качестве функции-конструктора с new. Он позволяет настраивать процесс создания экземпляров, проверять аргументы конструктора и применять определенные шаблоны создания объектов.
class Person {
  constructor(name) {
    this.name = name;
  }
}

const handler = {
  construct(target, argumentsList) {
    console.log(`Creating new instance of "${target.name}" with arguments: ${argumentsList}`);
    return new target(...argumentsList);
  }
};

const ProxyPerson = new Proxy(Person, handler);

const john = new ProxyPerson('John'); // Output: Creating new instance of "Person" with arguments: John
В примере кода мы определяем объект handler с помощью construct. Когда прокси-объект вызывается с new, construct перехватывает создание объекта. Мы логируем сообщение, указывающее на создание нового экземпляра с конкретными аргументами. Ловушку construct можно использовать для реализации пользовательской логики инициализации, проверки входных данных или обеспечения требуемого поведения во время создания объекта.

Прокси для валидации и безопасности

Одним из мощных вариантов использования JavaScript Proxy является проверка данных и безопасность. Прокси могут использоваться для обеспечения соблюдения определенных правил и ограничений на доступ к данным и их изменение. Например, мы можем гарантировать, что доступ к определенным свойствам возможен только при определенных условиях, или ограничить несанкционированные изменения. Рассмотрим следующий пример:
const target = {
  password: 'secretpassword'
};

const handler = {
  get(target, property) {
    if (property === 'password') {
      console.log('Access denied!');
      return undefined;
    }
    return target[property];
  },
  set(target, property, value) {
    if (property === 'password') {
      console.log('Unauthorized modification!');
      return false;
    }
    target[property] = value;
    return true;
  }
};

const proxy = new Proxy(target, handler);

console.log(proxy.username); // Output: undefined
console.log(proxy.password); // Output: Access denied!

proxy.password = 'newpassword'; // Output: Unauthorized modification!
В этом примере get ограничивает доступ к свойству password, запрещая его извлечение. Аналогичным образом, ловушка set предотвращает несанкционированное изменение свойства password. Используя JavaScript Proxy, мы можем добавить дополнительный уровень проверки и безопасности к нашим данным, гарантируя, что выполняются только разрешенные операции.

Ограничения и совместимость с браузерами

Хотя прокси JavaScript является мощной функцией, у него есть некоторые ограничения, о которых следует помнить. Во-первых, не все браузеры поддерживают прокси, поэтому важно проверить совместимость браузеров, прежде чем использовать их в продакшене. Однако с ростом внедрения ECMAScript 6 поддержка прокси стала более распространенной. Еще одно ограничение заключается в том, что прокси не могут перехватывать определенные операции, которые считаются фундаментальными или внутренними для JavaScript. К ним относятся такие операции, как Object.preventExtensions(), Object.isExtensible() и Object.setPrototypeOf(). Поэтому прокси могут подходить не для всех сценариев и вариантов использования. Кроме того, стоит отметить, что использование прокси может привести к снижению производительности, особенно при работе с крупномасштабными приложениями. Перехват и настройка операций с объектами сопряжены с затратами. Хотя прокси могут быть оптимизированы в определенных случаях, важно учитывать потенциальное влияние на производительность и оценивать, перевешивают ли преимущества компромиссы с производительностью.

Реальные варианты использования прокси в JavaScript

JavaScript Proxy находит практическое применение в различных реальных сценариях. Вот несколько примеров:
  • Логирование: прокси можно использовать для логирования операций с объектами и отслеживания изменений, предоставляя ценную отладочную информацию.
  • Кэширование: прокси могут реализовывать механизмы кэширования для повышения производительности за счет хранения и извлечения вычисленных или дорогостоящих значений.
  • Мемоизация: прокси можно использовать для реализации мемоизации, которая помогает оптимизировать вызовы функций, кэшируя их результаты на основе предоставленных аргументов.
  • Контроль доступа: прокси могут применять правила контроля доступа, гарантируя, что доступ к определенным свойствам или операциям могут получить только авторизованные сущности.
Это всего лишь несколько случаев, когда прокси JavaScript можно использовать для улучшения функциональности кода и предоставления элегантных решений сложных проблем.

Итоги

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