Nội dung chính
Giới thiệu
Chào mọi người!🤗 Nếu bạn là người mới bắt đầuvừa bắt đầuhọc backend, hoặc có thể bạnđã biếtnhững kiến thức cơ bản nhưng muốn hiểu sâu hơn về logicđằng sau máy chủ, định tuyến yêu cầu và tất cả những khái niệm nền tảng đó, tôi thực sự hy vọng bài viết này sẽgiúp ích cho bạn.🥰
Hoàn toàn bình thường khi cảm thấy bối rối lúc đầu hoặc khôngnắm bắtmọi thứ ngay lập tức, bạn sẽ hiểu ra vào đúng thời điểm thôi!
Có thể bài viết này sẽlàm sáng tỏ mọi thứ, hoặc có thể nó chỉ xác nhận những gì bạn đã biết!
Chúng ta sẽ thảo luận các khái niệm sau:
- Định tuyến:Frontend vs Backend
- TừNguồnđếnMáy chủ
- Phát triển vs Sản xuất:
Không Chỉ Là Một Lệnh - Thứ Tự Quan Trọng:
Xử lýĐường Dẫn Quan Trọngtrong Express
Định tuyến:
Frontend vs Backend
Định tuyến có nghĩa là quyết địnhđoạn mã nàohoặctrang nàođể trả về khi người dùng truy cập mộtURLcụ thể (một đường dẫn, hoặc “route”).
Đây là điểmkhác biệt lớn đầu tiêngiữa hai thế giới.
Định tuyến Frontend (Vite, React Router)
Khi chúng ta làm việc với một framework frontend hiện đại (như React với Vite hoặc Create React App), chúng ta không xử lý việc định tuyến các tệp tĩnh:
→Máy Chủ Phát Triển Thông Minh:
Máy chủ phát triển của Vite được thiết kế để làm một việc:
giúp chúng ta code nhanh.
Nó biết cách đọc các tệp nguồn của chúng ta (những tệp trongsrc/) và phục vụ chúng tự động dưới các URL tương ứng.
→Định Tuyến Tự Động:
Nếu mã HTML hoặc React của chúng ta tham chiếu đến/styles/main.css, máy chủ Vitebiết ngay lập tứcnơiđể tìm tệp vật lý (trong bộ nhớ hoặc hệ thống tệp của nó) vàgửinó ngay lập tức đếntrình duyệt.
Chúng ta không viết mã đặc biệt cho việc này❗
→Tập Trung Vào Logic Ứng Dụng:
Sử dụng các thư viện như React Router, chúng ta chỉ tập trung vào logic ứng dụng (ví dụ:
“Nếu URL là/profile, hiển thị thành phần Profile”).
Việc định tuyến này xảy ra trong trình duyệt, sau khi các tệp JavaScript đã được tải.
Định Tuyến Thủ Công Trong Backend (Express.js)
Express.js chỉ là một framework tối giản.
Nó không đưa ra giả định nào và không có “phép thuật” tích hợp sẵn.
→Định Tuyến Là Thủ Công:
Một khi chúng ta đặt Express.js làm chủ quản lý máy chủ chính, chúng ta phải nói rõ ràng cho nó biết phải làm gì cho mọi yêu cầu đến nó.
Chúng ta có muốn yêu cầuPOST/api/loginxác minh mật khẩu trong cơ sở dữ liệu không❓ Chúng ta phải viếtapp.post('/api/login',...).
Chúng ta có muốn yêu cầuGET/api/userstrả về danh sách người dùng không❓ Chúng ta phải viếtapp.get('/api/users', ...).
→Không Biết Về Tệp Tĩnh:
Không có hướng dẫn, nếu nó nhận được yêu cầu cho/main.css, Expresskhông biết đây là một tệp tĩnh.
Nó sẽ tìm kiếm một route API hoặc logic kết xuất.
Vì nókhông tìm thấy, nó ngầm trả về404 Not Found.
Khi mới bắt đầu, Máy Chủ Phát Triển Vite thực hiệnphép thuật thầm lặng,nó tự động xử lý tất cả việc định tuyếnvà phục vụ các tệp tĩnh của chúng ta (như CSS và hình ảnh) trực tiếptừ bộ nhớ.
Tuy nhiên, khi chúng ta giới thiệu Express.js,phép thuật dừng lại.
Express không biết gì và yêu cầuhướng dẫn rõ ràng.
Chúng ta phảithủ côngxác định các route API của mình (app.get('/api/data')) và, quan trọng nhất, chúng taphải sử dụngexpress.static()để thủ công nói với Express:
“Đây là nơi bạn có thể tìm thấytất cả các tệp tĩnh bạn cầncho trang web.” Không có bước thủ công này, các tệp CSS và JavaScript của chúng ta sẽvô hình với máy chủ, dẫn đến các trang bị hỏng❗
Từ Nguồn Đến Máy Chủ
Tệp Tĩnh Trong Phát Triển:
Trong quá trình phát triển, khi chúng ta chạynpm run devtrong dự án React của mình:
→Không Có Thư Mục Vật Lý:
Máy chủ Vite đọc các tệp của chúng ta (.jsx,.css) trực tiếp từ thư mụcsrc/vàgiữ chúngtrong RAM (bộ nhớ).
Nó không tạo ra một thư mục vật lý với các tệp đã biên dịch.
→Tốc Độ:
Bất kỳ thay đổi nào chúng ta lưu sẽ ngay lập tức được truyền đếntrình duyệt qua HMR(Hot Module Reloading), làm cho trải nghiệm coding trở nên mượt mà.
→Chưa Sẵn Sàng Cho Sản Xuất:
Mặc dù nhanh, chế độ này khôngổn định,bảo mật, hoặctối ưu hóađể được truy cập bởi hàng ngàn người dùng thực trên internet.
Từ src đến dist:
Khi Nào và Tại Sao Chúng Ta Biên Dịch❓ Trước khi xuất bản ứng dụng (chuyển sang Sản xuất), chúng ta phải biên dịch mã:
→Lệnh:npm run build.
→Tối Ưu Hóa:
Lệnh này lấy tất cả các tệp nguồn của chúng ta (React,CSS, v.v.),thu nhỏchúng (làm cho chúng nhỏ hơn nhiều),nốichúng (đặt chúng vào ít gói hơn), vàtối ưu hóachúng để đạt tốc độ tải tối đa.
→Kết Quảdist:
Tất cả các tệp được tối ưu hóa này được đặt vào một thư mục vật lý mới, thường được gọi làdisthoặcbuild.
Thư mục này chứa phiên bảntĩnh cuối cùng của frontend, sẵn sàng để được phục vụ.
Giới thiệuexpress.static():
→Làm Thế Nào Để Chúng Ta Phục Vụ Thư Mục dist❓ Bây giờ chúng ta đã cóthư mục dist được tối ưu hóa, chúng ta phải nói với Express (backend) rằngđây là thư mụcnó cần phục vụ công khai:
→Giải Pháp Làexpress.static():
Dòng mã này là hướng dẫn chúng ta đưa cho máy chủ Express:
// in production, Express serves the frontend's 'dist' folderapp.use(express.static(path.join(__dirname,'client/dist')));
→Chức năng:
Khi một yêu cầu HTTPđến máy chủvàkhông khớp với route APInào chúng ta đã định nghĩa (ví dụ:
không bắt đầu bằng/api), Express sẽ tìm trongthư mục dist!
Nếu tìm thấy file phù hợp với URL được yêu cầu (ví dụ:/style.css), nó sẽ gửi ngay choclientvà dừng quá trình định tuyến❗
Phát triển vs Sản xuất:
→ Nếu chúng ta thấy lệnhnpm run devđược sử dụng trong cảfrontend(React) vàbackend(Express), chúng ta có thể nghĩ chúng làm việc giống nhau.Không phải vậy❗ Đây làđiểm khác biệt quan trọng, đặc biệt khi nghĩ vềbảo mậtvàhiệu suất.
→Hai phiên bảnnpm run dev:
Hai Máy chủ, Hai Mục đích
Khi nói vềChế độ Phát triển(Dev) vàChế độ Sản xuất(Prod), chúng ta đang mô tảtrạng tháivàmục đíchcủa mã đang chạy.
Chế độ Phát triển (bàn làm việc):
Chế độ Phát triển làbàn làm việccủa chúng ta, được tối ưu cho tốc độ và gỡ lỗi.
→Mục đích:
mục tiêu duy nhất là hỗ trợlập trình nhanh,kiểm thử, vàgỡ lỗi.
→Thiết lập Frontend:
máy chủ chạyVite Dev Servertrên một cổng (ví dụ:5173).
→Công cụ Backend:
chúng ta sử dụng các công cụ nhưnodemonđể tự độngkhởi động lạimáy chủ ngay khi chúng ta lưu thay đổi.
→Lỗi:
lỗi được hiển thị chi tiết và rõ ràng (stack traces) và in trực tiếp ra console, giúp bạnsửa vấn đềngay lập tức.
→Tối ưu hóa:
file được phục vụ nguyên trạng, vớitối ưu hóa tối thiểu, vì kích thước file và tốc độ không phải là mối quan tâm chính.
Chế độ Sản xuất (máy chủ công khai trực tiếp)
Chế độ Sản xuất là phiên bản cuối cùng, được tối ưu hóa để cung cấp cho công chúng, được xây dựng choổn địnhvàbảo mật.
→Mục đích:
mục tiêu là cung cấp trải nghiệm ổn định,nhanh, vàbảo mậtcho công chúng.
→Thiết lập Frontend:
mã frontend đượcbuildtrước (biên dịch và thu nhỏ) và sau đó được phục vụ bởimáy chủ Express duy nhấttrên cổng được chỉ định (ví dụ:3000).
→Công cụ Backend:
chúng ta sử dụng các trình quản lý tiến trìnhđược tối ưu hóa cao(như PM2) để giữ máy chủ chạy liên tục và mạnh mẽ.
→Lỗi:
lỗi được ghi nhậnnội bộnhưng không bao giờ hiển thị chongười dùng công khai, ngăn chặn rò rỉ bảo mật❗
→Tối ưu hóa:
file được thu nhỏ mạnh mẽ và cache tích cực để đạthiệu suất tối đavà thời gian tảinhanh hơncho tất cả người dùng.
Rủi ro Bảo mật:
Tại sao không chạy chế độ phát triển trong môi trường sản xuất❓
Chạy máy chủ Express trong Chế độ Phát triển trên máy chủ công khai trực tiếp làlỗ hổng bảo mật nghiêm trọngvàkhông được khuyến khích.
→Rò rỉ Bảo mật:
Lỗi phát triển thường bao gồmdữ liệu nhạy cảm, như đường dẫn file trên máy chủ của bạn,biến môi trường, hoặc thậm chícái nhìnvào cấu trúc cơ sở dữ liệu của bạn.
Tiết lộ thông tin này cho kẻ tấn công làlỗ hổng nghiêm trọng❗
→Hiệu suất:
Chế độ dev được xây dựng chotiện lợi, không phải tốc độ.
Bạn sẽ trải nghiệm thời gianphản hồi chậm, quản lýtài nguyênkém và khả năngmở rộnghạn chế dưới tải người dùng thực.
→Ổn định:
Các công cụ nhưnodemonđược thiết kế đểkhởi động lạimã của bạn bất cứ khi nào bạn lưu thay đổi.
Hành vi này không thể đoán trước vàcực kỳ không ổn địnhcho ứng dụng công khai cần chạy 24/7❗
Thứ tự Quan trọng:
Xử lý Đường dẫn Quan trọng trong Express
Bây giờ, hãy xem quy trình quan trọng của việcthiết lậproutes vàmiddlewaretheo đúng thứ tự để đảm bảo Express xử lý tất cả yêu cầu chính xác.
Vai trò của Middleware:
app.use()
Trong Express, Middleware đơn giản là một hàm thực thi trong chu kỳrequest-response.
Nó có quyền truy cập vào đối tượngrequestreq, đối tượngresponseres, và hàmnextnexttrong chu kỳ.
→app.use(middleware):
Phương thức này chủ yếu được sử dụng để đăng ký middleware.
Nó thực thi hàm được cung cấp cho TẤT CẢ phương thứcHTTP(GET,POST, v.v.) và cho TẤT CẢđường dẫn(trừ khi chúng ta chỉ định tiền tố đường dẫn).
→Quy tắc Vàng:
Middleware thực thi theođúng thứ tựchúng được định nghĩa trong mã của chúng ta.
Khi một middleware gửiresponse(ví dụ:res.send('Hello')), chu kỳ dừng lại và không cómiddlewarehoặcroutexử lý nào tiếp theo được thực thi❗
Ví dụ về Thứ tự Luồng:
app.use(cors)app.use(express.json)app.use('/api', apiRouter)app.use(express.static(dist_path))<
– Đây là điểm phân tách quan trọng❗- …
các route khác …
Bẫy React Router:
Tại sao Chúng ta Cầnapp.get('*')❓Đây làmảnh ghépcuối cùng của câu đố liên kết logicbackendcủa chúng ta với ứng dụngfrontend.
→Mục đích:
routeapp.get('*')là trình xử lýBắt mọi yêu cầu.
Nó sử dụng ký tự đại diện*để khớp vớimọi yêu cầuGETmàchưađược xử lý bởi bất kỳ middleware hoặc route nào được định nghĩa trước đó.
→Tình huống:
khi người dùng nhập một liên kết trực tiếp nhưmysite.com/profilevào trình duyệt, máy chủ sẽ nhận được yêu cầu cho/profile.
- Expresskiểm tracác route
API(ví dụ:/api).
Không khớp❗ - Expresskiểm tra
express.static()để tìm file tên profile.
Không khớp❗ - Yêu cầuđược chuyểntới
app.get('*').
→Giải pháp:
chúng ta hướng dẫn Express phục vụ file ứng dụng chính (index.html) chomọi đường dẫn không khớp.
app.get('*',(req,res)=>{// we tell Express:
"Send the HTML file and let React Router handle the URL."res.sendFile(path.resolve(FRONTEND_BUILD_PATH,'index.html'));});
→Kết quả:
Trình duyệt tảiindex.html, React tiếp quản, và React Router đọc URL/profilerồihiển thị component chính xác, tạo ra trải nghiệm ứng dụng một trang liền mạch❗
Kết luận❤️
Nếu bạn là người mới bắt đầu với phát triển backend và những điều này chưa thực sự rõ ràng ngay lập tức, đừng thất vọng, chúng chắc chắn có thểgây bối rối lúc đầu.
Tôi nghĩ tất cả chúng ta đều từng có những thắc mắc này khimới bắt đầu…
Tôi biết tôi chắc chắn đã từng❗ 🙃
Thách thức chính là chúng ta cầnthay đổi tư duyvàcáchsuy nghĩ, từ việc phục vụ tự động trong bộ nhớ của công cụ frontend như Vite, sang cách tiếp cậnthủ công hơnvàcó cấu trúc về routingcủa Express.
Tôi hy vọng bài viết nàygiúp ích cho bạn, hoặc có lẽ nhắc bạn nhớ về những khó khăn bạn đã đối mặt ởgiai đoạn đầuhành trình backend của mình.🤗







![[Tự học C++] Số dấu phẩy động(float, double,…) trong C++](https://cafedev.vn/wp-content/uploads/2019/12/cafedevn_c_develoment-100x70.jpg)

