Next.js 15 發(fā)布,要用好他變得更難了
前幾天,Next.js 15 正式發(fā)布了。然后我就馬不停蹄的通過(guò)重構(gòu)一個(gè)項(xiàng)目去感受 Next.js。
整體使用下來(lái)的感覺(jué)是,要用好 Next.js 的難度更高了。
這里最核心的兩個(gè)原因,一方面是底層支持了 React 19,未來(lái)會(huì)成為主流的開(kāi)發(fā)方式。另外一方面是 App Router 的重要性進(jìn)一步被官方團(tuán)隊(duì)強(qiáng)調(diào),經(jīng)過(guò)一年多的持續(xù)預(yù)熱和強(qiáng)推,Page Router 到 App Router 的升級(jí)已經(jīng)成為了不可逆的趨勢(shì)。
一、React 19
實(shí)際上在這個(gè)時(shí)間節(jié)點(diǎn),React 19 還沒(méi)有正式發(fā)布,他目前還只是 RC 版,但是,Next.js 的默認(rèn)項(xiàng)目中,已經(jīng)直接使用 React 19 RC 了。
React 19 的開(kāi)發(fā)思維和之前的版本有很大的不同。其中最主要的影響,就是在底層代碼中,完全的、徹底刪除了 legacy
模式。
從 React 17 開(kāi)始,React 中就存在兩種模式。
一種是傳統(tǒng)的 legacy 模式。我們可以暫且簡(jiǎn)單粗暴的將其翻譯為同步模式。因?yàn)樵撃J较碌拇蠖鄶?shù)關(guān)鍵底層函數(shù),都是基于 sync 來(lái)命名。例如 shceduleSyncCallback、performSyncWorkOnRoot 等等。
另外一種就是 Concurrent 模式。也就是我們聽(tīng)到很多的并發(fā)模式。但是由于長(zhǎng)期以來(lái),對(duì)于并發(fā)模式的思考與支持并不成熟,因此在實(shí)際的開(kāi)發(fā)中,有意識(shí)的使用并發(fā)模式來(lái)解決實(shí)際項(xiàng)目問(wèn)題的團(tuán)隊(duì)并不算多。因此,并發(fā)模式更多的出現(xiàn)在技術(shù)類文章里,或者面試?yán)?。?shí)際上要怎么用他,許多團(tuán)隊(duì)也還沒(méi)有一個(gè)非常明確的方向與感受。
當(dāng)然,經(jīng)過(guò)長(zhǎng)時(shí)間的預(yù)熱與宣傳,并發(fā)模式到目前為止,已經(jīng)形成了共識(shí),在 React 19 中,legacy 模式的所有代碼,都被直接刪除。那么也就意味著,如果我們要用好 React 19,就必須要對(duì)并發(fā)模式有一個(gè)非常深刻的理解。
也正因?yàn)槿绱?,許多項(xiàng)目要升級(jí)到 React 19 將會(huì)存在不少難度。當(dāng)然,由于并發(fā)模式實(shí)際上已經(jīng)過(guò)渡了很多年,許多團(tuán)隊(duì)的項(xiàng)目本身就是直接基于 React 18 來(lái)開(kāi)發(fā),那么升級(jí)的難度會(huì)小很多。
除此之外,React 19 所提倡的開(kāi)發(fā)模式,與之前的版本相比,也出現(xiàn)了很大的區(qū)別。
一種很重要的思路就是:我們需要盡可能的把 useEffect 變成一個(gè)非常小眾的 hook,只會(huì)偶爾使用一下。
這會(huì)讓許多 React 開(kāi)發(fā)者感受到不適應(yīng)。因此,在我的付費(fèi)小冊(cè)《React 19 全解》中,我花了很大的篇幅來(lái)介紹 React 19 的細(xì)節(jié),幫助大家能夠快速掌握這種新的開(kāi)發(fā)思維。
?
小冊(cè)地址:usehook.cn
二、App Router
App Router 實(shí)際上也已經(jīng)經(jīng)歷了一兩年時(shí)間的預(yù)熱,但是通過(guò)群友的反饋來(lái)看,依然會(huì)有很多同學(xué)在使用他的過(guò)程中感覺(jué)不適應(yīng)。
在 Next.js 15 中,App Router 變成了官方團(tuán)隊(duì)主推的方案。這里最核心的是要重推 React Server Component【簡(jiǎn)稱:RSC】
這就不是一個(gè)簡(jiǎn)單的升級(jí)了,而是一次完全的變革。
開(kāi)發(fā)者需要非常明確的區(qū)分客戶端組件與服務(wù)端組件。如果用得不好,你可能會(huì)發(fā)現(xiàn)你的項(xiàng)目里到處都是 use client,這樣不僅不能享受到 Next.js 給你帶來(lái)的提升,反而會(huì)感覺(jué)處處受到限制。
關(guān)于這個(gè)點(diǎn),許多人會(huì)對(duì) use client 的使用感到困惑的原因是因?yàn)椋瑳](méi)有理解到 use client 他代表的含義是:邊界,而不僅僅是標(biāo)識(shí)當(dāng)前組件是一個(gè)客戶端組件。
這里的邊界指的是,服務(wù)端與客戶端組件的邊界。我們只需要標(biāo)識(shí)邊界,那么邊界之下的所有組件,都會(huì)自動(dòng)變成客戶端組件,而不需要我們手動(dòng)在每個(gè)組件中添加 use client。
當(dāng)然,這里的上下級(jí)關(guān)系,指的并不是父子結(jié)點(diǎn),而是文件引入的結(jié)構(gòu)。
這帶來(lái)的一個(gè)強(qiáng)度很大的影響就是:對(duì)于使用者的組件合理拆分有很高的要求。這會(huì)讓許多的基礎(chǔ)使用者感覺(jué)到不適應(yīng)。許多初中級(jí)開(kāi)發(fā)者很難做到合理的拆分組件。
因此,他們會(huì)吐槽說(shuō),Next.js 的組件拆得太碎了,過(guò)度拆分等等。但是有的時(shí)候這種拆分是必須的,如果我們能夠合理的把服務(wù)端邏輯與客戶端邏輯處理好,對(duì)于項(xiàng)目的整體運(yùn)行性能是非常有幫助的。這是 Next.js 追求的一個(gè)很重要的指標(biāo)。
對(duì)組件的屬性問(wèn)題分析不明確的話,經(jīng)常會(huì)在開(kāi)發(fā)中遇到報(bào)錯(cuò)。因?yàn)榉?wù)端組件有非常多的 api 是不能使用的。如果你在編寫這個(gè)組件的過(guò)程中,并沒(méi)有思考該組件到底是 server 組件,還是 client 組件,你大概率會(huì)遇到很多報(bào)錯(cuò)
當(dāng)我們使用瀏覽器才存在的一些行為,例如點(diǎn)擊事件時(shí),我們就必須使用 client 組件。這要求我們?cè)陂_(kāi)發(fā)階段,就要做好動(dòng)靜分離,把點(diǎn)擊事件相關(guān)的邏輯單獨(dú)拆分出去重新封裝一個(gè)客戶端組件
三、SSG or SSR
這可能是很多人難受的一個(gè)比較重要點(diǎn)。因?yàn)樵?App Router 中,有幾個(gè)重要的 API 消失了。他們就是
- 1、getStaticProps
- 2、getInitialProps
- 3、getServerSideProps
這些 API 的消失,會(huì)讓初學(xué)者很難清晰的判斷出來(lái) SSG 與 SSR 的界限在哪里。例如,當(dāng)我們想要提前獲取到頁(yè)面所需要的數(shù)據(jù)時(shí),就使用 getStaticProps,這樣,頁(yè)面呈現(xiàn)所需要的數(shù)據(jù),就可以在構(gòu)建的時(shí)候獲得
這幾個(gè) API 存在的好處就是,頁(yè)面到底是 SSG 還是 SSR,是由開(kāi)發(fā)者來(lái)控制的。
但是在 App Router 中,邏輯大變。
開(kāi)發(fā)者不再需要去區(qū)分我到底需要渲染成什么 SSG 還是 SSR。
如果我們要切換到動(dòng)態(tài)渲染內(nèi)容,那么可能需要通過(guò)調(diào)整緩存策略來(lái)實(shí)現(xiàn)。如下圖所示
這造成的結(jié)果是,可能我們寫的代碼大多數(shù)都會(huì)被渲染成 SSG,而當(dāng)我們需要一些動(dòng)態(tài)生成的頁(yè)面【SSR】,就進(jìn)一步要求我們對(duì)每個(gè)動(dòng)態(tài) API 有深刻的理解。
學(xué)習(xí)成本變得更高了。
但是我個(gè)人更喜歡這樣的方式。因?yàn)樵趯?shí)際的開(kāi)發(fā)中,我們發(fā)現(xiàn),如果項(xiàng)目中存在大量動(dòng)態(tài)渲染的內(nèi)容,當(dāng)訪問(wèn)量規(guī)模變大之后,服務(wù)端的壓力會(huì)變得非常大,從而讓訪問(wèn)速度變得更慢。因此,盡可能少的使用 SSR 是我在項(xiàng)目開(kāi)發(fā)中的一個(gè)比較大的原則。
我會(huì)盡量把與具體用戶強(qiáng)關(guān)聯(lián)的展示信息放到客戶端組件中去處理。這里的前提是,經(jīng)過(guò)分析發(fā)現(xiàn),與客戶強(qiáng)關(guān)聯(lián)的內(nèi)容大多數(shù)情況下都沒(méi)必要做 SEO。
四、總結(jié)
Next.js 15 發(fā)布之后,對(duì)三方生態(tài)庫(kù)來(lái)說(shuō),是一個(gè)巨大的挑戰(zhàn)。因?yàn)樵S多庫(kù)對(duì) RSC 的支持并不是那么友好。因此在升級(jí)之前,大家一定要做好調(diào)研工作。評(píng)估好具體的風(fēng)險(xiǎn)與影響。
當(dāng)然,新版本在開(kāi)發(fā)體驗(yàn)上的提升是非常明顯的,無(wú)論是構(gòu)建速度,還是編譯速度,又或者是項(xiàng)目性能,都有非常明顯的提升。如果三方庫(kù)的影響是可控的,那么升級(jí)到新版本所帶來(lái)的收益非常大。