Reactã¨Cordovaã§ãWeb/ã¢ãã¤ã«ã®ãã¤ããªããã¢ããªãä½ã£ã話
Reactã¨Cordovaã使ã£ã¦ããã©ã¦ã¶åãã®WebUI + Androidã§åãã¹ããã¢ã㪠ãæä¾ãããµã¼ãã¹ããä¸äººã§ä½ã£ã¦ã¿ã話ã§ãã
ãµããªã¼
- ä½ã£ããã®
- æ大ã®èª²é¡:ä½æ¥é
- ä¸äººã§ä½ãããããã«æèãããã¨
- åãçµã¿1: Cordovaã使ã£ã¦ãWeb UI/ã¹ããã¢ããªã®ã³ã¼ããå ±éåãã
- åãçµã¿2: ã¬ã¤ã¤ã¼ãã¢ã¼ããã¯ãã£ãæ¡ç¨ããå ±æã§ããã³ã¼ããæ大åãã
- åãçµã¿3: ã¦ããããã¹ããæ¸ã
- ã¾ã¨ã
ä½ã£ããã®
èªåã ãã®åå¼ã¢ã«ã´ãªãºã ã§ã誰ã§ãããã¾ãããããããã«FXèªååå¼ãéå§ã§ãããã·ã¹ãã ãã¬ã¼ããã¬ã¼ã ã¯ã¼ã¯ã§ãã
- ã¢ã«ã´ãªãºã ã®ä½æãããã¯ãã¹ãããªã¢ã«å£åº§ã§ã®èªååå¼ã¾ã§ãããä¸ã¤ã§å¯è½ã
- åå¼ã¢ã«ã´ãªãºã ã¯Rubyã§è¨è¿°ãã¡ã¼ã«éä¿¡ãåå¼ã¿ã¤ãã³ã°ã®Pushéç¥ããAPIãå¼ã³åºãã ãã§å®ç¾ã§ãã¾ãã
- åå¼ã®ç¶æ³ã¯ãã¹ããã¢ããª/Web UIã§ãã¤ã§ãã©ãã§ã確èªãå¯è½ãã¢ã«ã´ãªãºã ã®ç®¡çãã§ããã®ã§ãç¸å ´ãæ¥ã«åãã¦ãå®å¿ã§ãã
- ã¯ã©ã¦ããã©ãããã©ã¼ã ã®Herokuã«å¯¾å¿ãéç¨ãä½ã³ã¹ãã
- ãªã¼ãã³ã½ã¼ã¹ãã½ã¼ã¹ã³ã¼ãã¯GitHubã§å ¬éãã¦ãã¾ãã
ã¹ã¯ãªã¼ã³ã·ã§ãããããã¤ãã
Web UI:
ã¹ããã¢ããª:
ã³ã¼ãè¦æ¨¡
UIå´ã®ã³ã¼ãè¦æ¨¡ã¯ä»¥ä¸ã®éãããã¹ãã±ã¼ã¹ãå«ããåè¨ã§ã31000è¡ãããã§ãã
WebUI | ã¢ã㪠| åè¨ | |
---|---|---|---|
ã½ã¼ã¹ã³ã¼ã | 15222 | 4454 | 19676 |
ãã¹ãã±ã¼ã¹ | 9676 | 1574 | 11250 |
ã¾ããããã¨ã¯å¥ã«ããµã¼ãã¼ã®ã³ã¼ã(ruby)ããã½ã¼ã¹ã³ã¼ã/ãã¹ãã±ã¼ã¹ãããã¦ã27000è¡ãããããã¾ãã
æ§æ
- UIã¯ãããããã·ã³ã°ã«ãã¼ã¸ã¢ããªã±ã¼ã·ã§ã³ã§ãECMAScript2015 ã§æ¸ãã¦ãã¾ãã
- Babelã§ã³ã³ãã¤ã«ãã¦ãWebpackã§çµ±åããå½¢ã
- ãããªã¢ã«ãã¶ã¤ã³ãæ¡ç¨ãã¦ãã¦ãReactã§åãUIã©ã¤ãã©ãªã® Material UI ãå©ç¨ãã¦ãã¾ãã
åç½®ãã¯ä»¥ä¸ã
æ大ã®èª²é¡:ä½æ¥é
éçºã«ããã£ã¦ã®æ大ã®èª²é¡ã¯ããªãã¨ãã£ã¦ãä½æ¥éã§ããã
åæã®æ¤è¨æ®µéã§ããWebã¢ã㪠or ã¢ãã¤ã«ã¢ããªã®ã©ã¡ããã«æ³¨åã§ããªãã?ãã¨èãã¾ãããã
Pushéç¥ãã¢ãã¤ã«ã§ã®ã·ã¹ãã 管çã«å¯¾å¿ãããã¹ããæ代ã®FXã·ã¹ãã ãã¬ã¼ããã¬ã¼ã ã¯ã¼ã¯ã«ãããã£ãã
- åæ§ã®æ©è½ãæä¾ããã½ããã¯ãã§ã«ããã®ã§ãããã¢ãã¤ã«ã§ã®åå¼ç¶æ³ã®ç¢ºèªãã¢ã«ã´ãªãºã ã®ç®¡çã«é£ããã£ãã®ããéçºã®ãã£ããã«ããªã£ã¦ãã¾ãã
ã¨ã¯ãããã¹ããã§åå¼ã¢ã«ã´ãªãºã ãä½æããããåå¼çµæãåæããã®ã¯ãã©ãã
ã¨ãããã¨ã§ã両æ¹å¿ è¦ã¨ããçµè«ã«è³ãã¾ãããããã¨æ±ºã¾ãã°ãå¾ã¯ãå¦ä½ã«ãã¦ä½ããããã§ãã
ä¸äººã§ä½ãããããã«æèãããã¨
以ä¸ã®2ç¹ããç¹ã«æèãã¾ããã
- ã³ã¼ãã®å
±éå/åå©ç¨
- Web UI/ã¹ããã¢ããªã®ã³ã¼ããå¯è½ãªéãå ±éåãã¦ãéçºã³ã¹ããåæ¸ããã
- ã¦ããããã¹ã
- ã¦ããããã¹ãã§åã ã®ã¢ã¸ã¥ã¼ã«ã®åä½ãä¿éã
- æ©è½è¿½å ãå¤æ´ããä½ã³ã¹ãã§ç´ æ©ãè¡ããããã«ããã
ããã¦ãâã®ããã®å ·ä½çãªåãçµã¿ã¨ãã¦ã以ä¸ãè¡ãã¾ããã
- Cordovaã使ã£ã¦ãWeb UI/ã¹ããã¢ããªã®ã³ã¼ããå ±éåãã
- ã¬ã¤ã¤ã¼ãã¢ã¼ããã¯ãã£ãæ¡ç¨ããå ±æã§ããã³ã¼ããæ大åãã
- ã¦ããããã¹ããæ¸ã
åãçµã¿1: Cordovaã使ã£ã¦ãWeb UI/ã¹ããã¢ããªã®ã³ã¼ããå ±éåãã
ã¹ããã¢ããªãCordovaã使ã£ããã¤ããªããã¢ããªã«ãã¦ãWeb UI/ã¹ããã¢ããªã®ã³ã¼ããå ±éåãã¾ããã
- Cordovaã使ããã¨ã§ãJavaScript + HTML + CSSã§ã¹ããã¢ããªãä½ããã¨ãã§ããã®ã§ãåæ§ã®æ§æã§ä½æããWeb UIã®ã³ã¼ããåå©ç¨ã§ããããã«ãªãã¾ãã
- React Native ã¨ããé¸æè¢ãããã¾ããããUIå´ã®éçºã«çæããæç¹ã§ã¯Androidã«ã¯å¯¾å¿ãã¦ããªãã£ããããè¦éãã¾ããã
- Cordovaã¯ãWebã§ã®æ å ±ãè±å¯ã§ãå®ç¸¾ãå¤ããã£ãç¹ããã©ã¹ã§ããã
- ã¾ããPushéç¥ã課é決æ¸é¢é£ã®ãã©ã°ã¤ã³ããã£ããã¨ã大ããã§ãã
- æ¡ç¨ã«å½ãã£ã¦æ¸å¿µã ã£ããUIã®ããã©ã¼ãã³ã¹ã«ã¤ãã¦ã¯ããããããªãããã¿ã¤ããä½ã£ã¦æ¤è¨¼ãã¾ããã
- ãã¯ãããã¤ãã£ãã¢ããªããã¯ãã£ãããã¦ãã¾ãããã²ã¼ã ã®ãããªããã©ã¼ãã³ã¹ãã¢ããªã®ä¾¡å¤ã«ç´çµããæ§è³ªã®ã½ããã¦ã§ã¢ã§ã¯ãªãã®ã§ãåé¡ãªãã¬ãã«ã¨å¤æã
- ãããããããªãªã¼ã¹ã¾ã§ã®éçºå·¥æ°ãæãããã¨ãã¾ãããªãªã¼ã¹å¾ã®æ©è½å¼·åãæ¹åãã¹ãã¼ãæãæã£ã¦è¡ãããã¨ããéè¦ãã¾ããã unageanu.hatenablog.com
ãUIã®åå¿é度ã¨ã使ãåæã¨ããå®éã©ããªãããªã®?ãã¨æ°ã«ãªãæ¹ã¯ããã¦ã³ãã¼ããã¦ã試ãé ããã°ã¨æãã¾ãã(ã¾ãã©ããããã¨ããããã¾ã! / 30æ¥ã®ç¡æãã©ã¤ã¢ã«æéå
ã«ãå®æå©ç¨ã解é¤ããã°è«æ±ã¯çºçããªãã®ã§å¤§ä¸å¤«ã§ã)
ãªããCrosswalkã®å¹æã¯çµ¶å¤§ã§ãããä½æé度ãåçã«åä¸ãããããæªããåãããã¦ããç®æãæ²»ã£ããã apkã®ãµã¤ãº/èµ·åæéã¯å¢ãã¾ãããæ¡ç¨ãã価å¤ã¯ããã¾ãã
åãçµã¿2: ã¬ã¤ã¤ã¼ãã¢ã¼ããã¯ãã£ãæ¡ç¨ããå ±æã§ããã³ã¼ããæ大åãã
MVVMã®ã¬ã¤ã¤ã¼ãã¢ã¼ããã¯ãã£ãæ¡ç¨ãã¦ãModelã¬ã¤ã¤ã®ã³ã¼ããå
±éåã
ViewãViewModelããå¤æ´ã®åº¦åãã«å¿ãã¦æé©ãªã¬ã¤ã¤ã¼ã§ã«ã¹ã¿ãã¤ãºãããã¨ã§ãå
±æã§ããã³ã¼ããæ大åãã¾ããã
- Model/View/ViewModel + éä¿¡ãªã©ã®Infrastructureã§æ§æã
- Model(UIã«ä¾åããªããã¢ããªå ±éã®ã³ã¢ãã¡ã¤ã³)ã¯ãWeb UI/ã¹ããã¢ããªã§ãã®ã¾ã¾å ±æã
- ã¹ã¿ã¤ã«ã®å¤æ´ã ãã§æ¸ãå ´åã¯ãCSSã®ã¬ãã«ã§ã«ã¹ã¿ãã¤ãºã
- DOMæ§é ãå¤ããå¿ è¦ãããå ´åã¯ãView(Reactã³ã³ãã¼ãã³ã)ã®ã³ã¼ããå·®ãæ¿ãã¦å¯¾å¿ã
- Viewã§ç®¡çãããã¼ã¿ãæ©è½ãããããç°ãªãå ´åã¯ãViewModel(Modelãã©ãããã¦ãViewã«ä¾åãããã¼ã¿ãæä½ãæä¾ããã¯ã©ã¹ç¾¤)ã®ã¬ãã«ã§ãã«ã¹ã¿ãã¤ãºãã¦åå©ç¨ã
- ä¾ãã°ãWebUIã§ã¯ã"éç¥ã®ä¸è¦§ã表示ããæ©è½"ã¨"é¸æããéç¥ã®è©³ç´°ã表示ããæ©è½"ãåãç»é¢ã§æä¾ãã¦ãã¾ãããã¹ããã¢ããªã§ã¯å¥ç»é¢ã«ãã¦ãã¾ãã ãã®ãããWebUIçã§ã¯ãéç¥ã®ä¸è¦§ç»é¢ã®ViewModelã§éç¥ä¸è¦§ã¨é¸æç¶æ ã管çãã¦ãã¾ãããã¹ããã¢ããªçã§ã¯2ã¤ã®ViewModelã§ç®¡çããå½¢ã«å¤æ´ãã¦ãã¾ãã
ãã®ã»ããInfrastructureã®éä¿¡é¨åãGoogle Analyticsã§ã®å©ç¨ç¶æ³è§£æããWebUI/ã¹ããã¢ããªã§ã«ã¹ã¿ãã¤ãºãã¦å©ç¨ãã¦ãã¾ãã
- éä¿¡é¨åã¯æ¥ç¶å REST APIãå·®ãæ¿ããå¿ è¦ããããã(Web UIã¯èªãã¹ãã«ãã¹ããã¢ããªã¯UIã§è¨å®ãããµã¼ãã¼ã«æ¥ç¶)ãURLResolverã¨ãã¦æ©è½ãåãåºãã«ã¹ã¿ãã¤ãºãã¦ãã¾ãã
- å©ç¨ç¶æ³è§£æã¯ãã¹ããã¢ããªã§ã¯Java APIã使ãããã«ã¹ã¿ãã¤ãºããã¯ã©ã¹ãç¨æãã¦å·®ãæ¿ãã
以ä¸ã¯ãå©ç¨ç¶æ³è§£æã®ã³ã¼ãã®æç²ã§ãã ã¤ã³ã¿ã¼ãã§ã¤ã¹ãçµ±ä¸ãã¤ã¤ãå®è£ ãå·®ãæ¿ããå½¢ã§ãå¥ã®ã¯ã©ã¹ãç¨æãã¾ããã
WebUIç:
export default class GoogleAnalytics { // ç¥ // Google Analytics ã®JavaScriptçAPIãå¼ã³åºã sendEvent( action, label="", value={} ) { this.run((ga) => ga('send', 'event', this.category, action, label, value)); } sendTiming( category, timingVar, time, label ) { this.run((ga) => { ga('timing', category, timingVar, time, label); }); } // ç¥ }
ã¹ããã¢ããªç:
export default class GoogleAnalytics { // ç¥ // Cordova google-analytics-plugin ã®APIãå¼ã³åºã // https://github.com/danwilson/google-analytics-plugin sendEvent( action, label="", value={} ) { this.run((ga) => { ga.trackEvent(this.category, action, label, value, () => {}, (e) => console.log(e)); }); } sendTiming( category, timingVar, time, label ) { this.run((ga) => { ga.trackTiming(category, time, timingVar, label, () => {}, (e) => console.log(e.message)); }); } // ç¥ }
ããããã³ã³ãã¼ãã³ãã®å·®ãæ¿ãã¯ãDI(Dependency Injection)ã³ã³ãããå©ç¨ãã¦ãè¡ãããã«ãã¾ããã DIã³ã³ããã使ããã¨ã§ãã³ã³ãã¼ãã³ãã®å¤å´ã§ãç°å¢ãã¨ã«ã©ã®ã¯ã©ã¹ã使ãã®ã宣è¨çã«æå®ã§ããããã«ãªãã¾ãã
WebUIçã®ã³ã³ãã¼ãã³ãå®ç¾©:
binder.bind("googleAnalytics").to("utils.GoogleAnalytics") .withProperties({ category: "web-ui", version: "1.0" }).onInitialize("initialize");
ã¹ããã¢ããªçã®ã³ã³ãã¼ãã³ãå®ç¾©:
// ã¹ããã¢ããªç¨ã®ã¯ã©ã¹ã使ãããã«å¤æ´ binder.bind("googleAnalytics").to("app.utils.GoogleAnalytics") .withProperties({ category: "app", // category, versionãã«ã¹ã¿ãã¤ãºã version: "1.0.6" }).onInitialize("initialize");
å©ç¨å´ã®ã³ã¼ãã¯ä»¥ä¸ã®ãããªæããInject
ã¨ãã¦ãã¼ã¯ãã¦ããã¨ãã³ã³ãã¼ãã³ãå®ç¾©ã§å®£è¨ãããã³ã³ãã¼ãã³ãã注å
¥ãããã®ã§ãå©ç¨å´ã¯APIãå¼ã³åºãã ãã§ãã
export default class RmtService extends AbstractService { constructor() { this.urlResolver = ContainerJS.Inject; this.xhrManager = ContainerJS.Inject; this.googleAnalytics = ContainerJS.Inject; // ã³ã³ãã¼ãã³ãå®ç¾©ã§å®£è¨ãããã³ã³ãã¼ãã³ããæ³¨å ¥ããã } // ç¥ putAgentSetting(settings) { this.googleAnalytics.sendEvent( "put rmt agent setting" ); // GAã«ã¤ãã³ããéä¿¡ return this.xhrManager.xhr(this.serviceUrl("agents"), "PUT", settings); } }
åãçµã¿3: ã¦ããããã¹ããæ¸ã
MVVMã¢ã¼ããã¯ãã£ãæ¡ç¨ãããã¨ã§ãView以å¤ã®é¨åã¯DOMã«ä¾åããªããªãããã容æã«ã¦ããããã¹ããã§ããããã«ãªãã¾ãã ã¦ããããã¹ããæèçã«ç¨æãããã¨ã§ãä¸äººã§ãä½ã¨ãä½ããããã¨ãã§ããããªãã¨æãã¾ãã
- ä¸äººã§éçºããã¨ããããããµã¼ãã¼å´ã®éçºã§1ãæãããrubyã®ã³ã¼ãã°ããæ¸ãã¦ããã¨ãèªåãæ¸ããã³ã¼ãã§ãå¿ãã¦ãã¾ã£ã¦ãå¤ãªãã°ãåãè¾¼ãã ããã¾ãããã¹ãã³ã¼ãããããã¨ã§ãã°ã¬ã¼ããæ¤ç¥ããããã°ã«æ°ä»ããã¨ãå¤ãããã¾ããã
- ã¾ããã³ã¼ããã©ãåãããã«ã¤ãã¦ãããã¹ãã±ã¼ã¹ãã¿ãã¨æãåºããã¨ãããã¨ãããã¾ããã
ã¦ããããã¹ãã§ç¢ºèªããã"åã"ã³ã¼ããå°ããã¤ç©ã¿ä¸ãã¦ãããã¨ããä½ãæ¹ã¯ãå¿ççã«ãè¯ãã£ãã¨æãã¾ãã ä¸äººãªã®ã§ãéçºæéã¯ã©ããã¦ãé·ããªãã¾ããããã£ã¦ããã£ã¦ãã¿ã¹ã¯ããªããªããªããçµãããè¦ããªãæãã«ãªãã¾ããã"åã"ã³ã¼ããç©ã¿ä¸ãã£ã¦ãããã¨ã§ãæ¯æ¥å°ããã¤ã§ãã³ã¼ããæ¸ãã¦ããã°ããã¤ãå®æããã¨æããããã«ãªãã¾ãã
ãªããåçãªãªã¼ã¹ã®æ®µéã§ã¯Viewã®ãã¹ãã¯çç¥ãã¾ããã
- åçãªãªã¼ã¹ã§ã¯ãUIã®ç²¾ç·»åãªã©ã§DOMã®æ§é ãå¤æ´ããå¯è½æ§ãé«ãããã¹ããä½æãã¦ããå¹æãå¤æ´ã³ã¹ãã«è¦åããªãã¨èãã¾ããã
- ã¾ããViewã®æ©è½ã¯ãViewModelã®ç¶æ ã«å¿ãã¦ä»®æ³DOMãçæãããã ãã§ããã¹ããã¹ããã¸ãã¯ãå°ãªã && ViewModelã®ãã¹ãã§ãã¢ããªã±ã¼ã·ã§ã³ãããããç¶æ ã®æã¯ãããªããããæä½ãå®è¡ããã¨ããããç¶æ ã«ãªããã¨ãã£ãæ©è½ã¯ç¢ºèªã§ãã¦ãããã¨ããã®ãçç±ã¨ãã¦ããã¾ãã
- åãçç±ã§ãE2Eãã¹ããè¡ã£ã¦ãã¾ããããã®ãããã¯ãä»å¾ããµã¼ãã¹ãå©ç¨ãããããã«ãªã£ã¦ãã¡ã³ããã³ã¹ã®æ¯éã大ãããªã£ã¦ããã®èª²é¡ã¨èãã¦ãã¾ãã
ã¾ã¨ã
- Cordovaã使ã£ã¦ã¡ããã¨ä½ãã°ãä¸äººã§ããµã¼ãã¹ãä½ããã
- ãããã¿ã¤ããæåã®MVPã¯ãCordovaã§ç´ æ©ãã³ã¹ãããããã«ä½ããããã¦ãæãããããã£ãããã¤ãã£ãã§ä½ãç´ããã¨ããé¸æè¢ã¯å²ã¨ã¢ãªãªã®ã§ã¯ã¨æãã¾ãã
- ãã¡ãããããã©ã¼ãã³ã¹ãéè¦ãªã¢ããªã®å ´åã¯ãä¸ç¸å¿ãªå ´åãããã¾ããã
- ã¦ããããã¹ãéè¦ããã¹ã¿ããªãã£ã¯æåããèæ
®ãã¦ä½ããã
- ã¦ããããã¹ããç¨æãããã¨ã§ãå¤æ´ãããç´ãã®ã³ã¹ããæå°ã«ã§ãããã¼ã¿ã«ã®éçºã³ã¹ãã大ããåæ¸ã§ãã¾ãã
- ã¦ããããã¹ãã§ç¢ºèªããã"åã"ã³ã¼ããå°ããã¤ç©ã¿ä¸ãã¦ãããã¨ããä½ãæ¹ã¯ãå¿ççã«ãå¹æ大ã§ãã
- Crosswalkéãã使ããã