За время своей работы я достаточно изучил Figma, чтобы не только хорошо ей пользоваться и получать результат, но и начать делиться своими знаниями.
За время своей работы я достаточно изучил Figma, чтобы не только хорошо ей пользоваться и получать результат, но и начать делиться своими знаниями.
За время своей работы я достаточно изучил Figma, чтобы не только хорошо ей пользоваться и получать результат, но и начать делиться своими знаниями.
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><linkrel="stylesheet"href="styles.css"><title>Магазин Продуктов</title></head><body><header><h1>Д…
body {font-family: Arial, sans-serif;margin: 0;padding: 0;}header {background-color: #333;color: white;text-align: center;padding: 10px0;}section {margin: 20px;}form {display: grid;gap: 10px;}button {padding: 5px10px;background-color: #333;color: white;border: none;cursor: pointer;}/* Стили для шапки сайта */header {background-color: #333;color: wh…
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><linkrel="stylesheet"href="styles.css"><title>Продажа авиабилетов</title></head><body><header><h1>Путешествуй по всему миру с нами!</h1><nav>&…
/* Reset default margin and padding for typography */body, h1, h2, h3, p, ul {margin: 0;padding: 0;font-family: Arial, sans-serif;line-height: 1.6;}/* Global container styles */.container {max-width: 1200px;margin: 0auto;padding: 20px;}/* Header styles */header {background-color: #333;color: white;padding: 15px0;text-align: center;}headerh1 {margin…
Самый страшный заказчик для дизайнера — он сам. Недостижимая планка качества, раздувающийся скоуп работ и отсутствие дедлайнов — это всё симптомы собственного творческого проекта.
Я Никита Колюгин, дизайнер в продуктах с миллионами пользователей. Помогаю дизайнерам расти в своем телеграм-канале.
Цель публикации — разобраться, какова структура компонентов Vue. js, что такое реактивные свойства и как они работают.
Компоненты — это фрагменты кода, представляющие собой независимые модули, которые могут быть повторно использованы в различных частях приложения.
Валидация форм является важной частью фронтенд-разработки, которая помогает улучшить пользовательский опыт и предотвратить ошибки при отправке данных на сервер. В этой статье мы рассмотрим, как использовать библиотеку AJV совместно с Vue.js и TypeScript для создания мощной системы валидации формы.
Проведение опросов, викторин и других игровых обучающих активностей среди сотрудников является частью корпоративной культуры многих компаний.
При этом по ряду причин использование существующих веб приложений бывает затруднительным, либо невозможным. В связи с чем, задача разработки собственной легковесной, адаптируемой и расширяемой платформ…
Сражу скажу я знаю что в коммах будет срач,но прошу помогите с освоением.
Я буду использовать Flask.
Основываясь на описанной структуре, для примера создам компонент, который приветствует пользователя по имени, в том числе позволяет нажатием кнопки поприветствовать Vue в ответ. Но прежде отмечу, что одним из плюсов Vue. js является возможность при разработке отталкиваться как от шаблона приложения, так и от работы с данными. А поскольку в моем примере шаблон носит условный характер, буду следовать второму пути.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Для начала приведу код компонента полностью, затем рассмотрю детально.
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"Component.vue\n\nПривет, {{ customName }}!
\n \nСоздание компонента начинается с определения входных свойств, которые должны быть переданы извне. В моем случае – это имя пользователя customName, которое ожидается в качестве значения в атрибуте props. Также в качестве внутренней переменной объявляю и устанавливаю значение по умолчанию defaultName, на случай, если никаких свойств в props передано не будет.
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"props: [\"customName\"],\ndata() {\n return {\n defaultName: \"пользователь\",\n }\n},","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Исходя из этого, мне следует определить, было ли фактически передано значение customName и, если нет – подменить ожидаемое дефолтным. Это возможно сделать с помощью специального метода created(), который автоматически вызывается после создания экземпляра компонента. Created относится к одному из хуков жизненного цикла компонентов, о которых подробнее можно узнать в официальной документации (https://vuejs. org/guide/essentials/lifecycle. html).
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"created () {\n if (!this.customName) {\n return this.customName = this.defaultName\n }\t\n },","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Также я хочу, чтобы мой компонент при нажатии на кнопку, выводил ответное приветствие ко Vue, для чего напишу соответствующий метод answer():
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"methods: {\n answer() {\n this.customName = \"Vue\"\n }\n},","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Наконец, остается лишь указать где в шаблоне полученные данные будут отображаться и как вызываться функция. Соответствующую разметку прописываю в блоке template:
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"\nПривет, {{ customName }}!
\n \nКак говорил ранее, главная особенность компонентов – это их переиспользуемость. Это означает, что я могу импортировать полученный компонент в родительский и вызывать его многократно. Привожу ниже код родительского компонента Parent. vue:
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"Parent.vue\n\nЗдесь вызывается три экземпляра дочернего компонента Component. vue, причем каждый экземпляр является самостоятельным и не зависит от других. Это, в свою очередь, позволяет вызывать их с собственными атрибутами. Например, первый вызывается без передачи атрибутов, что должно привести к замене имени пользователя дефолтным значением. Во второй экземпляр я передаю строку с именем пользователя, а в третий – переменную newName с заранее заданным значением. Таким образом, на странице отобразятся все три экземпляра с разным приветствием, а при нажатии на каждую из кнопок произойдет независимый от остальных ответ.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Если компоненты помогают упорядочить блоки интерфейсов, то их динамичность целиком зависит от реактивных свойств.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Реактивность в Vue. js — это механизм, который автоматически обновляет представление компонентов при изменении данных, от которых они зависят. Реактивные свойства реализованы с помощью автоматического метода Vue. observable() . При вызове в него передается объект, который нужно сделать реактивным. В результате получается новый объект, который содержит все свойства и методы оригинального объекта, но с возможностью отслеживания изменений. Когда свойство реактивного объекта изменяется, Vue. js автоматически обновляет все зависимые свойства, в том числе обновляет пользовательский интерфейс.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Для работы с реактивными свойствами Vue. js предоставляет ряд атрибутов, методов и директив, такие как watch, computed, v-model и другие. Метод watch, например, позволяет отслеживать изменения конкретного свойства данных и выполнять определенные действия при их изменении. Computed свойства позволяют вычислять значения на основе данных и обновляться только при изменении зависимых свойств. Директива v-model позволяет связать данные с элементом формы, таким как input, select или textarea. При изменении значения элемента, данные автоматически обновляются, а при изменении данных — обновляется значение элемента формы.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Ниже приведен пример использования директивы v-model, которая связывает новый элемент input, в написанном ранее компоненте, с переменной enteredName. В результате, при вводе имени в поле input автоматически последует изменение приветствия, что является наглядным примером реактивности Vue. js.
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"Component.vue\n\nПривет,\n {{ enteredName }}\n {{ customName }}!\n
\n \nПодводя итог отмечу, что благодаря своей реактивности и компонентной структуре Vue. js предоставляет практически безграничные возможности создания интерактивных интерфейсов, тем самым помогая решать самый широкий спектр корпоративных задач с помощью легких адаптируемых веб-приложений. Грамотное использование компонентов помогает существенно сократить время разработки, а также упростить дальнейшее сопровождение кода. Тогда как реактивные свойства обеспечивают динамику и отзывчивость, что, в конечном итоге, создает удобство и позитивный опыт использования приложения для конечных пользователей.
"}}],"summaryContent":null,"isExistSummaryContent":false,"warningFromEditor":null,"warningFromEditorTitle":null,"counters":{"comments":0,"favorites":3,"reposts":0,"views":1240,"hits":512,"reads":null,"online":0},"dateFavorite":0,"hitsCount":512,"isCommentsEnabled":true,"isLikesEnabled":true,"isRemovedByUserRequest":false,"isFavorited":false,"isPinned":false,"repostId":null,"repostData":null,"subscribedToTreads":false,"isEditorial":false,"isAudioAvailable":false,"audioUrl":null,"isAudioAvailableToGenerate":false,"commentEditor":{"enabled":true,"who":null,"text":"","until":null,"reason":null,"type":"everybody"},"isBlur":false,"isPublished":true,"isDisabledAd":false,"withheld":[],"ogTitle":null,"ogDescription":null,"url":"https://vc.ru/id2075784/806999-vuejs-sozdaem-dinamicheskii-interfeis","author":{"id":2075784,"name":"Sergey Zhuravlev","nickname":null,"description":null,"uri":"","avatar":{"type":"image","data":{"uuid":"e04c02c7-f3c9-568d-9ceb-57ee8443ee6c","width":1200,"height":797,"size":375070,"type":"jpg","color":"3f3835","hash":"","external_service":[]}},"cover":null,"achievements":[{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":3385880,"userId":2075784,"count":0,"shareImage":"https://api.vc.ru/achievements/share/3385880"}],"lastModificationDate":1764924296,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":false,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"subsite":{"id":2075784,"name":"Sergey Zhuravlev","nickname":null,"description":null,"uri":"","avatar":{"type":"image","data":{"uuid":"e04c02c7-f3c9-568d-9ceb-57ee8443ee6c","width":1200,"height":797,"size":375070,"type":"jpg","color":"3f3835","hash":"","external_service":[]}},"cover":null,"achievements":[{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":3385880,"userId":2075784,"count":0,"shareImage":"https://api.vc.ru/achievements/share/3385880"}],"lastModificationDate":1764924296,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":false,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"reactions":{"counters":[{"id":1,"count":3}],"reactionId":0},"isNews":false,"source":null,"clusters":[],"donations":{"amount":0,"isDonated":false},"commentsSeenCount":null}},{"type":"entry","data":{"id":782333,"customUri":null,"subsiteId":2171673,"title":"Валидация формы с помощью AJV, Vue.js и TypeScript","date":1691063041,"dateModified":1691063041,"blocks":[{"type":"text","cover":true,"hidden":false,"anchor":"","data":{"text":"Валидация форм является важной частью фронтенд-разработки, которая помогает улучшить пользовательский опыт и предотвратить ошибки при отправке данных на сервер. В этой статье мы рассмотрим, как использовать библиотеку AJV совместно с Vue.js и TypeScript для создания мощной системы валидации формы.
"}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Что такое AJV?"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"AJV (Another JSON Schema Validator) - это быстрая библиотека валидации данных в формате JSON с поддержкой JSON Schema. JSON Schema - это язык описания структуры и валидации данных в формате JSON. AJV позволяет проверять данные по подготовленным схемам валидации.
"}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Подготовка проекта"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Прежде чем начать, убедитесь, что у вас уже есть:
"}},{"type":"list","cover":false,"hidden":false,"anchor":"","data":{"items":["Node.js v18.16.1.
","@vue/cli 5.0.8"],"type":"UL"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Давайте создадим проект новый проект с помощью Vue CLI с такими параметрами:
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"vue create ajv-validation","lang":""}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"b06b36d7-bd9d-5a6e-a2e5-88fabbaa79ba","width":963,"height":210,"size":40856,"type":"png","color":"0f0c15","hash":"","external_service":[],"base64preview":"/9j/4AAQSkZJRgABAQIALwAvAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAAKAAoDAREAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAwQJ/8QAJRAAAQIFAQkAAAAAAAAAAAAAAgEDAAQFERITISIkMlJTgZLS/8QAGAEAAgMAAAAAAAAAAAAAAAAAAQIAAwT/xAAaEQEAAgMBAAAAAAAAAAAAAAAAAQIRElFx/9oADAMBAAIRAxEAPwDOFxqmEWTjAASXuAqY+OSNvsDWvJCUtTMlsdkv1F8RND4LUZmZyXiHNpkq76w1RQ6rvdP2WLcybWOP/9k="}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Установим необходимые зависимости в нашем проекте:
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"npm install ajv ajv-formats ajv-errors","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Создание файла схемы валидации login.json"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"{\n \"$id\": \"/login.json\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"required\": [\"login\", \"password\"],\n \"properties\": {\n \"login\": {\n \"type\": \"string\",\n \"format\": \"email\",\n \"errorMessage\": \"enter a valid email address\"\n },\n \"password\": {\n \"type\": \"string\",\n \"minLength\": 6,\n \"maxLength\": 1024\n }\n }\n}","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"В этой схеме мы определяем тип каждого поля (строка), а также устанавливаем некоторые правила валидации, такие как формат email и минимальная длина пароля. Поля \"login\" и \"password\" обязательны для заполнения.
"}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Давайте кратко пройдёмся по 2 важным методам:"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"onLogin — это метод, который вызывается при попытке входа пользователя в систему (логине). Он выполняет проверку и валидацию введенных пользователем данных и предпринимает соответствующие действия в зависимости от результата проверки.
"}},{"type":"list","cover":false,"hidden":false,"anchor":"","data":{"items":["Проверка введенных данных на корректность с помощью функции валидации validate. Эта функция использует схему валидации данных и проверяет соответствие данных этой схеме. Возвращается флаг isValid, который указывает, прошла ли валидация успешно.
","Если данные некорректны (isValid равен false), выполняется обработка ошибок.
"],"type":"UL"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"function onLogin() {\n errors.value.clear();\n if (validator.validate(\"/login.json\", formData.value)) {\n // valid, do nothing\n } else if (validator.errors?.length) {\n for (const [, e] of validator.errors.entries()) {\n if (!e.message) {\n continue;\n }\n const fieldName = e.instancePath.substring(1);\n const fieldErrors: string[] = errors.value.get(fieldName) || [];\n fieldErrors.push(e.message);\n errors.value.set(fieldName, fieldErrors);\n }\n }\n }","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"onBlur — это метод, который вызывается при событии \"blur\" (потеря фокуса) на текстовом поле ввода формы. Он используется для валидации данных, введенных пользователем, когда пользователь переходит с поля на другой элемент формы или щелкает вне текстового поля.
"}},{"type":"list","cover":false,"hidden":false,"anchor":"","data":{"items":["Получить имя (идентификатор) и значение поля ввода, на котором произошло событие \"blur\".
","Получить схему валидации для данного поля.
","Проверка значения поля на корректность с помощью функции валидации validate.","Если значение поля некорректно (isValid равен false), выполняется обработка ошибки.
"],"type":"UL"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"function onBlur(e: any) {\n const fieldName = e.target.id;\n const fieldValue = e.target.value;\n if (\n validator.validate(`/login.json#/properties/${fieldName}`, fieldValue)\n ) {\n errors.value.delete(fieldName);\n } else if (validator.errors?.length) {\n errors.value.set(\n fieldName,\n validator.errors.map((e) => e.message) as string[]\n );\n }\n }","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Заключение"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Теперь у вас есть пример, как использовать AJV с Vue.js и TypeScript для валидации формы. Это позволяет создавать мощные и гибкие системы валидации, которые помогут улучшить пользовательский опыт и обеспечить корректную обработку данных на сервере.
"}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Исходный код"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"\nПроведение опросов, викторин и других игровых обучающих активностей среди сотрудников является частью корпоративной культуры многих компаний.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"При этом по ряду причин использование существующих веб приложений бывает затруднительным, либо невозможным. В связи с чем, задача разработки собственной легковесной, адаптируемой и расширяемой платформы игровых активностей с динамическими пользовательскими интерфейсами (DUI) является актуальной. Отмечу, что DUI плотно вошли в практику современной веб-разработки. Зачастую, без них невозможно представить себе реализацию ключевого функционала приложения, поскольку они предоставляют значительную гибкость и интерактивность.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Но прежде чем перейти к основной теме публикации, сделаю короткое отступление на причины выбора именно Vue.js в конкуренции с самым популярным на текущий момент React. Если не углубляться в технологии, то главное преимущество Vue.js - его простая и понятная структура, упрощающая использование данного фреймворка при так называемой фулл-стек разработке. Разделение логики и функционала компонентов от основной html-разметки, позволяют быстрее и в наиболее полной мере погружаться в разработку специалистам разного профиля, что, в конечном итоге, позволяет экономить трудовые и временные ресурсы при разработке небольших приложений, не требующих вовлечение большой команды с узким разделением ролей.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"В этом отношении, React, например, имеет несколько иной подход, при котором синтаксис html «растворяется» в JavaScript коде, расширяясь в вариацию языка JSX. Что, в конечном итоге, требует более узкой специализации от разработчиков и более сложного взаимодействия между участниками.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Тем не менее, важно понимать, что выбор фреймворка среди «большой тройки» React, Angular, Vue.js, часто является делом вкуса, поскольку все они позволяют реализовывать требуемый функционал в полной мере. И сложно представить себе задачу, которую можно решить только на React, но не получится на Vue.js и наоборот. А, учитывая вышесказанное, для написания своего приложения окончательный выбор пал на Vue.js.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"И прежде чем приступать к созданию приложения с DUI на основе Vue.js необходимо понимать суть и структуру его компонентов, а также что такое реактивные свойства и как они работают.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Компоненты - это фрагменты кода, представляющие собой независимые модули, которые могут быть повторно использованы в различных частях приложения.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Компоненты являются важной частью разработки пользовательского интерфейса. Они позволяют разработчикам создавать приложения эффективнее, уменьшая время и затраты на разработку за счет переиспользования кода, что уменьшает время на написание, отладку и выявление ошибок.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Компоненты могут быть созданы для любых элементов интерфейса, таких как кнопки, поля ввода, меню и т.д. Каждый компонент может содержать свои собственные CSS-стили, JavaScript и HTML-разметку. Это позволяет разработчикам легко настраивать и изменять каждый компонент в соответствии с требованиями проекта.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Структурно компоненты состоят из трех блоков:
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"\nПривет, {{name}}
\n\n\n","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"В примере синим шрифтом выделен раздел
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Основываясь на описанной структуре, для примера создам компонент, который приветствует пользователя по имени, в том числе позволяет нажатием кнопки поприветствовать Vue в ответ. Но прежде отмечу, что одним из плюсов Vue.js является возможность при разработке отталкиваться как от шаблона приложения, так и от работы с данными. А поскольку в моем примере шаблон носит условный характер, буду следовать второму пути.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Для начала приведу код компонента полностью, затем рассмотрю детально.
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"Component.vue\n\nПривет, {{ customName }}!
\n \nСоздание компонента начинается с определения входных свойств, которые должны быть переданы извне. В моем случае – это имя пользователя customName, которое ожидается в качестве значения в атрибуте props. Также в качестве внутренней переменной объявляю и устанавливаю значение по умолчанию defaultName, на случай, если никаких свойств в props передано не будет.
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"props: [\"customName\"],\ndata() {\n return {\n defaultName: \"пользователь\",\n }\n},","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Исходя из этого, мне следует определить, было ли фактически передано значение customName и, если нет – подменить ожидаемое дефолтным. Это возможно сделать с помощью специального метода created(), который автоматически вызывается после создания экземпляра компонента. Created относится к одному из хуков жизненного цикла компонентов, о которых подробнее можно узнать в официальной документации (https://vuejs.org/guide/essentials/lifecycle.html).
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"created () {\n if (!this.customName) {\n return this.customName = this.defaultName\n }\t\n },","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Также я хочу, чтобы мой компонент при нажатии на кнопку, выводил ответное приветствие ко Vue, для чего напишу соответствующий метод answer():
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"methods: {\n answer() {\n this.customName = \"Vue\"\n }\n},","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Наконец, остается лишь указать где в шаблоне полученные данные будут отображаться и как вызываться функция. Соответствующую разметку прописываю в блоке template:
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"\nПривет, {{ customName }}!
\n \nКак говорил ранее, главная особенность компонентов – это их переиспользуемость. Это означает, что я могу импортировать полученный компонент в родительский и вызывать его многократно. Привожу ниже код родительского компонента Parent.vue:
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"Parent.vue\n\nЗдесь вызывается три экземпляра дочернего компонента Component.vue, причем каждый экземпляр является самостоятельным и не зависит от других. Это, в свою очередь, позволяет вызывать их с собственными атрибутами. Например, первый вызывается без передачи атрибутов, что должно привести к замене имени пользователя дефолтным значением. Во второй экземпляр я передаю строку с именем пользователя, а в третий – переменную newName с заранее заданным значением. Таким образом, на странице отобразятся все три экземпляра с разным приветствием, а при нажатии на каждую из кнопок произойдет независимый от остальных ответ.
"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"b1b82900-3131-559d-9d60-abadb911ccf3","width":898,"height":510,"size":110341,"type":"png","color":"9e9e9f","hash":"","external_service":[],"base64preview":"/9j/4AAQSkZJRgABAQIAJQAlAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAAKAAoDAREAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAABAUHCP/EACYQAAAEAwcFAAAAAAAAAAAAAAECAwUABBEGEhMhIzHRIiU1Q5P/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/EABURAQEAAAAAAAAAAAAAAAAAAAAR/9oADAMBAAIRAxEAPwDSVmXefVZLWqKuahgTCUAhjLKGKXVHYK3ggEJ3mYvm7qluPsX5iisM7Y2g2Pgg3ywCsEviaReuh8q5ZxAGLS1V8ZKfEvEKP//Z"}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Если компоненты помогают упорядочить блоки интерфейсов, то их динамичность целиком зависит от реактивных свойств.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Реактивность в Vue.js - это механизм, который автоматически обновляет представление компонентов при изменении данных, от которых они зависят. Реактивные свойства реализованы с помощью автоматического метода Vue.observable(). При вызове в него передается объект, который нужно сделать реактивным. В результате получается новый объект, который содержит все свойства и методы оригинального объекта, но с возможностью отслеживания изменений. Когда свойство реактивного объекта изменяется, Vue.js автоматически обновляет все зависимые свойства, в том числе обновляет пользовательский интерфейс.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Для работы с реактивными свойствами Vue.js предоставляет ряд атрибутов, методов и директив, такие как watch, computed, v-model и другие. Метод watch, например, позволяет отслеживать изменения конкретного свойства данных и выполнять определенные действия при их изменении. Computed свойства позволяют вычислять значения на основе данных и обновляться только при изменении зависимых свойств. Директива v-model позволяет связать данные с элементом формы, таким как input, select или textarea. При изменении значения элемента, данные автоматически обновляются, а при изменении данных - обновляется значение элемента формы.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Ниже приведен пример использования директивы v-model, которая связывает новый элемент input, в написанном ранее компоненте, с переменной enteredName. В результате, при вводе имени в поле input автоматически последует изменение приветствия, что является наглядным примером реактивности Vue.js.
"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"4568b934-7e91-570b-8691-29151f359715","width":941,"height":307,"size":58663,"type":"png","color":"9c9d9e","hash":"","external_service":[],"base64preview":"/9j/4AAQSkZJRgABAQIAJQAlAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAAKAAoDAREAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAACAIH/8QAKBAAAQIEAwgDAAAAAAAAAAAAAQIDAAQFBgcRMRITFCNBQlNhkZLT/8QAFgEBAQEAAAAAAAAAAAAAAAAAAQAC/8QAGBEBAQEBAQAAAAAAAAAAAAAAAAERAiH/2gAMAwEAAhEDEQA/AHRal23pdmGVxVGrSxptTlytuWU0y40oDZQQoA5qzzJ0jXVnV8mCTGa8fil0uqqfMz+cGEni86Vjmr0PcfUCVvnvKv7GJP/Z"}}}]}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"Component.vue\n\nПривет,\n {{ enteredName }}\n {{ customName }}!\n
\n \nРазобравшись с основными принципами построения компонентов, предлагаю рассмотреть реализацию практической задачи: динамического интерфейса корпоративной викторины. Согласно заданию, мне требуется отобразить блоки с вопросом и вариантами ответов, дождаться ответа пользователя, выделить цветом верный и неверный варианты, и, в зависимости от текущего статуса игры, скрыть или сделать неактивными часть полей и кнопок, и др.
"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"491561ac-411d-597d-9a10-03b1e2a04088","width":955,"height":526,"size":104286,"type":"png","color":"cfe1e9","hash":"","external_service":[],"base64preview":"/9j/4AAQSkZJRgABAQIAJQAlAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAAKAAoDAREAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABgcJ/8QAJBAAAQQBAwMFAAAAAAAAAAAAAQIDBAURAAYhBxITMTJBUXH/xAAXAQADAQAAAAAAAAAAAAAAAAACAwUE/8QAIhEBAAIBAwMFAAAAAAAAAAAAAQACAwQRIQUS8BQxQWLh/9oADAMBAAIRAxEAPwDRGdc7gG6U11UK1cNxppYefcIUCQc4AGD6fY0y2O5UQkrLqNT6jswle3jlXf8AYXPU2yYJYXGaKmz2HAGMjjjnRWw5Rfaba9Q6YAWuj59ZJup1nZRN6xWYthJZb8LB7W3VJHvPwDqziBo7yDfix58wNLvrxMp5KbmcAHFAASF8DP7p4G0TZ5Z//9k="}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Исходя из такой формулировки задания, схема моего приложения будет выглядеть следующим образом:
"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"b16925ec-1844-5d52-ba2b-65c9d58afea4","width":924,"height":615,"size":78470,"type":"png","color":"edeaee","hash":"","external_service":[],"base64preview":"/9j/4AAQSkZJRgABAQIAJQAlAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAAKAAoDAREAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAABAn/xAAgEAACAgIABwAAAAAAAAAAAAABAgADBBESEyExM3GS/8QAFgEBAQEAAAAAAAAAAAAAAAAAAgMB/8QAGhEBAAMAAwAAAAAAAAAAAAAAAAECIRFBUf/aAAwDAQACEQMRAD8Ap9jNZbju9dTMeJl0H0ektbJ1KulhbdeJvuHmPS0eoDk2ezNt0NSR2hJ//9k="}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"На схеме указаны пять компонентов:
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"- Title.vue - заголовки и инструкции.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"- Main.vue - текст вопроса, объяснение ответа, если предусмотрено.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"- Info.vue - информация о верном/неверном ответе.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"- Button.vue - кнопка с вариантом ответа.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"- Next.vue - переход к следующему вопросу.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Все они являются дочерними для основного родительского. Предполагается, что родительский компонент будет отправлять запросы на сервер, получать данные викторины и распределять эти данные между соответствующими дочерними компонентами.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Итак, компоненты Title и Main реализуют в себе наиболее простой и базовый функционал, рассмотренный мной в примерах выше: в качестве динамических данных Title.vue получает номер вопроса и общее количество вопросов в викторине, показывая пользователю на каком этапе игры он находится. Тогда как в Main.vue из родительского компонента передается и отображается текст задания.
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"Title.vue\n\n{{ number }}/{{ quiz_length }}
\n{{ question }}
\nЧуть интереснее с точки динамичности компонент Info.vue. Он также получает свойства из родительского, однако в зависимости от их значения ведет себя по-разному. Первоначально ожидается, что компонент не виден пользователю до выбора варианта ответа. А затем он должен не только проинформировать о правильности ответа, но и применить к своему содержимому разный цветовой стиль. Это достигается использованием системы директив <v-if> <v-else>, и присвоением элементу <div> разного дополнительного класса в зависимости от значения переменной <is_right>.
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"Info.vue\n\n\n ВЕРНО!\n НЕВЕРНО!\n
\nАналогичный подход я использую в компоненте Next.vue, с той лишь разницей, что кнопка «Далее» должна быть неактивной до выбора варианта ответа, чтобы не позволить пользователю пропустить вопрос, а затем так же как и Info.vue менять стиль и содержимое при разных сценариях:
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"Next.vue\n\nСтоит обратить внимание, что компонент Next.vue не только принимает данные из родительского компонента, но и передает событие и данные (в нашем случае пустой объект) обратно в родительский с помощью такой конструкции:
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"getNext() {\n this.$emit('getNext', {})\n}","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Здесь метод getNext() создает одноименное событие, которое будет отслеживаться в родительском компоненте. Таким образом, при его срабатывании родительский компонент получит данные переданного в событии массива, что позволит благодаря реактивным свойствам выполнить последующие действия. В моем случае, при нажатии кнопки «Далее» в дочернем компоненте, отправляется запрос на получение нового задания в родительском. Более широко это используется в компоненте Button.vue:
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"Button.vue\n\n{{ buttonProps.title }}
\nВ Button.vue используются практически все вышеперечисленные методы и директивы. Из наиболее интересного здесь, как по директиве v-on:click, запускается метод answered(), который отправляет на сервер служебную информацию, а затем возвращает в родительский компонент событие с массивом данных о том, какой из вариантов был выбран и был ли этот вариант правильным. Что важно, на основе этих данных родительский компонент реактивно меняет значения, которые передаются во все экземпляры текущего и в другие компоненты, заставляя их динамически реагировать. Т.е. происходит своеобразный цикл: из родительского компонента в дочерние передается значение, в одном из дочерних оно изменяется, возвращается в родительский, из которого уже в измененном виде снова передается в дочерние, меняя их свойства.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Благодаря этому, в моем приложении при клике на один из вариантов ответа, родительский компонент не только заставляет перекраситься ошибочному варианту в красный цвет, но и сообщает экземпляру Button.vue с верным вариантом подсветиться зеленым. В то же время, на основе этих данных меняется отображение и цвет компонента Info.vue с полем верно/неверно. Наконец, кнопка «Далее» в компоненте Next.vue меняет стиль и становится активной для запроса следующего задания. Т.е. динамически изменяются практически все элементы интерфейса.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Подводя итог отмечу, что благодаря своей реактивности и компонентной структуре Vue.js предоставляет практически безграничные возможности создания интерактивных интерфейсов, тем самым помогая решать самый широкий спектр корпоративных задач с помощью легких адаптируемых веб-приложений. Грамотное использование компонентов помогает существенно сократить время разработки, а также упростить дальнейшее сопровождение кода. Тогда как реактивные свойства обеспечивают динамику и отзывчивость, что, в конечном итоге, создает удобство и позитивный опыт использования приложения для конечных пользователей.
"}}],"summaryContent":null,"isExistSummaryContent":false,"warningFromEditor":null,"warningFromEditorTitle":null,"counters":{"comments":0,"favorites":0,"reposts":0,"views":672,"hits":11327,"reads":null,"online":0},"dateFavorite":0,"hitsCount":11327,"isCommentsEnabled":true,"isLikesEnabled":true,"isRemovedByUserRequest":false,"isFavorited":false,"isPinned":false,"repostId":null,"repostData":null,"subscribedToTreads":false,"isEditorial":false,"isAudioAvailable":false,"audioUrl":null,"isAudioAvailableToGenerate":false,"commentEditor":{"enabled":true,"who":null,"text":"","until":null,"reason":null,"type":"everybody"},"isBlur":false,"isPublished":true,"isDisabledAd":false,"withheld":[],"ogTitle":null,"ogDescription":null,"url":"https://vc.ru/id447345/765510-vuejs-sozdanie-dinamicheskih-polzovatelskih-interfeisov-s-pomoshyu-komponentov-i-reaktivnyh-svoistv","author":{"id":447345,"name":"NTA","nickname":null,"description":null,"uri":"","avatar":{"type":"image","data":{"uuid":"e7175678-aaab-09f8-0271-7b3af7ac4670","width":1207,"height":1207,"size":83090,"type":"jpg","color":"e3ebf8","hash":"","external_service":[]}},"cover":{"cover":{"type":"image","data":{"uuid":"8575b55d-a1f0-53ee-9ad6-78268f9ea45c","width":1280,"height":373,"size":56005,"type":"jpg","color":"b0b9ca","hash":"","external_service":[]}},"cover_y":0},"achievements":[{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":4980908,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/4980908"},{"title":"3 года на vc.ru","code":"registration_3_years","description":"Провёл 3 года вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"d9d72ac5-bcb5-55e0-8c72-b99251e5cdd9","formats":{"glb":"https://static.vc.ru/achievements/shark.glb","usdz":"https://static.vc.ru/achievements/shark.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.5205078125,"textY":0.341796875,"logoX":0.5205078125,"logoY":0.4609375,"logoXNoText":0.5,"logoYNoText":0.3662109375},"id":1356821,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/1356821"},{"title":"5 лет на vc.ru","code":"registration_5_years","description":"Провёл 5 лет вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"a9140d54-73b8-5f40-afa8-449fbaafd42b","formats":{"glb":"https://static.vc.ru/achievements/whale.glb","usdz":"https://static.vc.ru/achievements/whale.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.533203125,"textY":0.658203125,"logoX":0.533203125,"logoY":0.77734375,"logoXNoText":0.4375,"logoYNoText":0.66015625},"id":130113,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/130113"}],"lastModificationDate":1764924296,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":true,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"subsite":{"id":447345,"name":"NTA","nickname":null,"description":null,"uri":"","avatar":{"type":"image","data":{"uuid":"e7175678-aaab-09f8-0271-7b3af7ac4670","width":1207,"height":1207,"size":83090,"type":"jpg","color":"e3ebf8","hash":"","external_service":[]}},"cover":{"cover":{"type":"image","data":{"uuid":"8575b55d-a1f0-53ee-9ad6-78268f9ea45c","width":1280,"height":373,"size":56005,"type":"jpg","color":"b0b9ca","hash":"","external_service":[]}},"cover_y":0},"achievements":[{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":4980908,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/4980908"},{"title":"3 года на vc.ru","code":"registration_3_years","description":"Провёл 3 года вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"d9d72ac5-bcb5-55e0-8c72-b99251e5cdd9","formats":{"glb":"https://static.vc.ru/achievements/shark.glb","usdz":"https://static.vc.ru/achievements/shark.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.5205078125,"textY":0.341796875,"logoX":0.5205078125,"logoY":0.4609375,"logoXNoText":0.5,"logoYNoText":0.3662109375},"id":1356821,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/1356821"},{"title":"5 лет на vc.ru","code":"registration_5_years","description":"Провёл 5 лет вместе с vc.ru. Получена 23 июля 2025.","previewUuid":"a9140d54-73b8-5f40-afa8-449fbaafd42b","formats":{"glb":"https://static.vc.ru/achievements/whale.glb","usdz":"https://static.vc.ru/achievements/whale.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.533203125,"textY":0.658203125,"logoX":0.533203125,"logoY":0.77734375,"logoXNoText":0.4375,"logoYNoText":0.66015625},"id":130113,"userId":447345,"count":0,"shareImage":"https://api.vc.ru/achievements/share/130113"}],"lastModificationDate":1764924296,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":true,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"reactions":{"counters":[{"id":1,"count":2}],"reactionId":0},"isNews":false,"source":null,"clusters":[],"donations":{"amount":0,"isDonated":false},"commentsSeenCount":null}},{"type":"entry","data":{"id":688584,"customUri":null,"subsiteId":1510590,"title":"Мой первый сайт","date":1683553262,"dateModified":1683553262,"blocks":[{"type":"text","cover":true,"hidden":false,"anchor":"","data":{"text":"Сражу скажу я знаю что в коммах будет срач,но прошу помогите с освоением.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Я буду использовать Flask.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Начнем с начала:
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"pip install Flask","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Теперь сделаем минимальную аппликацию.
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"from flask import Flask\n#импорты\napp = Flask(__name__)\n#создание перменной app с которой мы будем работать после\n@app.route(\"/\")\ndef hello_world():\n return \"Hello, World!
\"","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"В этой аппликиций сейчас только показывается \"Hello world\".Теперь добавим рендер html.
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"from flask import Flask, render_template\n\napp = Flask(__name__)\n\n@app.route('/')\ndef hello():\n return render_template('main.html')\nif __name__ == '__main__':\n app.run()","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Теперь напишем main.html
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"\n\n\n\n Hi, I'm John Doe. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin vitae lectus ac metus\n pulvinar elementum a sed lacus. Sed sed metus at urna porta ultricies vel vitae velit. Aliquam convallis,\n purus ac commodo commodo, ipsum erat congue eros, vitae efficitur libero urna nec mi. Integer vel luctus\n velit, a pellentesque justo.\n
\nТеперь добавил социальнные сети.
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"\n\n\n \n \n \nЯ знаю что это не самый чистый и лучший код,если можете помогите улучшить мой знания в html,css и js.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Пока,а забыл подпишись на меня.
"}}],"summaryContent":null,"isExistSummaryContent":false,"warningFromEditor":null,"warningFromEditorTitle":null,"counters":{"comments":1,"favorites":0,"reposts":0,"views":605,"hits":148,"reads":null,"online":0},"dateFavorite":0,"hitsCount":148,"isCommentsEnabled":true,"isLikesEnabled":true,"isRemovedByUserRequest":false,"isFavorited":false,"isPinned":false,"repostId":null,"repostData":null,"subscribedToTreads":false,"isEditorial":false,"isAudioAvailable":false,"audioUrl":null,"isAudioAvailableToGenerate":false,"commentEditor":{"enabled":true,"who":null,"text":"","until":null,"reason":null,"type":"everybody"},"isBlur":false,"isPublished":true,"isDisabledAd":false,"withheld":[],"ogTitle":null,"ogDescription":null,"url":"https://vc.ru/id1510590/688584-moi-pervyi-sait","author":{"id":1510590,"name":"Python Idea","nickname":null,"description":"Раскажу вам о всех чертогах постижения IT и покажу что программистом могут стать все, если захотят и подпишутся на меня,конечно.","uri":"","avatar":{"type":"image","data":{"uuid":"2f87cc08-8d35-5587-947b-9b51331b9a0b","width":2560,"height":1440,"size":1257538,"type":"png","color":"1c1c1c","hash":"","external_service":[]}},"cover":{"cover":{"type":"image","data":{"uuid":"d61baea8-5da2-5fb5-8aa8-b95dde4fbbb3","width":1920,"height":1080,"size":913739,"type":"png","color":"14120f","hash":"","external_service":[]}},"cover_y":26},"achievements":[{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":3943935,"userId":1510590,"count":0,"shareImage":"https://api.vc.ru/achievements/share/3943935"}],"lastModificationDate":1764924296,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":false,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"subsite":{"id":1510590,"name":"Python Idea","nickname":null,"description":"Раскажу вам о всех чертогах постижения IT и покажу что программистом могут стать все, если захотят и подпишутся на меня,конечно.","uri":"","avatar":{"type":"image","data":{"uuid":"2f87cc08-8d35-5587-947b-9b51331b9a0b","width":2560,"height":1440,"size":1257538,"type":"png","color":"1c1c1c","hash":"","external_service":[]}},"cover":{"cover":{"type":"image","data":{"uuid":"d61baea8-5da2-5fb5-8aa8-b95dde4fbbb3","width":1920,"height":1080,"size":913739,"type":"png","color":"14120f","hash":"","external_service":[]}},"cover_y":26},"achievements":[{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":3943935,"userId":1510590,"count":0,"shareImage":"https://api.vc.ru/achievements/share/3943935"}],"lastModificationDate":1764924296,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":false,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"reactions":{"counters":[],"reactionId":0},"isNews":false,"source":null,"clusters":[],"donations":{"amount":0,"isDonated":false},"commentsSeenCount":null}}],"ogTitle":null,"ogDescription":null,"isAnonymized":true}};