องทำ Monorepo ให้ JavaScript Project ด้วย Lerna

Published

5 August, 2020

Language

English

Written by

Share

ลองทำ Monorepo ให้ JavaScript Project ด้วย Lerna

หลาย ๆ คนอาจเคยได้ยินคำว่า Monorepo กันมาบ้างแล้ว แล้วคุณรู้หรือไม่ว่า Monorepo มันคืออะไรกันแน่? Blog นี้เราจะมาอธิบายคำว่า Monorepo ให้คุณเข้าใจ และพาทำ Monorepo ให้ JavaScript Project โดยใช้ Lerna กัน

Monorepo คืออะไร?

เมื่อนานมาแล้วคุณอาจจะเคยเห็นหรืออาจเคยทำหลายๆ Project อยู่บน Git Repository เดียว (Monolith) ซึ่งเมื่อ Project ใหญ่ขึ้นมากๆ ก็จะพบกับความปวดหัวเพราะไฟล์เยอะแยะไปหมด แถมยังไม่มีจัดระเบียบไฟล์ให้เรียบร้อยอีกต่างหาก

ต่อมา Multirepo จึงเกิดขึ้น คือการแยกให้ 1 Git Repository มีแค่ 1 Project เท่านั้น แต่ปัญหาที่พบคือหาก Developer 1 คน อยู่หลาย Project ก็ต้องสลับ Repository ไปมาจนเหนื่อย จึงทำให้ทุกวันนี้มี “Monorepo” หรือ “Multi-package Repository” เกิดขึ้นมา ซึ่งมันคือการที่เราทำหลาย Project ใน 1 Git Repository นั่นเอง

ข้อดี-ข้อเสียของ Monorepo

ข้อดี

      1. เป็น Single Source of Truth
      2. จัดการกับ Dependency ได้ง่าย
      3. สามารถ Share/Reuse code ได้ง่าย
      4. สามารถแก้หลาย ๆ Project ได้ใน Commit เดียว
      5. สามารถเห็นภาพรวมของทุก Project ได้ง่าย

ข้อเสีย

      1. อาจจะต้องใช้ Tool ช่วยในการทำ
      2. มีจำนวนไฟล์ที่เยอะ
      3. หากมี Project ใดที่ Build Failed อาจทำให้ Project อื่นพังไปด้วย

มาเริ่มทำ Monorepo ด้วย Lerna กันเลย

Lerna เป็น Tool ที่ช่วยในการทำ Monorepo ให้กับ JavaScript Project

“ A tool for managing JavaScript projects with multiple packages. ”

ก่อนอื่นให้ทำการสร้าง Project เปล่า ๆ ขึ้นมาก่อน

mkdir my-monorepo
cd my-monorepo

จากนั้นให้ทำการเพิ่ม Lerna เข้าไปยัง Project

npx lerna init
yarn install

ซึ่งในขึ้นตอนนี้จะทำการสร้างไฟล์ต่าง ๆ ดังนี้

my-monorepo/    node_modules/    packages/    package.json    lerna.json    yarn.lock

ต่อมาเราจะทำการเพิ่ม Package แรกเข้าไปยัง Project และเพื่อให้เข้าใจได้ง่าย เราจะทำการสร้าง Package โดยใช้ Create React App ที่เรารู้จักกันดีนั้นเอง

cd packages
npx create-react-app react-package
cd ..

ต่อมาเราจะลองเพิ่ม Vue Package เข้าไปยัง Project บ้าง

cd packages
npx @vue/cli create --default vue-package
cd ..

เนื่องจากคำสั่งรันของ Vue จะใช้คำสั่ง serve แต่เพื่อให้ทุก Package ใช้คำสั่งตรงกันหมด จึงขอเข้าไปแก้ Script ใน package.json

// packages/vue-package/package.json{ "name": "vue-package", "version": "0.1.0", "private": true, "scripts": {   "start": "vue-cli-service serve",  // update this line from "serve" to "start"   "build": "vue-cli-service build",   "lint": "vue-cli-service lint" }, "dependencies": {   "core-js": "^3.6.5",   "vue": "^2.6.11" }, "devDependencies": {   "@vue/cli-plugin-babel": "~4.4.0",   "@vue/cli-plugin-eslint": "~4.4.0",   "@vue/cli-service": "~4.4.0",   "babel-eslint": "^10.1.0",   "eslint": "^6.7.2",   "eslint-plugin-vue": "^6.2.2",   "vue-template-compiler": "^2.6.11" }, "eslintConfig": {   "root": true,   "env": {     "node": true   },   "extends": [     "plugin:vue/essential",     "eslint:recommended"   ],   "parserOptions": {     "parser": "babel-eslint"   },   "rules": {} }, "browserslist": [   "> 1%",   "last 2 versions",   "not dead" ]}

ลองดูผลลัพธ์ตอนนี้ดูบ้าง

yarn lerna clean -y
yarn lerna bootstrap
yarn lerna run start

ผลลัพธ์จาก react-package http://localhost:3000

ผลลัพธ์จาก vue-package http://localhost:8080

เพียงเท่านี้เราก็สามารถมีหลาย Package ใน Project เดียวได้แล้ว แต่เดี๋ยวก่อน… ทุกคนคงสัยว่าทำแบบนี้แล้วจะต่างอะไรกับ Multirepo ล่ะ? งั้นเราลองสร้างอีก Package ที่จะเอาไปใช้ทั้งใน react-package กับ vue-package กัน

mkdir packages/shared-package
cd packages/shared-package
yarn init
cd ../..

จากนั้นทำการสร้างไฟล์ index.js และ .eslintrc.js เข้าไปให้ shared-package กัน

touch packages/shared-package/index.js
touch packages/shared-package/.eslintrc.js
// packages/shared-package/index.js
module.exports = 'Hello from shared-package'
// packages/shared-package/.eslintrc.js
module.exports = {  "env": {      "browser": true,      "commonjs": true,      "es2020": true  },  "extends": "eslint:recommended",  "parserOptions": {      "ecmaVersion": 11  },  "rules": {  }};

ทีนี้ลองทำการเรียกใช้ shared-package ใน react-package กัน

// packages/react-package/package.json{  "name": "react-package",  "version": "0.1.0",  "private": true,  "dependencies": {    "@testing-library/jest-dom": "^4.2.4",    "@testing-library/react": "^9.3.2",    "@testing-library/user-event": "^7.1.2",    "react": "^16.13.1",    "react-dom": "^16.13.1",    "react-scripts": "3.4.1",    "shared-package": "^1.0.0"  // add this line  },  "scripts": {    "start": "react-scripts start",    "build": "react-scripts build",    "test": "react-scripts test",    "eject": "react-scripts eject"  },  "eslintConfig": {    "extends": "react-app"  },  "browserslist": {    "production": [      ">0.2%",      "not dead",      "not op_mini all"    ],    "development": [      "last 1 chrome version",      "last 1 firefox version",      "last 1 safari version"    ]  }}
// packages/react-package/src/App.js
import sharedMessage from 'shared-package';  // add this line
import React from 'react';
import logo from './logo.svg';
import './App.css';
function App() {  return (    <div className="App">      <header className="App-header">        <img src={logo} className="App-logo" alt="logo" />        <p>{sharedMessage}</p>  {/* update this line */}        <a          className="App-link"          href="https://reactjs.org"          target="_blank"          rel="noopener noreferrer"        >          Learn React        </a>      </header>    </div>  );}
export default App;

จากนั้นลองทำการเรียกใช้ shared-package ใน vue-package

// packages/vue-package/package.json{  "name": "vue-package",  "version": "0.1.0",  "private": true,  "scripts": {    "start": "vue-cli-service serve",    "build": "vue-cli-service build",    "lint": "vue-cli-service lint"  },  "dependencies": {    "core-js": "^3.6.5",    "vue": "^2.6.11",    "shared-package": "^1.0.0"  // add this line  },  "devDependencies": {    "@vue/cli-plugin-babel": "~4.4.0",    "@vue/cli-plugin-eslint": "~4.4.0",    "@vue/cli-service": "~4.4.0",    "babel-eslint": "^10.1.0",    "eslint": "^6.7.2",    "eslint-plugin-vue": "^6.2.2",    "vue-template-compiler": "^2.6.11"  },  "eslintConfig": {    "root": true,    "env": {      "node": true    },    "extends": [      "plugin:vue/essential",      "eslint:recommended"    ],    "parserOptions": {      "parser": "babel-eslint"    },    "rules": {}  },  "browserslist": [    "> 1%",    "last 2 versions",    "not dead"  ]}
// packages/vue-package/src/App.vue<template>  <div id="app">    <img alt="Vue logo" src="./assets/logo.png">    <HelloWorld :msg="message"/>  <!-- update this line -->  </div></template><script>
import sharedMessage from 'shared-package'  // add this line
import HelloWorld from './components/HelloWorld.vue'
export default {  name: 'App',  components: {    HelloWorld  },  // add these codes  computed: {    message: function() {      return sharedMessage    }  }}</script><style>#app {  font-family: Avenir, Helvetica, Arial, sans-serif;  -webkit-font-smoothing: antialiased;  -moz-osx-font-smoothing: grayscale;  text-align: center;  color: #2c3e50;  margin-top: 60px;}</style>

มาดูผลลัพธ์ตอนนี้กันเลย

yarn lerna bootstrap
yarn lerna run start

เพียงเท่านี้เราก็ได้ข้อความ “Hello from shared-package” ที่ได้จาก shared-package มาใช้แล้ว

บทสรุป

Lerna เป็น tool ที่ช่วยให้เราสร้าง Monorepo ให้กับ JavaScript Project โดยการทำ Monorepo ก็มีทั้งข้อดี-ข้อเสีย การจะเลือกใช้หรือไม่ใช้ก็ควรพิจารณาความเหมาะสมของ Project ก่อน ซึ่งในบทความนี้เป็นเพียงการสอนการทำ Monorepo แบบง่าย ๆ เท่านั้น หากใครสนใจสามารถลองเอาไปพัฒนาต่อได้

แล้วอย่าลืมติดตามบทความอื่นๆ จากเราได้ที่ SennaLabs Blogs

Written by
Senna Labs
Senna Labs

Keep me posted
to follow product news, latest in technology, solutions, and updates

More than 120,000 people/day  visit to read our blogs

Beyond the Labs

Explore all

3 July, 2025
JS class syntax
เชื่อว่าหลายๆคนที่เขียน javascript กันมา คงต้องเคยสงสัยกันบ้าง ว่า class ที่อยู่ใน js เนี่ย มันคืออะไร แล้วมันมีหน้าที่ต่างกับการประกาศ function อย่างไร?เรามารู้จักกับ class ให้มากขึ้นกันดีกว่า class เปรียบเสมือนกับ blueprint หรือแบบพิมพ์เขียว ที่สามารถนำไปสร้างเป็นสิ่งของ( object ) ตาม blueprint หรือแบบพิมพ์เขียว( class ) นั้นๆได้ โดยภายใน class
03 July, 2025

by

JS class syntax
3 July, 2025
15 สิ่งที่ทุกธุรกิจต้องรู้เกี่ยวกับ 5G
ผู้ให้บริการเครือข่ายในสหรัฐฯ ได้เปิดตัว 5G ในหลายรูปแบบ และเช่นเดียวกับผู้ให้บริการเครือข่ายในยุโรปหลายราย แต่… 5G มันคืออะไร และทำไมเราต้องให้ความสนใจบทความนี้ได้รวบรวม 15 สิ่งที่ทุกธุรกิจต้องรู้เกี่ยวกับ 5G เพราะเราปฏิเสธไม่ได้เลยว่ามันกำลังจะถูกใช้งานอย่างกว้างขวางขึ้น1. 5G หรือ Fifth-Generation คือยุคใหม่ของเทคโนโลยีเครือข่ายไร้สายที่จะมาแทนที่ระบบ 4G ที่เราใช้อยู่ในปัจจุบัน ซึ่งมันไม่ได้ถูกจำกัดแค่มือถือเท่านั้น แต่รวมถึงอุปกรณ์ทุกชนิดที่เชื่อมต่ออินเตอร์เน็ตได้2. 5G คือการพัฒนา 3 ส่วนที่สำคัญที่จะนำมาสู่การเชื่อมต่ออุปกรณ์ไร้สายต่างๆขยายช่องสัญญาณขนาดใหญ่ขึ้นเพื่อเพิ่มความเร็วในการเชื่อมต่อการตอบสนองที่รวดเร็วขึ้นในระยะเวลาที่น้อยลงความสามารถในการเชื่อมต่ออุปกรณ์มากกว่า 1 ในเวลาเดียวกัน3. สัญญาณ 5G นั้นแตกต่างจากระบบ
03 July, 2025

by

15 สิ่งที่ทุกธุรกิจต้องรู้เกี่ยวกับ 5G
3 July, 2025
จัดการ Array ด้วย Javascript (Clone Deep)
ในปัจจุบันนี้ ปฏิเสธไม่ได้เลยว่าภาษาที่ถูกใช้ในการเขียนเว็บต่าง ๆ นั้น คงหนีไม่พ้นภาษา Javascript ซึ่งเป็นภาษาที่ถูกนำไปพัฒนาเป็น framework หรือ library ต่าง ๆ มากมายผู้พัฒนาหลายคนก็มีรูปแบบการเขียนภาษา Javascript ที่แตกต่างกัน เราเลยมีแนวทางการเขียนที่หลากหลาย มาแบ่งปันเพื่อน ๆ เกี่ยวกับการจัดการ Array ด้วยภาษา Javascript กัน เรามาดูตัวอย่างกันเลยดีกว่าโดยปกติแล้วการ copy ค่าจาก value type ธรรมดา สามารถเขียนได้ดังนี้
03 July, 2025

by

จัดการ Array ด้วย Javascript (Clone Deep)

Let’s build digital products that are
simply awesome !

We will get back to you within 24 hours!Say hello
Please tell us your ideas.
- Senna Labsmake it happy
Contact ball
Contact us bg 2
Contact us bg 4
Contact us bg 1

Contact Senna Labs at :

hello@sennalabs.com28/11 Soi Ruamrudee, Lumphini, Pathumwan, Bangkok 10330+66 62 389 4599
© 2022 Senna Labs Co., Ltd.All rights reserved.