Миссия Angular — сделать DX лучше с помощью автономных компонентов

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

История

Первая итерация Angular (AngularJs) стала переломным моментом в развитии фронт-энда веб-разработки. Магия создания двусторонней привязки без необходимости создавать обработчики событий для элементов была мгновенной победой для сообщества. Но проблема заключалась в том, что эта «магия» могла (и чаще всего так и происходило) создавать не поддерживаемые и непроизводительные веб-приложения по мере роста сложности приложения.

Поэтому команда Angular решила пересмотреть и переписать с нуля фреймворк Angular. (Это вызвало почти бунт в сообществе, поскольку AngularJs был активно внедрен и НЕ был совместим с будущими версиями Angular, но команда Angular извлекла из этого урок, создав функцию ngUpdate в CLI). В ходе переделки основное внимание уделялось организации кода и сопровождаемости кодовой базы. Это было сделано благодаря тому, что у команды Angular было очень много мнений о том, как должны быть написаны приложения Angular. Одно из мнений команды Angular заключается в том, что использование системы типов делает вещи более удобными в обслуживании, и в результате разработчики приложений Angular были вынуждены использовать TypeScript, подмножество Javascript, которое переносит код TypeScript в Javascript.

Другим основным принципом сопровождаемости является ngModule. Эта концепция заключается в том, что связанный набор функций должен содержаться в модуле. Это, наряду с тем, что код TypeScript нужно было транспонировать, позволило компилятору Angular создавать меньшие пакеты Javascript для отправки в браузер. Но за это пришлось заплатить. Компилятору необходимо было читать файл ngModule, чтобы объединить части кода вместе. Например, компонент / директива не могли быть использованы, если они не были объявлены в модуле. Сервис не может быть внедрен (подробнее об инъекции зависимостей позже) в класс, если он не был зарегистрирован (или предоставлен) в модуле. Маршрутизатор мог лениво загружать пучки Javascript, но компилятор должен был знать модуль (или пучок Javascript), который необходимо лениво загрузить.

В следующих разделах мы покажем, как Angular 14 устраняет необходимость создания ngModules для выполнения вышеуказанных задач. Примечание: в следующих разделах я буду использовать термин feature вместо ngModule, чтобы показать, что основной принцип сопровождаемости не теряется при новом способе написания приложений Angular. Вы по-прежнему должны создавать каркас вашего приложения на основе функций. Большая часть этого теперь возможна благодаря переработке движка представления Angular в Ivy. Ivy сделал так, что компилятор может с большей уверенностью знать, как создавать связки Javascript. Ivy — это отдельная большая тема, но просто знайте, что многое из того, что мы можем делать сейчас в приложениях Angular, стало возможным благодаря Ivy.

Компоненты

Компоненты, которые будут использоваться в функции, должны быть объявлены в файле ngModule, как показано ниже.

Старый способ

   @NgModule({
     imports: [
       CommonModule /* This is an Angular Module that allows what is 
                     exported form this module to be used 
                     in other modules */
     ],
     declarations: [
       MyComponent
     ],
     exports: [ // Anything in this array allows us to use in other modules
       MyComponent
     ]
   })
   export class MyModule(){}

    @Component({
       selector: 'my-comp',
       template: `<div></div>`,
       styleUrls: ['./my-component.component.scss']
    })
    export class MyComponent{}
Войти в полноэкранный режим Выйти из полноэкранного режима

Чтобы использовать его в новой функции, нужно сделать следующее:

   @NgModule({
     imports: [
       CommonModule,
       MyModule
     ]
   })
   export class NewModule{}
Войти в полноэкранный режим Выйти из полноэкранного режима

Новый способ

@Component({
  selector: 'my-comp',
  template: '<div></div>',
  styleUrls: ['./my-component.component.scss'],
  standalone: 'true',
  imports: [
    CommonModule
  ],
  exportAs: 'my-comp'
})
export class MyComponent(){}
Войти в полноэкранный режим Выйти из полноэкранного режима

Чтобы использовать его из устаревшего модуля, вы можете сделать следующее:

@NgModule({
  imports: [
    CommonModule,
    MyComponent
  ]
})
export class NewModule{}
Войти в полноэкранный режим Выйти из полноэкранного режима

Чтобы использовать его из другого отдельного компонента, вы можете сделать следующее:

@Component({
  ...,
  imports: [
    CommonModule,
    MyComponent
  ],
  standalone: true
})
export class NewComponent
Войти в полноэкранный режим Выйти из полноэкранного режима

Инъекция зависимостей и сервисы

Angular в значительной степени опирается на использование инъекции зависимостей в качестве основного компонента Angular. Инъекция зависимостей обеспечивает способ логического разделения проблем в кодовой базе, чтобы сделать ваши приложения более удобными для сопровождения и тестирования.

В Angular экземпляр класса, который внедряется в другой класс, должен быть зарегистрирован. Это может быть сделано на уровне приложения, модуля или компонента. Например, если сервис зарегистрирован в файле app.module.ts, который является модулем, загружаемым при запуске приложения, класс, зарегистрированный в модуле app, будет singleton. Это означает, что в приложении работает только один экземпляр класса.

Angular сильно склоняется к тому, чтобы сделать injectable класс синглтоном, поэтому они создали способ регистрации класса внутри класса. Для этого вы можете просто зарегистрировать класс следующим образом

@Injectable({
  providedIn: 'root'
})
export class MyService{}
Войти в полноэкранный режим Выйти из полноэкранного режима

Маршрутизация

Мы говорили о возможности ленивой загрузки модулей (или пучков Javascript) только тогда, когда код действительно необходим. Это делается с помощью системы маршрутизации Angular. Поскольку раньше мы использовали модули для группировки кода, Angular может определить, когда именно отправлять пакет в браузер для использования, основываясь на том, перешел ли пользователь по определенному маршруту. Ниже мы покажем старый способ, а затем покажем, как это сделать в Angular 14 с помощью отдельных компонентов.

Старый способ

main.ts
platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch((err) =>  console.error(err));


app.module.ts
const routes: Route[] = [
  {
    'path': 'my-feature',
    'loadChildren': () => import('./my-feature/my-feature.module').then(m => m.MyModule)
  },
  {
    'path': '',
    redirectTo: 'my-feature'
  }
];
@NgModule({
  imports: [
    RouterModule.forRoot(routes),
    StoreModule.forRoot({})
  ]
})
export class AppModule{}


my-feature.ts
const routes: Route[] = [
  {
    path: 'comp-one',
    component: ComponentOne
  },
  {
    path: '',
    component: ComponentTwo,
    pathMatch: 'full'
  }
];
@NgModule({
  imports: [
    CommonModule,
    RouterModule.forFeature(routes),
    StoreModule.forFeature('my-feature', featureReducer)
  ]
})
export class FeatureOne{}
Войти в полноэкранный режим Выйти из полноэкранного режима

Новый способ


my-feature.routes.ts
const MY_FEATURE_ROUTES: Route[] = [
  {
    path: 'comp-one',
    loadComponent: () => import('./components/comp-one.component.ts').then(c => c.ComponentOne)
  },
  {
    path: '',
    loadComponent: () => import('./components/comp-two.component.ts’).then(c => c.ComponentTwo),
    pathMatch: 'full'
  }
];

main.ts

const routes: Route[] = [
  {
    path: 'my-feature',
    loadChildren: () => import('./my-feature.routes.ts').then(m => m.MY_FEATURE_ROUTES),
    providers: [
      importProvidersFrom(
        StoreModule.forFeature('my-feature', featureReducer)
      )
    ]
  }
];

bootstrapApplication(
  AppComponent,
  {
     providers: [
       importProvidersFrom(
         RouterModule.forRoot(routes),
         StoreModule.forRoot({}),
         EffectsModule.forRoot([]),
         StoreDevtoolsModule.instrument({}),
         HttpClientModule,
         BrowserAnimationsModule
       )
    ]
  }
)
Войти в полноэкранный режим Выйти из полноэкранного режима

Вот и все. Теперь нам не нужны никакие модули в наших приложениях Angular! Счастливого кодинга!

Следуйте за мной в Twitter

Оцените статью
devanswers.ru
Добавить комментарий