前端時(shí)間用react寫網(wǎng)站豪硅,但是一直都是采用前端渲染的方式。最近兩天有時(shí)間挺物,研究一下怎么實(shí)現(xiàn)react的后端渲染懒浮。
一、環(huán)境
- Webpack
- React
- NodeJS
二识藤、思路
前端構(gòu)建工具采用了Webpack砚著,React組件使用ES6語(yǔ)法編寫。由于Webpack支持像引入普通JS模塊一樣引入圖片痴昧、樣式等資源文件稽穆,所以React組建內(nèi)的圖片和樣式都是通過(guò)import方式引入的。因此赶撰,要實(shí)現(xiàn)服務(wù)端渲染舌镶,要做三件事情柱彻。
- 編寫后端渲染所需的入口文件,該入口文件的主要作用是輸出首頁(yè)HMTL餐胀;
- 使用Webpack編譯入口文件哟楷,使得Node解析器能夠加載并執(zhí)行(因?yàn)镽eact組件是使用ES6語(yǔ)法編寫的,而且圖片和樣式文件的加載使用的是只有Webpack能夠識(shí)別的模塊加載方式否灾,所以Node解析器是無(wú)法直接加載執(zhí)行后端入口文件的)
- 把樣式代碼從React組件中提取出來(lái)卖擅,放到上一步生成的HTML代碼中,這樣瀏覽器才能正常顯示從服務(wù)端發(fā)送過(guò)去的頁(yè)面墨技。
三惩阶、遇到的問(wèn)題
本次代碼實(shí)現(xiàn)上借鑒了Webpack官方提供的例子:react-webpack-server-side-example,但是遇到幾個(gè)坑扣汪。
坑一: Webpack版本不一樣琳猫,導(dǎo)致直接使用官方例子提供的style-collector.loader.js
提取CSS代碼時(shí)失敗。
官方例子使用的Webpack版本是"webpack": "~1.3.1-beta7"
私痹,而我使用的Webpack版本是"webpack": "^1.12.2"
脐嫂。
可能是由于版本升級(jí)導(dǎo)致API有所變化,所以當(dāng)在style-collector.loader.js
文件里面調(diào)用下圖這一段代碼去提取CSS時(shí)紅色框框內(nèi)的那一句話直接返回的并不是CSS代碼紊遵,而是一個(gè)數(shù)組账千。
所以,我把在紅色框框的那一段代碼后面加了數(shù)組索引[0][1]
這樣返回的就是要提取的CSS了暗膜。
坑二: 官方版本的提取CSS的代碼直接搬過(guò)來(lái)不能用匀奏,提取不到任何東西,即使解決了坑一学搜。
讓我們來(lái)看看官方的例子是怎么提取CSS的娃善。
首先:官方的style-collector.loader.js
是這樣的。
這段代碼定義了一個(gè)Webpack loader瑞佩,即加載器聚磺。它的大概意思就是,在加載css文件的地方炬丸,插入一段JS代碼瘫寝,這段JS代碼的作用是調(diào)用style-collector.js的add方法,而css代碼會(huì)轉(zhuǎn)成字符串作為參數(shù)傳給add方法稠炬。
然后焕阿,我們來(lái)看看style-collector.js
文件,它是這樣的首启。
這個(gè)模塊定義了兩個(gè)方法暮屡,一個(gè)collect方法和一個(gè)add方法。其中毅桃,默認(rèn)定義的add方法是一個(gè)空方法褒纲。有點(diǎn)奇怪愁溜,我們繼續(xù)往下看。
在官方例子的服務(wù)端入口文件page.js里面外厂,有這么一段:
紅色框框內(nèi)的代碼是提取CSS用的,這里調(diào)用了style-collector.js的collect方法代承,然后傳了一個(gè)回調(diào)函數(shù)給collect方法汁蝶,回調(diào)函數(shù)的作用是把Application組件渲染成字符串。那么這里是怎么提取CSS呢论悴?
回頭看看style-collector.js的內(nèi)容掖棉,在collect方法里面,執(zhí)行回調(diào)函數(shù)之前膀估,定義了一個(gè)stuff數(shù)組用于存放CSS幔亥,并且改變了模塊默認(rèn)的add方法;執(zhí)行回調(diào)函數(shù)之后察纯,就返回了CSS了帕棉。所以,所有把戲都藏在回調(diào)函數(shù)里頭饼记。
那么香伴,執(zhí)行回調(diào)函數(shù)的時(shí)候發(fā)生了什么呢?
我們知道具则,回調(diào)函數(shù)只做了一件事即纲,那就是把Application組件渲染成字符串。讓我們來(lái)看看Application組建的內(nèi)容博肋。
Soga低斋!原來(lái)在Application組件渲染的時(shí)候,加載了Application.css文件匪凡,而先前說(shuō)過(guò)膊畴,
style-collector.loader.js
這個(gè)加載器會(huì)在加載CSS文件的地方插入一段JS代碼,這段JS代碼的作用是調(diào)用style-collector.js的add方法病游,而css代碼會(huì)轉(zhuǎn)成字符串作為參數(shù)傳給add方法巴比。所以,在執(zhí)行回調(diào)函數(shù)的過(guò)程中礁遵,調(diào)用了一次style-collector.js
的add方法轻绞,把CSS添加到了stuff數(shù)組中,所以執(zhí)行完回調(diào)函數(shù)之后佣耐,自然就提取到了CSS政勃。
好了,了解完官方提取CSS的原理兼砖,來(lái)看看我的代碼是怎么寫的奸远。
首先既棺,我的React組件是這樣寫的。
與官方的區(qū)別在于懒叛,我是在組件的頭部丸冕,而不是在render方法中引入CSS文件的。問(wèn)題就出在這里薛窥。
如果我把CSS文件的引入寫在組件的頭部胖烛,用Webpack編譯的時(shí)候,
style-collector.loader.js
會(huì)在組件頭部插入一段JS代碼诅迷,這段JS代碼的作用上面已經(jīng)提過(guò)了佩番,就是調(diào)用style-collector.js
的add方法。當(dāng)我從page.js里面引入組件的時(shí)候(也是在page.js的頭部引入)罢杉,就已經(jīng)執(zhí)行了這一段代碼趟畏,而此時(shí)style-collector.js的collect方法還沒有被執(zhí)行,默認(rèn)的add方法還沒有被改變滩租,所以最終提取不到CSS赋秀。
所以,我把style-collector.js
的內(nèi)容作了修改律想,如下圖:
這樣沃琅,無(wú)論從哪里加載CSS文件,都可以把CSS添加到stuff數(shù)組中蜘欲,最后調(diào)用collect方法即可獲取所有的CSS益眉。如下圖:
四、總結(jié)
-
代碼不能照搬照抄姥份,要理解其原理郭脂,然后根據(jù)自己的實(shí)際情況加以運(yùn)用。其實(shí)官方文檔開頭就寫了You shouldn't use the code, only the idea. 啪啪澈歉,打臉展鸡!
Webpack Github 文檔 - 很久沒有體驗(yàn)這種發(fā)現(xiàn)問(wèn)題與解決問(wèn)題的樂(lè)趣了,我記得從小我就很喜歡這種樂(lè)趣埃难。不忘初心莹弊,方得始終!