摘要:踩坑幾乎一整年沒(méi)咋寫(xiě)文章,主要是懶,加上工作也挺忙。遇到一些坑,也有一些收獲這里記錄一下。個(gè)人習(xí)慣使用啟動(dòng)服務(wù)。總結(jié)說(shuō)了上面那么多,其實(shí)官方文檔里都有相關(guān)例子,就當(dāng)我的個(gè)人踩坑記錄吧。
Next.js踩坑
幾乎一整年沒(méi)咋寫(xiě)文章,主要是懶,加上工作也挺忙。但是想趁著年底發(fā)一篇,希望明年更勤奮一點(diǎn)。其實(shí)不是沒(méi)東西寫(xiě),就是想深入一個(gè)東西還是很困難的,要查各種資料,最終還是懶就是了。
next.js是react的同構(gòu)庫(kù),很多文章里把他當(dāng)作一個(gè)腳手架,也不是不行,但是個(gè)人認(rèn)為next.js比一般的腳手架能做的更多,但也有局限性。這兩天下班回去實(shí)踐了一下nextjs的開(kāi)發(fā)。遇到一些坑,也有一些收獲這里記錄一下。
請(qǐng)求數(shù)據(jù)nextjs沒(méi)有客戶(hù)端的生命周期,只有一個(gè)靜態(tài)方法getInitialProps,所以獲取接口數(shù)據(jù)也只能在這個(gè)方法里了。getInitialProps的返回?cái)?shù)據(jù)就作為該組件的props。getInitialProps有兩個(gè)參數(shù):req和res,也就是我們非常熟悉的http參數(shù)。說(shuō)句題外話(huà),現(xiàn)有的node web框架都是req作為輸入,res作為輸出,增加各種中間件。
所以個(gè)人覺(jué)得nextjs的組件形式太適合無(wú)狀態(tài)組件了。下面是一個(gè)簡(jiǎn)單的例子代碼:
// 獲取電影列表并渲染 class MovieList extends Component { static async getInitialProps(){ const data = await getMovieList() return { list: data } } render () { return ({this.props.list.map(movie => {) } }})}
當(dāng)然這里最終僅僅是服務(wù)端輸出的列表,我們可能還會(huì)有其他操作,比如刪除加載下一頁(yè)之類(lèi)的,但是這些操作都沒(méi)必要在服務(wù)端操作的。添加幾個(gè)相應(yīng)的方法即可。
路由管理nextjs的路由是基于文件系統(tǒng)的,相當(dāng)清晰和簡(jiǎn)單,比如在pages文件夾下面增加一個(gè)movie-detail組件,并寫(xiě)上相應(yīng)的代碼,我們就可以訪(fǎng)問(wèn)/movie-detail 這個(gè)路由了。起初覺(jué)得這樣的路由形式實(shí)在太優(yōu)雅了,但是用久了就會(huì)發(fā)現(xiàn)很多問(wèn)題。
路由嵌套首先是嵌套路由,比如我想建立/user/profile這個(gè)路由,這個(gè)其實(shí)很好解決,就是在pages文件夾下面依次嵌套就行了:
具名路由其次是沒(méi)有官方實(shí)現(xiàn)具名路徑,什么是具名路徑呢?就是/movie/:id這里這種形式,個(gè)人感覺(jué)nextjs在這方面是追隨react-router4的。vuejs的同構(gòu)框架nuxtjs則不存在這個(gè)問(wèn)題,因?yàn)関ue-router本身也是統(tǒng)一管理路由的。先不說(shuō)這種情況的好壞,還是找找解決方案吧。
根據(jù)我找到的實(shí)例和文檔,目前有兩種解決方案:
使用query代替具名路下圖可以看到其實(shí)在nextjs router里query是存在的。
那我們需要訪(fǎng)問(wèn)具名路由頁(yè)面的時(shí)候可以這么寫(xiě), 將id用query傳過(guò)去/movie-detail?id=xxx:
// 電影詳情頁(yè)面 class MovieDetail extends Component { static async getInitialProps({ req }) { const { id } = req.query const detail = await getDetail(id) return { detail } } render () { return ( // do anything you want ) } }custom server 解決
使用query傳參數(shù)過(guò)去確實(shí)可以解決問(wèn)題,但是太不優(yōu)雅,與rest的思想也不太符合。所以next社區(qū)找到了另一個(gè)解決方案,使用custom server。
在說(shuō)具體方案之前我們我們可以了解一下,說(shuō)到底nextjs并不是一個(gè)生成靜態(tài)資源的腳手架,next最終還是要多帶帶部署node服務(wù)的。也就是nextjs其實(shí)內(nèi)置了一個(gè)http服務(wù),如果我們不使用custom sever的話(huà),內(nèi)置服務(wù)還是可以很好的幫我們完成渲染頁(yè)面的任務(wù)。
但是如果我們的node不僅僅是渲染頁(yè)面,還需要寫(xiě)接口。那么這時(shí)候的情況就很類(lèi)似傳統(tǒng)后端的開(kāi)發(fā)模式了:不僅僅需要寫(xiě)接口還需要渲染頁(yè)面。
很顯然nextjs的內(nèi)置http服務(wù)是無(wú)法完成這個(gè)任務(wù)的,我們需要更加完善的web 框架。畢竟專(zhuān)業(yè)的事還是交給專(zhuān)業(yè)的。這時(shí)候就是custom server大顯身手的時(shí)候了。nextjs里也有一系列的例子:
那么custom server是如何解決具名路徑的問(wèn)題的呢?我們是借用nextjs的渲染能力。這里以express為例,具體代碼如下:
// server.js const express = require("express") const next = require("next") const dev = process.env.NODE_ENV !== "production" const app = next({ dev, quiet: false }) const handle = app.getRequestHandler() const SERVE_PORT = process.env.SERVE_PORT || 8001 app.prepare().then(() => { const server = express() server.get("/movie-detail/:id", async (req, res) => { // 渲染movie-detail這個(gè)組件 const html = await app.renderToHTML(req, res, "/movie-detail", req.query) res.send(html) }) server.get("*", (req, res) => handle(req, res)) server.listen(SERVE_PORT, err => { if (err) throw err console.log(`> Ready on http://localhost:${SERVE_PORT}`) }) })
上面是server.js的簡(jiǎn)略代碼,當(dāng)然在組件里我們也要做相應(yīng)處理,代碼如下
// /pages/movie-detail.jsx // 電影詳情頁(yè)面 class MovieDetail extends Component { static async getInitialProps({ req }) { const { id } = req.params const detail = await getDetail(id) return { detail, id } } render () { return ( // do anything you want ) } }頁(yè)面緩存
對(duì)于csr的的react應(yīng)用來(lái)說(shuō),渲染耗時(shí)100ms并不是什么太大問(wèn)題,但是到了服務(wù)端,100ms很明顯是沒(méi)法忍受的。首先客戶(hù)端渲染并不會(huì)造成服務(wù)器資源的浪費(fèi),其實(shí)也不會(huì)對(duì)服務(wù)器造成太大鴨梨。但是服務(wù)端就不一樣了。一旦用戶(hù)量大了,勢(shì)必會(huì)引起各種問(wèn)題,所以頁(yè)面緩存還是很有必要的。
具體頁(yè)面緩存在哪里并不是我們考量的范圍,同樣頁(yè)面緩存也需要用到custom server,具體服務(wù)端框架自定吧。這里以lru-cache為例做一個(gè)簡(jiǎn)單的頁(yè)面緩存,其實(shí)換成其他的諸如redis也是沒(méi)有任何問(wèn)題的。
const dev = process.env.NODE_ENV !== "production" const next = require("next") const express = require("express") const LRUCache = require("lru-cache") const ssrCache = new LRUCache({ max: 1000, // cache item count maxAge: 1000 * 60 * 60, // 1 hour }) const app = next({ dev, quiet: false }) const handle = app.getRequestHandler() const SERVE_PORT = process.env.SERVE_PORT || 8001 app.prepare().then(() => { const server = express() server.get("/", async (req, res) => { renderAndCache(req, res, "/", { ...req.query }) }) server.get("/movie-detail/:id", async (req, res) => { renderAndCache(req, res, "/movie-detail", { ...req.query }) }) server.get("*", (req, res) => handle(req, res)) server.listen(SERVE_PORT, err => { if (err) throw err console.log(`> Ready on http://localhost:${SERVE_PORT}`) }) }) const getCacheKey = req => `${req.url}` // 緩存并渲染頁(yè)面,具體是重新渲染還是使用緩存 async function renderAndCache(req, res, pagePath, queryParams) { const key = getCacheKey(req) if (ssrCache.has(key)) { res.setHeader("x-cache", "HIT") res.send(ssrCache.get(key)) return } try { const html = await app.renderToHTML(req, res, pagePath, queryParams) // Something is wrong with the request, let"s skip the cache if (res.statusCode !== 200) { res.send(html) return } // Let"s cache this page ssrCache.set(key, html) res.setHeader("x-cache", "MISS") res.send(html) } catch (err) { app.renderError(err, req, res, pagePath, queryParams) } }
其中renderAndCache是關(guān)鍵。這里判斷頁(yè)面是否有緩存,如果有的話(huà)則直出緩存內(nèi)容。否則的話(huà)就重新渲染。至于緩存時(shí)間還有緩存大小看個(gè)人設(shè)置了,這里不贅述了。
部署上線(xiàn)部署上線(xiàn)這一塊實(shí)在沒(méi)什么好說(shuō)的,簡(jiǎn)單的話(huà)直接起一個(gè)node服務(wù)的就可以,復(fù)雜一點(diǎn)就要包括報(bào)警重啟等等,都是看個(gè)人情況的。
個(gè)人習(xí)慣使用supervisor啟動(dòng)node服務(wù)。
總結(jié)說(shuō)了上面那么多,其實(shí)官方文檔里都有相關(guān)例子,就當(dāng)我的個(gè)人踩坑記錄吧。
對(duì)于nextjs來(lái)說(shuō),我認(rèn)為如果是展示型的應(yīng)用,就應(yīng)該放心大膽的用起來(lái)。不光開(kāi)發(fā)快還爽,同時(shí)屏蔽webpack配置,有什么理由不用?
如果是功能性的,比如一系列的繪圖組件則完成沒(méi)必要使用了,對(duì)于canvas之類(lèi)的還是必須用客戶(hù)端渲染,然而nextjs又沒(méi)有生命周期,用nextjs可能會(huì)相當(dāng)坑。
對(duì)于個(gè)人開(kāi)發(fā)這我則是相當(dāng)推薦。何必去配置webpack浪費(fèi)生命啊。
如果是完全靜態(tài)的應(yīng)用,我推薦gatsbyjs。具體怎么使用則是另外一個(gè)話(huà)題了。
如有謬誤,輕點(diǎn)噴。 over
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/101514.html
摘要:接下來(lái)我們運(yùn)行一下。因此,我們需要新建一個(gè)目錄。接下在再重新啟動(dòng)一下試試。因?yàn)槟J(rèn)開(kāi)啟服務(wù)端渲染,也就無(wú)需我們進(jìn)行任何的配置,因此現(xiàn)在這個(gè)極其簡(jiǎn)單的應(yīng)用就是一個(gè)。代碼地址參考文章服務(wù)端渲染與打造高可靠與高性能的同構(gòu)解決方案 寫(xiě)在前面 說(shuō)實(shí)話(huà),我個(gè)人還是覺(jué)得文筆越來(lái)越不錯(cuò)了,以前的文章都是一個(gè)問(wèn)題悶到天黑,文章寫(xiě)的有點(diǎn)亂由于文章過(guò)于龐大導(dǎo)致不是一氣呵成的,思路有時(shí)候會(huì)很混亂。所以我也準(zhǔn)備...
摘要:官方也有一個(gè)的工具,但是只能簡(jiǎn)單的安裝基礎(chǔ)的依賴(lài),或者通過(guò)他們提供的某個(gè)例子來(lái)構(gòu)建自己的項(xiàng)目。由于目前還在開(kāi)發(fā)階段只支持上面這些的配置也是平常我用的比較多的配置。 因?yàn)楣镜臉I(yè)務(wù)需求現(xiàn)在開(kāi)發(fā)都用nextjs來(lái)實(shí)現(xiàn)React的服務(wù)端渲染,為了之后開(kāi)發(fā)方便自己寫(xiě)了個(gè)腳手架工具。 官方也有一個(gè)create-next-app的工具,但是只能簡(jiǎn)單的安裝基礎(chǔ)的依賴(lài),或者通過(guò)他們提供的某個(gè)例子來(lái)構(gòu)建...
摘要:樣式在中寫(xiě)樣式,一般可以歸為類(lèi),一類(lèi)是基于文件的傳統(tǒng)方式包括,等,另一類(lèi)則是。我們回到我們的代碼中,更改,代碼如下在標(biāo)簽中,我們寫(xiě)我們的,必須包含在中,否則會(huì)報(bào)錯(cuò)。至此,的基礎(chǔ)概念已經(jīng)介紹完了,更高級(jí)的用法,可以參考官方的例子。 本篇教程基于上一篇的基礎(chǔ),主要講解服務(wù)端渲染,樣式以及部署相關(guān)的一些知識(shí),若你沒(méi)有看過(guò)上一篇的內(nèi)容,或者你看過(guò)又忘了,建議重新去看一遍。 順便說(shuō)一句,Next...
摘要:是一個(gè)基于實(shí)現(xiàn)的服務(wù)端渲染框架,地址為。本文先從簡(jiǎn)單地基礎(chǔ)概念開(kāi)始,一步一步帶大家認(rèn)識(shí)。本篇教程到此結(jié)束,后面會(huì)跟大家介紹的服務(wù)端渲染及以及部署相關(guān)的一下概念及示例代碼。 Next.js是一個(gè)基于React實(shí)現(xiàn)的服務(wù)端渲染框架,github地址為next.js。 使用Next.js實(shí)現(xiàn)服務(wù)端渲染是一件非常簡(jiǎn)單的事,在這里,你完全可以不用自己去寫(xiě)webpack等配置,Next.js全都幫...
摘要:中文站點(diǎn)中文站當(dāng)前翻譯版本為。注意將不能使用在子組件中。只能使用在頁(yè)面中。替換路由組件默認(rèn)將新推入路由棧中。以防服務(wù)端渲染發(fā)生錯(cuò)誤,建議事件寫(xiě)在生命周期里。禁止文件路由默認(rèn)情況,將會(huì)把下的所有文件匹配路由如渲染為如果你的項(xiàng)目使用 Next.js 是一個(gè)輕量級(jí)的 React 服務(wù)端渲染應(yīng)用框架。 Next.js中文站點(diǎn) http://nextjs.frontendx.cn Next.j...
閱讀 2234·2021-11-17 09:33
閱讀 2774·2021-11-12 10:36
閱讀 3396·2021-09-27 13:47
閱讀 884·2021-09-22 15:10
閱讀 3485·2021-09-09 11:51
閱讀 1392·2021-08-25 09:38
閱讀 2758·2019-08-30 15:55
閱讀 2608·2019-08-30 15:53