今天就跟大家聊聊有關(guān)怎么實(shí)現(xiàn)ssr服務(wù)端渲染,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
公司主營(yíng)業(yè)務(wù):網(wǎng)站建設(shè)、網(wǎng)站制作、移動(dòng)網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。成都創(chuàng)新互聯(lián)公司是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來(lái)驚喜。成都創(chuàng)新互聯(lián)公司推出儋州免費(fèi)做網(wǎng)站回饋大家。
什么是ssr
最初聽說有單頁(yè)面的服務(wù)端渲染的時(shí)候,就理解為類似傳統(tǒng)的服務(wù)端路由+模板渲染,只是需要用單頁(yè)面應(yīng)用的框架寫。后面尋思這樣好像有點(diǎn)傻,再一了解,原來(lái)只是在首次加載的時(shí)候,后端進(jìn)行當(dāng)前路徑頁(yè)面的組件渲染和數(shù)據(jù)請(qǐng)求,組裝成html返回給前端,用戶就能很快看到看到頁(yè)面,當(dāng)html中的js資源加載完成后,剩下執(zhí)行和運(yùn)行的就是一般的單頁(yè)面應(yīng)用。 所以ssr是后端模板渲染和單頁(yè)面的組合。 ssr有兩種模式,單頁(yè)面和非單頁(yè)面模式,第一種是后端首次渲染的單頁(yè)面應(yīng)用,第二種是完全使用后端路由的后端模版渲染模式。他們區(qū)別在于使用后端路由的程度。
優(yōu)勢(shì)
ssr的兩個(gè)明顯的優(yōu)勢(shì):首次加載快和seo。 為什么說首次加載快呢。 一個(gè)普通的單頁(yè)面應(yīng)用,首次加載的時(shí)候需要把所有相關(guān)的靜態(tài)資源加載完畢,然后核心js才會(huì)開始執(zhí)行,這個(gè)過程就會(huì)消耗一定的時(shí)間,接著還會(huì)請(qǐng)求網(wǎng)絡(luò)接口,最終才能完全渲染完成。
ssr模式下,后端攔截到路由,找到對(duì)應(yīng)組件,準(zhǔn)備渲染組件,所有的js資源在本地,排除了js資源的網(wǎng)絡(luò)加載時(shí)間,接著只需要對(duì)當(dāng)前路由的組件進(jìn)行渲染,而頁(yè)面的ajax請(qǐng)求,可能在同一臺(tái)服務(wù)器上,如果是的話速度也會(huì)快很多。最后后端把渲染好的頁(yè)面反回給前端。 注意:頁(yè)面能很快的展示出來(lái),但是由于當(dāng)前返回的只是單純展示的dom、css,其中的js相關(guān)的事件等在客戶端其實(shí)并沒有綁定,所以最終還是需要js加載完以后,對(duì)當(dāng)前的頁(yè)面再進(jìn)行一次渲染,稱為同構(gòu)。 所以ssr就是更快的先展示出頁(yè)面的內(nèi)容,先讓用戶能夠看到。 為什么seo友好呢,因?yàn)樗阉饕媾老x在爬取頁(yè)面信息的時(shí)候,會(huì)發(fā)送HTTP請(qǐng)求來(lái)獲取網(wǎng)頁(yè)內(nèi)容,而我們服務(wù)端渲染首次的數(shù)據(jù)是后端返回的,返回的時(shí)候已經(jīng)是渲染好了title,內(nèi)容等信息,便于爬蟲抓取內(nèi)容。
如何實(shí)現(xiàn)
大致對(duì)ssr有了一個(gè)了解,我們現(xiàn)在需要對(duì)實(shí)現(xiàn)整理一下大致實(shí)現(xiàn)思路和流程。
1.選擇一個(gè)單頁(yè)面框架(我目前選擇的是react)
2.選擇node服務(wù)端框架(我目前選擇的是koa2)
3.實(shí)現(xiàn)核心邏輯,讓node服務(wù)端能夠路由和渲染單頁(yè)面組件(這一點(diǎn)分為很多小實(shí)現(xiàn)點(diǎn),后面說)
4.優(yōu)化開發(fā)和發(fā)布環(huán)境自動(dòng)化構(gòu)建工具(webpack)
開始實(shí)現(xiàn)之前創(chuàng)建一個(gè)react-ssr項(xiàng)目,項(xiàng)目下創(chuàng)建client和server目錄用于寫客戶端和服務(wù)端代碼,webpack目錄用于weppack文件配置。
1.react應(yīng)用
安裝react依賴,在client中創(chuàng)建好一個(gè)基礎(chǔ)的react文件夾結(jié)構(gòu),并寫好一個(gè)可以運(yùn)行的有路由配置的應(yīng)用,client文件目錄如下:
2.server應(yīng)用
安裝koa和相關(guān)依賴,在server中創(chuàng)建好一個(gè)基礎(chǔ)的服務(wù)端文件夾結(jié)構(gòu),并寫好一個(gè)簡(jiǎn)單的可運(yùn)行的后端應(yīng)用服務(wù)。server文件夾如下:
3.核心實(shí)現(xiàn)
因?yàn)橛袀}(cāng)庫(kù)代碼就不對(duì)基礎(chǔ)代碼做解釋,現(xiàn)在我們有一個(gè)可以單獨(dú)運(yùn)行的react單頁(yè)面應(yīng)用和一個(gè)后端應(yīng)用,他們都有各自的路由。接下來(lái)我們做改造,實(shí)現(xiàn)ssr的單頁(yè)面模式(非單頁(yè)面模式僅僅是做部分調(diào)整,因此這里只講實(shí)現(xiàn)單頁(yè)面模式)。
核心實(shí)現(xiàn)分為以下幾步:
1) 后端攔截路由,根據(jù)路徑找到需要渲染的react頁(yè)面組件X
2)調(diào)用組件X初始化時(shí)需要請(qǐng)求的接口,同步獲取到數(shù)據(jù)后,使用react的renderToString方法對(duì)組件進(jìn)行渲染,使其渲染出節(jié)點(diǎn)字符串。
3)后端獲取基礎(chǔ)html文件,把渲染出的節(jié)點(diǎn)字符串插入到body之中,同時(shí)也可以操作其中的title,script等節(jié)點(diǎn)。返回完整的html給客戶端。
4)客戶端獲取后端返回的html,展示并加載其中的js,最后完成react同構(gòu)。
1)我們?cè)诳蛻舳藢憆eact的時(shí)候,router常規(guī)的會(huì)定義一個(gè)數(shù)組,存放組件和對(duì)應(yīng)的path,然后注冊(cè)路由,如下:
上面說過,實(shí)現(xiàn)ssr就是實(shí)現(xiàn)單頁(yè)面應(yīng)用+首次服務(wù)端渲染,所以我們本身就是做的一個(gè)單頁(yè)面應(yīng)用。 現(xiàn)在實(shí)現(xiàn)了單頁(yè)面應(yīng)用,需要實(shí)現(xiàn)首次服務(wù)端渲染。 服務(wù)端的應(yīng)用啟動(dòng)以后,接受到url請(qǐng)求,比如訪問 http://localhost:9999/ ,后端服務(wù)獲取到當(dāng)前的path為/,這個(gè)時(shí)候我們就希望后端找到配置path為‘/'的上圖的Index組件,對(duì)其進(jìn)行渲染。 我們?cè)赾lient的router文件夾中建立兩個(gè)js文件index和pages:
pages 里配置路由路徑和組件的映射,代碼大致如下,使其能被客戶端路由和服務(wù)端路由同時(shí)使用。
在server路由中代碼大致是這樣的,在服務(wù)端獲取到get請(qǐng)求以后,匹配路徑,如果路徑path是有映射頁(yè)面組件的,獲取到此組件并渲染,這就是我們的第一步:后端攔截路由,根據(jù)路徑找到需要渲染的react頁(yè)面組件。
2)如上圖,匹配到組件以后,執(zhí)行了組件的getInitialProps方法(和nextjs的命名保持一致),此方法是一個(gè)封裝的靜態(tài)方法,主要用于獲取初始化所需要的ajax數(shù)據(jù),在服務(wù)端會(huì)同步獲取,而后通過ssrData參數(shù)傳入組件prorps并執(zhí)行組件渲染。 此方法在客戶端依然是異步請(qǐng)求。 這一步比較重要,為什么我們需要一個(gè)靜態(tài)方法,而不是直接把請(qǐng)求寫在willmount中呢。 因?yàn)樵诜?wù)端使用renderToString渲染組件時(shí),生命周期只會(huì)執(zhí)行到willmount之后的第一次render,在willmount內(nèi)部,請(qǐng)求是異步的,第一次render完成的時(shí)候,異步的數(shù)據(jù)都沒有獲取到,這個(gè)時(shí)候renderToString就已經(jīng)返回了。 那我們頁(yè)面的初始化數(shù)據(jù)就沒有了,返回的html不是我們所期望的。 因此定義了一個(gè)靜態(tài)方法,在組件實(shí)例化之前獲取到這個(gè)方法,同步執(zhí)行,數(shù)據(jù)獲取完成后,通過props把數(shù)據(jù)傳入給組件進(jìn)行渲染。 那么這個(gè)方法是如何實(shí)現(xiàn)的呢? 我們根據(jù)代碼截圖來(lái)看base.js:
首先在client的pages里新建一個(gè)base組件,base繼承React.Component,所有pages里的頁(yè)面組件都需要繼承這個(gè)base,base有一個(gè)靜態(tài)方法getInitialProps,此方法主要是返回組件初始化需要的異步數(shù)據(jù)。 如果有初始化的ajax請(qǐng)求,就應(yīng)該重寫在此方法里,并且return數(shù)據(jù)對(duì)象。 constructor判斷了頁(yè)面組件是否有初始化定義的state靜態(tài)方法,有的話傳遞給組件實(shí)例化的state對(duì)象,如果props有傳入ssrData,把ssrData傳遞值給組件state對(duì)象。 base中的componentWillMount會(huì)判斷是否還需要去執(zhí)行g(shù)etInitialProps方法,如果在服務(wù)端渲染的時(shí)候,數(shù)據(jù)已經(jīng)在組件實(shí)例化之前同步獲取并傳入了props,所以忽略。 如果在客戶端環(huán)境,分兩種情況,第一種:用戶第一次進(jìn)到頁(yè)面,這時(shí)候是服務(wù)端去請(qǐng)求的數(shù)據(jù),服務(wù)端獲取到數(shù)據(jù)后在服務(wù)端渲染組件,同時(shí)也會(huì)把數(shù)據(jù)存放在html的script代碼中,定義一個(gè)全局變量ssrData,如下圖,react在注冊(cè)單頁(yè)面應(yīng)用并且同構(gòu)的時(shí)候會(huì)把全局ssrData傳遞給頁(yè)面組件,這個(gè)時(shí)候頁(yè)面組件在客戶端同構(gòu)渲染的時(shí)候,就可以延續(xù)使用服務(wù)端之前的數(shù)據(jù),這樣也保持了同構(gòu)的一致性,也避免了一次重復(fù)請(qǐng)求。 第二種情況:就是當(dāng)前用戶在單頁(yè)面之中切換路由,這樣就沒有服務(wù)端渲染,那么就執(zhí)行g(shù)etInitialProps方法,把數(shù)據(jù)直接返回給state,幾乎等同于在willmount中執(zhí)行請(qǐng)求。 這樣封裝我們就可以用一套代碼兼容服務(wù)端渲染和單頁(yè)面渲染。
client/app.js
再看看如何寫頁(yè)面組件,下面是頁(yè)面組件Index的截圖,Index繼承Base,定義了靜態(tài)state,組件constructor方法會(huì)把此對(duì)象傳遞給組件實(shí)例化的state對(duì)象中,之所以用靜態(tài)方法來(lái)寫默認(rèn)數(shù)據(jù),是想保證定義的默認(rèn)state先傳遞給實(shí)例對(duì)象的state,接口請(qǐng)求傳遞的props數(shù)據(jù)后傳遞給實(shí)例對(duì)象的state。 為什么不直接寫state屬性而要加static,因?yàn)閟tate屬性會(huì)執(zhí)行在constructor之后,這樣會(huì)覆蓋constructor定義的state,也就是會(huì)覆蓋我們getInitialProps返回的數(shù)據(jù)。
注意:在服務(wù)端渲染環(huán)境下,執(zhí)行renderToString的時(shí)候,組件會(huì)被實(shí)例化,并且返回字符串形式的dom,這個(gè)過程react組件的生命周期只會(huì)執(zhí)行到willmount之后的render。
3)我們寫好一個(gè)html文件,大致如下。 當(dāng)前已經(jīng)渲染出了相應(yīng)的節(jié)點(diǎn)字符串,后端需要返回html文本,內(nèi)容應(yīng)該包含標(biāo)題,節(jié)點(diǎn)和最后需要加載的打包好的js,依次去替換html占位部分。
index.html
server/router.js
4)最后客戶端js加載完成后,會(huì)運(yùn)行react,并且執(zhí)行同構(gòu)方法ReactDOM.hydrate,而不是平時(shí)用的ReactDOM.render。
以下是首次渲染過程大致流程圖,點(diǎn)擊查看大圖
css處理
現(xiàn)在我們已經(jīng)完成了最核心的邏輯,但是有一個(gè)問題。 我發(fā)現(xiàn)在后端渲染組件的時(shí)候,style-loader會(huì)報(bào)錯(cuò),style-loader會(huì)找到組件依賴的css,并在組件加載時(shí),把style載入到html header中,但是我們?cè)诜?wù)端渲染的時(shí)候,沒有window對(duì)象,因此style-loader內(nèi)部代碼會(huì)報(bào)錯(cuò)。 服務(wù)端webpack需要移除style-loader,用其他方法代替,后來(lái)我把樣式賦值給組件靜態(tài)變量,然后通過服務(wù)端渲染一并返回給前端,但是有個(gè)問題,我只能拿到當(dāng)前組件的樣式,子組件的樣式?jīng)]辦法拿到,如果要給子組件再添加靜態(tài)方法,再想辦法去取,那就太麻煩了。 后來(lái)我找到了一個(gè)庫(kù)isomorphic-style-loader可以支持我們想要的功能,看了下它的源碼和使用方法,通過高階函數(shù)把樣式賦值給組件,然后利用react的Context,拿到當(dāng)前需要渲染的所有組件的樣式,最后把style插入到html中,這樣解決了子組件樣式無(wú)法導(dǎo)入的問題。 但是我覺得有點(diǎn)麻煩,首先需要定義所有組件的高階函數(shù)和引入這個(gè)庫(kù),然后在router之中需要寫相關(guān)代碼收集style,最后插入到html中。 后來(lái)我定義了一個(gè)ProcessSsrStyle方法,入?yún)⑹莝tyle文件,邏輯是判斷環(huán)境,如果是服務(wù)端把style加載到當(dāng)前組件的dom中,如果是客戶端就不處理(因?yàn)榭蛻舳擞衧tyle-loader)。 實(shí)現(xiàn)和使用非常簡(jiǎn)單,如下:
ProcessSsrStyle.js
使用:
服務(wù)端返回html的內(nèi)容如下,用戶馬上能夠看到完整的頁(yè)面樣式,而當(dāng)客戶端react同構(gòu)完成后,dom會(huì)被替換為純dom,因?yàn)镻rocessSsrStyle方法在客戶端不會(huì)輸出style,最終style-loader執(zhí)行后header中也會(huì)有樣式,,頁(yè)面不會(huì)出現(xiàn)不一致的變化,對(duì)于用戶來(lái)說這一切都是無(wú)感的。
至此,最核心的功能已經(jīng)實(shí)現(xiàn),但是在后來(lái)的開發(fā)中,我發(fā)現(xiàn)事情還并沒有那么簡(jiǎn)單,因?yàn)殚_發(fā)環(huán)境似乎太不友好了,開發(fā)效率低,需要手動(dòng)重啟。
開發(fā)環(huán)境
先說說最初的開發(fā)環(huán)境如何工作:
npm run dev啟動(dòng)開發(fā)環(huán)境
webpack.client-dev.js打包服務(wù)端代碼,代碼會(huì)被打包到dist/server中
webpack.server-dev.js打包客戶端代碼,代碼會(huì)被打包到dist/client中
啟動(dòng)服務(wù)端應(yīng)用,端口9999
啟動(dòng)webpack-dev-server, 端口8888
webpack打包后,啟動(dòng)了兩個(gè)服務(wù),一個(gè)是服務(wù)端的app應(yīng)用、端口為9999,一個(gè)是客戶端的dev-server、端口為8888,dev-server會(huì)監(jiān)聽和打包c(diǎn)lient代碼,可以在客戶端代碼更新的時(shí)候,實(shí)時(shí)熱更新前端代碼。 當(dāng)訪問localhost:9999時(shí),server會(huì)返回html,我們的server返回的html中的js腳本路徑是指向的dev-serve端口的地址,如下圖。 也就是說,客戶端的程序和服務(wù)端的程序被分別打包,并且運(yùn)行兩個(gè)不同的端口服務(wù)。
在生產(chǎn)環(huán)境下,因?yàn)椴恍枰猟ev-server去監(jiān)聽和熱更新,因此只一個(gè)服務(wù)就足夠, 如下圖,服務(wù)端注冊(cè)靜態(tài)資源文件夾:
server/app.js
目前的構(gòu)建系統(tǒng),區(qū)分了生產(chǎn)環(huán)境和開發(fā)環(huán)境,現(xiàn)在的開發(fā)環(huán)境構(gòu)建是沒有什么問題的。 但是開發(fā)環(huán)境問題就比較明顯,存在的最大問題是服務(wù)端沒有熱更新或者重新打包重啟。 這樣會(huì)導(dǎo)致很多問題,最嚴(yán)重的就是前端已經(jīng)更新了組件,但是服務(wù)端并沒有更新,所以在同構(gòu)的時(shí)候會(huì)出現(xiàn)不一致,就會(huì)導(dǎo)致報(bào)錯(cuò),有些報(bào)錯(cuò)會(huì)影響運(yùn)行,解決辦法只有重啟。 這樣的開發(fā)體驗(yàn)是無(wú)法忍受的。 后來(lái)我開始考慮做服務(wù)端的熱更新。
監(jiān)聽、打包、重啟
最初我的方法是監(jiān)聽修改,打包然后重啟應(yīng)用。 還記得我們的client/router/pages.js文件嗎,客戶端和服務(wù)端的路由都引入了這個(gè)文件,所以服務(wù)端和客戶端的打包依賴都有pages.js,因此所有pages的組件相關(guān)的依賴都可以被客戶端和服務(wù)端監(jiān)聽,當(dāng)一個(gè)組件更新了,dev-server已經(jīng)幫助我們監(jiān)聽和熱更新了客戶端代碼,現(xiàn)在我們要自己來(lái)處理以下如何更新和重啟服務(wù)端代碼。 其實(shí)方法很簡(jiǎn)單,就是在服務(wù)端打包配置里開啟監(jiān)聽,然后在插件配置中,寫一個(gè)重啟的插件,插件代碼如下:
當(dāng)webpack首次運(yùn)行之后,插件會(huì)啟動(dòng)一個(gè)子進(jìn)程,運(yùn)行app.js,當(dāng)文件發(fā)生變動(dòng)后,再次編譯,判斷是否有子進(jìn)程,如果有殺掉子進(jìn)程,然后重啟子進(jìn)程,這樣就實(shí)現(xiàn)了自動(dòng)重啟。 因?yàn)榭蛻舳撕头?wù)端是兩個(gè)不同的打包服務(wù)和配置,當(dāng)文件被修改,他們同時(shí)會(huì)重新編譯,為了保證編譯后運(yùn)行符合預(yù)期,要保證服務(wù)端先編譯完成,客戶端后編譯完成,所以在客戶端的watch配置里,增加一點(diǎn)延遲,如下圖,默認(rèn)是300毫秒,所以服務(wù)端是300毫秒后執(zhí)行編譯,而客戶端是1000毫秒后執(zhí)行編譯。
現(xiàn)在解決了重啟問題,但是我覺得還不夠,因?yàn)樵陂_發(fā)的大部分時(shí)間里pages.js中組件,也就是展示端的代碼更新頻率會(huì)很高,如果老是去重啟編譯后端的代碼,我覺得效率太低。 因此我覺得再做一次優(yōu)化。
抽離client/router/pages單獨(dú)打包
流程應(yīng)該是這樣的,增加一個(gè)webpack.server-dev-pages.js配置文件,單獨(dú)監(jiān)聽和打包出dist/pages,服務(wù)端代碼判斷如果是開發(fā)環(huán)境,在路由監(jiān)聽方法中每次執(zhí)行都重新獲取dist/pages包,服務(wù)端監(jiān)聽配置忽略client文件夾。 看起來(lái)有點(diǎn)懵逼,其實(shí)最終的效果就是當(dāng)pages中依賴的組件發(fā)生了更新,webpack.server-dev-pages.js重新編譯并打包到dist/pages中,服務(wù)端app不編譯和重啟,只需要在服務(wù)端app路由中重新獲取最新的dist/pages包,就保證了服務(wù)應(yīng)用更新了所有客戶端組件,而服務(wù)端應(yīng)用并不會(huì)編譯和重啟。 當(dāng)服務(wù)端本身的代碼發(fā)生了修改,還是會(huì)自動(dòng)編譯和重啟。 所以最終我們的開發(fā)環(huán)境需要啟動(dòng)3個(gè)打包配置
webpack.server-dev-pages
webpack.server-dev
webpack.client-dev
server/router,如何清除和更新pages包
至此,比較滿意的開發(fā)環(huán)境基本實(shí)現(xiàn)了。 后來(lái)又覺得每次更新css都需要去重新打包后端的pages也沒有必要,加上同構(gòu)的時(shí)候css不一致,僅僅只有警告,沒有實(shí)質(zhì)影響,因此我在server-dev-pages中忽略了less文件(因?yàn)槲矣玫膌ess)。 這樣會(huì)導(dǎo)致一個(gè)問題,因?yàn)闆]有更新pages,所以頁(yè)面會(huì)刷新時(shí)會(huì)先展示舊的樣式,然后同構(gòu)完成又立馬變成新樣式,在開發(fā)環(huán)境中這一瞬間是可以接受的,也不影響什么。 但是避免了無(wú)謂的編譯。
沒有做的事情
封裝成一個(gè)更有包裹性的三方腳手架
css作用域控制
封裝性更強(qiáng)的webpack配置
開發(fā)環(huán)境下,圖片路徑會(huì)出現(xiàn)不一致
最初做自己小站的目的是學(xué)習(xí),加上自己使用,因此有太多個(gè)性的東西。 從自己的小站中抽離了出來(lái),已經(jīng)刪去了很多包和代碼,只為了讓他人更能快速理解其中的核心代碼。 代碼中有很多注釋都能幫助他人理解,如果大家想使用當(dāng)前庫(kù)開發(fā)一個(gè)自己的小站,是完全可以的,也可以幫助大家更好的理解它。 如果是用于商業(yè)項(xiàng)目,推薦nextjs。 css沒有做作用域控制,因此如果想隔離作用域,手動(dòng)添加上層css隔離,比如.index{ ..... }包裹一層,或者嘗試自己引入三方包。 webpack通用的配置可以封裝成一個(gè)文件,然后在每個(gè)文件里引入,再個(gè)性修改。 但是之前看其他代碼的時(shí)候發(fā)現(xiàn),這種方法,會(huì)增加閱讀難度,加上本身配置內(nèi)容不多,所以不做封裝,看起來(lái)更直觀。 開發(fā)環(huán)境下,圖片路徑會(huì)出現(xiàn)不一致,比如客戶端地址請(qǐng)求地址是localhost...assets/xx.jpg,而服務(wù)端是assets/xx.jpg,可能會(huì)有警告,但是不影響。 因?yàn)橹皇且粋€(gè)是絕對(duì)路徑,一個(gè)是相對(duì)路徑。
看完上述內(nèi)容,你們對(duì)怎么實(shí)現(xiàn)ssr服務(wù)端渲染有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。
文章標(biāo)題:怎么實(shí)現(xiàn)ssr服務(wù)端渲染
網(wǎng)站路徑:http://aaarwkj.com/article42/pccpec.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供微信公眾號(hào)、動(dòng)態(tài)網(wǎng)站、微信小程序、做網(wǎng)站、品牌網(wǎng)站建設(shè)、網(wǎng)站制作
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)