ИИ для веб-разработки
Привет, друзья!
В праздники я провел небольшое исследование представленных сегодня на рынке бесплатных инструментов с участием «искусственного интеллекта» для помощи в веб-разработке, результатами которого хочу с вами поделиться, авось кому-нибудь пригодится.
Обратите внимание, что я акцентировал внимание на бесплатных решениях для написания кода клиентской части веб-приложений.
Список протестированных инструментов:
Далее я подробнее расскажу о первых трех (Codeium, Devv, V0) и немного о двух следующих за ними (ChatGPT, GPT4All) решениях из представленных в списке, как наиболее «выдающихся» с моей точки зрения. Остальные сервисы (начиная с cursor
и ниже) в той или иной степени похожи на codeium
и `devv`, но показывают более плохие результаты генерации кода по запросу, исправления ошибок и документирования кода, а также менее удобны в использовании и иногда требуют дополнительных настроек, например, указания ключа OpenAI, установки лишних инструментов (тулкитов), являются условно бесплатными и т.п.
❯ Сodeium
Сodeium — это набор инструментов для помощи в написании нового и улучшении существующего кода, документации к нему и т.д. Работает в контексте кодовой базы, т.е. предложения по дополнению/модификации основаны на коде проекта, его стиле (используемых подходах и принципах), форматировании и т.п. Поставляется в виде расширения для VSCode, плагина для JetBrains и других редакторов. Поддерживает 70+ языков программирования. Полностью бесплатный.
В настоящее время команда codeium
разрабатывает собственный редактор кода — Windsurf, где разработчик и ИИ занимаются парным программированием (затея сомнительная, но окэй :))
Преимущества использования
Контекстуальные предложения и дополнения кода
codeium
анализирует кодовую базу целиком, включая стиль, форматирование и используемые подходы. Это обеспечивает согласованность и единообразие во всех компонентах и функциях- автоматически предлагает код, который соответствует существующим стандартам, что упрощает внедрение новых компонентов и сокращает время на ревью
Поддержка документации
- Хорошая кодовая база должна включать не только код, но и качественную документацию: примеры использования компонентов, их ограничения и API
codeium
помогает как в генерации новой, так и в улучшении существующей документации
Масштабируемость для больших проектов
- Код проекта обычно разрастается со временем, включает десятки и сотни компонентов
codeium
поддерживает работу с крупными проектами и эффективно обрабатывает большие кодовые базы
Поддержка 70+ языков программирования
- При разработке приложений используются различные технологии:
- Javascript/Typescript для логики компонентов
- CSS/TailwindCSS/CSS-in-JS для стилизации
- Markdown/MDX для документации
codeium
поддерживает все эти «языки» и позволяет гибко переключаться между разными частями проекта
Интеграция с популярными IDE
- Плагины для VSCode, JetBrains и других редакторов делают
codeium
удобным инструментом для команд, использующих разные инструменты разработки
Резюме
codeium
упрощает повторное использование кода, что критично при создании универсальных компонентов- улучшает согласованность кода за счет анализа стиля и предоставления адаптированных рекомендаций
- поддержка документации позволяет облегчить онбординг разработчиков, которые будут поддерживать или расширять проект
Попросим codeium
написать TS-функцию для извлечения свойств объекта по ключам:
Я бы вместо T extends object
использовал T extends Record
, в остальном к сгенерированному коду вопросов нет, что просили, то и получили.
Специально для адептов функционального программирования ;):
const extractProps = <
T extends Record,
K extends keyof T,
>(
props: T,
...keys: K[]
) =>
Object.fromEntries(
keys.filter((key) => key in props).map((key) => [key, props[key]]),
) as Pick
❯ Devv_
Devv_ — это «поисковик следующего поколения для разработчиков». Показывает отличные результаты в генерации кода по запросу, в частности на JavaScript/TypeScript/React. Дает развернутые и понятные комментарии к сгенерированному коду. Полностью бесплатный.
Преимущества использования
Высокое качество генерируемого кода
devv_
очень эффективен при работе с JavaScript/TypeScript/React, которые являются основой многих веб-приложений- может генерировать качественные, чистые и современные компоненты, соответствующие актуальным практикам и технологиям
Развернутые комментарии
- При разработке проекта важно не только писать код, но и объяснять сложную логику для последующей поддержки и развития другими разработчиками
devv_
генерирует понятные и развернутые комментарии, что улучшает читаемость кода и упрощает развитие проекта
Решение узких задач и разработка небольших компонентов
- Для создания отдельных компонентов (например, кнопок, карточек или модальных окон)
devv_
позволяет быстро генерировать код по конкретному запросу - это сокращает время на разработку и позволяет сосредоточиться на более высокоуровневых задачах (например, архитектуре системы)
Доступность
devv_
работает бесплатно в браузере, не требует дополнительных настроек и может быть использован на любом этапе разработки
Резюме
devv_
хорошо подходит для быстрого прототипирования компонентов и генерации примеров использования- объяснение кода помогает создавать самодокументирующуюся кодовую базу, что облегчает работу других разработчиков
- удобный веб-интерфейс делает
devv_
отличным инструментом для небольших и средних команд, которые могут использовать его для проверки идей и разработки MVP
Попросим devv_
реализовать алгоритм Кнута-Морриса-Пратта (здесь про алгоритм можно почитать подробнее и найти пример его реализации):
/**
* The Knuth-Morris-Pratt (KMP) algorithm is a string-searching algorithm that searches for occurrences of a "word" W within a main "text string" S.
* It utilizes a pre-computed table (LPS array - Longest Proper Prefix Suffix) to optimize the search process.
*
* @param text The main text string to search within.
* @param pattern The word (pattern) to search for in the text.
* @returns An array of indices representing the starting positions of all occurrences of the pattern within the text.
*/
function knuthMorrisPratt(text: string, pattern: string): number[] {
const textLength = text.length;
const patternLength = pattern.length;
// 1. Build LPS (Longest Proper Prefix Suffix) array
const lps = buildLPS(pattern);
// 2. Search for pattern in text
const occurrences: number[] = [];
let textIndex = 0;
let patternIndex = 0;
while (textIndex < textLength) {
if (text[textIndex] === pattern[patternIndex]) {
textIndex++;
patternIndex++;
if (patternIndex === patternLength) {
// Pattern found!
occurrences.push(textIndex - patternLength);
patternIndex = lps[patternIndex - 1]; // Reset patternIndex using LPS
}
} else {
if (patternIndex !== 0) {
patternIndex = lps[patternIndex - 1]; // Shift pattern based on LPS
} else {
textIndex++; // Move to the next character in the text
}
}
}
return occurrences;
}
/**
* Builds the LPS (Longest Proper Prefix Suffix) array for a given pattern.
*
* @param pattern The pattern string.
* @returns The LPS array.
*/
function buildLPS(pattern: string): number[] {
const patternLength = pattern.length;
const lps: number[] = new Array(patternLength).fill(0);
let length = 0; // Length of the previous longest prefix suffix
lps[0] = 0; // lps[0] is always 0
let i = 1;
while (i < patternLength) {
if (pattern[i] === pattern[length]) {
length++;
lps[i] = length;
i++;
} else {
if (length !== 0) {
length = lps[length - 1]; // Fallback to previous LPS value
} else {
lps[i] = 0;
i++;
}
}
}
return lps;
}
// Example usage:
const text = "ABC ABCDAB ABCDABCDABDE";
const pattern = "ABCDABD";
const indices = knuthMorrisPratt(text, pattern);
console.log(`Pattern found at indices: ${indices}`); // Output: Pattern found at indices: 15
const text2 = "ABABDABACDABABCABAB";
const pattern2 = "ABABCABAB";
const indices2 = knuthMorrisPratt(text2, pattern2);
console.log(`Pattern found at indices: ${indices2}`); // Output: Pattern found at indices: 10
const text3 = "AAAAABAAABA";
const pattern3 = "AAAA";
const indices3 = knuthMorrisPratt(text3, pattern3);
console.log(`Pattern found at indices: ${indices3}`); // Output: Pattern found at indices: 0,1
const text4 = "THIS IS A TEST TEXT";
const pattern4 = "TEST";
const indices4 = knuthMorrisPratt(text4, pattern4);
console.log(`Pattern found at indices: ${indices4}`); // Output: Pattern found at indices: 10
Сгенерированный код работает примерно так, как и должен.
Попросим devv_
реализовать хук React/TS для извлечения ширины и высоты элемента при изменении его размеров:
import { useState, useRef, useLayoutEffect, useCallback } from 'react';
import ResizeObserver from 'resize-observer-polyfill';
interface Size {
width: number | undefined;
height: number | undefined;
}
type UseResizeObserverOptions = {
box?: 'border-box' | 'content-box' | 'device-pixel-content-box';
};
function useResizeObserver(
options: UseResizeObserverOptions = {}
): [React.RefObject, Size] {
const { box = 'content-box' } = options;
const ref = useRef(null);
const [size, setSize] = useState({ width: undefined, height: undefined });
const handleResize = useCallback(
(entries: ResizeObserverEntry[]) => {
if (!Array.isArray(entries)) {
return;
}
const entry = entries[0];
const newWidth = entry.contentRect.width;
const newHeight = entry.contentRect.height;
setSize({ width: newWidth, height: newHeight });
},
[]
);
useLayoutEffect(() => {
if (!ref.current) return;
const resizeObserver = new ResizeObserver(handleResize);
resizeObserver.observe(ref.current, { box });
return () => {
resizeObserver.disconnect();
};
}, [ref.current, box, handleResize]);
return [ref, size];
}
export default useResizeObserver;
import React from 'react';
import useResizeObserver from './useResizeObserver';
const MyComponent: React.FC = () => {
const [ref, size] = useResizeObserver();
return (
{size.width && size.height && (
Width: {size.width}px, Height: {size.height}px
)}
);
};
export default MyComponent;
К этому коду есть несколько вопросов, например:
- на сегодняшний день нам не нужен полифил
ref.current
в зависимостях хукаuseLayoutEffect()
не нужен- вместо
useLayoutEffect()
я бы использовалuseEffect()
и т.д.
Но, в целом, код довольно качественный и с ним можно работать.
❯ V0
v0 — AI-помощник, специализирующийся на веб-разработке. Показывает отличные результаты в написании кода на JavaScript/TypeScript по запросу. Генерирует компоненты React с использованием современных подходов и технологий. Дает развернутые и понятные комментарии к написанному коду. Полностью бесплатный.
Преимущества использования
Фокус на веб-разработке
v0
специально заточен под React и TypeScript, которые сегодня фактически являются стандартом разработки веб-приложений- генерирует компоненты с использованием современных подходов (например, хуков React, TailwindCSS или стилизованных компонентов (Styled Components)).
Автоматизация создания компонентов
- Позволяет быстро и эффективно генерировать модульные и переиспользуемые компоненты на основе запроса
- подходит для создания как автономных компонентов (например, кнопок, иконок, выпадающих списков), так и составных компонентов (например, форм или карточек)
Развернутые комментарии и объяснения
v0
генерирует развернутые пояснения к сгенерированному коду. Это помогает документировать кодовую базу и обеспечивает прозрачность логики компонентов
Быстрая интеграция в проекты
- Созданные компоненты легко интегрируются в существующие проекты
- позволяет следовать принципам DRY (Don’t Repeat Yourself) и атомарному подходу к разработке компонентов
Простота использования
v0
работает бесплатно в браузере, не требует установки дополнительных инструментов и предоставляет простой, понятный и удобный интерфейс
Резюме
v0
отлично подходит для генерации и оптимизации компонентов, что ускоряет разработку веб-приложений- фокус на React и современных технологиях делает его особенно полезным при разработке библиотек компонентов (дизайн-систем)
- объяснения и комментарии помогают команде быстро разбираться в коде и легко расширять проект
Попросим v0
сгенерировать код компонента React «Модальное окно» с помощью элемента HTML dialog
:
// Предполагается использование серверных компонентов Next.js
'use client'
import React, { useRef, useEffect, ReactNode } from 'react'
import { X } from 'lucide-react'
import { Button } from '@/components/ui/button'
interface ModalProps {
isOpen: boolean
onClose: () => void
children: ReactNode
title: string
}
export function Modal({ isOpen, onClose, children, title }: ModalProps) {
const dialogRef = useRef(null)
useEffect(() => {
const dialogElement = dialogRef.current
if (!dialogElement) return
if (isOpen) {
dialogElement.showModal()
} else {
dialogElement.close()
}
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
onClose()
}
}
if (isOpen) {
document.addEventListener('keydown', handleKeyDown)
}
return () => {
document.removeEventListener('keydown', handleKeyDown)
}
}, [isOpen, onClose])
const handleBackdropClick = (e: React.MouseEvent) => {
if (e.target === dialogRef.current) {
onClose()
}
}
return (
{/* dialog, как мы и просили */}
)
}
'use client'
import { useState } from 'react'
import { Modal } from '@/components/Modal'
import { Button } from '@/components/ui/button'
export default function Home() {
const [isModalOpen, setIsModalOpen] = useState(false)
return (
Modal Demo
setIsModalOpen(false)}
title="Example Modal"
>
This is the content of the modal. You can put any React components or HTML here.
)
}
I’ll create a React modal component using the HTML element. This approach provides a native, accessible modal solution with minimal JavaScript. Let’s build this component step by step.
This implementation creates a reusable Modal
component that uses the HTML element. Here’s a breakdown of the key features:
- The
Modal
component acceptsisOpen
,onClose
,children
, andtitle
as props. - It uses the
useRef
hook to get a reference to theelement.
- The
useEffect
hook is used to control the opening and closing of the modal based on theisOpen
prop. - It implements a click handler on the backdrop to close the modal when clicking outside of it.
- The component is styled using Tailwind CSS classes for a clean, modern look.
- It uses the
Button
component from the UI library for consistency. - The modal is accessible, supporting closing via the Escape key and using semantic HTML.
In the Home
component, we demonstrate how to use the Modal
:
- We use the
useState
hook to manage the open state of the modal. - A button is provided to open the modal.
- The
Modal
component is rendered with the necessary props, including the content as children.
This implementation provides a flexible, accessible, and easy-to-use modal component that leverages the native element for better performance and built-in accessibility features.
Не то, чтобы к сгенерированному коду не было вопросов, но в качестве отправной точки вполне подойдет.
❯ ChatGPT и GPT4All
ChatGPT — это написанное на Rust десктопное приложение для Mac, Windows и Linux. О ChatGPT написано млн статей, так что, с вашего позволения, я сразу перейду к примеру использования приложения.
Скормим ChatGPT
скриншот кода, сгенерированного codeium
и попросим его улучшить:
Yes, I can improve this code snippet. I’ll refine it to make it more concise and efficient while adhering to best practices. Here’s the updated code:
Improved Code
function extractPropertiesByKeys(obj: T, keys: K[]): Pick {
return keys.reduce((result, key) => {
if (key in obj) {
result[key] = obj[key];
}
return result;
}, {} as Pick);
}
Improvements Made
- Replaced the for…of Loop with reduce:
Using Array.prototype.reduce is more functional and concise for building an object iteratively.
- Removed the Need for Type Assertion in the Return Statement:
The result is initialized as {} as Pick
- Avoided the Partial Utility Type:
The Pick type is sufficient for ensuring that only the specified keys are included in the resulting object. Using Partial was unnecessary and could lead to confusion.
- Better Readability and Clarity:
This version is shorter and aligns with modern JavaScript/TypeScript functional programming practices.
Example Usage
interface Person {
name: string;
age: number;
address: string;
}
const person: Person = {
name: "Alice",
age: 30,
address: "123 Main St"
};
const selectedProperties = extractPropertiesByKeys(person, ["name", "age"]);
console.log(selectedProperties); // Output: { name: "Alice", age: 30 }
Let me know if you«d like further refinements or additional explanations!
Попросим ChatGPT
придать коду более функциональный вид:
Где-то такой вариант утилиты мы уже видели :)
Certainly! A functional programming approach emphasizes immutability and the use of higher-order functions. Here’s the rewritten code using a functional programming paradigm:
Functional Programming Version
function extractPropertiesByKeys(obj: T, keys: K[]): Pick {
return Object.fromEntries(
keys
.filter((key) => key in obj)
.map((key) => [key, obj[key]])
) as Pick;
}
Explanation of Functional Approach
filter
:
Filters the keys array to include only keys that exist in the object.map
:
Transforms each valid key into a [key, value] pair (tuples) suitable for creating an object.Object.fromEntries
:
Converts the array of key-value pairs into an object.
Example Usage
interface Person {
name: string;
age: number;
address: string;
}
const person: Person = {
name: "Alice",
age: 30,
address: "123 Main St"
};
const selectedProperties = extractPropertiesByKeys(person, ["name", "age"]);
console.log(selectedProperties); // Output: { name: "Alice", age: 30 }
Why This Is Functional
- Immutability: No variables are mutated; each function call produces a new result.
- Higher-Order Functions: filter and map are used for processing collections in a declarative way.
- Declarative Readability: The code describes what to do rather than how to do it.
Let me know if you«d like more refinements or explanations!
В завершение кратко рассмотрим еще одно интересное решение — GPT4All.
GPT4All
— это десктопное приложение для работы с локальными (!) большими языковыми моделями. Показывает хорошие результаты в написании кода по запросу, в частности на JavaScript/TypeScript/React. Дает развернутые и понятные комментарии к сгенерированному коду. Бесплатный, но для работы требуется БЯМ (например, модель Llama 3 весит 4,34 ГБ и требует 8 ГБ оперативной памяти, минимум). Работает как онлайн, так и оффлайн.
Это единственное готовое бесплатное решение, которое я обнаружил, позволяющее использовать всю мощь ИИ без доступа к Интернету, что может быть важно в некоторых ситуациях, например, когда вы опасаетесь, что ваши данные без вашего ведома могут оказаться в распоряжении сервиса, предоставляющего услуги.
В заключение отмечу, что в своей работе я часто использую codeium
и ChatGPT
и иногда прибегаю к помощи v0
.
Делитесь вашими находками в комментариях.
Happy coding!
Новости, обзоры продуктов и конкурсы от команды Timeweb.Cloud — в нашем Telegram-канале ↩