{"id":904,"date":"2021-07-29T08:48:32","date_gmt":"2021-07-29T08:48:32","guid":{"rendered":"https:\/\/egany.com\/blogs\/?p=904"},"modified":"2021-09-08T08:19:37","modified_gmt":"2021-09-08T08:19:37","slug":"frontend-du-ky-s1e2-egany-apps-phan-2-noi-de-hon-lam","status":"publish","type":"post","link":"https:\/\/egany.com\/blogs\/frontend-du-ky-s1e2-egany-apps-phan-2-noi-de-hon-lam\/","title":{"rendered":"Frontend Du K\u00fd S1E2 | EGANY Apps (ph\u1ea7n 2): N\u00f3i d\u1ec5 h\u01a1n l\u00e0m"},"content":{"rendered":"<p>Nh\u01b0 \u0111\u00e3 n\u00f3i \u1edf ph\u1ea7n m\u1ed9t, <strong>Frontend Du K\u00fd S1E2 | EGANY Apps<\/strong> s\u1ebd k\u1ec3 v\u1ec1 nh\u1eefng l\u1ed7 h\u1ed5ng trong quy tr\u00ecnh l\u00e0m vi\u1ec7c c\u0169ng nh\u01b0 nh\u1eefng v\u01b0\u1edbng m\u1eafc v\u1ec1 k\u1ef9 thu\u1eadt m\u00e0 team g\u1eb7p ph\u1ea3i trong qu\u00e1 tr\u00ecnh ph\u00e1t tri\u1ec3n EGANY Apps.<\/p>\n<p>N\u1ebfu b\u1ea1n ch\u01b0a \u0111\u1ecdc ph\u1ea7n m\u1ed9t, b\u1ea1n c\u00f3 th\u1ec3 \u0111\u1ecdc tr\u01b0\u1edbc <a href=\"https:\/\/egany.com\/blogs\/frontend-du-ky-s1e1-egany-apps-phan-1-van-su-khoi-dau-nan\/\">t\u1ea1i \u0111\u00e2y<\/a>.<\/p>\n<h2>Gi\u1edbi thi\u1ec7u<\/h2>\n<p><img decoding=\"async\" src=\"https:\/\/egany.cdn.vccloud.vn\/blogs\/wp-content\/uploads\/2021\/07\/egany-apps-sign-in-screen-700x338.png\" alt=\"egany-apps-sign-in-screen\" \/><\/p>\n<p>Sau khi th\u1ed1ng nh\u1ea5t \u0111\u01b0\u1ee3c quy tr\u00ecnh l\u00e0m vi\u1ec7c c\u0169ng nh\u01b0 c\u00f4ng ngh\u1ec7 \u0111\u1ec3 th\u1ef1c hi\u1ec7n d\u1ef1 \u00e1n, team b\u1eaft \u0111\u1ea7u qu\u00e1 tr\u00ecnh ph\u00e1t tri\u1ec3n.<\/p>\n<p>K\u1ebf ho\u1ea1ch l\u00fac b\u1ea5y gi\u1edd bao g\u1ed3m:<\/p>\n<ul>\n<li>T\u1eadp trung ph\u00e1t tri\u1ec3n s\u01b0\u1eddn d\u1ef1 \u00e1n: authentication (sign-in\/sign-out), layout, &#8230;<\/li>\n<li>Ph\u00e1t tri\u1ec3n component \u0111\u1ec3 s\u1eed d\u1ee5ng chung<\/li>\n<li>Chuy\u1ec3n \u0111\u1ed5i c\u00e1c dashboard \u1edf h\u1ec7 th\u1ed1ng c\u0169<\/li>\n<\/ul>\n<p>\u0110\u00e2y l\u00e0 l\u00fac c\u00e1c v\u1ea5n \u0111\u1ec1 b\u1eaft \u0111\u1ea7u xu\u1ea5t hi\u1ec7n. \u0110\u1ec3 d\u1ec5 d\u00e0ng h\u01a1n cho c\u00e1c b\u1ea1n th\u00ec m\u00ecnh s\u1ebd chia c\u00e1c v\u1ea5n \u0111\u1ec1 th\u00e0nh hai nh\u00f3m ch\u00ednh, c\u1ee5 th\u1ec3:<\/p>\n<ul>\n<li>K\u1ef9 thu\u1eadt<\/li>\n<li>Quy tr\u00ecnh<\/li>\n<\/ul>\n<p>C\u00f9ng b\u1eaft \u0111\u1ea7u t\u00ecm hi\u1ec3u nha!<\/p>\n<blockquote>\n<p>Trong b\u00e0i vi\u1ebft m\u00ecnh s\u1ebd g\u1ecdi b\u00ean \u0111\u1ed1i t\u00e1c e-commerce (bao g\u1ed3m Haravan v\u00e0 Sapo) l\u00e0 <strong>platform<\/strong> \u0111\u1ec3 tr\u00e1nh vi\u1ec7c l\u1eb7p l\u1ea1i t\u00ean c\u1ee7a c\u00e1c n\u1ec1n t\u1ea3ng \u0111\u00f3.<\/p>\n<\/blockquote>\n<h2>K\u1ef9 thu\u1eadt<\/h2>\n<p><img decoding=\"async\" src=\"https:\/\/egany.cdn.vccloud.vn\/blogs\/wp-content\/uploads\/2021\/07\/van-de-ki-thuat-600x450.png\" alt=\"van-de-ki-thuat\" \/><\/p>\n<blockquote>\n<p>Ngu\u1ed3n \u1ea3nh: <a href=\"https:\/\/dribbble.com\/shots\/7133803-Programming\" rel=\"nofollow noopener\" target=\"_blank\">https:\/\/dribbble.com\/shots\/7133803-Programming<\/a><\/p>\n<\/blockquote>\n<h3>1. X\u00e1c th\u1ef1c ng\u01b0\u1eddi d\u00f9ng<\/h3>\n<p>EGANY Apps kh\u00f4ng c\u00f3 m\u1ed9t h\u1ec7 th\u1ed1ng x\u00e1c th\u1ef1c ho\u00e0n ch\u1ec9nh. EGANY ch\u1ec9 \u0111\u00f3ng vai tr\u00f2 l\u00e0 trung gian cho ph\u00eda platform. Flow x\u00e1c th\u1ef1c s\u1ebd nh\u01b0 h\u00ecnh sau:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/egany.cdn.vccloud.vn\/blogs\/wp-content\/uploads\/2021\/07\/sign-in-flow.jpg\" alt=\"sign-in-flow\" \/><\/p>\n<ol>\n<li>EGANY Apps g\u1eedi th\u00f4ng tin ng\u01b0\u1eddi d\u00f9ng c\u0169ng nh\u01b0 th\u00f4ng tin \u1ee9ng d\u1ee5ng v\u1ec1 ph\u00eda backend<\/li>\n<li>Backend nh\u1eadn th\u00f4ng tin v\u00e0 truy xu\u1ea5t th\u00eam m\u1ed9t v\u00e0i thu\u1ed9c t\u00ednh kh\u00e1c v\u00e0 g\u1eedi cho ph\u00eda platform<\/li>\n<li>Ph\u00eda platform ti\u1ebfn h\u00e0nh x\u00e1c th\u1ef1c v\u00e0 tr\u1ea3 API token l\u1ea1i cho ph\u00eda backend. Qu\u00e1 tr\u00ecnh n\u00e0y th\u01b0\u1eddng ch\u1ec9 l\u00e0m m\u1ed9t l\u1ea7n khi c\u00e0i \u0111\u1eb7t ho\u1eb7c khi ng\u01b0\u1eddi d\u00f9ng revoke (thu h\u1ed3i) token<\/li>\n<li>Backend t\u1ea1o m\u1ed9t m\u00e3 OTP \u0111\u1ec3 x\u00e1c th\u1ef1c v\u1edbi EGANY Apps<\/li>\n<li>EGANY Apps nh\u1eadn m\u00e3 x\u00e1c th\u1ef1c v\u00e0 g\u1eedi ng\u01b0\u1ee3c l\u1ea1i ph\u00eda backend<\/li>\n<li>Backend t\u1ea1o token x\u00e1c th\u1ef1c v\u00e0 g\u1eedi l\u1ea1i cho EGANY Apps<\/li>\n<\/ol>\n<p>M\u1ed9t s\u1ed1 b\u1ea1n c\u00f3 th\u1ec3 th\u1eafc m\u1eafc l\u00e0 v\u00ec sao ph\u00eda backend kh\u00f4ng g\u1eedi tr\u1ef1c ti\u1ebfp token v\u1ec1 ph\u00eda EGANY Apps lu\u00f4n m\u00e0 ph\u1ea3i t\u1ea1o ra m\u1ed9t token trung gian. L\u00fd do nh\u01b0 sau:<\/p>\n<ul>\n<li><strong>H\u1ea1n ch\u1ebf r\u1ee7i ro l\u1ed9 token.<\/strong> Do API ph\u00eda platform \u0111\u1ec1u l\u00e0 public API, n\u1ebfu \u0111\u1ec3 l\u1ed9 token c\u1ee7a kh\u00e1ch th\u00ec thi\u1ec7t h\u1ea1i s\u1ebd t\u01b0\u01a1ng \u0111\u1ed1i l\u1edbn. H\u01a1n n\u1eefa, n\u1ebfu token do EGANY qu\u1ea3n l\u00fd th\u00ec khi g\u1eb7p s\u1ef1 c\u1ed1 s\u1ebd x\u1eed l\u00fd d\u1ec5 d\u00e0ng v\u00e0 nhanh ch\u00f3ng h\u01a1n, kh\u00f4ng b\u1ecb ph\u1ee5 thu\u1ed9c v\u00e0o ph\u00eda platform<\/li>\n<li><strong>Ph\u1ee5c v\u1ee5 business.<\/strong> \u0110\u1ed3ng \u00fd l\u00e0 EGANY Apps c\u00f3 th\u1ec3 t\u1ef1 l\u00e0m \u0111\u01b0\u1ee3c nhi\u1ec1u th\u1ee9 v\u1edbi API token tr\u1ea3 v\u1ec1 tr\u1ef1c ti\u1ebfp. Tuy nhi\u00ean, c\u00e1c t\u00e1c v\u1ee5 li\u00ean quan \u0111\u1ebfn business (nghi\u1ec7p v\u1ee5) bu\u1ed9c ph\u1ea3i x\u1eed l\u00fd \u1edf backend. C\u00e1c nghi\u1ec7p v\u1ee5 bao g\u1ed3m qu\u1ea3n l\u00fd h\u1ea1n m\u1ee9c, qu\u1ea3n l\u00fd h\u1ea1n s\u1eed d\u1ee5ng c\u0169ng nh\u01b0 gi\u1edbi h\u1ea1n c\u00e1c t\u00ednh n\u0103ng c\u1ee7a \u1ee9ng d\u1ee5ng t\u00f9y v\u00e0o g\u00f3i c\u01b0\u1edbc (pricing plan) m\u00e0 kh\u00e1ch \u0111ang s\u1eed d\u1ee5ng<\/li>\n<\/ul>\n<p>Xong v\u1ea5n \u0111\u1ec1 v\u1ec1 flow th\u00ec team g\u1eb7p ti\u1ebfp v\u1ea5n \u0111\u1ec1 v\u1ec1 l\u01b0u tr\u1eef token. M\u1ed7i \u1ee9ng d\u1ee5ng s\u1ebd c\u00f3 m\u1ed9t token ri\u00eang v\u00e0 kh\u00e1ch c\u00f3 th\u1ec3 \u0111\u0103ng nh\u1eadp m\u1ed9t ho\u1eb7c nhi\u1ec1u \u1ee9ng d\u1ee5ng c\u00f9ng m\u1ed9t l\u00fac. H\u01a1n n\u1eefa, do EGANY Apps h\u1ed7 tr\u1ee3 nhi\u1ec1u platform n\u00ean kh\u00e1ch c\u00f3 th\u1ec3 \u0111\u0103ng nh\u1eadp m\u1ed9t \u1ee9ng d\u1ee5ng tr\u00ean nhi\u1ec1u platform kh\u00e1c nhau. Ph\u1ee9c t\u1ea1p ph\u1ea3i kh\u00f4ng n\u00e0o? Sau khi th\u1ea3o lu\u1eadn v\u00e0 b\u00e0n b\u1ea1c th\u00ec team ch\u1ed1t \u0111\u01a1n nh\u01b0 sau:<\/p>\n<ul>\n<li>Cho ph\u00e9p \u0111\u0103ng nh\u1eadp nhi\u1ec1u \u1ee9ng d\u1ee5ng<\/li>\n<li>M\u1ed9t \u1ee9ng d\u1ee5ng ch\u1ec9 \u0111\u01b0\u1ee3c \u0111\u0103ng nh\u1eadp tr\u00ean m\u1ed9t platform. N\u1ebfu kh\u00e1ch h\u00e0ng \u0111\u0103ng nh\u1eadp m\u1ed9t \u1ee9ng d\u1ee5ng tr\u00ean nhi\u1ec1u platform kh\u00e1c nhau th\u00ec ch\u1ec9 platform cu\u1ed1i c\u00f9ng m\u1edbi \u0111\u01b0\u1ee3c t\u00ednh<\/li>\n<\/ul>\n<p>Team l\u1ef1a ch\u1ecdn l\u01b0u tr\u1eef token d\u01b0\u1edbi d\u1ea1ng <code>Array<\/code>. \u0110\u00e2y kh\u00f4ng ph\u1ea3i l\u00e0 m\u1ed9t s\u1ef1 l\u1ef1a ch\u1ecdn h\u1ee3p l\u00fd v\u00ec token th\u01b0\u1eddng \u0111\u01b0\u1ee3c truy c\u1eadp d\u01b0\u1edbi d\u1ea1ng <code>key<\/code> n\u00ean hi\u1ec7u n\u0103ng s\u1ebd kh\u00f4ng t\u1ed1t. V\u00ed d\u1ee5:<\/p>\n<pre><code class=\"language-javascript\">\/\/ Array\nconst token = tokenArr.find(isCurrentAuthSession)\n\/\/ =&gt; V\u1ec1 l\u00fd thuy\u1ebft l\u00e0 O(n)\n\n\/\/ Map\nconst tokenKey = getCurrentAuthSessionKey()\nconst token = tokenMap.get(tokenKey)\n\/\/ =&gt; V\u1ec1 l\u00fd thuy\u1ebft l\u00e0 O(1)<\/code><\/pre>\n<p>T\u1ea5t nhi\u00ean, v\u1ec1 l\u00fd thuy\u1ebft l\u00e0 nh\u01b0 v\u1eady nh\u01b0ng tr\u00ean th\u1ef1c t\u1ebf th\u00ec s\u1ebd kh\u00f4ng nh\u1eadn ra \u0111\u01b0\u1ee3c \u1ea3nh h\u01b0\u1edfng r\u00f5 r\u1ec7t do s\u1ed1 l\u01b0\u1ee3ng ph\u1ea7n t\u1eed (\u1edf \u0111\u00e2y l\u00e0 phi\u00ean \u0111\u0103ng nh\u1eadp c\u1ee7a ng\u01b0\u1eddi d\u00f9ng) kh\u00e1 l\u00e0 th\u1ea5p. D\u00f9 v\u1eady th\u00ec \u1edf nh\u1eefng phi\u00ean b\u1ea3n sau n\u00e0y, team v\u1eabn quy\u1ebft \u0111\u1ecbnh thay th\u1ebf <code>Array<\/code> b\u1eb1ng <code>Map<\/code> \u0111\u1ec3 ti\u1ec7n cho vi\u1ec7c truy xu\u1ea5t.<\/p>\n<h3>2. Global state management<\/h3>\n<p>Sau khi x\u00e1c th\u1ef1c ng\u01b0\u1eddi d\u00f9ng th\u00e0nh c\u00f4ng, h\u1ea7u h\u1ebft c\u00e1c \u1ee9ng d\u1ee5ng \u0111\u1ec1u l\u01b0u tr\u1eef l\u1ea1i th\u00f4ng tin x\u00e1c th\u1ef1c \u0111\u1ec3 s\u1eed d\u1ee5ng trong su\u1ed1t th\u1eddi gian \u1ee9ng d\u1ee5ng ho\u1ea1t \u0111\u1ed9ng. EGANY Apps c\u0169ng kh\u00f4ng ph\u1ea3i l\u00e0 ngo\u1ea1i l\u1ec7.<\/p>\n<p>\u0110\u1ec3 gi\u1ea3i quy\u1ebft b\u00e0i to\u00e1n n\u00e0y th\u00ec team c\u00f3 hai l\u1ef1a ch\u1ecdn l\u00fac b\u1ea5y gi\u1edd:<\/p>\n<ul>\n<li><a href=\"https:\/\/react-redux.js.org\/\" rel=\"nofollow noopener\" target=\"_blank\">Redux<\/a><\/li>\n<li><a href=\"https:\/\/mobx.js.org\/getting-started.html\" rel=\"nofollow noopener\" target=\"_blank\">MobX<\/a><\/li>\n<\/ul>\n<p>Sau khi tham kh\u1ea3o m\u1ed9t s\u1ed1 ngu\u1ed3n tr\u00ean m\u1ea1ng v\u00e0 tr\u1ea3i nghi\u1ec7m th\u1eed th\u00ec team quy\u1ebft \u0111\u1ecbnh ch\u1ecdn MobX. L\u00fd do:<\/p>\n<ul>\n<li>Nhu c\u1ea7u c\u1ee7a team kh\u00f4ng qu\u00e1 ph\u1ee9c t\u1ea1p. C\u1ea3 2 \u0111\u1ec1u \u0111\u00e1p \u1ee9ng \u0111\u1ee7<\/li>\n<li>Syntax \u0111\u01a1n gi\u1ea3n v\u00e0 \u0111\u1ee1 r\u01b0\u1eddm r\u00e0 h\u01a1n Redux (t\u00ednh theo th\u1eddi \u0111i\u1ec3m tri\u1ec3n khai, sau khi React hooks ra \u0111\u1eddi th\u00ec Redux \u0111\u00e3 g\u1ecdn g\u00e0ng h\u01a1n r\u1ea5t nhi\u1ec1u)<\/li>\n<li>D\u1ec5 hi\u1ec3u, d\u1ec5 s\u1eed d\u1ee5ng<\/li>\n<\/ul>\n<p>D\u00f9 v\u1eady MobX v\u1eabn c\u00f2n m\u1ed9t s\u1ed1 h\u1ea1n ch\u1ebf sau:<\/p>\n<ul>\n<li>Bu\u1ed9c ph\u1ea3i s\u1eed d\u1ee5ng Class component<\/li>\n<li>Mu\u1ed1n debug \u1edf console bu\u1ed9c team ph\u1ea3i s\u1eed d\u1ee5ng h\u00e0m <code>toJS()<\/code> \u0111\u1ec3 in d\u1eef li\u1ec7u do d\u1eef li\u1ec7u \u0111\u01b0\u1ee3c l\u01b0u \u1edf d\u1ea1ng <code>Observable<\/code><\/li>\n<li>C\u1ea7n c\u00e0i \u0111\u1eb7t v\u00e0 thi\u1ebft l\u1eadp d\u1ef1 \u00e1n m\u1ed9t ch\u00fat th\u00ec m\u1edbi d\u00f9ng \u0111\u01b0\u1ee3c c\u00e1c syntax c\u1ee7a MobX<\/li>\n<\/ul>\n<p>Ngo\u00e0i nh\u1eefng kh\u00f3 kh\u0103n tr\u00ean th\u00ec team kh\u00e1 h\u00e0i l\u00f2ng v\u1edbi gi\u1ea3i ph\u00e1p \u1edf th\u1eddi \u0111i\u1ec3m hi\u1ec7n t\u1ea1i.<\/p>\n<h3>3. Dynamic route (url) v\u1edbi Next.js<\/h3>\n<p>Sau khi c\u00f3 khung s\u01b0\u1eddn x\u00e1c th\u1ef1c ng\u01b0\u1eddi d\u00f9ng c\u0169ng nh\u01b0 m\u00f3c n\u1ed1i th\u00f4ng tin v\u00e0o <em>global state<\/em> th\u00ec team b\u1eaft tay v\u00e0o ph\u00e1t tri\u1ec3n giao di\u1ec7n dashboard.<\/p>\n<p>N\u1ebfu c\u00e1c b\u1ea1n ch\u01b0a bi\u1ebft th\u00ec Next.js s\u1eed d\u1ee5ng <em>file-based routing<\/em>, ngh\u0129a l\u00e0 \u0111\u01b0\u1eddng d\u1eabn file s\u1ebd t\u01b0\u01a1ng \u1ee9ng v\u1edbi \u0111\u01b0\u1eddng d\u1eabn trang. V\u00ed d\u1ee5:<\/p>\n<ul>\n<li><strong>\u0110\u01b0\u1eddng d\u1eabn file:<\/strong> <code>pages\/products\/index.jsx<\/code><\/li>\n<li><strong>\u0110\u01b0\u1eddng d\u1eabn trang<\/strong> <code>\/products<\/code><\/li>\n<\/ul>\n<p>ho\u1eb7c<\/p>\n<ul>\n<li><strong>\u0110\u01b0\u1eddng d\u1eabn file:<\/strong> <code>pages\/products\/productId.jsx<\/code><\/li>\n<li><strong>\u0110\u01b0\u1eddng d\u1eabn trang<\/strong> <code>\/products\/productId<\/code><\/li>\n<\/ul>\n<p>C\u00f3 l\u1ebd c\u00e1c b\u1ea1n c\u0169ng nh\u1eadn ra v\u1ea5n \u0111\u1ec1 \u1edf \u0111\u00e2y r\u1ed3i ph\u1ea3i kh\u00f4ng? Trong th\u1ef1c t\u1ebf s\u1ebd xu\u1ea5t hi\u1ec7n v\u00f4 s\u1ed1 <code>productId<\/code> v\u00e0 n\u1ebfu ph\u1ea3i t\u1ea1o t\u1eebng file cho m\u1ed7i trang chi ti\u1ebft s\u1ea3n ph\u1ea9m theo <code>id<\/code> th\u00ec.. g\u1ea7n nh\u01b0 kh\u00f4ng th\u1ec3, \u0111\u00fang kh\u00f4ng n\u00e0o?<br \/>\n\u0110\u00f3 l\u00e0 v\u1ea5n \u0111\u1ec1 m\u00e0 dynamic route (hay c\u00f2n g\u1ecdi l\u00e0 dynamic url) gi\u1ea3i quy\u1ebft. N\u00f3 s\u1ebd t\u1ef1 \u0111\u1ed9ng truy\u1ec1n <code>productId<\/code> v\u00e0o m\u1ed9t trang (template) ph\u00f9 h\u1ee3p v\u00e0 tr\u1ea3 v\u1ec1 HTML t\u01b0\u01a1ng \u1ee9ng.<\/p>\n<p>Ti\u1ebfc thay, \u1edf th\u1eddi \u0111i\u1ec3m n\u00e0y, <strong>Next.js v7<\/strong> ch\u01b0a h\u1ed7 tr\u1ee3 s\u1eb5n m\u00e0 bu\u1ed9c l\u00f2ng team ph\u1ea3i custom \u0111\u1ec3 x\u1eed l\u00fd. C\u1ee5 th\u1ec3 l\u00e0 s\u1eed d\u1ee5ng <code>express<\/code> \u0111\u1ec3 qu\u1ea3n l\u00fd Server-side Rendering cho Next.js. M\u1ed9t \u0111o\u1ea1n code m\u1eabu cho dynamic route s\u1ebd nh\u01b0 sau:<\/p>\n<pre><code class=\"language-javascript\">server.get(&#039;\/products\/:id&#039;, (req, res) =&gt; {\n  const pageTemplate = &#039;\/products\/id&#039;\n  const productId = req.params.id\n\n  app.render(req, res, pageTemplate, { id: productId })\n})<\/code><\/pre>\n<p>Tuy nhi\u00ean, khi ch\u1ea1y v\u1edbi custom server nh\u01b0 tr\u00ean th\u00ec s\u1ebd m\u1ea5t \u0111i t\u00ednh n\u0103ng <code>hot-reload<\/code> (m\u1ed9t d\u1ea1ng auto reload nh\u01b0ng gi\u1eef l\u1ea1i <code>state<\/code> hi\u1ec7n t\u1ea1i c\u1ee7a \u1ee9ng d\u1ee5ng), khi\u1ebfn cho tr\u1ea3i nghi\u1ec7m khi code gi\u1ea3m \u0111i \u0111\u00e1ng k\u1ec3, \u0111\u1eb7c bi\u1ec7t l\u00e0 khi l\u00e0m vi\u1ec7c v\u1edbi form ho\u1eb7c modal.<\/p>\n<h3>4. Custom component<\/h3>\n<p>Nh\u01b0 \u0111\u00e3 \u0111\u1ec1 c\u1eadp \u1edf ph\u1ea7n 1 th\u00ec team \u0111\u00e3 quy\u1ebft \u0111\u1ecbnh d\u1ef1ng b\u1ed9 component ri\u00eang d\u1ef1a tr\u00ean n\u1ec1n t\u1ea3ng CSS \u0111\u00e3 c\u00f3 t\u1eeb c\u00e1c s\u1ea3n ph\u1ea9m tr\u01b0\u1edbc \u0111\u00f3.<\/p>\n<p>Do ch\u01b0a c\u00f3 kinh nghi\u1ec7m nhi\u1ec1u n\u00ean vi\u1ec7c ph\u00e2n t\u00e1ch component c\u0169ng g\u1eb7p t\u01b0\u01a1ng \u0111\u1ed1i kh\u00f3 kh\u0103n. \u0110\u00e1ng l\u01b0u \u00fd nh\u1ea5t c\u00f3 l\u1ebd l\u00e0 vi\u1ec7c t\u00edch h\u1ee3p c\u00e1c th\u01b0 vi\u1ec7n b\u00ean ngo\u00e0i, \u0111\u1eb7c bi\u1ec7t l\u00e0 th\u01b0 vi\u1ec7n rich-text editor.<\/p>\n<p>C\u00e1c th\u01b0 vi\u1ec7n rich-text editor \u0111a s\u1ed1 \u0111\u1ec1u s\u1eed d\u1ee5ng object <code>window<\/code> trong source code c\u1ee7a m\u00ecnh. Ng\u1eb7t n\u1ed7i khi render l\u1ea7n \u0111\u1ea7u ti\u00ean th\u00ec trang l\u1ea1i \u0111\u01b0\u1ee3c ch\u1ea1y \u1edf server, l\u00e0m g\u00ec c\u00f3 object <code>window<\/code> m\u00e0 d\u00f9ng. \u1ea4y v\u1eady l\u00e0 team l\u1ea1i ph\u1ea3i l\u00e0m \u0111\u1ee7 tr\u00f2 \u0111\u1ec3 page c\u00f3 th\u1ec3 \u0111\u01b0\u1ee3c render th\u00e0nh c\u00f4ng tr\u00ean server.<\/p>\n<p>Ch\u01b0a h\u1ebft, ngo\u00e0i rich-text editor th\u00ec team c\u00f2n ph\u1ea3i optimize color-picker. Khi ng\u01b0\u1eddi d\u00f9ng ch\u1ecdn m\u00e0u, event <code>onChange<\/code> s\u1ebd \u0111\u01b0\u1ee3c g\u1ecdi r\u1ea5t nhi\u1ec1u l\u1ea7n. N\u1ebfu qu\u1ea3n l\u00fd kh\u00f4ng kh\u00e9o s\u1ebd t\u1ea1o ra hi\u1ec7n t\u01b0\u1ee3ng gi\u1eadt\/kh\u1ef1ng UI r\u1ea5t kh\u00f3 ch\u1ecbu. Team ch\u1ee7 \u0111\u1ed9ng \u00e1p d\u1ee5ng c\u00e1c th\u1ee7 thu\u1eadt debounce (m\u1ed9t d\u1ea1ng delay function) \u0111\u1ec3 c\u1ea3i thi\u1ec7n hi\u1ec7u n\u0103ng cho component n\u00e0y<\/p>\n<h3>5. Docker<\/h3>\n<p>Kh\u00f3 kh\u0103n cu\u1ed1i c\u00f9ng c\u00f3 l\u1ebd l\u00e0 t\u1ea1o dockerfile \u0111\u1ec3 tri\u1ec3n khai \u1ee9ng d\u1ee5ng.<\/p>\n<p>Do c\u0169ng ch\u01b0a c\u00f3 nhi\u1ec1u kinh nghi\u1ec7m v\u1ec1 m\u1ea3ng n\u00e0y n\u00ean l\u1ed7i c\u0169ng t\u01b0\u01a1ng \u0111\u1ed1i nhi\u1ec1u. N\u1ed5i b\u1eadt nh\u1ea5t c\u00f3 l\u1ebd l\u00e0 l\u1ec7nh <code>npm install --production<\/code><\/p>\n<p>N\u1ebfu b\u1ea1n ch\u01b0a bi\u1ebft, <code>--production<\/code> flag (c\u1edd) s\u1ebd lo\u1ea1i b\u1ecf to\u00e0n b\u1ed9 package n\u1eb1m trong <code>devDependencies<\/code> v\u00e0 ch\u1ec9 c\u00e0i \u0111\u1eb7t c\u00e1c package c\u00f3 trong <code>dependencies<\/code>. V\u1ec1 l\u00fd thuy\u1ebft th\u00ec n\u00f3 s\u1ebd gi\u1ea3m th\u1eddi gian deploy v\u00ec s\u1ed1 l\u01b0\u1ee3ng package s\u1ebd gi\u1ea3m \u0111i \u0111\u00e1ng k\u1ec3. Trong th\u1ef1c t\u1ebf, m\u1ed9t s\u1ed1 package \u1edf <code>devDependencies<\/code> l\u1ea1i c\u1ea7n thi\u1ebft \u0111\u1ec3 build \u0111\u01b0\u1ee3c project, \u0111\u01a1n c\u1eed nh\u01b0 <code>node-sass<\/code> \u0111\u1ec3 compile file <code>.scss<\/code><\/p>\n<p>N\u1ebfu nh\u1edb kh\u00f4ng nh\u1ea7m th\u00ec team m\u1ea5t kho\u1ea3ng m\u1ed9t bu\u1ed5i chi\u1ec1u ch\u1ec9 \u0111\u1ec3 t\u1ea1o v\u00e0 tri\u1ec3n khai th\u00e0nh c\u00f4ng \u1ee9ng d\u1ee5ng b\u1eb1ng docker.<\/p>\n<h2>Quy tr\u00ecnh<\/h2>\n<p><img decoding=\"async\" src=\"https:\/\/egany.cdn.vccloud.vn\/blogs\/wp-content\/uploads\/2021\/07\/van-de-quy-trinh-600x450.png\" alt=\"van-de-quy-trinh\" \/><\/p>\n<blockquote>\n<p>Ngu\u1ed3n \u1ea3nh: <a href=\"https:\/\/dribbble.com\/shots\/9707966-Web-Site-Developing-Process\" rel=\"nofollow noopener\" target=\"_blank\">https:\/\/dribbble.com\/shots\/9707966-Web-Site-Developing-Process<\/a><\/p>\n<\/blockquote>\n<h3>1. B\u1ecf b\u01b0\u1edbc review code<\/h3>\n<p>Tuy trong quy tr\u00ecnh \u0111\u00e3 ch\u1ed1t c\u00f3 b\u01b0\u1edbc t\u1ea1o pull request c\u0169ng nh\u01b0 review code nh\u01b0ng khi b\u1eaft \u0111\u1ea7u ph\u00e1t tri\u1ec3n th\u00ec kh\u1ed1i l\u01b0\u1ee3ng c\u00f4ng vi\u1ec7c ph\u00e1t sinh kh\u00e1 nhi\u1ec1u, kh\u00f4ng \u0111\u1ee7 th\u1eddi gian \u0111\u1ec3 th\u1ef1c hi\u1ec7n review. Team quy\u1ebft \u0111\u1ecbnh b\u1ecf b\u01b0\u1edbc review code \u0111\u1ec3 ti\u1ebft ki\u1ec7m th\u1eddi gian.<\/p>\n<p>\u0110\u00e2y l\u00e0 quy\u1ebft \u0111\u1ecbnh kh\u00f4ng t\u1ed1t v\u00e0 d\u1eabn t\u1edbi nhi\u1ec1u h\u1eadu qu\u1ea3 x\u1ea5u nh\u01b0:<\/p>\n<ul>\n<li>Ch\u1ea5t l\u01b0\u1ee3ng code kh\u00f4ng cao. Th\u01b0\u1eddng xuy\u00ean xu\u1ea5t hi\u1ec7n c\u00e1c \u0111o\u1ea1n code kh\u00f4ng \u0111\u01b0\u1ee3c format ho\u1eb7c x\u1eed l\u00fd theo h\u01b0\u1edbng kh\u00f4ng h\u1ee3p l\u00fd, g\u00e2y gi\u1ea3m hi\u1ec7u n\u0103ng ho\u1eb7c th\u1eadm ch\u00ed g\u00e2y l\u1ed7i cho \u1ee9ng d\u1ee5ng<\/li>\n<li>Kh\u00f4ng nh\u1ea5t qu\u00e1n khi c\u00f9ng x\u1eed l\u00fd m\u1ed9t v\u1ea5n \u0111\u1ec1<\/li>\n<li>C\u00e1c th\u00e0nh vi\u00ean kh\u00f4ng c\u00f3 c\u01a1 h\u1ed9i h\u1ecdc h\u1ecfi<\/li>\n<\/ul>\n<p>Hi\u1ec7n t\u1ea1i team \u0111\u00e3 c\u00f3 m\u1ed9t s\u1ed1 gi\u1ea3i ph\u00e1p t\u1ea1m th\u1eddi \u0111\u1ec3 kh\u1eafc ph\u1ee5c t\u00ecnh tr\u1ea1ng tr\u00ean. Ch\u1eafc \u0111\u1ec3 b\u00e0i sau m\u00ecnh chia s\u1ebb nh\u00e9!<\/p>\n<h3>2. Single point of failure<\/h3>\n<p>Th\u01b0\u1eddng \u1edf nh\u1eefng b\u01b0\u1edbc quan tr\u1ecdng nh\u01b0 merge code ho\u1eb7c deploy th\u00ec ch\u1ec9 c\u00f3 m\u1ed9t ng\u01b0\u1eddi \u0111\u1ea3m nh\u1eadn. \u0110i\u1ec1u n\u00e0y l\u00e0 h\u1ee3p l\u00fd v\u00ec h\u1ecd l\u00e0 nh\u1eefng ng\u01b0\u1eddi n\u1eafm r\u00f5 d\u1ef1 \u00e1n nh\u1ea5t v\u00e0 khi l\u00e0m vi\u1ec7c v\u1edbi c\u00e1c team kh\u00e1c th\u00ec m\u1ed9t ng\u01b0\u1eddi \u0111\u1ea1i di\u1ec7n s\u1ebd d\u1ec5 trao \u0111\u1ed5i h\u01a1n r\u1ea5t nhi\u1ec1u.<\/p>\n<p>Th\u1ebf nh\u01b0ng, n\u1ebfu kh\u00f4ng c\u00f3 ng\u01b0\u1eddi backup th\u00ec s\u1ebd l\u00e0 m\u1ed9t tai h\u1ecda. \u0110\u00e3 x\u1ea3y ra tr\u01b0\u1eddng h\u1ee3p EGANY Apps g\u1eb7p l\u1ed7i l\u1edbn tr\u00ean <code>production<\/code> v\u00e0 kh\u00f4ng may h\u00f4m \u0111\u1ea5y b\u1ea1n ph\u1ee5 tr\u00e1ch deploy l\u1ea1i c\u00f3 vi\u1ec7c c\u00e1 nh\u00e2n kh\u00f4ng th\u1ec3 l\u00ean c\u00f4ng ty. \u1ea4y v\u1eady l\u00e0 anh em ph\u1ea3i xin l\u1ed7i v\u00e0 h\u1eb9n kh\u00e1ch l\u1ea1i v\u00e0o ng\u00e0y mai.<\/p>\n<p>\u0110\u00e2y l\u00e0 m\u1ed9t thi\u1ebfu s\u00f3t l\u1edbn trong team v\u00e0 hi\u1ec7n \u0111ang d\u1ea7n \u0111\u01b0\u1ee3c kh\u1eafc ph\u1ee5c b\u1eb1ng c\u00e1c bu\u1ed5i workshop training c\u0169ng nh\u01b0 t\u00e0i li\u1ec7u \u0111i k\u00e8m d\u1ef1 \u00e1n.<\/p>\n<h3>3. Giao ti\u1ebfp trong team<\/h3>\n<p>Giao ti\u1ebfp trong giai \u0111o\u1ea1n n\u00e0y c\u00f3 l\u1ebd l\u00e0 nguy\u00ean nh\u00e2n l\u1edbn nh\u1ea5t d\u1eabn t\u1edbi c\u00e1c kh\u00f3 kh\u0103n b\u00ean tr\u00ean.<\/p>\n<p>Tuy c\u00e1c th\u00e0nh vi\u00ean ch\u1ee7 \u0111\u1ed9ng li\u00ean h\u1ec7 l\u1eabn nhau khi c\u1ea7n merge code ho\u1eb7c deploy nh\u01b0ng ngo\u00e0i nh\u1eefng vi\u1ec7c \u0111\u00f3 th\u00ec l\u1ea1i \u00edt trao \u0111\u1ed5i nh\u1eefng vi\u1ec7c kh\u00e1c v\u1edbi nhau. \u0110i\u1ec1u \u0111\u00f3 d\u1eabn t\u1edbi c\u00e1c v\u1ea5n \u0111\u1ec1 nh\u01b0 kh\u00f4ng \u0111\u1ed3ng nh\u1ea5t trong x\u1eed l\u00fd ho\u1eb7c \u0111\u00f4i khi l\u00e0 vi\u1ec7c l\u1eb7p code kh\u00f4ng \u0111\u00e1ng c\u00f3.<\/p>\n<p>R\u1ea5t may l\u00e0 giao ti\u1ebfp gi\u1eefa c\u00e1c team v\u1edbi nhau kh\u00e1 \u1ed5n, l\u00e0m vi\u1ec7c r\u1ea5t ch\u1ee7 \u0111\u1ed9ng v\u00e0 \u0103n \u00fd n\u00ean d\u1ef1 \u00e1n v\u1eabn ph\u00e1t tri\u1ec3n \u0111\u1ec1u, kh\u00f4ng b\u1ecb kh\u1ef1ng l\u1ea1i nhi\u1ec1u.<\/p>\n<h2>T\u1ea1m k\u1ebft<\/h2>\n<p>C\u00f3 c\u00f4ng m\u00e0i s\u1eaft, c\u00f3 ng\u00e0y n\u00ean kim. D\u00f9 c\u00f2n nhi\u1ec1u v\u1ea5n \u0111\u1ec1 trong quy tr\u00ecnh c\u0169ng nh\u01b0 c\u00e1c v\u1ea5n \u0111\u1ec1 li\u00ean quan t\u1edbi k\u1ef9 thu\u1eadt nh\u01b0ng team v\u1eabn ho\u00e0n th\u00e0nh \u0111\u01b0\u1ee3c m\u1ee5c ti\u00eau ra m\u1eaft phi\u00ean b\u1ea3n \u0111\u1ea7u ti\u00ean v\u00e0o th\u00e1ng 4 n\u0103m 2019 v\u1edbi th\u00f4ng s\u1ed1:<\/p>\n<ul>\n<li>5 \u1ee9ng d\u1ee5ng<\/li>\n<li>1 platform<\/li>\n<\/ul>\n<p>Ch\u01b0a h\u00e0i l\u00f2ng v\u1edbi k\u1ebft qu\u1ea3 tr\u00ean, team ti\u1ebfp t\u1ee5c qu\u00e1 tr\u00ecnh ph\u00e1t tri\u1ec3n EGANY Apps v\u1edbi c\u00e1c m\u1ee5c ti\u00eau sau:<\/p>\n<ul>\n<li>H\u1ed7 tr\u1ee3 th\u00eam \u00edt nh\u1ea5t m\u1ed9t platform<\/li>\n<li>Ph\u00e1t tri\u1ec3n c\u00e1c \u1ee9ng d\u1ee5ng d\u1ea1ng subscription thay v\u00ec one-time payment<\/li>\n<li>C\u1ea3i thi\u1ec7n quy tr\u00ecnh c\u0169ng nh\u01b0 source code \u0111\u1ec3 c\u00f3 n\u1ec1n t\u1ea3ng v\u1eefng ch\u1eafc h\u01a1n<\/li>\n<\/ul>\n<p>Ch\u1eb7ng \u0111\u01b0\u1eddng c\u00f2n d\u00e0i, ch\u1eafc h\u1eb9n c\u00e1c b\u1ea1n \u1edf b\u00e0i vi\u1ebft ti\u1ebfp theo nha!<\/p>\n<p>C\u00e1m \u01a1n c\u00e1c b\u1ea1n \u0111\u00e3 \u0111\u1ecdc. Happy hacking!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Nh\u01b0 \u0111\u00e3 n\u00f3i \u1edf ph\u1ea7n m\u1ed9t, Frontend Du K\u00fd S1E2 | EGANY Apps s\u1ebd k\u1ec3 v\u1ec1 nh\u1eefng l\u1ed7 h\u1ed5ng trong quy tr\u00ecnh l\u00e0m vi\u1ec7c c\u0169ng nh\u01b0 nh\u1eefng v\u01b0\u1edbng m\u1eafc v\u1ec1 k\u1ef9 thu\u1eadt m\u00e0 team g\u1eb7p ph\u1ea3i trong qu\u00e1 tr\u00ecnh ph\u00e1t tri\u1ec3n EGANY Apps. N\u1ebfu b\u1ea1n ch\u01b0a \u0111\u1ecdc ph\u1ea7n m\u1ed9t, b\u1ea1n c\u00f3 th\u1ec3 \u0111\u1ecdc tr\u01b0\u1edbc t\u1ea1i \u0111\u00e2y. Gi\u1edbi thi\u1ec7u Sau khi th\u1ed1ng nh\u1ea5t \u0111\u01b0\u1ee3c quy tr\u00ecnh l\u00e0m vi\u1ec7c c\u0169ng nh\u01b0 c\u00f4ng ngh\u1ec7 \u0111\u1ec3 th\u1ef1c hi\u1ec7n d\u1ef1 \u00e1n, team b\u1eaft \u0111\u1ea7u qu\u00e1 tr\u00ecnh ph\u00e1t tri\u1ec3n. K\u1ebf ho\u1ea1ch l\u00fac b\u1ea5y gi\u1edd bao g\u1ed3m: T\u1eadp trung ph\u00e1t tri\u1ec3n s\u01b0\u1eddn d\u1ef1 \u00e1n: authentication (sign-in\/sign-out), layout,<\/p>\n","protected":false},"author":2,"featured_media":909,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[27,1],"tags":[68,91,97,113,90,95,32,98,64,96],"_links":{"self":[{"href":"https:\/\/egany.com\/blogs\/wp-json\/wp\/v2\/posts\/904"}],"collection":[{"href":"https:\/\/egany.com\/blogs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/egany.com\/blogs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/egany.com\/blogs\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/egany.com\/blogs\/wp-json\/wp\/v2\/comments?post=904"}],"version-history":[{"count":0,"href":"https:\/\/egany.com\/blogs\/wp-json\/wp\/v2\/posts\/904\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/egany.com\/blogs\/wp-json\/wp\/v2\/media\/909"}],"wp:attachment":[{"href":"https:\/\/egany.com\/blogs\/wp-json\/wp\/v2\/media?parent=904"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/egany.com\/blogs\/wp-json\/wp\/v2\/categories?post=904"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/egany.com\/blogs\/wp-json\/wp\/v2\/tags?post=904"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}