นี่เป็นบทความติดตามผลเกี่ยวกับ "แอปมือถือไฮบริด: การแชร์ตรรกะระหว่างแอป Angular2 และ Ionic2" ในปี 2559 นี่เป็นความพยายามครั้งแรกของเราในการแบ่งปันตรรกะทางธุรกิจที่ใช้ Ngrx ระหว่างแอป Angular2 และแอปมือถือ Ionic2 แต่ใช้ npm link
“แฮ็ก” ที่ค่อนข้างไม่เสถียร โดยมีโปรเจ็กต์ Typescript npm แยกกันสามโปรเจ็กต์
ด้วย CLI ล่าสุดของ Angular8 และ Ionic4 ขณะนี้มีวิธีที่สวยงามมากในการบรรลุเป้าหมายนี้ ต้องขอบคุณโครงสร้างโปรเจ็กต์แบบ mono-repo
เป้าหมายยังคงเหมือนเดิม:
- สรุปตรรกะทางธุรกิจทั้งหมด (ตาม @ngrx/store) ในโมดูลหลัก และ
- รักษาเค้าโครงมุมมอง มาร์กอัป และตรรกะการนำทางเฉพาะในโครงการแอปมือถือและเว็บ
หมายเหตุ: @ngrx/store คือการจัดการสถานะที่ขับเคลื่อนด้วย RxJS ที่ได้รับแรงบันดาลใจจาก Redux สำหรับแอป Angular ปัจจุบันเป็นวิธีที่ได้รับความนิยมมากที่สุดในการจัดโครงสร้างตรรกะทางธุรกิจที่ซับซ้อนในแอป Angular
แอปสาธิตและ repo Github
นี่คือ repo ของการพิสูจน์แนวคิดของเรา:
repo ใช้โครงสร้าง mono-repo 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
โมดูล หลัก ให้:
- อินเทอร์เฟซ 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 async
มุมมองเท่านั้นที่ "รู้" เกี่ยวกับการดำเนินการที่มีอยู่เพื่อจัดส่งและโมเดลสถานะที่สามารถสมัครรับข้อมูลได้
การรวมแอป Ionic เข้ากับ Nx Workspace
ตามค่าเริ่มต้น Nx Workspaces ไม่รองรับแอป Ionic เนื่องจาก Ionic ใช้ CLI ของตัวเองและการพึ่งพาในการสร้าง ทดสอบ และเรียกใช้แอป /app/mobile จึงมี /app/mobile/package.json ของตัวเอง (ซึ่งเป็นอิสระ จากพื้นที่ทำงานหลัก /package.json) และดังนั้นจึงเป็น /app/mobile/node_modules ของตัวเอง
การกำหนดค่าแบบกำหนดเองของอิออน
ใน package.json Ionic มี hook หลายตัวเพื่อปรับแต่งการกำหนดค่าเริ่มต้น เรากำลังใช้ตัวเลือกการกำหนดค่า 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
เพื่อให้สามารถใช้โมดูลที่ใช้ร่วมกันของเราและได้รับ Typescript ความละเอียดของโมดูล ที่ถูกต้อง เราจะใช้คุณสมบัติ การแมปเส้นทาง ที่กำหนดเองใน /app/mobile/tsconfig.json ในแอปของเรา เราใช้ @ngrx-demo แต่คุณสามารถใช้ @local เป็นตัวอย่างได้
// app/mobile/tsconfig.json { "compilerOptions": { ... "baseUrl": ".", "paths": { "@ngrx-demo/*": ["../../libs/*"] } ... }
และ ปลั๊กอิน TSConfig paths webpack ในการกำหนดค่า 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)’ ];
เครื่องมือ Dev ของร้านค้า Ngrx
และสุดท้ายไม่ท้ายสุด ในระหว่างการพัฒนา เครื่องมือ Ngrx Store Dev เป็นสิ่งจำเป็นในการดีบักแอปของคุณ แต่จะต้องปิดใช้งานในสภาพแวดล้อมการใช้งานจริง เราใช้การแมปเส้นทาง @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 { }
ข้อดีของพื้นที่ทำงานและ mono-repo
แค่นั้นแหละ! อย่างที่คุณเห็น โครงสร้างโปรเจ็กต์ใหม่นี้สะอาดกว่าโซลูชันเริ่มต้นปี 2559 มาก
มันได้รับประโยชน์จากข้อดีทั้งหมดที่อธิบายไว้ในเอกสาร Nx ทำไมถึงเป็นพื้นที่ทำงาน (ยกเว้นการจัดการการพึ่งพาแบบรวมเนื่องจากแอป Ionic ต้องการการพึ่งพาของตัวเอง):
เวอร์ชันแบบรวม
ทุกสิ่งในคอมมิตปัจจุบันนั้นทำงานร่วมกัน
ฉลากหรือสาขาสามารถจับภาพได้เหมือนกัน
ส่งเสริมการใช้รหัสร่วมกันและการใช้ซ้ำ
ง่ายต่อการแยกโค้ดออกเป็นโมดูล lib
ง่ายต่อการใช้งาน/ปรับใช้โค้ดนั้นและการเปลี่ยนแปลงล่าสุด
คุณประโยชน์ในการปรับโครงสร้างใหม่
โปรแกรมแก้ไขโค้ดและ IDE ต่างตระหนักถึง “พื้นที่ทำงาน”
สามารถมีคอมมิตเดียวสำหรับรีแฟกเตอร์ที่ครอบคลุมแอปพลิเคชันในโดเมน
ประสบการณ์ของนักพัฒนาซอฟต์แวร์ที่สอดคล้องกัน
ตรวจสอบให้แน่ใจว่าโค้ดที่ต้องพึ่งพาที่จำเป็นทั้งหมดพร้อมใช้งาน
หากคุณมีคำถามหรือข้อเสนอแนะเพื่อปรับปรุงแอปสาธิต อย่าลังเลที่จะส่งปัญหาหรือขอดึง!
หมายเหตุ:เรากำลังรับสมัครงาน! คุณเป็นนักพัฒนา fullstack หรือ front-end ที่ต้องการทำงานบน Angular, Java หรือ Groovy หรือไม่? คุณต้องติดต่อฉันเพื่อเข้าร่วมทีมในฝันของเราในปารีส!
หากคุณชอบบทความนี้ โปรดกดปุ่ม ❤ เพื่อแนะนำ สิ่งนี้จะช่วยให้ผู้ใช้สื่อรายอื่นค้นพบสิ่งนี้ได้ง่ายขึ้น