Di chuyển từ jQuery sang Next.js: Hướng dẫn

theanh

Administrator
Nhân viên
jQuery đã phục vụ tốt cho các nhà phát triển trong nhiều năm. Tuy nhiên, các thư viện (như React) và Framework (như Next.js) hiện đang mang đến cho chúng ta nhiều tính năng hiện đại hơn để giúp cải thiện hiệu suất và khả năng bảo trì mã của chúng ta. Hướng dẫn này sẽ chỉ cho bạn cách viết lại trang jQuery của bạn bằng Next.js để tận dụng tất cả các tính năng mới này, chẳng hạn như định tuyến phía máy khách để chuyển đổi mượt mà hơn và khả năng tách mã thành các thành phần để có thể tái sử dụng nhiều hơn.

Bắt đầu​

Cách dễ nhất để bắt đầu với Next.js là chạy npx create-next-app. Lệnh này sẽ tạo khung cho một dự án cho bạn. Tuy nhiên, để hiểu lệnh này thực hiện chức năng gì, chúng ta sẽ tạo ứng dụng của mình từ đầu.

Đầu tiên, chúng ta sẽ tạo dự án Next.js bằng npm init. Bạn có thể tiếp tục với các thiết lập mặc định, vì chúng ta sẽ thay đổi chúng sau. Sau đó, chúng ta muốn cài đặt React và Next.js bằng cách sử dụng:
Mã:
npm install react react-dom next
Tiếp theo, chúng ta có thể mở tệp package.json và thay thế scripts mặc định bằng:
Mã:
"scripts": { "dev": "next", "build": "next build", "start": "next start"}
Điều này cho phép bạn chạy npm run dev để khởi động máy chủ phát triển; npm run build để xây dựng ứng dụng của bạn; và npm run start để khởi động máy chủ của ứng dụng đã xây dựng đó.

Để thêm các trang — giống như bạn sẽ làm index.html với jQuery — hãy tạo một thư mục có tên pages và tạo một tệp có tên index.jsx trong đó. Bên trong tệp này, hãy đặt mã sau:
Mã:
export default function Index() { return [HEADING=1]Hello World[/HEADING] ;}
Bây giờ, bằng cách chạy npm run start và điều hướng đến localhost:3000, bạn sẽ thấy thẻ h1 được hiển thị. Tên của hàm này không quan trọng, vì vậy bạn có thể gọi nó theo bất kỳ tên nào bạn muốn. Tuy nhiên, đừng sử dụng hàm mũi tên ẩn danh vì điều này sẽ ngăn làm mới nhanh hoạt động.

CSS​

Trong jQuery, bạn có thể chỉ định CSS theo trang, nhập các bảng định kiểu khác nhau cho các trang khác nhau. Điều này cũng có thể thực hiện được trong Next.js bằng cách sử dụng thành phần next/head và thẻ link theo cùng cách như jQuery. Dù sao thì, có nhiều cách thân thiện với hiệu suất hơn để thực hiện việc này trong Next.js.

Bảng định kiểu toàn cục​

Cách đầu tiên là sử dụng bảng định kiểu toàn cục. Để thực hiện, chúng ta cần tạo một Ứng dụng tùy chỉnh bằng cách tạo tệp _app.js bên trong thư mục pages. Điểm khởi đầu cho tệp này như sau:
Mã:
function MyApp({ Component, pageProps }) { return }export default MyApp
Ở đầu tệp này, bạn có thể thêm một câu lệnh import và import bất kỳ tệp CSS nào bạn muốn. Ví dụ: nếu bạn đã tạo một thư mục riêng ở cấp gốc có tên là styles và đặt main.css vào đó, thì bạn sẽ thêm:
Mã:
import "../styles/main.css"
Bây giờ, bất cứ thứ gì bạn đặt bên trong tệp này sẽ được áp dụng trong toàn bộ ứng dụng của bạn.

Mô-đun CSS​

Tùy chọn tiếp theo là mô-đun CSS — cho phép bạn chỉ định CSS ở bất kỳ đâu trong ứng dụng của mình. Họ sẽ tạo tên lớp duy nhất từ các lớp bạn cung cấp, do đó bạn có thể sử dụng cùng một tên lớp ở nhiều nơi trong mã ứng dụng của mình.

Mở rộng ví dụ hello world ban đầu, bạn có thể tạo tệp index.module.css trong cùng thư mục rồi viết lệnh import:
Mã:
import styles from "./index.module.css"
Sau đó, nếu bạn định nghĩa lớp heading trong tệp CSS, bạn có thể thực hiện như sau:
Mã:
export default function Index() { return [HEADING=1]Hello World[/HEADING] ;}
và các kiểu đó sẽ chỉ được áp dụng cho phần tử đó.

JSX có kiểu​

Tùy chọn tích hợp cuối cùng là JSX có kiểu. Tùy chọn này tương tự nhất với việc thêm thẻ ở đầu trang của bạn để xác định một số kiểu. Chỉ cần thêm jsx vào thẻ và sử dụng chuỗi mẫu bên trong, như sau:
Mã:
{` .heading { font-weight: 700 `}
Tùy chọn này có ưu điểm là có thể thay đổi khi chạy. Ví dụ: nếu bạn muốn cung cấp độ đậm phông chữ trong các prop thành phần của mình, bạn có thể thực hiện:
Mã:
{` .heading{ font-weight: ${props.fontWeight} `}
Một nhược điểm của phương pháp này là nó đưa JavaScript thời gian chạy bổ sung vào ứng dụng của bạn, làm tăng kích thước thêm 12kb (3kb đã nén gzip).

Sự kiện​

Trong jQuery, bạn có thể thiết lập các sự kiện để phản hồi các phần tử DOM. Để bạn có ý tưởng, bạn có thể muốn thực thi mã khi thẻ p được nhấp và thực hiện như sau:
Mã:
$( "p" ).click(function() { console.log( "Bạn đã nhấp vào một đoạn văn!" );});
Thay vào đó, React sử dụng trình xử lý sự kiện — mà bạn có thể đã thấy trong HTML — như onclick. Lưu ý rằng React sử dụng camelCase thay thế, vì vậy onclick phải được tham chiếu là onClick. Do đó, việc viết lại ví dụ nhỏ này vào React sẽ như sau:
Mã:
export default function Index() { function clickParagraph(){ console.log("Bạn đã nhấp vào một đoạn văn!"); } return 
Hello World
;}
Mỗi phương pháp đều có ưu và nhược điểm riêng. Trong jQuery, bạn có thể dễ dàng thực hiện điều gì đó cho tất cả đoạn văn, trong khi ở React, bạn phải chỉ định cho mỗi đoạn văn. Tuy nhiên, đối với các cơ sở mã lớn hơn, việc phải chỉ định giúp bạn dễ dàng xem điều gì sẽ xảy ra với tương tác với bất kỳ phần tử nào, trong khi bạn có thể đã quên mất hàm jQuery.

Hiệu ứng​

Hiệu ứng được sử dụng trong jQuery để hiển thị và ẩn nội dung. Bạn có thể đã có thứ gì đó như thế này:
Mã:
$( "p" ).hide();
Trong React, hành vi này được triển khai bằng cách sử dụng kết xuất có điều kiện. Bạn có thể thấy điều này bằng cách kết hợp nó với sự thay thế cho các sự kiện mà chúng ta vừa thấy:
Mã:
import {useState} from "react"export default function Index() { const [show, setShow] = useState(true); function clickButton(){ setShow(false) } return (  [HEADING=1]Hello world[/HEADING] {show && Click me}  )}
Khi bạn nhấp vào nút này, nó sẽ thay đổi giá trị của show thành false và do đó, câu lệnh sẽ không hiển thị bất cứ thứ gì. Điều này có thể được mở rộng bằng toán tử điều kiện để hiển thị một thứ này hay thứ khác, tùy thuộc vào giá trị như sau:
Mã:
show ? 
Hiển thị điều này nếu show là đúng
 : 
Hiển thị điều này nếu show là sai

Lấy dữ liệu​

Trong jQuery, Ajax được sử dụng để lấy dữ liệu bên ngoài mà không cần tải lại. Trong React, điều này có thể được thực hiện bằng cách sử dụng hook useEffect. Đối với ví dụ này, chúng ta sẽ lấy tỷ giá hối đoái từ API công khai khi trang tải:
Mã:
import { useState, useEffect } from "react";export default function Index() { const [er, setEr] = useState(true); useEffect(async () => { const result = await fetch("https://api.exchangerate.host/latest"); const exchangerate = await result.json(); setEr(exchangerate.rates["GBP"]); }, []); return (  [HEADING=1]Hello world[/HEADING] 
Tỷ giá hối đoái: {er}
  );}
useEffect lấy một hàm và một mảng phụ thuộc. Hàm thực hiện việc truy xuất dữ liệu, sử dụng async làm API fetch một cách không đồng bộ. Sau đó, chúng ta có thể đặt bất kỳ trạng thái nào mà chúng ta muốn ở đó và trạng thái đó sẽ được cập nhật trên trang. Mảng phụ thuộc xác định giá trị nào thay đổi sẽ chạy hàm. Trong trường hợp này, nó được đặt thành một mảng rỗng, nghĩa là hàm sẽ chỉ chạy khi trang tải lần đầu.

Ngoài ra, Next.js còn cung cấp các tùy chọn để lấy dữ liệu trên máy chủ hoặc tại thời điểm dựng. Đối với việc lấy dữ liệu tại thời điểm dựng, có thể sử dụng hàm getStaticProps. Hàm này cải thiện hiệu suất vì dữ liệu có thể được cung cấp cùng với trang — thay vì phải chờ dịch vụ bên ngoài. Để sử dụng hàm này, hãy tạo hàm này trong trang vì nó không hoạt động trong các thành phần.
Mã:
export async function getStaticProps() { return { props: {}, }}
Bạn có thể thực hiện bất kỳ thao tác lấy dữ liệu nào bạn muốn trước khi trả về, sau đó, truyền dữ liệu đến trang trong props — sau đó, dữ liệu được cung cấp cho trang và có thể được truy cập trong props.

Bằng cách thay thế tên hàm từ getStaticProps thành getServerSideProps, hàm sẽ được gọi trên mọi yêu cầu, mang đến cho bạn sự linh hoạt để sử dụng các hàm Node.js nếu cần. Nó cũng cho phép bạn thực hiện nhiều yêu cầu dữ liệu trên máy chủ và xử lý chúng để giảm băng thông mà máy khách sử dụng.

Bạn cũng có tùy chọn trung gian giữa hai tùy chọn được gọi là Tái tạo tĩnh gia tăng. Tùy chọn này sẽ tạo một trang tĩnh theo cùng cách như getStaticProps, nhưng cho phép bạn chỉ định thời gian xác thực lại — sẽ tạo lại trang khi có yêu cầu đến nhiều nhất là trong khoảng thời gian bạn chỉ định. Để thực hiện điều này, bên cạnh các prop, bạn cũng nên bao gồm khóa revalidate với thời gian tính bằng giây mà bạn muốn.

Đối tượng thành phần tử DOM​

Với jQuery, bạn phải cẩn thận với phương pháp sử dụng để biến đối tượng thành phần tử DOM. Ví dụ phổ biến nhất về điều này là tạo danh sách các mục vì với jQuery, vòng lặp qua các mục sẽ thêm từng mục vào DOM một. Với React, DOM ảo được sử dụng để tạo sự khác biệt của trạng thái mới so với trạng thái hiện tại. Điều này có nghĩa là mặc dù thêm các mục vào vòng lặp, chúng vẫn được thêm vào DOM thực dưới dạng một thao tác.

Điều này được thực hiện bằng hàm map trong JavaScript, tại đó bạn có thể ánh xạ từng mục với một số JSX.
Mã:
export default function Index() { const fruits = ["Apple", "Orange", "Pear"]; return (  [HEADING=1]Hello world[/HEADING] [LIST] {fruits.map((fruit) = > ( 
[*] {fruit} ))} [/LIST]  );}
Lưu ý rằng phần tử bên trong map cần có key prop. Điều này được sử dụng trong quy trình diffing được thảo luận ở trên, giúp React dễ dàng phân biệt giữa từng phần tử, do đó mỗi phần tử này phải là duy nhất.

Deffereds​

Việc sử dụng deferreds trong jQuery có thể được thay thế bằng chức năng promise gốc của JavaScript. Cú pháp cho deffereds được thiết kế để phản ánh chức năng của các lời hứa, do đó cú pháp phải quen thuộc và không yêu cầu quá nhiều thay đổi. Một ví dụ về nơi deffereds có thể được sử dụng là trong lấy dữ liệu. Nếu bạn thực hiện điều này bằng phương thức fetch trong JavaScript, thì bạn có thể thêm .then vào cuối lệnh lấy dữ liệu vì nó trả về một lời hứa. Mã này sẽ chỉ chạy khi lệnh lấy dữ liệu hoàn tất và do đó dữ liệu (hoặc lỗi) sẽ có mặt. Bạn có thể xem chức năng này đang được sử dụng tại đây:
Mã:
fetch("example.com").then((response) => { console.log(response)}).catch((error) => {console.error(error)})
Điều này sẽ lấy example.com và ghi lại phản hồi đã lấy trừ khi xảy ra lỗi — trong trường hợp này, phản hồi sẽ được ghi lại dưới dạng lỗi.

Ngoài cú pháp này, cú pháp async/await mới hơn cũng có thể được sử dụng. Những cú pháp này yêu cầu một hàm được định nghĩa là async, theo cùng cách mà bạn có thể xuất một hàm. Bạn có thể khai báo như sau:
Mã:
async function myFunction(){ return}
Bên trong hàm này, bạn có thể gọi thêm các hàm async bằng cách đặt await trước chúng, ví dụ:
Mã:
async function myFunction(){ const data = await fetch("example.com") return data}
Mã này sẽ trả về một lời hứa sẽ giải quyết khi dữ liệu được truy xuất, do đó cần phải gọi nó bên trong một hàm asynchronous để chờ kết quả. Tuy nhiên, để cũng bắt được lỗi, bạn sẽ cần phải viết một điều kiện để kiểm tra trạng thái phản hồi — nếu data.ok không đúng, thì sẽ có lỗi được đưa ra. Sau đó, bạn có thể gói các câu lệnh này trong một khối try catch, thay vì sử dụng .catch. Bạn có thể đọc thêm về các phương pháp này trong bài viết này.

Cải tiến​

Định tuyến​

Next.js sử dụng định tuyến hệ thống tệp, rất giống với việc sử dụng các trang .html khác nhau trong một trang web truyền thống. Tuy nhiên, hệ thống này cũng cung cấp các tính năng vượt trội hơn thế, cung cấp các tuyến động và cho phép truy cập một trang trong một phạm vi url.

Ví dụ: nếu bạn có một blog, bạn có thể giữ tất cả các tệp của mình trong /blog/*, tạo một tệp [slug].jsx bên trong thư mục blog — cho phép nội dung đó được phục vụ cho tất cả các trang trong blog. Sau đó, bạn có thể sử dụng bộ định tuyến trong Next.js để tìm tuyến đường nào đã được điều hướng đến, như sau:
Mã:
const router = useRouter()const { slug } = router.query

API routes​

Các tuyến API cho phép bạn cũng viết phần phụ trợ bên trong ứng dụng Next.js của mình. Để sử dụng các tuyến này, hãy tạo một thư mục api trong thư mục pages của bạn — bây giờ, bất kỳ tệp nào được tạo bên trong thư mục này sẽ chạy trên máy chủ chứ không phải máy khách, như với các trang còn lại.

Để bắt đầu với những điều này, bạn cần xuất một hàm mặc định từ tệp và hàm này có thể sử dụng hai tham số. Tham số đầu tiên sẽ là yêu cầu đến và tham số thứ hai sẽ cho phép bạn tạo phản hồi. Một tuyến API cơ bản có thể được viết như sau:
Mã:
export default function handler(request, response) { response.status(200).json({ magazine: 'Smashing' })}

Limitations​

jQuery UI​

Bạn có thể sử dụng jQuery UI trong ứng dụng của mình để làm giao diện người dùng, nhưng React không cung cấp thư viện UI chính thức như thế này. Tuy nhiên, một loạt các giải pháp thay thế đã được tạo ra. Hai trong số những giải pháp phổ biến nhất là Reach UIReact Aria. Cả hai giải pháp thay thế này đều tập trung rất nhiều vào Khả năng truy cập, đảm bảo rằng dự án bạn tạo ra có thể được nhiều người dùng hơn sử dụng.

Hoạt ảnh​

Mặc dù bạn có thể sử dụng kết xuất có điều kiện thay vì hiệu ứng, nhưng điều này không cung cấp tất cả các chức năng giống nhau, vì bạn không thể thực hiện các thao tác như làm mờ nội dung. Một thư viện giúp cung cấp chức năng này là React Transition Group — cho phép bạn xác định các chuyển tiếp vào và ra.

Kết luận​

Chuyển từ jQuery sang Next.js là một công việc lớn, đặc biệt là đối với các cơ sở mã lớn. Tuy nhiên, quá trình di chuyển này cho phép bạn sử dụng các khái niệm mới hơn (chẳng hạn như tìm nạp dữ liệu tại thời điểm xây dựng) và thiết lập cho bạn các đường dẫn di chuyển đơn giản sang các phiên bản mới của React và Next.js — cùng với các tính năng mà chúng mang lại.

React có thể giúp bạn tổ chức mã của mình tốt hơn (điều này đặc biệt quan trọng đối với các cơ sở mã lớn) và mang lại cải tiến hiệu suất đáng kể thông qua việc sử dụng DOM ảo. Nhìn chung, tôi tin rằng việc di chuyển từ jQuery sang Next.js là xứng đáng và tôi hy vọng rằng nếu bạn quyết định di chuyển, bạn sẽ tận hưởng tất cả các tính năng mà React và Next.js cung cấp.

Đọc thêm​

  • “Cách di chuyển từ jQuery sang Next.js,” Facundo Giuliani
  • “Cái gì, khi nào, tại sao và làm thế nào về tính năng phần mềm trung gian mới của Next.js,” Sam Poder
  • “Bản địa hóa ứng dụng Next.js của bạn,” Átila Fassina
  • “Cách duy trì ứng dụng Next.js lớn,” Nirmalya Ghosh
 
Back
Bên trên