為什么需要SSR?
SSR: Server side rendering服務(wù)端渲染, 指的是頁(yè)面在服務(wù)器端已經(jīng)生成了完成的html頁(yè)面結(jié)構(gòu), 不需要瀏覽器解析.
單頁(yè)面富應(yīng)用, index.html, 剛開(kāi)始基本沒(méi)東西.
- 首屏加載速度慢, 剛開(kāi)始的時(shí)候是空白的, index.html下載之后(前端渲染的時(shí)候是瀏覽器來(lái)進(jìn)行的, 可以交給服務(wù)端node操作):
- 需要先請(qǐng)求并執(zhí)行js文件
- 發(fā)送ajax
- 獲取數(shù)據(jù)并生成html結(jié)構(gòu)渲染(ssr從瀏覽器轉(zhuǎn)到Nodejs)
- SEO搜索引擎優(yōu)化, 初始頁(yè)面獲取不到相應(yīng)權(quán)重信息.
補(bǔ)充概念:
同構(gòu)應(yīng)用: 一套既可以在服務(wù)端, 又可以在客戶端運(yùn)行的代碼.
- 同構(gòu)是一種ssr的形態(tài), 是現(xiàn)代ssr的一種表現(xiàn)形式
- 當(dāng)用戶發(fā)出請(qǐng)求時(shí), 現(xiàn)在服務(wù)器通過(guò)ssr渲染出首頁(yè)的內(nèi)容
- 但是對(duì)應(yīng)的代碼同樣可以在客戶端被執(zhí)行
- 執(zhí)行的目的包括事件綁定等, 以及其他頁(yè)面切換時(shí)也可以在客戶端被渲染
使用SSR的方式
方式一: 手動(dòng)搭建SSR框架;
方式二: 使用已經(jīng)成熟的SSR框架: Next.js (用于react);
- 安裝Next.js腳手架:
npm install -g create-next-app - 創(chuàng)建Next.js項(xiàng)目
create-next-app next-demo
創(chuàng)建好的項(xiàng)目目錄如下:
- pages下每個(gè)文件就是一個(gè)頁(yè)面, 相當(dāng)于是一個(gè)路由映射(next已經(jīng)幫我們配置好了后端路由)
這里我們手動(dòng)添加了About頁(yè)面, 并且在index.js里面提供了跳轉(zhuǎn).
如果我不想每次都發(fā)很多請(qǐng)求去獲取頁(yè)面, 那么我們可以用另一種方式來(lái)進(jìn)行前端渲染, 直接跳轉(zhuǎn)到About頁(yè).
-
使用next/link包里面的Link組件, 下圖可以看到,我們請(qǐng)求的是同服務(wù)器下被打包好的about.js文件.
image.png - webpack-hmr?page=/about更改當(dāng)前路由進(jìn)行頁(yè)面跳轉(zhuǎn), 根據(jù)about進(jìn)行前端渲染.
// 這里L(fēng)ink只是作為功能來(lái)使用, 里面最好添加相應(yīng)的子元素
<Link href="/about">
<a>關(guān)于</a>
</Link>
頁(yè)面標(biāo)題
通常我們希望每一個(gè)頁(yè)面都有自己的標(biāo)題, 而不是什么localhost路徑.
- 通過(guò)next/head導(dǎo)出的Head組件來(lái)完成, 這個(gè)組件會(huì)被加載到當(dāng)前頁(yè)面的head標(biāo)簽中
- 同理, 對(duì)應(yīng)的logo也是這么做
<Head>
<title>wwqwwqwwq</title>
</Head>
共享組件(實(shí)際上是個(gè)高階組件)
有時(shí)候我們想要不同頁(yè)面的head和footer是一樣的, 但如果沒(méi)有其他方式, 我們只能每個(gè)文件都去添加這么兩個(gè)組件, 重復(fù)代碼太多.
-
這里我們?cè)陧?xiàng)目頂層進(jìn)行共享組件抽離.
AppLayout
主頁(yè)
Index
About
還有另一種方法是將共享組件放在_app.js(框架在這里渲染index)文件中使用
_document.js文件
在這個(gè)文件中可以對(duì)我們的html文檔進(jìn)行自定義, 官網(wǎng)鏈接https://www.nextjs.cn/docs/advanced-features/custom-document.
結(jié)構(gòu)解析
- Html組件就是我們?cè)阡秩緯r(shí)我們?cè)诳刂婆_(tái)Elements下看到的html
- Main是我們App中寫的所有內(nèi)容
修改后記得重新yarn dev編譯啟動(dòng)一下
相關(guān)樣式添加
在react中有三種樣式, 普通css, module.css, css in js.(筆者個(gè)人喜歡使用styled-components)
// 全局
import "../styles/app.css";
// 模塊
import styles from '../styles/Home.module.css';
// 生效
<h1 className={styles.title}>Home頁(yè)面</h1>
// 不生效
<h1 className='title'>測(cè)試</h1>
// styled-jsx, 筆者寫起來(lái)很難受...
<style>{`
p {
color: blue;
text-decoration: underline;
}
`}</style>
- 全局樣式可以直接import到_app文件下.
- 模塊樣式import到相應(yīng)的文件, 然后通過(guò)className綁定到對(duì)應(yīng)的元素上面. 這種方式相當(dāng)于一個(gè)模塊, 在哪里使用在哪里綁定才會(huì)生效.
-
styled-jsx(用起來(lái)像vue): 一種css in js的技術(shù), 在nextjs中默認(rèn)集成, 會(huì)使用的話就不需要單獨(dú)安裝styled-components, 但筆者還是喜歡使用styled-components...
image.png - 使用styled-components: 一般情況對(duì)每個(gè)頁(yè)面要單獨(dú)創(chuàng)建一個(gè)文件夾, 這樣顯得項(xiàng)目比較有條理.
- 這里有個(gè)問(wèn)題需要留意一下, 因?yàn)槲覀?strong>通過(guò)<Link />來(lái)進(jìn)行跳轉(zhuǎn)的時(shí)候?qū)儆贑SR客戶端渲染, 但如果強(qiáng)硬刷新頁(yè)面的時(shí)候?qū)儆诜?wù)端渲染, 這時(shí)候就會(huì)有問(wèn)題了. 這個(gè)時(shí)候會(huì)提示匹配對(duì)應(yīng)的className屬性, 因?yàn)槟J(rèn)情況下, 服務(wù)端不會(huì)給類名進(jìn)行匹配的.
- 但在nextjs中可以通過(guò)插件(babel-plugin-styled-components用于styled-components在服務(wù)端渲染時(shí)正常生效)進(jìn)行一個(gè)特殊的配置, 在babel轉(zhuǎn)化的時(shí)候, 對(duì)styled js進(jìn)行正確的轉(zhuǎn)化, 這個(gè)屬于devDependencies.
- 我們?cè)诟夸浵聞?chuàng)建.babelrc文件(進(jìn)行babel轉(zhuǎn)化時(shí)會(huì)讀取這個(gè)文件, 運(yùn)行編譯時(shí)), 改了配置記得重新yarn dev.
{
"presets": [
"next/babel"
],
"plugins": [
"styled-components"
]
}
路由嵌套
之前我們已經(jīng)知道, 每個(gè)文件對(duì)應(yīng)一個(gè)路由信息.
所以我們使用的時(shí)候, 在對(duì)應(yīng)組件的文件夾下, 再創(chuàng)建對(duì)應(yīng)的組件(/文件夾)即可.
頁(yè)面跳轉(zhuǎn)中參數(shù)的傳遞
第一種方式: Link標(biāo)簽
當(dāng)我們點(diǎn)擊推薦列表中的某一項(xiàng)時(shí), 我們希望url跳轉(zhuǎn)后, 頁(yè)面進(jìn)行同步.
react中我們可以使用/recommend/:id
來(lái)進(jìn)行參數(shù)傳遞, 但在nextjs中怎么使用呢?
使用query參數(shù)的方式/recomend?id=${item}
, 這里我們可以看到, 對(duì)應(yīng)的參數(shù)已經(jīng)傳到url里面了.
接下來(lái)如何使用呢? 通過(guò)next/router導(dǎo)出的useRouter, 截圖可以看到這樣是成功的.
import { useRouter } from "next/router";
const route = useRouter();
<h2>Recommend: {router.query.id}</h2>
第二種方式: 代碼跳轉(zhuǎn)
除了第一種Link標(biāo)簽跳轉(zhuǎn), 還有另一種代碼跳轉(zhuǎn)的方式.
// index.js
import Router from "next/router";
const recommendItemClick = (item) => {
Router.push({
pathname: "/recommend",
query: {
id: item,
},
});
};
<li key={item} onClick={(e) => recommendItemClick(item)}>
推薦數(shù)據(jù): id{item}
</li>
// ./recommend/index.js
const router = useRouter();
<h2>Recommend: {router.query.id}</h2>
在頁(yè)面中發(fā)送網(wǎng)絡(luò)請(qǐng)求(可以在瀏覽器,也可以在服務(wù)器中執(zhí)行)
這里我們需要注意一點(diǎn), 不能像在react中在useEffect中發(fā)請(qǐng)求然后存到useState中, 因?yàn)樵诜?wù)端渲染時(shí), 在第一次渲染時(shí)已經(jīng)渲染好了.
當(dāng)我們使用useState再渲染時(shí), 服務(wù)端是不會(huì)幫我們做的.
如果我們確實(shí)想要在頁(yè)面中請(qǐng)求一些數(shù)據(jù), 那網(wǎng)絡(luò)請(qǐng)求的代碼應(yīng)該放在哪里?
- 我們通過(guò)index.js組件Home的getInitialProps屬性進(jìn)行操作, 將其賦值為一個(gè)函數(shù), 這個(gè)函數(shù)會(huì)在組件第一次渲染前進(jìn)行, 可以是一個(gè)異步操作.
Home.getInitialProps = async (props) => {
const res = await axios.get({
url: "接口",
});
return {
name: "wwq",
banners: res.data.data.banner.list,
recommends: res.data.data.recommend.list,
};
};
<h2>輪播圖數(shù)據(jù):</h2>
<ul>
{banners.map((item, index) => {
return <li key={item?.acm}>{item.title}</li>;
})}
</ul>
-
這里我們r(jià)eturn的對(duì)象在Home組件里頭是可以獲取到的, 然后我們?cè)趐ostman中進(jìn)行測(cè)試, 可以看到, 對(duì)應(yīng)的數(shù)據(jù)是成功渲染出來(lái)的.
image.png
這里只是簡(jiǎn)單分享一下react的ssr入門是怎么進(jìn)行的, 如果有進(jìn)一步開(kāi)發(fā)需求或進(jìn)階需求, 可以移步nextjs官網(wǎng).