) {}\n}","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Будет скомпилирован в:
"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"var TodosComponent = /** @class */ (function () {\n function TodosComponent(store) {\n this.store = store;\n this.todos = this.store.pipe(select('todos'));\n }\n\n TodosComponent.ngComponentDef = defineComponent({\n type: TodosComponent,\n selectors: [[\"todos-cmp\"]],\n factory: function TodosComponent_Factory(t) {\n return new (t || TodosComponent)(directiveInject(Store));\n },\n consts: 2,\n vars: 3,\n template: function TodosComponent_Template(rf, ctx) {\n if (rf & 1) { // create dom\n pipe(1, \"async\");\n template(0, TodosComponent_div_Template_0, 2, 1, null, _c0);\n } if (rf & 2) { // update dom\n elementProperty(0, \"ngForOf\", bind(pipeBind1(1, 1, ctx.todos)));\n }\n },\n encapsulation: 2\n });\n\n return TodosComponent;\n}());","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Данный пример взял из этой статьи.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Инкрементальный DOM в Angular, по сравнению с Vue и React, напротив, использует более прямой подход, обновляя только те части реального DOM, которые фактически изменились, без необходимости сравнивать целые деревья виртуального DOM.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Инкрементальный DOM в Angular часто считается более эффективным с точки зрения производительности, поскольку он применяет изменения напрямую к реальному DOM без дополнительных вычислительных затрат на сравнение деревьев виртуального DOM
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Компания Google и поставила перед собой задачу — сделать так, чтобы приложения показывали хорошую производительность на мобильных устройствах. А значит, была необходима оптимизация размера бандла и объёма потребляемой памяти.
"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"Подводя итоги, мы поверхностно рассмотрели, как используется DOM на определенной библиотеке. Для общего понятия принципа работы хорошо подойдет новичкам. Понимание какой-либо технологии, алгоритма приходит со временем. Надеюсь, статья помогла разобраться в этой области.
"}}],"summaryContent":null,"isExistSummaryContent":false,"warningFromEditor":null,"warningFromEditorTitle":null,"counters":{"comments":4,"favorites":0,"reposts":0,"views":304,"hits":134,"reads":null,"online":0},"dateFavorite":0,"hitsCount":134,"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/id2171673/1136491-poverhnostnyi-analiz-raboty-dom-na-bibliotekah-javascript","author":{"id":2171673,"name":"Глеб","nickname":null,"description":null,"uri":"","avatar":{"type":"image","data":{"uuid":"c47fed1f-4644-5868-9229-c5de72d3e1e6","width":960,"height":1280,"size":131778,"type":"jpg","color":"cbc9cb","hash":"","external_service":[],"base64preview":"/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAQDAwQDAwQEAwQFBAQFBgoHBgYGBg0JCggKDw0QEA8NDw4RExgUERIXEg4PFRwVFxkZGxsbEBQdHx0aHxgaGxr/2wBDAQQFBQYFBgwHBwwaEQ8RGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhr/wAARCAAKAAoDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABwYI/8QAHxAAAgICAwADAAAAAAAAAAAAAQIDBAURAAchBkFR/8QAFQEBAQAAAAAAAAAAAAAAAAAABAX/xAAYEQADAQEAAAAAAAAAAAAAAAAAARECMf/aAAwDAQACEQMRAD8AmexuvVzvy/sbL3a9k2arRHGxoBqwxQBvD6wGvr94PpiCEUPRUMANgxnw80dkbEzZjNFpZCRdlGyx4gYrD46XF0pJaFWSR4I2ZmgUkkqNknXKCxFaE2+H/9k="}},"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":3291565,"userId":2171673,"count":0,"shareImage":"https://api.vc.ru/achievements/share/3291565"}],"lastModificationDate":1764937314,"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":2171673,"name":"Глеб","nickname":null,"description":null,"uri":"","avatar":{"type":"image","data":{"uuid":"c47fed1f-4644-5868-9229-c5de72d3e1e6","width":960,"height":1280,"size":131778,"type":"jpg","color":"cbc9cb","hash":"","external_service":[],"base64preview":"/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAQDAwQDAwQEAwQFBAQFBgoHBgYGBg0JCggKDw0QEA8NDw4RExgUERIXEg4PFRwVFxkZGxsbEBQdHx0aHxgaGxr/2wBDAQQFBQYFBgwHBwwaEQ8RGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhr/wAARCAAKAAoDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABwYI/8QAHxAAAgICAwADAAAAAAAAAAAAAQIDBAURAAchBkFR/8QAFQEBAQAAAAAAAAAAAAAAAAAABAX/xAAYEQADAQEAAAAAAAAAAAAAAAAAARECMf/aAAwDAQACEQMRAD8AmexuvVzvy/sbL3a9k2arRHGxoBqwxQBvD6wGvr94PpiCEUPRUMANgxnw80dkbEzZjNFpZCRdlGyx4gYrD46XF0pJaFWSR4I2ZmgUkkqNknXKCxFaE2+H/9k="}},"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":3291565,"userId":2171673,"count":0,"shareImage":"https://api.vc.ru/achievements/share/3291565"}],"lastModificationDate":1764937314,"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}},{"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 \n\n\n\n\n","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"github
"}}],"summaryContent":null,"isExistSummaryContent":false,"warningFromEditor":null,"warningFromEditorTitle":null,"counters":{"comments":2,"favorites":4,"reposts":0,"views":576,"hits":1000,"reads":null,"online":0},"dateFavorite":0,"hitsCount":1000,"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/id2171673/782333-validaciya-formy-s-pomoshyu-ajv-vuejs-i-typescript","author":{"id":2171673,"name":"Глеб","nickname":null,"description":null,"uri":"","avatar":{"type":"image","data":{"uuid":"c47fed1f-4644-5868-9229-c5de72d3e1e6","width":960,"height":1280,"size":131778,"type":"jpg","color":"cbc9cb","hash":"","external_service":[],"base64preview":"/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAQDAwQDAwQEAwQFBAQFBgoHBgYGBg0JCggKDw0QEA8NDw4RExgUERIXEg4PFRwVFxkZGxsbEBQdHx0aHxgaGxr/2wBDAQQFBQYFBgwHBwwaEQ8RGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhr/wAARCAAKAAoDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABwYI/8QAHxAAAgICAwADAAAAAAAAAAAAAQIDBAURAAchBkFR/8QAFQEBAQAAAAAAAAAAAAAAAAAABAX/xAAYEQADAQEAAAAAAAAAAAAAAAAAARECMf/aAAwDAQACEQMRAD8AmexuvVzvy/sbL3a9k2arRHGxoBqwxQBvD6wGvr94PpiCEUPRUMANgxnw80dkbEzZjNFpZCRdlGyx4gYrD46XF0pJaFWSR4I2ZmgUkkqNknXKCxFaE2+H/9k="}},"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":3291565,"userId":2171673,"count":0,"shareImage":"https://api.vc.ru/achievements/share/3291565"}],"lastModificationDate":1764937314,"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":2171673,"name":"Глеб","nickname":null,"description":null,"uri":"","avatar":{"type":"image","data":{"uuid":"c47fed1f-4644-5868-9229-c5de72d3e1e6","width":960,"height":1280,"size":131778,"type":"jpg","color":"cbc9cb","hash":"","external_service":[],"base64preview":"/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAQDAwQDAwQEAwQFBAQFBgoHBgYGBg0JCggKDw0QEA8NDw4RExgUERIXEg4PFRwVFxkZGxsbEBQdHx0aHxgaGxr/2wBDAQQFBQYFBgwHBwwaEQ8RGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhr/wAARCAAKAAoDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAABwYI/8QAHxAAAgICAwADAAAAAAAAAAAAAQIDBAURAAchBkFR/8QAFQEBAQAAAAAAAAAAAAAAAAAABAX/xAAYEQADAQEAAAAAAAAAAAAAAAAAARECMf/aAAwDAQACEQMRAD8AmexuvVzvy/sbL3a9k2arRHGxoBqwxQBvD6wGvr94PpiCEUPRUMANgxnw80dkbEzZjNFpZCRdlGyx4gYrD46XF0pJaFWSR4I2ZmgUkkqNknXKCxFaE2+H/9k="}},"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":3291565,"userId":2171673,"count":0,"shareImage":"https://api.vc.ru/achievements/share/3291565"}],"lastModificationDate":1764937314,"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":2}],"reactionId":0},"isNews":false,"source":null,"clusters":[],"donations":{"amount":0,"isDonated":false},"commentsSeenCount":null}}],"cursor":"PuR2GsZKFTvhhG1ZB43h69rVS6IzQAa09I6wtbz/zlbeaPDONLWnxk1d4WpAlG4=","isAnonymized":true}};