# NextJS 筆記 :::success :bookmark: 書籤 [TOC] ::: --- <style> #des{ letter-spacing : 2px; line-height : 30px; } </style> ## env file <div id="des"> 預設的情況下 .env.local內的變數無法在前端使用(看見),如果需要在前端使用的話需再變數名稱前面加上 "NEXT_PUBLIC" 這樣前端就收的到變數內容 </div> * env.local ``` SECRET_VAR =HELLOWROLD NEXT_PUBLIC_MY_FIRST_API = IMAPI ``` * index.js ```javascript= export const getServerSideProps = () => { console.log(process.env.SECRET_VAR); //output : HELLOWORLD console.log(process.env.NEXT_PUBLIC_MY_FIRST_API) //output : IMAPI return{ props : {} } } export default function Home(){ console.log(process.env.SECRET_VAR); //output : undifined console.log(process.env.NEXT_PUBLIC_MY_FIRST_API) //output : IMAPI return( ...someDummyJSX ) } ``` <div id="des"> nextjs提供了幾個環境變數檔案分別是 * .env.local * .env.development * .env.production 取決於目前產品的場景為何,development就是開發環境,production就是正式上線環境, 但在 **.env.development** 中定義的變數要是跟 **.env.local** 的變數名稱一樣, 則會以 **.env.local** 的變數為準。 </div> --- ## getServerSideProps <div id="des"> **getServerSideProps** 每當一位使用者訪問該頁面時,假設該頁面有getSeverSideProps的 函式,nextjs就會先來看裡面的內容並且執行,執行完後在render畫面然後提交給使用者,成本相當昂貴,因為每一次的訪問都會發送一次request。 **getServerSideProps** 有幾個較常用的參數例如:context context內會有前端傳送過來的 **request**(context.req), 而server端可以根據頁面狀況給予 **response**(context.res), 並返回狀態碼(context.res.statusCode = 418)。 </div> ```javascript= export const getSeverSideProps = (context) => { //fetch from DB or API from thrid party if(pagegone){ context.res.statusCode = 418 } // 導向404頁面 if(!pagenotfound){ return { notfound : true; } } // 重新導向 if(redirect){ return{ redirect : { destination : "localhost:3000", permanent : false //307 設定為true的話要小心cache的問題 } } } return { props: {}, }; }; ``` | 參數名稱| 說明 | 備註 | | -------- | -------- | -------- | |context|context內會有前端傳送過來的request,server可以給予response。|context.req,context.res| |notfound|可以在該函式自訂某情況觸發時將**notfound**設定為true並return,使用者就會導向404頁面|| |redirect|可以在該函示自訂某情況觸發時將使用者頁面重新導向至其他頁面,需注意的是 **permanent** 如果設定為 **true** 則會在使用者的cache儲存一份紀錄,再次造訪該函式頁面時又會再被重新導向一次,必須清除cache才有辦法解決。|destination:"網址" permanent: false| --- ## getStaticProps <div id="des"> **getStaticProps** 會在build time的時候去執行函式裡面的內容,我們可以在該函式中撰寫連線資料庫與操作資料庫的程式碼,或者是利用fetch API去取得第三方的資料,因為在**getStaticProps**中的 程式碼並不會讓使用者看到,所有的程式碼都是在server端執行。 假設是在 **local** 的話,每次重新整理都會產生一個新的 **count**,因為在**nextjs local**的環境下每重新整理就會重新 **request** 一次,而在線上模式時要是 **revalidate** 設定為5(秒),則每5秒該count才會被更新一次。 假設user1訪問該page,在這個page上第一次request,他得到的count 是 2.156... 則在5秒內不斷重新整理,得到的都會是2.156,5秒後重新整理,他得到的count不會再是2.156。 </div> ```javascript= export const getStaticProps = () => { return{ props : {count : Math.random()} revalidate : 5 } }; ``` <div id="des"> 在getStaticProps有兩種方法可以選擇第一種是在build time的時候就把page創建出來, 假設有20個static的page,就在build time的時候產出20個html所以在訪問的時候會非常快速。 因為都是已經build好的第二種是可以動態的配置假設有3500個頁面要產生不太可能在build time的時候產生。 這時候可以選擇動態配置假設有一個page的route是 /store/1 -> 並且該route是動態配置的,第一位訪問 /store/1 的使用者會先將getStaticProps內的內容跑過一遍(從資料庫撈資料 或者執行其他side effect) 所以第一位訪問 /store/1 的使用者會花取比較多的時間load頁面 後面的X萬位使用者訪問的時候將會非常快速。 </div> --- ## getStaticPaths <div id="des"> 當所產生的頁面需要動態產生時,可以使用 **getStaticPaths**,通常會跟 **getStaticProps** 搭配使用, 在動態的頁面必須先自訂該頁面的 **id** 為何,假設該檔案的目錄結構為 **/pages/store/[id].js** 以本地環境來當例子,根據以下程式碼會預先產生的頁面會是: </div> * localhost:3000/store/p-1 * localhost:3000/store/p-2 * localhost:3000/store/p-3 * [id].js ```javascript= export const getStaticPaths = () => { return{ fallback : false, paths : [ { parmas : { id : "p-1" } }, { parmas : { id : "p-2" } }, { parmas : { id : "p-3" } } ] } } ``` |參數名稱| true | false |blocking| | --- | --- |--- |---| |fallback|會馬上回傳一個empty的page,<br> 然後立即把內容拉下來渲染,所以需要handle資料第一時間<br>取得不到的case。|只要id沒有在paths裡面的<br>都會返回404|會等到整個畫面渲染完畢後<br>才提交給使用者| <div id="des"> 以下是fallback為true的例子 </div> * pages/posts/[id].js ```javascript= import { useRouter } from "next/dist/client/router"; import React from "react"; const User = ({ post }) => { const rotuer = useRouter(); if (rotuer.isFallback) { return <p>Loading... Please wait...</p>; } return <div>Hello {post?.body}</div>; }; export const getStaticPaths = async () => { const response = await fetch( `https://jsonplaceholder.typicode.com/posts?_limit=5` ); const posts = await response.json(); const paths = posts.map((post) => ({ params: { id: post.id.toString(), }, })); return { paths, fallback: true, }; }; ``` ## Proview Mode ## Dynamic Import <div id="des"> Dynamic Import 可以減輕網頁載入的負擔,假設js檔案過大的話。 假設使用者已經登入的話就只載入登入的 **component(Authenticated)**, 這樣設定的話,網頁就不會去載入未登入的 **component(NonAuthenticated)**。 </div> ```javascript= import dynamic from "next/dynamic" const AuthenticatedPanel = dynamic(() => import("./Authenticated")); const NonAuthenticatedPanel = dynamic(() => import("./NonAuthenticated")); export default function Home(){ return ( <> {user ? <AuthenticatedPanel /> : <NonAuthenticatedPanel /> </> ) } ``` ## IMAGE <div id="des"> 在nextjs裡面,通常不會使用原本的 **img** 標籤來放圖片,會用nextjs提供的 **IMAGE component**,該component針對圖片進行了優化, 像是 **lazy loading**、**optimize image size**, 預設情況下**IMAGE component**內的圖片在網頁上都會轉為**webp**, webp相比原本的png、jpg更輕量。 </div> ```javascript= <Image src="/thumb-1920-83315.png" alt="thumb image" width={1280} height={768} /> ```