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 相关文章推荐
JavaScript Event学习第六章 事件的访问
Feb 07 Javascript
JSONP 跨域访问代理API-yahooapis实现代码
Dec 02 Javascript
文本域光标操作的jQuery扩展分享
Mar 10 Javascript
window.location.href的用法(动态输出跳转)
Aug 09 Javascript
jQuery表单验证功能实例
Aug 28 Javascript
理解Angular数据双向绑定
Jan 10 Javascript
前端框架学习总结之Angular、React与Vue的比较详解
Mar 14 Javascript
node.js基于fs模块对系统文件及目录进行读写操作的方法详解
Nov 10 Javascript
vue的无缝滚动组件vue-seamless-scroll实例
Dec 18 Javascript
Layui Table js 模拟选中checkbox的例子
Sep 03 Javascript
JS常用排序方法实例代码解析
Mar 03 Javascript
js判断鼠标移入移出方向的方法
Jun 24 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+xslt在windows平台上
2006/10/09 PHP
多数据表共用一个页的新闻发布
2006/10/09 PHP
PHP读MYSQL中文乱码的解决方法
2006/12/17 PHP
PHP中子类重载父类的方法【parent::方法名】
2016/05/06 PHP
php中html_entity_decode实现HTML实体转义
2018/06/13 PHP
一个符号插入器 中用到的js代码
2007/09/04 Javascript
JS 创建对象(常见的几种方法)
2008/11/03 Javascript
七夕情人节丘比特射箭小游戏
2015/08/20 Javascript
JS实现淘宝支付宝网站的控制台菜单效果
2015/09/28 Javascript
基于JQuery实现分隔条的功能
2016/06/17 Javascript
jQuery基于ajax方式实现用户名存在性检查功能示例
2017/02/10 Javascript
Angularjs分页查询的实现
2017/02/24 Javascript
vue 全选与反选的实现方法(无Bug 新手看过来)
2018/02/09 Javascript
vue富文本编辑器组件vue-quill-edit使用教程
2018/09/21 Javascript
web页面和微信小程序页面实现瀑布流效果
2018/09/26 Javascript
小程序自定义单页面、全局导航栏的实现代码
2019/03/15 Javascript
vue配置文件实现代理v2版本的方法
2019/06/21 Javascript
javascript导出csv文件(excel)的方法示例
2019/08/25 Javascript
JavaScript onclick事件使用方法详解
2020/05/15 Javascript
[54:10]Spirit vs NB Supermajor小组赛 A组败者组决赛 BO3 第一场 6.2
2018/06/03 DOTA
Python表示矩阵的方法分析
2017/05/26 Python
详解如何利用Cython为Python代码加速
2018/01/27 Python
python数据处理 根据颜色对图片进行分类的方法
2018/12/08 Python
Python将列表中的元素转化为数字并排序的示例
2019/12/25 Python
Python importlib动态导入模块实现代码
2020/04/16 Python
python对接ihuyi实现短信验证码发送
2020/05/10 Python
Python 保存加载mat格式文件的示例代码
2020/08/04 Python
英国卫浴商店:Ergonomic Design
2019/09/22 全球购物
葡萄牙航空官方网站:TAP Air Portugal
2019/10/31 全球购物
JAVA中的关键字有什么特点
2014/03/07 面试题
公司运动会策划方案
2014/05/25 职场文书
2014年党风建设工作总结
2014/11/19 职场文书
2016学校先进党组织事迹材料
2016/02/29 职场文书
FP-growth算法发现频繁项集——发现频繁项集
2021/06/24 Python
python中取整数的几种方法
2021/11/07 Python
vue项目支付功能代码详解
2022/02/18 Vue.js