app/Modelsapp/Http/Controllersapp/ServicesFormRequest cho validate input, đặt trong app/Http/Requestsweb.php, api.php, admin.php, ...)Tên biến: camelCase, rõ ràng, miêu tả đúng chức năng
userId, totalAmount, isActiveTên hàm: camelCase, là động từ hoặc cụm động từ thể hiện hành động
getUserById(), calculateTotal(), isValid()Tránh viết tắt không phổ biến, ưu tiên sự rõ ràng
Tên boolean nên bắt đầu bằng is, has, can, should để thể hiện ý nghĩa đúng
User, OrderDetail, CustomerProfileControllerUserController, AuthControllercreate_users_table, add_status_to_orders_tableplan, user)posts, comments)| Mục đích | Tiền tố | Định dạng | Ví dụ |
|---|---|---|---|
| Tìm kiếm theo trường cụ thể | by |
scopeBy<Field> |
scopeById($id) |
| Kiểm tra trường boolean = true | is |
scopeIs<Flag> |
scopeIsActive() |
| Kiểm tra trường boolean = false | isNot |
scopeIsNot<Flag> |
scopeIsNotActive() |
| Kiểm tra sự tồn tại của mối quan hệ | has |
scopeHas<Relation> |
scopeHasPosts() |
| Kiểm tra mối quan hệ không tồn tại | hasNot |
scopeHasNot<Relation> |
scopeHasNotPosts() |
| Mối quan hệ không có | without |
scopeWithout<Relation> |
scopeWithoutPlan() |
php artisan make:request StoreUserRequestSử dụng authorize() nếu cần kiểm tra quyền
public function authorize() { return auth()->user()->can('create', User::class); }
Đặt rules rõ ràng trong rules()
public function rules() { return [ 'email' => 'required|email|unique:users,email', 'password' => 'required|min:8', ]; }
Sử dụng custom messages() nếu cần: Nếu bạn muốn thay đổi thông báo mặc định của Laravel cho các quy tắc validation, có thể định nghĩa các thông báo tùy chỉnh trong phương thức messages().
public function messages() { return [ 'email.required' => 'Email là bắt buộc.', 'email.email' => 'Email phải hợp lệ.', 'password.required' => 'Mật khẩu không được để trống.', 'password.min' => 'Mật khẩu phải có ít nhất 8 ký tự.', ]; }
Luôn định nghĩa inverse của mỗi quan hệ nếu có: Trong Laravel Eloquent, việc định nghĩa các quan hệ ngược (inverse) giúp việc truy vấn dữ liệu trở nên dễ dàng và hiệu quả hơn, đồng thời cũng giúp đảm bảo tính toàn vẹn của các mối quan hệ.
// Trong Model Post public function user() { return $this->belongsTo(User::class); } // Trong Model User public function posts() { return $this->hasMany(Post::class); }
Ưu tiên dùng with() để eager load tránh N+1: Tránh vấn đề N+1 query bằng cách sử dụng phương thức with() để eager load các mối quan hệ. Điều này giúp giảm số lượng truy vấn đến cơ sở dữ liệu và cải thiện hiệu suất của ứng dụng.
// Eager load mối quan hệ user và posts $posts = Post::with('user')->get();
Dùng accessors/mutators cho format hiển thị/ghi dữ liệu: Eloquent cung cấp các phương thức accessors và mutators để xử lý dữ liệu trước khi lưu vào cơ sở dữ liệu hoặc khi hiển thị ra ngoài. Accessors cho phép thay đổi cách dữ liệu được lấy, còn mutators giúp thay đổi dữ liệu trước khi lưu vào cơ sở dữ liệu.
// Accessor: Lấy tên đầy đủ của người dùng public function getFullNameAttribute() { return $this->first_name . ' ' . $this->last_name; } // Mutator: Thiết lập tên đầy đủ trước khi lưu vào cơ sở dữ liệu public function setFullNameAttribute($value) { $this->attributes['first_name'] = strtok($value, ' '); $this->attributes['last_name'] = substr(strrchr($value, ' '), 1); }
Sử dụng make: cho mọi lớp cần tạo: Laravel cung cấp lệnh make: để tạo các lớp như model, controller, migration, request, v.v. Việc sử dụng các lệnh này giúp bạn tiết kiệm thời gian và tạo ra cấu trúc mã nguồn rõ ràng.
php artisan make:model Customer
php artisan make:controller CustomerController
php artisan make:migration create_customers_table
php artisan make:request StoreCustomerRequest
Đặt tên rõ ràng, có thể dùng namespace con nếu cần: Đặt tên cho các lớp được tạo bởi lệnh make: sao cho rõ ràng và dễ hiểu. Bạn cũng có thể sử dụng namespace con nếu dự định nhóm các lớp liên quan lại với nhau trong một thư mục con.
php artisan make:model Models/Customer -mcrLệnh trên sẽ tạo một model
Customer trong thư mục app/Models, một migration (-m), một controller (-c), và một factory (-r).Dùng Resource khi trả dữ liệu API: Laravel cung cấp các class Resource để bạn có thể dễ dàng chuẩn hóa và định dạng dữ liệu trả về từ API. Việc sử dụng Resources giúp đảm bảo dữ liệu trả về có cấu trúc nhất quán và dễ bảo trì. Sử dụng các class như UserResource, PostCollection, v.v. để định dạng dữ liệu trả về.
use App\Http\Resources\UserResource; public function show($id) { $user = User::findOrFail($id); return new UserResource($user); }
Tách riêng định dạng trả về để dễ maintain: Để dễ dàng duy trì và mở rộng, hãy tách định dạng trả về từ API vào các class Resource và Collection riêng biệt. Điều này giúp việc thay đổi định dạng dữ liệu không ảnh hưởng trực tiếp đến logic xử lý của controller.
use App\Http\Resources\PostCollection; public function index() { $posts = Post::all(); return new PostCollection($posts); }
Các Resources có thể định nghĩa phương thức toArray() để chuyển đổi dữ liệu thành một mảng, và nếu cần, bạn có thể thêm phương thức toJson() để trả về dữ liệu dưới dạng JSON.
Dùng chuẩn PSR-12: Tuân thủ các chuẩn coding PSR-12 để đảm bảo mã nguồn dễ đọc, dễ bảo trì và nhất quán. PSR-12 quy định các quy tắc về định dạng mã, bao gồm cách đặt tên, thụt lề, khoảng trắng và các quy tắc khác để tạo ra mã nguồn chuẩn mực. Bạn có thể tham khảo chi tiết về chuẩn PSR-12 tại PSR-12: Extended Coding Style.
Tách logic phức tạp ra Service class: Để mã nguồn dễ duy trì và mở rộng, hãy tách các logic phức tạp ra các Service class. Điều này giúp controller trở nên nhẹ nhàng và chỉ chịu trách nhiệm cho việc xử lý các yêu cầu HTTP, còn việc xử lý logic sẽ được chuyển sang các lớp Service.
class UserService { public function createUser(array $data) { // Logic để tạo user } } // Trong controller $userService = new UserService(); $userService->createUser($data);
Hạn chế logic xử lý trong controller: Controller chỉ nên chịu trách nhiệm cho việc nhận request và trả response, không nên chứa quá nhiều logic xử lý. Mọi logic xử lý phức tạp nên được chuyển vào các lớp khác như Service, Repository, hoặc các lớp hỗ trợ khác.
// Controller public function store(Request $request) { // Tách logic vào service $userService = new UserService(); $userService->createUser($request->all()); return response()->json('User created successfully'); }
tests/Feature, tests/UnitRefreshDatabase khi test DBit_can_do_something/** @test */ public function it_can_create_a_user() { $response = $this->post('/register', [...]); $response->assertStatus(201); }
Escape output (dùng Blade mặc định {{ }}): Luôn luôn escape dữ liệu đầu vào khi hiển thị trên giao diện người dùng. Laravel Blade cung cấp cú pháp {{ }} để tự động escape dữ liệu và bảo vệ ứng dụng khỏi các cuộc tấn công XSS (Cross-Site Scripting).
<h1>{{ $user->name }}</h1>
Không để .env public: Đảm bảo rằng file .env không được phép truy cập từ bên ngoài. Đây là nơi lưu trữ các cấu hình nhạy cảm như mật khẩu, API keys, và các thông tin cấu hình khác.
.gitignore để đảm bảo .env không bị đẩy lên kho mã nguồn:.env
Validate toàn bộ input: Luôn luôn validate dữ liệu người dùng trước khi xử lý hoặc lưu trữ vào cơ sở dữ liệu. Sử dụng các phương thức built-in trong Laravel như FormRequest để kiểm tra và xác thực dữ liệu đầu vào.
public function rules() { return [ 'email' => 'required|email|unique:users,email', 'password' => 'required|min:8', ]; }
Dùng policies hoặc gates để kiểm tra quyền truy cập: Sử dụng policies và gates trong Laravel để kiểm tra quyền truy cập của người dùng vào các tài nguyên. Điều này giúp bảo mật và kiểm soát quyền truy cập vào các tính năng hoặc dữ liệu của ứng dụng.
// Trong Policy public function update(User $user, Post $post) { return $user->id === $post->user_id; } // Trong Controller $this->authorize('update', $post);
Variable Naming: Sử dụng camelCase cho tên biến, đảm bảo tên biến rõ ràng và mô tả đúng chức năng.
userId, totalAmount, isActiveFunction Naming: Sử dụng camelCase, và đặt tên hàm như động từ hoặc cụm động từ chỉ hành động.
getUserById(), calculateTotal(), isValid()Class Naming: Sử dụng PascalCase cho các lớp hoặc hàm khởi tạo.
UserManager, HttpRequestAvoid Uncommon Abbreviations: Ưu tiên sự rõ ràng thay vì sử dụng viết tắt không phổ biến.
Boolean Variable Naming: Bắt đầu tên biến boolean với is, has, can, hoặc should để thể hiện ý nghĩa.
let hoặc const thay vì var. Dùng let khi giá trị thay đổi và const khi giá trị không thay đổi.this.const calculateTotal = (price, quantity) => price * quantity;
camelCase cho tên biến, bắt đầu bằng chữ thường, với các từ tiếp theo viết hoa chữ cái đầu.
userName, productList, isAvailable.totalAmount, userEmail, isUserActive.camelCase cho tên hàm, bắt đầu bằng chữ thường và là động từ hoặc cụm động từ mô tả hành động.
getUserDetails(), calculateTotal(), updateProfile().PascalCase cho tên lớp, mỗi từ bắt đầu bằng chữ cái viết hoa.
UserManager, OrderHandler, HttpRequest.users, products, comments.userDetails, productInfo, orderSummary.UPPER_SNAKE_CASE cho các hằng số, mỗi từ được phân tách bằng dấu gạch dưới.
MAX_USERS, API_URL, DEFAULT_TIMEOUT.id, userData, price.temp hoặc data. Tên tham số cần phải rõ ràng hơn.is, has, can, hoặc should để thể hiện rõ ràng mục đích của biến.
isActive, hasPermission, canEdit, shouldDisplay._ trước tên biến để chỉ ra rằng biến đó là riêng tư (private).
_userData, _totalAmount, _apiKey.get và set cho các phương thức getter và setter, đi kèm với tên của thuộc tính mà nó thao tác.
getUser(), setUserData(), getTotalAmount(), setUserStatus().Sử dụng camelCase cho tên hàm, bắt đầu bằng chữ thường và sử dụng động từ hoặc cụm động từ mô tả hành động.
calculateTotal(), getUserDetails(), updateProfile().Tên hàm phải mô tả rõ ràng hành động hoặc kết quả mà hàm thực hiện.
| Purpose | Prefix | Format | Example |
|---|---|---|---|
| Get a value | get |
get<Field> |
getUser() |
| Set a value | set |
set<Field> |
setUserName() |
| Check a condition | is |
is<Field> |
isUserActive() |
| Handle an action | handle |
handle<Field> |
handleClick() |
| Initialize an object or class | init |
init<Field> |
initUser() |
| Perform an action with side effect | do |
do<Field> |
doSomething() |
| Perform an async action | fetch |
fetch<Field> |
fetchUserData() |
Async để dễ dàng nhận biết rằng hàm đó thực hiện hành động bất đồng bộ.
fetchUserDataAsync(), getUserDetailsAsync().handle cho các hàm xử lý sự kiện.
handleSubmit(), handleClick(), handleChange().is, has, can, should để rõ ràng hơn về mục đích của hàm.
isActive(), hasPermission(), canEdit(), shouldRender().generateRandomId(), formatDate(), parseJson().Sử dụng try...catch để bắt lỗi: Khi thực hiện các hành động có thể gặp lỗi, hãy sử dụng try...catch để bắt và xử lý lỗi một cách hợp lý.
try { // Some code that may throw an error let result = riskyFunction(); } catch (error) { console.error('An error occurred:', error); }
Đưa ra thông báo lỗi chi tiết: Khi gặp lỗi, cần phải thông báo đầy đủ và rõ ràng về lỗi đã xảy ra, tránh chỉ ghi chung chung hoặc bỏ qua thông báo.
try { const user = getUserData(userId); } catch (error) { console.error('Failed to fetch user data:', error.message); }
Tránh bỏ qua lỗi: Tránh việc bỏ qua lỗi mà không xử lý hoặc ghi lại thông tin về nó. Việc này có thể làm mất thông tin quan trọng khi debug.
try { riskyFunction(); } catch (error) { // Ignoring the error without any handling }
try { riskyFunction(); } catch (error) { console.error('Error occurred in riskyFunction:', error); // Handle the error or rethrow it }
class CustomError extends Error { constructor(message, statusCode) { super(message); this.name = this.constructor.name; this.statusCode = statusCode; } } try { throw new CustomError('Something went wrong!', 400); } catch (error) { console.error(error.message); // 'Something went wrong!' console.error(error.statusCode); // 400 }
Sử dụng try...catch trong async/await: Khi làm việc với các hàm bất đồng bộ, bạn nên sử dụng try...catch để xử lý các lỗi xảy ra trong quá trình thực thi bất đồng bộ.
async function fetchData() { try { const response = await fetch('/api/data'); const data = await response.json(); return data; } catch (error) { console.error('Error fetching data:', error); } }
Sử dụng .catch() trong Promises: Nếu không sử dụng async/await, bạn có thể sử dụng .catch() để xử lý lỗi trong các Promise.
fetchData() .then(data => { console.log(data); }) .catch(error => { console.error('Error:', error); });
Ghi lại lỗi với thông tin chi tiết: Khi có lỗi xảy ra, hãy ghi lại chi tiết lỗi để dễ dàng theo dõi và khắc phục.
try { throw new Error('Something went wrong'); } catch (error) { console.error('Error message:', error.message); console.error('Stack trace:', error.stack); }
Sử dụng công cụ logging: Bạn có thể tích hợp các công cụ logging như Sentry, LogRocket, hoặc Winston để ghi lại lỗi và theo dõi ứng dụng trong môi trường production.
Hiển thị lỗi người dùng thân thiện: Đối với người dùng cuối, tránh hiển thị thông báo lỗi chi tiết mà thay vào đó hãy hiển thị các thông báo lỗi thân thiện và dễ hiểu.
try { const result = processData(); } catch (error) { alert('There was an issue processing your request. Please try again later.'); }
Đảm bảo ứng dụng vẫn hoạt động bình thường: Khi xảy ra lỗi, ứng dụng không nên dừng hoàn toàn. Cần đảm bảo rằng lỗi không làm gián đoạn trải nghiệm người dùng.
Viết mã dễ đọc và dễ hiểu: Đảm bảo mã nguồn của bạn dễ đọc, dễ hiểu và dễ bảo trì. Sử dụng các tên biến, hàm rõ ràng và bình luận khi cần thiết.
Tuân thủ nguyên tắc DRY (Dont Repeat Yourself): Tránh lặp lại mã nguồn, tách các đoạn mã tái sử dụng thành các hàm hoặc module riêng biệt.
Sử dụng chức năng của JavaScript một cách hiệu quả: JavaScript có rất nhiều tính năng mạnh mẽ như destructuring, arrow functions, async/await, promises, v.v. Hãy tận dụng chúng để làm cho mã nguồn của bạn ngắn gọn và dễ đọc.
const { name, age } = user; const greet = (name) => `Hello, ${name}!`;
Tổ chức mã nguồn tốt: Đảm bảo rằng cấu trúc thư mục của dự án được tổ chức hợp lý, dễ mở rộng và bảo trì.
Sử dụng ESLint để kiểm tra mã nguồn: Thiết lập ESLint để đảm bảo mã nguồn tuân thủ các quy tắc và chuẩn của nhóm, tránh các lỗi phổ biến.
npm install eslint --save-dev npx eslint --init
Viết test tự động: Tạo các bài test cho những phần mã quan trọng trong ứng dụng của bạn. Điều này giúp bảo vệ ứng dụng khỏi các lỗi không mong muốn khi bạn thay đổi mã.
Giữ cho hàm nhỏ và đơn giản: Một hàm chỉ nên làm một việc và làm tốt việc đó. Điều này giúp giảm độ phức tạp và cải thiện khả năng tái sử dụng.
Sử dụng const và let thay vì var: Tránh sử dụng var vì nó có phạm vi hàm và dễ gây nhầm lẫn. Thay vào đó, hãy sử dụng const cho các giá trị không thay đổi và let cho các giá trị có thể thay đổi.
const PI = 3.14; // Giá trị không thay đổi let counter = 0; // Giá trị có thể thay đổi
Tránh tạo nhiều đối tượng không cần thiết: Mỗi khi tạo một đối tượng mới, JavaScript phải thực hiện nhiều bước như phân bổ bộ nhớ. Hãy tránh tạo ra các đối tượng không cần thiết hoặc lặp lại.
Tránh thay đổi DOM quá nhiều lần: Mỗi khi bạn thay đổi DOM, trình duyệt phải tính toán lại bố cục và hiển thị lại trang. Do đó, cố gắng nhóm các thao tác DOM lại với nhau để giảm thiểu số lần thay đổi.
documentFragment hoặc các thư viện như React, Vue để giảm thiểu thao tác DOM.Sử dụng Debouncing và Throttling: Khi làm việc với các sự kiện như scroll hoặc resize, sử dụng debouncing hoặc throttling để giảm thiểu số lần gọi hàm và cải thiện hiệu suất.
function debounce(fn, delay) { let timeout; return function() { clearTimeout(timeout); timeout = setTimeout(() => fn.apply(this, arguments), delay); }; }
Không lưu trữ thông tin nhạy cảm trong LocalStorage/SessionStorage: Tránh lưu trữ các thông tin nhạy cảm như mật khẩu hoặc token trong localStorage hoặc sessionStorage vì chúng có thể dễ dàng bị truy cập bởi kẻ tấn công.
Chống Cross-Site Scripting (XSS): Sử dụng các thư viện hoặc phương pháp mã hóa đầu ra (escape output) khi hiển thị dữ liệu từ người dùng hoặc dữ liệu không tin cậy trên giao diện.
const userInput = '<script>alert("XSS")</script>'; document.getElementById('output').textContent = userInput;
Chống Cross-Site Request Forgery (CSRF): Sử dụng token CSRF trong các form và yêu cầu API để bảo vệ ứng dụng khỏi các cuộc tấn công CSRF.
Document rõ ràng mã nguồn: Hãy luôn ghi chú và viết tài liệu cho các đoạn mã phức tạp, giải thích tại sao chúng được viết như vậy và chúng làm gì. Điều này giúp người khác (hoặc chính bạn trong tương lai) hiểu rõ hơn về mã nguồn.
Sử dụng Version Control (Git): Quản lý mã nguồn của bạn bằng Git và sử dụng các chiến lược như branching, commit message rõ ràng để đảm bảo dự án được phát triển một cách có tổ chức.
Cập nhật thư viện và công cụ thường xuyên: Đảm bảo rằng tất cả các thư viện và công cụ trong dự án đều được cập nhật để tránh các vấn đề bảo mật hoặc hiệu suất.
Viết test cho mã nguồn quan trọng: Việc viết test cho các phần mã quan trọng của ứng dụng là cần thiết để đảm bảo rằng mã hoạt động như mong đợi và dễ dàng phát hiện lỗi trong quá trình thay đổi mã.
Tách biệt test và mã thực thi: Đảm bảo rằng phần test được tách biệt rõ ràng với phần mã ứng dụng. Hãy tổ chức cấu trúc thư mục sao cho dễ dàng tìm kiếm và bảo trì test.
src/ components/ services/ tests/ components/ services/
Test phải dễ đọc và dễ hiểu: Các bài test phải được viết rõ ràng và dễ hiểu để bất kỳ ai cũng có thể dễ dàng đọc và hiểu mục đích của chúng.
Unit Tests: Kiểm tra các đơn vị mã riêng biệt (ví dụ: một hàm hoặc một class) để đảm bảo chúng hoạt động đúng.
// testAdd.js import { add } from './math'; test('add() adds two numbers', () => { expect(add(1, 2)).toBe(3); });
Integration Tests: Kiểm tra sự tương tác giữa các phần khác nhau của ứng dụng. Đảm bảo rằng các module hoặc component phối hợp hoạt động như mong đợi.
End-to-End (E2E) Tests: Kiểm tra toàn bộ ứng dụng từ đầu đến cuối, mô phỏng các hành động của người dùng và kiểm tra tính chính xác của các chức năng.
Jest: Jest là một framework test phổ biến cho JavaScript, dễ cấu hình và hỗ trợ các bài test đồng bộ và bất đồng bộ.
test('adds 1 + 2 to equal 3', () => { expect(1 + 2).toBe(3); });
Mocha: Mocha là một framework test linh hoạt với các tính năng mạnh mẽ để viết các bài test đơn giản hoặc phức tạp.
const assert = require('assert'); describe('Math', function() { it('should add 1 + 2 to equal 3', function() { assert.equal(1 + 2, 3); }); });
Chai: Chai là một thư viện assertion được sử dụng với Mocha để kiểm tra giá trị trong các bài test.
const { expect } = require('chai'); describe('Math', function() { it('should add 1 + 2 to equal 3', function() { expect(1 + 2).to.equal(3); }); });
Đặt tên bài test rõ ràng và mô tả chức năng: Đảm bảo tên của các bài test và các mô tả hàm rõ ràng, phản ánh đúng mục đích của bài test.
test('should return true when user is active', () => { const user = { active: true }; expect(isActive(user)).toBe(true); });
Dùng assertion libraries: Sử dụng các thư viện assertion để kiểm tra giá trị mà bạn nhận được từ mã nguồn có khớp với giá trị mong đợi hay không. Thư viện như expect, assert, và chai giúp việc kiểm tra trở nên dễ dàng hơn.
Test cả trường hợp thành công và thất bại: Đảm bảo rằng bài test của bạn không chỉ kiểm tra các trường hợp thành công mà còn kiểm tra các lỗi và tình huống ngoại lệ.
test('should throw an error if user is not found', () => { expect(() => findUser(999)).toThrow('User not found'); });
Chạy bài test tự động: Sử dụng lệnh npm test hoặc jest để chạy tất cả các bài test trong dự án. Các công cụ test hiện đại như Jest cung cấp khả năng chạy lại tự động mỗi khi có thay đổi trong mã nguồn.
npm testChạy thử nghiệm trong môi trường CI/CD: Tích hợp các bài test vào quy trình CI/CD để đảm bảo rằng mỗi thay đổi trong mã nguồn đều được kiểm tra tự động trước khi được triển khai.
Mocking: Mô phỏng các đối tượng hoặc hàm bên ngoài để kiểm tra các phần của mã mà không cần thực sự phụ thuộc vào các phần này.
jest.mock('./database', () => { return { getUser: jest.fn(() => 'mocked user') }; });
Stubbing: Thay thế các phần của ứng dụng bằng các stub để kiểm tra cách một đoạn mã tương tác với các phần khác mà không thực sự thực hiện các thao tác ngoài phạm vi.
const getUser = jest.fn().mockReturnValue({ name: 'John' });
jest --coverage để kiểm tra độ bao phủ mã của các bài test, giúp đảm bảo rằng mọi phần mã quan trọng đều được kiểm tra.
npm run test -- --coverage
jest-fetch-mock để giả lập các cuộc gọi API trong khi kiểm tra logic của ứng dụng.
import fetch from 'jest-fetch-mock'; fetch.mockResponseOnce(JSON.stringify({ data: '12345' })); test('fetches successfully from an API', async () => { const response = await fetch('/api/data'); const result = await response.json(); expect(result.data).toEqual('12345'); });
Sử dụng Modules (ES6): Các module giúp tách biệt mã nguồn và dễ dàng quản lý các phần khác nhau của ứng dụng. Hãy sử dụng cú pháp import và export của ES6 để quản lý mã nguồn của bạn.
// myModule.js export const greet = (name) => `Hello, ${name}!`; // main.js import { greet } from './myModule'; console.log(greet('World')); // Output: Hello, World!
Ưu tiên sử dụng import thay vì require: Cú pháp import mang lại tính rõ ràng và dễ đọc hơn so với require, đồng thời hỗ trợ tính năng tree-shaking trong bundlers như Webpack hoặc Rollup để tối ưu hóa kích thước tệp cuối cùng.
Đặt tên module rõ ràng: Các tên module cần phải mô tả chính xác chức năng của module đó. Tránh đặt tên quá chung chung hoặc mơ hồ.
// Tên module hợp lý import { calculateTotalPrice } from './utils/calculateTotalPrice';
Dùng export default cho module chính: Nếu một module chỉ có một chức năng chính, hãy sử dụng export default để xuất ra hàm hoặc đối tượng chính của module đó.
// file: math.js const add = (a, b) => a + b; export default add; // file: main.js import add from './math'; console.log(add(2, 3)); // Output: 5
Sắp xếp các imports theo thứ tự hợp lý: Để dễ dàng bảo trì và theo dõi, hãy sắp xếp các import theo nhóm:
Ví dụ:
// Bên ngoài thư viện import React from 'react'; import { useState } from 'react'; // Module nội bộ import { calculateTotal } from './utils/calculateTotal'; import { formatDate } from './utils/formatDate';
Tuân thủ các chuẩn mã nguồn: Đảm bảo rằng bạn tuân thủ các chuẩn mã nguồn nhất quán trong dự án, chẳng hạn như Prettier cho việc định dạng mã và ESLint cho việc kiểm tra các vấn đề về mã. Điều này giúp cải thiện sự nhất quán và chất lượng của mã nguồn.
{ "semi": true, "singleQuote": true, "trailingComma": "es5", "tabWidth": 2 }
Dùng const và let thay vì var: Hãy ưu tiên sử dụng const cho các giá trị không thay đổi và let cho các giá trị có thể thay đổi. Tránh sử dụng var, vì nó dễ gây nhầm lẫn và có phạm vi hoạt động không rõ ràng.
const name = 'John'; // Không thay đổi let age = 25; // Có thể thay đổi
Không sử dụng dấu chấm phẩy một cách tùy tiện: JavaScript tự động thêm dấu chấm phẩy, nhưng bạn vẫn nên thêm nó một cách rõ ràng sau mỗi câu lệnh để tránh các lỗi không mong muốn.
const x = 5; const y = 10;
Hạn chế các biểu thức phức tạp: Viết mã đơn giản, rõ ràng và dễ hiểu. Tránh sử dụng các biểu thức phức tạp và quá dài, điều này làm cho mã khó đọc và bảo trì.
const result = a && b || c && d;
const result = (a && b) || (c && d);
Sử dụng 4 khoảng trắng cho thụt lề: Đảm bảo tất cả mã nguồn sử dụng 4 khoảng trắng cho thụt lề.
function greet(name) { if (name) { console.log(`Hello, ${name}!`); } else { console.log('Hello, world!'); } }
Sắp xếp các đối tượng và mảng: Các đối tượng và mảng nên được sắp xếp sao cho dễ đọc, với các phần tử được căn chỉnh đẹp mắt.
const person = { firstName: 'John', lastName: 'Doe', age: 30, };
Tên hàm nên là động từ: Tên hàm nên phản ánh hành động mà hàm đó thực hiện. Sử dụng động từ mạnh và mô tả hành động rõ ràng.
getData(), calculateSum(), isValid()Tên biến nên rõ ràng và mô tả đúng chức năng: Các tên biến cần phải mô tả rõ ràng mục đích và vai trò của chúng. Tránh sử dụng các tên biến quá chung chung như data, item, temp.
const totalAmount = 100; const userName = 'John';
Dùng camelCase cho tên biến và hàm: JavaScript sử dụng kiểu đặt tên camelCase cho các tên biến và hàm.
let userAge = 25; function calculatePrice() { ... }
Ghi chú rõ ràng khi cần thiết: Viết chú thích ngắn gọn và rõ ràng để giải thích các phần mã phức tạp hoặc khó hiểu. Tránh ghi chú thừa thãi cho các phần mã rõ ràng.
// Tính toán tổng giá trị const total = price * quantity;
Sử dụng JSDoc để tài liệu hoá mã: Sử dụng JSDoc cho các hàm và đối tượng phức tạp để người đọc có thể hiểu rõ về chức năng và cách sử dụng.
/** * Tính toán tổng giá trị sản phẩm * @param {number} price - Giá trị của sản phẩm * @param {number} quantity - Số lượng sản phẩm * @returns {number} - Tổng giá trị */ function calculateTotal(price, quantity) { return price * quantity; }
try { const data = fetchData(); } catch (error) { console.error('Error fetching data:', error); }
Tái cấu trúc mã khi cần thiết: Đảm bảo mã của bạn dễ duy trì và có thể mở rộng trong tương lai. Nếu phát hiện mã phức tạp hoặc khó hiểu, hãy tái cấu trúc nó.
Áp dụng nguyên lý DRY (Don't Repeat Yourself): Tránh lặp lại mã không cần thiết. Tạo các hàm, lớp hoặc module tái sử dụng được để giảm thiểu mã lặp lại.