Мазмұнға өту

Аз болса да — күн сайын.

Angular 6 → 22: жаңарту жолы және неден сүрінесің

6 мин оқу

Осы бетте
Қысқаша
Angular-ды көп нұсқадан аттап жаңартудың бір ғана жолы бар: бір уақытта бір мажорлық нұсқа, әрқайсысынан кейін жасыл құрастыру мен тесттер. Үлкен бұзылулар сирек болады, бірақ оларды алдын ала болжауға болады: рендеринг қозғалтқышының ауысуы, құрастырушының ауысуы, standalone, сигналдар, zone.js. Төменде — 6-дан 22-ге дейінгі жол картасы, соңғы қадамдарда бізде не бұзылды (импорт сақиналары, бәрінің үстіндегі оверлейлер, zone.js) және монорепозиторийде мұның неге он рет емес, бір рет жасалатыны.

Бірінші ереже: бір-бірден нұсқа

Angular мажорлық нұсқаны шамамен жарты жылда бір рет шығарады. Жаңарту командасы:

npx ng update @angular/core@17 @angular/cli@17

ng update package.json ішіндегі нөмірді ғана өзгертпейді. Ол миграцияларды іске қосады — кодыңды жаңа нұсқаға сай қайта жазатын скрипттер. Миграциялар N-нен N+1-ге өтуге арналып жазылған. 12-ден 17-ге секірсең, 13, 14, 15 және 16 миграциялары орындалмайды, қолмен ретке келтіруге тура келеді.

Сондықтан цикл мынадай:

  1. Бір нұсқаға жаңарту.
  2. Құрастыру, тесттерді жүргізу, қолданбаны ашу.
  3. Бөлек коммитпен сақтау.
  4. Келесі нұсқа.

Әр нұсқаға бөлек коммит — дәл бір сатыға кері қайтуға және git bisect арқылы қайсысында бір нәрсе бұзылғанын табуға мүмкіндік.

Nx-тегі монорепозиторий үшін ng update орнына — nx migrate: ол Angular-ды да, Nx-тің өзін де, плагиндерді де келісімді түрде жаңартады.

npx nx migrate 21          # пишет новые версии в package.json и migrations.json
pnpm install
npx nx migrate --run-migrations

Ресми Angular Update Guide нұсқаулығымен салыстырып отыр: «қайдан» және «қайда» дегенді таңдайсың, ол қолмен не істеу керегін көрсетеді.

Жол картасы: үлкен сатылар қайда

Жаңартулардың көбі — «жаңарттым, бір-екі ескертуді түзедім, кеттім». Бірақ іргелі бір нәрсе өзгеретін бірнеше саты бар.

НұсқаНе өзгередіҚаупі неде
6ng update, RxJS 6ескі RxJS импорттарын (rxjs/add/operator/...) pipe()-ге қайта жазу керек
8import() арқылы жалқау жүктеумаршруттардағы './module#Module' түріндегі жолдар ескірген
9әдепкі бойынша Ivy қозғалтқышыView Engine үшін құрастырылған ескі кітапханалар; шаблондарды қатаңырақ тексеру
12–13View Engine жойылды, IE11 енді қолдау көрмейдіIvy-ді қолдамайтын кітапханалар мүлде жұмыс істемей қалады
14–15standalone-компоненттер, типтелген формаларескіше өмір сүруге болады, бірақ жаңа код басқаша жазылады
16–17сигналдар, жаңа control flow (@if, @for), esbuild-тегі құрастырушышаблондарды үлкен, бірақ механикалық рефакторинг
19әдепкі бойынша standalone: trueNgModule бар компоненттерге айқын standalone: false керек
20–22билдердің ауысуы, zone.js әдепкілерден кетеді, CDK оверлейлерді өзгертедітөменде толығырақ

Тәжірибеде ең ауыр саты — 9 және 13: мәселе әдетте сенің кодыңда емес, Ivy-ге ешкім жаңартпаған тәуелділіктерде. Шешімі біреу: кітапхананы ауыстыру немесе алып тастау.

Бізде қалай болды

Монорепозиторийге дейін бізде қолданбаның оннан астам көшірмесі болды, әрқайсысы бөлек жаңартылатын — яғни, шын мәнінде, жаңартылмайтын. Монорепоға көшу (толығырақ — nx-monorepo-from-copies) Angular 16-да басталды. Әрі қарай:

  • 16 → 17 — бастағаннан бір-екі ай өткен соң, бөлек коммитпен.
  • 17 → 19 — бір жарым жылдан кейін. Бұл қате: неғұрлым ұзақ тұрсаң, соғұрлым көп жиналады.
  • 19 → 20 → 21 → 22 — бір күнде, үш бөлек коммитпен, Nx 20 → 23 және TypeScript 5.7 → 6.0-мен бірге.

Соңғы секіріс дәл код ортақ болғандықтан жылдам болды: миграциялар барлық қолданбадан бірден өтті. Бірақ «бір күнде» — тек жаңарту ғана. Салдарын тағы екі апта ретке келтірдік.

20-саты: жаңа билдер

Ескі @angular-devkit/build-angular:browser билдері (webpack негізінде) @angular/build:application-ге (esbuild негізінде) ауыстырылды. Әр қолданбаның project.json файлында:

// было
"executor": "@angular-devkit/build-angular:browser",
"options": { "main": "src/main.ts", "buildOptimizer": true, "vendorChunk": true }

// стало
"executor": "@angular/build:application",
"options": { "browser": "src/main.ts" }

Опциялардың бір бөлігі жай жоғалды (buildOptimizer, vendorChunk, namedChunks) — жаңа құрастырушыға олар керек емес. Нәтиженің құрылымы да өзгерді: файлдар енді dist/<app>/browser/ ішінде жатады. Деплой dist/<app>/ көшірсе — серверге артық папка кетеді. Біз мұны CI-да ұстадық.

Жаңа құрастырушы айтарлықтай жылдам. Бірақ ол модульдердің ретіне қатаңырақ — және бұл келесі тармаққа алып келеді.

Тырма: импорт сақиналары

Жаңартудан кейін беттердің бір бөлігі рантаймда NG0919 («Cannot read @Component metadata») қатесімен құлай бастады. Құрастыру өтіп жатты.

Себебі — циклдік импорт. Мысал:

// list.page.ts
import { EditDialog } from './edit.dialog';   // страница открывает диалог

// edit.dialog.ts
import { ListPage } from './list.page';        // диалог берёт тип у страницы

Сақинадағы модульдер болжауға келмейтін ретпен жүктеледі. Angular imports: [EditDialog] оқыған кезде EditDialog класы әлі undefined болуы мүмкін. Ескі құрастырушы оларды кездейсоқ «сәтті» ретпен жүктейтін. Жаңасы — жоқ.

Сақинаны қалай үзу керек, жақсыдан нашарға қарай:

  1. Ортақ нәрсені үшінші файлға шығару. Тұрақтылар, токендер, интерфейстер — екеуі де тәуелді *.constants.ts не *.interface.ts ішіне.
  2. Тек типті импорттау. Тек тип керек болса, import type { ListPage } from './list.page'. TypeScript мұндай импортты компиляция кезінде алып тастайды, рантаймда сақина жоқ.
  3. Типті әлсірету. Мысалы, MatDialogRef<EditDialog> орнына MatDialogRef<any>. Жұмыс істейді, бірақ тип тексерісін жоғалтады.

Шынын айтсам: жаңартуды тезірек шығару үшін бірнеше жерде үшінші нұсқаны істедік. Бұл қарыз, ол жазылып қойылған. Дұрысы — екіншісі.

Ал кітапханалар арасында сақиналар пайда болмас үшін қатаң шекаралар керек: тәуелділіктер тек бір бағытта, линтермен тексеріледі.

Тырма: диалогтардың астындағы тосттар

Angular CDK-ны 22-ге жаңартқаннан кейін қалқымалы хабарламалар (тосттар) модальді терезелердің астында пайда бола бастады.

Себебі: CDK 22 әдепкі бойынша оверлейлерді (диалогтар, ашылмалы тізімдер, тултиптер) Popover API арқылы — браузердің top layer деп аталатын қабатында рендерлейді. Top layer бүкіл құжаттың үстінен салынады, ал z-index оған әсер етпейді. Кәдімгі құжаттағы z-index: 1001 бар тост санды қанша көтерсең де, top layer-дегі диалогтың астында қалады.

Ескі мінез-құлықты бір провайдермен қайтарамыз:

import { OVERLAY_DEFAULT_CONFIG } from '@angular/cdk/overlay';

providers: [
  { provide: OVERLAY_DEFAULT_CONFIG, useValue: { usePopover: false } },
]

Сабақ: тек Angular-дың ғана емес, CDK мен Material-дың да changelog-ын оқы. Олар бірге жаңартылады, ал мінез-құлықты бөлек өзгертеді.

21-саты: zone.js және HttpClient

zone.js — барлық асинхронды оқиғаларды (шертулер, таймерлер, HTTP) ұстап алып, Angular-ға «бірдеңе болды, экранды тексер» дейтін кітапхана. Angular одан сигналдарға қарай кетіп барады, жаңа қолданбалар zone.js-сіз жасалады.

Бар қолданба үшін жаңарту кезіндегі миграция ештеңе өзгермеуі үшін айқын провайдер қосты:

bootstrapApplication(AppComponent, {
  ...appConfig,
  providers: [provideZoneChangeDetection(), ...appConfig.providers],
});

Бұл миграцияның дұрыс мінез-құлқы: жаңарту мінез-құлықты байқатпай өзгертпеуі керек. Zoneless-ке көшу — бөлек тестілеуі бар бөлек міндет.

Сол сияқты миграция HTTP-клиент XMLHttpRequest-те қалуы үшін provideHttpClient(...) ішіне withXhr() қосты. Егер сенде прогресі бар файл жүктеу болса — жаңартудан кейін оны тексер.

Жаңартудан кейін: жетілдіру

Нұсқаны жаңарту — соңы емес. Жаңа Angular код жазудың жаңа тәсілдерін ұсынады, оларға біртіндеп көшкен жөн, әйтпесе бір-екі жылдан кейін тағы «ескі код» болады:

  • конструктор арқылы енгізудің орнына inject();
  • компонент күйі үшін сигналдар (signal, computed);
  • *ngIf/*ngFor орнына @if/@for;
  • жазылымдар үшін takeUntilDestroyed().

inject()-ге көшуді бөлек міндет ретінде жасадық: бір кітапханаға бір коммит. Осылай ревью кішкентай болып қалады, ал бір нәрсе бұзылса — қай жерде екені түсінікті. Көбіне ресми автоматты миграциялар бар (ng generate @angular/core:inject және соған ұқсастар) — қолмен қайта жазба, соларды іске қос.

Чек-лист

  1. Бір уақытта бір мажорлық нұсқа, бөлек коммит.
  2. package.json қолмен түзету емес, ng update / nx migrate.
  3. Әрқайсысынан кейін: құрастыру, тесттер, қолданбаны көзбен қарау.
  4. Бастамас бұрын — тәуелділіктер тізімі: қайсысы мақсатты нұсқаны қолдамайды.
  5. Angular және CDK/Material changelog-ы.
  6. Жаңа рантайм қателері (NG0919 және т. б.) — алдымен импорт сақиналарын ізде.
  7. Бір нұсқада бір жарым жыл тұрма: жылына екі жаңарту үш жылда бір жаңартудан арзан.

Байланысты жазбалар: nx-monorepo-from-copies, tailwind-v3-to-v4-monorepo, ci-nx-affected.