Это продолжение статьи Гибридные мобильные приложения: совместное использование логики между приложениями Angular2 и Ionic2. В 2016 году это была наша первая попытка поделиться бизнес-логикой на основе Ngrx между приложением Angular2 и мобильным приложением Ionic2, но при этом использовался довольно нестабильный npm link хак с тремя отдельными проектами npm Typescript.

С последними интерфейсами командной строки Angular8 и Ionic4 теперь есть очень элегантный способ добиться этого благодаря структуре проекта монорепо.

Цели остались прежними:

  • инкапсулировать всю бизнес-логику (на основе @ ngrx / store) в основной модуль и
  • сохраняйте конкретный макет представления, разметку и логику навигации в проектах мобильных и веб-приложений.

Примечание: @ ngrx / store - это управление состоянием на основе RxJS, вдохновленное Redux для приложений Angular. В настоящее время это самый популярный способ структурировать сложную бизнес-логику в приложениях Angular.

Демо-приложение и репозиторий на Github

Вот репо нашего доказательства концепции:



Репо использует структуру монорепозитория Nx Workspace (с настраиваемой интеграцией Ionic, подробно описанной позже):

  • /, корень проекта на основе Angular (версия 8.1.2) и @ ngrx / store (версия 8.1.0), управляемый Angular CLI
  • / libs / core, общий модуль с логикой состояния, редукторов и действий,
  • / apps / web, веб-приложение.
  • / apps / mobile, мобильное приложение на базе Ionic (версия 4.7.1), управляемое Ionic CLI

Для демонстрации мы используем пример кода счетчика (действия и редукторы) из @ ngrx / store.

Модуль core обеспечивает:

  • CounterState интерфейс, модель состояния счетчика
  • Редуктор counterReducer, управление состоянием счетчика на основе отправленных действий,
  • Действия счетчика увеличения, уменьшения и сброса

В мобильном или веб-приложении мы используем эти активы для построения модели состояния приложения.

import {CounterState} from ‘@ngrx-demo/core’;
export interface AppState {
  counter: CounterState,
  // Add other states here
}

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

import {ActionReducerMap} from '@ngrx/store';
import {counterReducer} from '@ngrx-demo/core';
import {AppState} from './app.state';
const reducers: ActionReducerMap<AppState> = {
    counter: counterReducer
};
@NgModule({
  imports: [
    BrowserModule,
    StoreModule.forRoot(reducers)
  ]
  bootstrap: [
    AppComponent
  ],
  declarations: [
    AppComponent
  ],
  
})
export class AppModule { }

Теперь мы можем использовать эти активы в любом компоненте представления приложения.

// HTML component template
<h2>
  {{counter$ | async}}
</h2>
<p>
  <button (click)=”increment()”> +1 </button>
  <button (click)=”decrement()”> -1 </button>
</p>
<p>
  <button (click)=”reset()”> Reset </button>
</p>

С соответствующим компонентом:

// Typescript angular component
import {CounterActions} from '@ngrx-demo/core';
import {AppState} from './app.state';
@Component({
  templateUrl: 'some.component.html'
})
export class SomeComponent {
  counter$: Observable<number>;
  constructor(private store: Store<AppState>) {
    this.counter$ = this.store.select(s => s.counter.total);
  }
  decrement() {
    this.store.dispatch(new CounterActions.DecrementAction());
  }
  increment() {
    this.store.dispatch(new CounterActions.IncrementAction());
  }
  reset() {
    this.store.dispatch(new CounterActions.ResetAction());
  }
}

Вся бизнес-логика полностью заключена в общий модуль. Например, мы могли бы добавить в микс @ ngrx / effects, чтобы обрабатывать асинхронные вызовы API.

Представление «знает» только о доступных действиях для отправки и модели состояния, на которую оно может подписаться.

Интеграция приложения Ionic в рабочую область Nx

По умолчанию Nx Workspaces не поддерживает приложения Ionic. Поскольку Ionic использует собственный интерфейс командной строки и зависимости для создания, тестирования и запуска приложения, / app / mobile имеет собственный /app/mobile/package.json (который не зависит от из основного рабочего пространства /package.json), и поэтому он является собственным / app / mobile / node_modules.

Ионная настраиваемая конфигурация

В package.json Ionic предоставляет несколько хуков для настройки конфигурации по умолчанию. Мы используем параметры конфигурации ionic_watch и ionic_webpack и 2 пользовательских файла конфигурации, хранящихся в / app / mobile / config.

// app/mobile/package.json
{
  ...
  "description": “An Ionic project”,
  "config": {
    "ionic_watch": "./config/watch.config.js",
    "ionic_webpack": "./config/webpack.config.js"
   },
  ...
}

Разрешение модуля машинописного текста

Чтобы иметь возможность использовать наши общие модули и получить правильное разрешение модуля Typescript, мы используем настраиваемое свойство сопоставления путей в /app/mobile/tsconfig.json. В нашем приложении мы используем @ ngrx-demo, но вы можете, например, использовать @local.

// app/mobile/tsconfig.json
{
 "compilerOptions": {
 ...
   "baseUrl": ".",
   "paths": {
     "@ngrx-demo/*": ["../../libs/*"]
   }
 ...
}

И TSConfig paths plugin webpack plugin в нашей настраиваемой конфигурации Webpack, чтобы Webpack знал об этих настраиваемых сопоставлениях путей.

// app/mobile/config/webpack.config.js
const TsconfigPathsPlugin = require(‘tsconfig-paths-webpack-plugin’);
const resolveConfig = {
 extensions: [‘.ts’, ‘.js’, ‘.json’],
 modules: [path.resolve(‘node_modules’)],
 plugins: [
   new TsconfigPathsPlugin({})
 ]
};
// Default config update
const webpackConfig = require(‘../node_modules/@ionic/app-scripts/config/webpack.config’); 
webpackConfig.dev.resolve = resolveConfig;
webpackConfig.prod.resolve = resolveConfig;

Живая перезагрузка

Приятно иметь возможность получать выгоду от интерактивной перезагрузки во время разработки: при запуске ionic serve, если файл обновляется в / libs / *, изменения обнаруживаются, и мобильное приложение автоматически перезагружается в вашем браузере. Для этого мы используем настраиваемую конфигурацию часов:

// app/mobile/config/watch.config.js
// Default config update
const watchConfig = require(‘../node_modules/@ionic/app-scripts/config/watch.config’);
watchConfig.srcFiles.paths = [
 ‘{{SRC}}/**/*.(ts|html|s(c|a)ss)’,
 ‘../../libs/**/*.(ts|html|s(c|a)ss)’
];

Инструменты разработчика Ngrx Store

И, наконец, что не менее важно, во время разработки Ngrx Store Dev Tools необходимо для отладки вашего приложения, но он должен быть отключен в производственной среде. Для этого мы используем настраиваемые сопоставления путей @ app / env (чтобы имитировать переменные среды в приложениях Angular) благодаря https://github.com/gshigeto/ionic-environment-variables.

// app/mobile/tsconfig.json
{
 "compilerOptions": {
 ...
    "baseUrl": ".",
    "paths": {
      "@app/env": ["src/environments/environment"],
      "@ngrx-demo/*": ["../../libs/*"] 
    }
 ...
}

С сопоставлением псевдонимов в нашей настраиваемой конфигурации Webpack.

// app/mobile/config/webpack/config.json
...
webpackConfig.dev.resolve.alias = {
 "@app/env": path.resolve('../src/environments/environment.ts')
};
webpackConfig.prod.resolve.alias = {
 "@app/env": path.resolve('../src/environments/environment.prod.ts')
};

Затем мы можем просто импортировать переменные среды в наше приложение Ionic!

import {StoreModule} from '@ngrx/store';
import {StoreDevtoolsModule} from '@ngrx/store-devtools';
...
import {environment} from '@app/env';
@NgModule({
  ...
  imports: [
    BrowserModule,
    IonicModule.forRoot(AppComponent),
    DemoCoreModule,
    StoreModule.forRoot(reducers),
    EffectsModule.forRoot([]),
    !environment.production ? StoreDevtoolsModule.instrument({maxAge: 25}) : []
  ],
  ...
})
export class AppModule { }

Преимущества рабочего пространства и монорепо

Вот и все! Как видите, эта новая структура проекта намного чище, чем первоначальное решение 2016 года.

Он обладает всеми преимуществами, описанными в документации Nx Зачем нужна рабочая область (за исключением унифицированного управления зависимостями, поскольку приложение Ionic требует собственных зависимостей):

Единое управление версиями

Все в этом текущем коммите работает вместе

Ярлык или ветка могут фиксировать то же самое

Продвигает совместное использование и повторное использование кода

Легко разбить код на модули библиотеки

Легко использовать / внедрять этот код и последние изменения в нем.

Преимущества рефакторинга

Редакторы кода и IDE осведомлены о «рабочем пространстве»

Может иметь одну фиксацию для рефакторинга, охватывающего приложения в домене.

Единый опыт разработчика

Обеспечивает доступность всего необходимого зависимого кода

Если у вас есть какие-либо вопросы или предложения по улучшению демонстрационного приложения, не стесняйтесь отправлять вопрос или запрос на перенос!

Примечание: мы нанимаем! Вы обалденный fullstack или front-end разработчик, который хочет работать на Angular, Java или Groovy? Вы должны связаться со мной, чтобы присоединиться к команде нашей мечты в Париже!

Если вам понравилась эта статья, нажмите кнопку ❤, чтобы порекомендовать ее. Это поможет другим пользователям Medium это обнаружить.