React SSR样式及SEO的实践


Posted in Javascript onOctober 22, 2018

前一篇主要记录了一下SSR配置以及结合Redux的使用。这里简单说一下React SSR中样式处理和更优雅的SEO

SSR样式

在React客户端渲染,添加样式很容易。写一个css样式文件,在对应组件中引用。标签上通过className这个属性调用对应样式就万事Ok了。当然我们需要在webpack中配置loader来解析css文件。一般的配置如下(使用css modules):

module: {
 rules: [{
  test: /\.css?$/,
  use: ['style-loader', {
   loader: 'css-loader',
   options: {
    importLoader: 1,
    modules: true,
    localIdentName: '[name]_[local]_[hash:base64:5]'
   }
  }]
 }]
}

需要先通过css-loader解析css文件,之后再通过style-loader将样式放在html的style标签中。

那么SSR也这样行吗~

yarn dev

跑一下服务,发现命令行报这个错误:

return window && document && document.all && !window.atob;
^

ReferenceError: window is not defined

原因在于服务器端渲染哪里有window对象,哪里有DOM啊。我们是通过虚拟DOM。renderToString这个方法生成出来的html字符串。stackoverflow搜了一下发现了isomorphic-style-loader这个专门用于同构的style-loader。

话不多少搞起来。客户端的webpack配置不需要变更还是使用css-loader+style-loader。服务器端就使用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一下,不报错了但是会闪一下屏。禁用掉js发现server端生成的html并没有样式,当客户端JS接管程序之后才会有样式出现。这样的体验相当糟糕。

当然我们确实没有向服务器端生成的HTML添加style标签。

现在服务器返给我们的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>
 `

这时我们想到了context这个玩意。在server端render之前。我们设置一个

let context = {
 css: []
}

我们还知道在服务端渲染的时候有this.props.staticContext这样一个props拿到我们设置context。另外isomorphic-style-loader提供给我们了

_getCss()这个方法。可以在SSR过程中拿到样式。有了这两个必要条件。我们就可以在每一个用到样式的Component中通过componentWillMount这个生命周期

添加这样一段代码:

componentWillMount () {
 if (this.props.staticContext) { // 只有服务端渲染时候有this.props.staticContext以及_getCss()
  this.props.staticContext.css.push(styles._getCss())
 }
}

这样样式就存储在context这个变量的css数组中咯,改造一下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>
  `

万事?,当然我们可以进一步优化,把componentWillMount所做的事情提出来搞一个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} />
  }
 }
}

这样简单的封装一个HOC,之后涉及样式的时候直接通过withStylesHOC包裹一下就好。例如一个结合Redux的Home组件:

export default connect(mapState, mapDispatch)(withStyle(Home, styles))

React SSR样式及SEO的实践

SSR-SEO

费大力气通过一个node中间层去实现首屏的SSR,除开首屏速度之外,就是SEO这一大块了,对于一个商业网站来讲真的很重要。

SEO(Search Engine Optimization)? 通过一些技术手段让网站在搜索引擎的排名尽量靠前一点。由于客户端渲染出来的网站只有<div id='root'>这样的html节点。大多数搜索引擎分析不出来网站上有什么。SSR直接渲染出来HTML,这样对搜索引擎就友好了很多。

SSR中的SEO

这里我们使用github上的一个库react-helmet首先需要在对应的页面组件中引入react-helmet,就可以在Helmet标签内自由添加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所说的。在server端这样处理

ReactDOMServer.renderToString(<Handler />);
const helmet = Helmet.renderStatic();

并在返回的html字符串中 ${helmet.title.toString()} ${helmet.meta.toString()}进行填充

<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>

重新跑一下 搞定!

当然SSR-SEO绝不这么简单。仅仅在页面上添加head标签内加上title 和meta标签影响是有限的。8102年的搜索爬虫已经不单单去匹配title和 description,而是全稳的匹配(也就是说title和descript有影响但是影响很小)搜索爬虫会把整个网站所有的文本收集起来进行分析。

那么如何做好SEO

题外话顺便说一下如何做好SEO。一个网站无非三大块内容,文字、多媒体、链接。要做到的是文字的原创性,图片的原创性以及高清度还有站内链接尽量和站内内容相关。

React SSR样式及SEO的实践

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
HTML Dom与Css控制方法
Oct 25 Javascript
javascript开发技术大全-第3章 js数据类型
Jul 03 Javascript
在线一元二次方程计算器实例(方程计算器在线计算)
Dec 22 Javascript
Jquery之Bind方法参数传递与接收的三种方法
Jun 24 Javascript
详细解读JavaScript的跨浏览器事件处理
Aug 12 Javascript
JavaScript为事件句柄绑定监听函数实例详解
Dec 15 Javascript
JavaScript中使用数组方法汇总
Feb 16 Javascript
Node.js开启Https的实践详解
Oct 25 Javascript
jquery.rotate.js实现可选抽奖次数和中奖内容的转盘抽奖代码
Aug 23 jQuery
Angular中点击li标签实现更改颜色的核心代码
Dec 08 Javascript
Vue项目添加动态浏览器头部title的方法
Jul 11 Javascript
Vue 动态路由的实现及 Springsecurity 按钮级别的权限控制
Sep 05 Javascript
微信开发之微信jssdk录音功能开发示例
Oct 22 #Javascript
ztree加载完成后显示勾选节点的实现代码
Oct 22 #Javascript
electron实现qq快捷登录的方法示例
Oct 22 #Javascript
Electron-vue脚手架改造vue项目的方法
Oct 22 #Javascript
angular 服务的单例模式(依赖注入模式下)详解
Oct 22 #Javascript
vue 配置多页面应用的示例代码
Oct 22 #Javascript
微信小程序引用iconfont图标的方法
Oct 22 #Javascript
You might like
探讨PHP调用时间格式的参数详解
2013/06/06 PHP
php生成txt文件标题及内容的方法
2014/01/16 PHP
ThinkPHP的L方法使用简介
2014/06/18 PHP
详解PHP编码转换函数应用技巧
2016/10/22 PHP
thinkPHP实现多字段模糊匹配查询的方法
2016/12/01 PHP
php实现通过stomp协议连接ActiveMQ操作示例
2020/02/23 PHP
关于实现代码语法标亮 dp.SyntaxHighlighter
2007/02/02 Javascript
document.all的一个比较完整的总结及案例
2013/01/31 Javascript
node.js入门教程
2014/06/01 Javascript
Nodejs Post请求报socket hang up错误的解决办法
2014/09/25 NodeJs
JavaScript获取两个数组交集的方法
2015/06/09 Javascript
基于Jquery实现仿百度百科右侧导航代码附源码下载
2015/11/27 Javascript
jQuery Mobile框架中的表单组件基础使用教程
2016/05/17 Javascript
jQuery实现的简单百分比进度条效果示例
2016/08/01 Javascript
JS 在数组指定位置插入/删除数据的方法
2017/01/12 Javascript
lhgcalendar时间插件限制只能选择三个月的实现方法
2017/07/03 Javascript
angular1.x ui-route传参的三种写法小结
2018/08/31 Javascript
微信小程序时间戳转日期的详解
2019/04/30 Javascript
详细教你微信公众号正文页SVG交互开发技巧
2019/07/25 Javascript
微信公众号开发之微信支付代码记录的实现
2019/10/16 Javascript
JS图片懒加载技术实现过程解析
2020/07/27 Javascript
[01:16:01]VGJ.S vs Mski Supermajor小组赛C组 BO3 第一场 6.3
2018/06/04 DOTA
以Python的Pyspider为例剖析搜索引擎的网络爬虫实现方法
2015/03/30 Python
Python的__builtin__模块中的一些要点知识
2015/05/02 Python
django实现用户注册实例讲解
2019/10/30 Python
Django项目使用ckeditor详解(不使用admin)
2019/12/17 Python
美国著名的团购网站:Woot
2016/08/02 全球购物
JD Sports法国:英国篮球和运动时尚的领导者
2017/09/28 全球购物
Brasty罗马尼亚:购买手表、香水、化妆品、珠宝
2020/04/21 全球购物
班长岗位职责
2013/11/10 职场文书
优秀学生自我鉴定范例
2013/12/18 职场文书
2014年计算机专业个人自我评价
2014/01/19 职场文书
小学教师师德反思
2014/02/03 职场文书
群众路线剖析材料(四风问题)
2014/10/08 职场文书
培养联系人考察意见
2015/06/01 职场文书
运动会开幕式主持词
2015/07/01 职场文书