React SSR樣式及SEO的實(shí)踐
前一篇主要記錄了一下SSR配置以及結(jié)合Redux的使用。這里簡(jiǎn)單說(shuō)一下React SSR中樣式處理和更優(yōu)雅的SEO
SSR樣式
在React客戶端渲染,添加樣式很容易。寫一個(gè)css樣式文件,在對(duì)應(yīng)組件中引用。標(biāo)簽上通過(guò)className這個(gè)屬性調(diào)用對(duì)應(yīng)樣式就萬(wàn)事Ok了。當(dāng)然我們需要在webpack中配置loader來(lái)解析css文件。一般的配置如下(使用css modules):
module: { rules: [{ test: /\.css?$/, use: ['style-loader', { loader: 'css-loader', options: { importLoader: 1, modules: true, localIdentName: '[name]_[local]_[hash:base64:5]' } }] }] }
需要先通過(guò)css-loader解析css文件,之后再通過(guò)style-loader將樣式放在html的style標(biāo)簽中。
那么SSR也這樣行嗎~
yarn dev
跑一下服務(wù),發(fā)現(xiàn)命令行報(bào)這個(gè)錯(cuò)誤:
return window && document && document.all && !window.atob;
^ReferenceError: window is not defined
原因在于服務(wù)器端渲染哪里有window對(duì)象,哪里有DOM啊。我們是通過(guò)虛擬DOM。renderToString
這個(gè)方法生成出來(lái)的html字符串。stackoverflow
搜了一下發(fā)現(xiàn)了isomorphic-style-loader這個(gè)專門用于同構(gòu)的style-loader。
話不多少搞起來(lái)??蛻舳说膚ebpack配置不需要變更還是使用css-loader+style-loader。服務(wù)器端就使用css-loader+isomorphic-style-loader了(和style-loader用法一波一樣)
// webpack.server.js module: { rules: [{ test: /\.css?$/, use: ['isomorphic-style-loader', { loader: 'css-loader', options: { importLoader: 1, modules: true, localIdentName: '[name]_[local]_[hash:base64:5]' } }] }] }
配置好了Run一下,不報(bào)錯(cuò)了但是會(huì)閃一下屏。禁用掉js發(fā)現(xiàn)server端生成的html并沒(méi)有樣式,當(dāng)客戶端JS接管程序之后才會(huì)有樣式出現(xiàn)。這樣的體驗(yàn)相當(dāng)糟糕。
當(dāng)然我們確實(shí)沒(méi)有向服務(wù)器端生成的HTML添加style標(biāo)簽。
現(xiàn)在服務(wù)器返給我們的html是這樣的
return ` <html> <head> <title>ssr</title> </head> <body> <div id='root' >${ content }</div> <script> window.context = { state: ${ JSON.stringify(store.getState()) } } </script> <script src='/index.js' ></script> </body> </html> `
這時(shí)我們想到了context這個(gè)玩意。在server端render之前。我們?cè)O(shè)置一個(gè)
let context = { css: [] }
我們還知道在服務(wù)端渲染的時(shí)候有this.props.staticContext
這樣一個(gè)props拿到我們?cè)O(shè)置context
。另外isomorphic-style-loader
提供給我們了
_getCss()這個(gè)方法??梢栽赟SR過(guò)程中拿到樣式。有了這兩個(gè)必要條件。我們就可以在每一個(gè)用到樣式的Component中通過(guò)componentWillMount
這個(gè)生命周期
添加這樣一段代碼:
componentWillMount () { if (this.props.staticContext) { // 只有服務(wù)端渲染時(shí)候有this.props.staticContext以及_getCss() this.props.staticContext.css.push(styles._getCss()) } }
這樣樣式就存儲(chǔ)在context
這個(gè)變量的css數(shù)組中咯,改造一下server端的html輸出代碼:
const cssStr = context.css.length ? context.css.join('\n') : '' return ` <html> <head> <title>ssr</title> <style>${cssStr}</style> </head> <body> <div id='root' >${content}</div> <script> window.context = { state: ${JSON.stringify(store.getState())} } </script> <script src='/index.js' ></script> </body> </html> `
萬(wàn)事👌,當(dāng)然我們可以進(jìn)一步優(yōu)化,把componentWillMount所做的事情提出來(lái)搞一個(gè)HOC(高階組件)。
withStylesHOC.js
import React, { Component } from 'react' export default (DecoratedComponent, styles) => { return class NewComponent extends Component { componentWillMount () { if (this.props.staticContext) { this.props.staticContext.css.push(styles._getCss()) } } render () { return <DecoratedComponent {...this.props} /> } } }
這樣簡(jiǎn)單的封裝一個(gè)HOC,之后涉及樣式的時(shí)候直接通過(guò)withStylesHOC
包裹一下就好。例如一個(gè)結(jié)合Redux的Home組件:
export default connect(mapState, mapDispatch)(withStyle(Home, styles))
SSR-SEO
費(fèi)大力氣通過(guò)一個(gè)node中間層去實(shí)現(xiàn)首屏的SSR,除開(kāi)首屏速度之外,就是SEO這一大塊了,對(duì)于一個(gè)商業(yè)網(wǎng)站來(lái)講真的很重要。
SEO(Search Engine Optimization)– 通過(guò)一些技術(shù)手段讓網(wǎng)站在搜索引擎的排名盡量靠前一點(diǎn)。由于客戶端渲染出來(lái)的網(wǎng)站只有<div id='root'>
這樣的html節(jié)點(diǎn)。大多數(shù)搜索引擎分析不出來(lái)網(wǎng)站上有什么。SSR直接渲染出來(lái)HTML,這樣對(duì)搜索引擎就友好了很多。
SSR中的SEO
這里我們使用github上的一個(gè)庫(kù)react-helmet
首先需要在對(duì)應(yīng)的頁(yè)面組件中引入react-helmet
,就可以在Helmet標(biāo)簽內(nèi)自由添加title、meta咯
// Home.jax import { Helmet } from 'react-helmet' class Home extends Component { render() { return ( <Fragment> <Helmet> <title>SRR-Home</title> <meta name='description' content='this is a home Component' /> </Helmet> ... ... </<Fragment>> ) } }
之后按照readme所說(shuō)的。在server端這樣處理
ReactDOMServer.renderToString(<Handler />); const helmet = Helmet.renderStatic();
并在返回的html字符串中 ${helmet.title.toString()} ${helmet.meta.toString()}
進(jìn)行填充
<html> <head> ${helmet.title.toString()} ${helmet.meta.toString()} <style>${cssStr}</style> </head> <body> <div id='root' >${content}</div> <script> window.context = { state: ${JSON.stringify(store.getState())} } </script> <script src='/index.js' ></script> </body> </html>
重新跑一下 搞定!
當(dāng)然SSR-SEO絕不這么簡(jiǎn)單。僅僅在頁(yè)面上添加head標(biāo)簽內(nèi)加上title 和meta標(biāo)簽影響是有限的。8102年的搜索爬蟲已經(jīng)不單單去匹配title和 description,而是全穩(wěn)的匹配(也就是說(shuō)title和descript有影響但是影響很小)搜索爬蟲會(huì)把整個(gè)網(wǎng)站所有的文本收集起來(lái)進(jìn)行分析。
那么如何做好SEO
題外話順便說(shuō)一下如何做好SEO。一個(gè)網(wǎng)站無(wú)非三大塊內(nèi)容,文字、多媒體、鏈接。要做到的是文字的原創(chuàng)性,圖片的原創(chuàng)性以及高清度還有站內(nèi)鏈接盡量和站內(nèi)內(nèi)容相關(guān)。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
React實(shí)現(xiàn)過(guò)渡效果更新時(shí)間展示功能
創(chuàng)建一個(gè)組件,實(shí)時(shí)展示時(shí)分秒,并且動(dòng)態(tài)更新數(shù)據(jù),這篇文章主要介紹了React實(shí)現(xiàn)過(guò)渡效果更新時(shí)間展示功能,需要的朋友可以參考下2024-07-07React Native實(shí)現(xiàn)地址挑選器功能
這篇文章主要為大家詳細(xì)介紹了React Native仿地址挑選器功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10使用webpack搭建react開(kāi)發(fā)環(huán)境的方法
本篇文章主要介紹了使用webpack搭建react開(kāi)發(fā)環(huán)境的方法,在這篇文章中我們開(kāi)始利用我們之前所學(xué)搭建一個(gè)簡(jiǎn)易的React開(kāi)發(fā)環(huán)境,用以鞏固我們之前學(xué)習(xí)的Webpack知識(shí)。一起跟隨小編過(guò)來(lái)看看吧2018-05-05詳解React開(kāi)發(fā)中使用require.ensure()按需加載ES6組件
本篇文章主要介紹了詳解React開(kāi)發(fā)中使用require.ensure()按需加載ES6組件,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-05-05詳解React-Native解決鍵盤遮擋問(wèn)題(Keyboard遮擋問(wèn)題)
本篇文章主要介紹了React-Native解決鍵盤遮擋問(wèn)題(Keyboard遮擋問(wèn)題),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07React使用Mobx6.x共享狀態(tài)問(wèn)題
這篇文章主要介紹了React使用Mobx6.x共享狀態(tài)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10react中使用echarts,并實(shí)現(xiàn)tooltip循環(huán)輪播方式
這篇文章主要介紹了react中使用echarts,并實(shí)現(xiàn)tooltip循環(huán)輪播方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01新建的React Native就遇到vscode報(bào)警解除方法
這篇文章主要為大家介紹了新建的React Native就遇到vscode報(bào)警解除方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10