前言
本文是基于react ssr的入门教程,在实际项目中使用还需要做更多的配置和优化,比较适合第一次尝试react ssr的小伙伴们。技术涉及到 koa2 + react,案例使用create-react-app创建
SSR 介绍
Server Slide Rendering,缩写为 ssr 即服务器端渲染,这个要从SEO说起,目前react单页应用HTML代码是下面这样的
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="shortcut icon" href="favicon.ico" rel="external nofollow" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/> <meta name="theme-color" content="#000000" /> <title>React App</title> </head> <body> <noscript>You need to enable JavaScript to run this app.</noscript> <div id="root"></div> <script src="/UploadFiles/2021-04-02/main.js">
- 如果main.js 加载比较慢,会出现白屏一闪的现象。
- 传统的搜索引擎爬虫因为不能抓取JS生成后的内容,遇到单页web项目,抓取到的内容啥也没有。在SEO上会吃很多亏,很难排搜索引擎到前面去。
React SSR(react服务器渲染)正好解决了这2个问题。
React SSR介绍
这里通过一个例子来带大家入坑!先使用create-react-app创建一个react项目。因为要修改webpack,这里我们使用react-app-rewired启动项目。根目录创建一个server目录存放服务端代码,服务端代码我们这里使用koa2。目录结构如下:
这里先来看看react ssr是怎么工作的。
这个业务流程图比较清晰了,服务端只生成HTML代码,实际上前端会生成一份main.js提供给服务端的HTML使用。这就是react ssr的工作流程。有了这个图会更好的理解,如果这个业务没理解清楚,后面的估计很难理解。
react提供的SSR方法有两个renderToString 和 renderToStaticMarkup,区别如下:
- renderToString 方法渲染的时候带有 data-reactid 属性. 在浏览器访问页面的时候,main.js能识别到HTML的内容,不会执行React.createElement二次创建DOM。
- renderToStaticMarkup 则没有 data-reactid 属性,页面看上去干净点。在浏览器访问页面的时候,main.js不能识别到HTML内容,会执行main.js里面的React.createElement方法重新创建DOM。
实现流程
好了,我们都知道原理了,可以开始coding了,目录结构如下:
create-react-app 的demo我没动过,直接用这个做案例了,前端项目基本上就没改了,等会儿我们服务器端要使用这个模块。代码如下:
render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <p> Edit <code>src/App.js</code> and save to reload. </p> <a className="App-link" href="https://reactjs.org" rel="external nofollow" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> </div> ); } } export default App;
在项目中新建server目录,用于存放服务端代码。为了简化,我这里只有2个文件,项目中我们用的ES6,所以还要配置下.babelrc
.babelrc 配置,因为要使用到ES6
{ "presets": [ "env", "react" ], "plugins": [ "transform-decorators-legacy", "transform-runtime", "react-hot-loader/babel", "add-module-exports", "transform-object-rest-spread", "transform-class-properties", [ "import", { "libraryName": "antd", "style": true } ] ] }
index.js 项目入口做一些预处理,使用asset-require-hook过滤掉一些类似 import logo from "./logo.svg";
这样的资源代码。因为我们服务端只需要纯的HTML代码,不过滤掉会报错。这里的name,我们是去掉了hash值的
require("asset-require-hook")({ extensions: ["svg", "css", "less", "jpg", "png", "gif"], name: '/static/media/[name].[ext]' }); require("babel-core/register")(); require("babel-polyfill"); require("./app");
public/index.html html模版代码要做个调整,{{root}}
这个可以是任何可以替换的字符串,等下服务端会替换这段字符串。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" rel="external nofollow" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/> <meta name="theme-color" content="#000000" /> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" rel="external nofollow" /> <title>React App</title> </head> <body> <noscript>You need to enable JavaScript to run this app.</noscript> <div id="root">{{root}}</div> </body> </html>
app.js 服务端渲染的主要代码,加载App.js,使用renderToString 生成html代码,去替换掉 index.html 中的 {{root}}
部分
import App from '../src/App'; import Koa from 'koa'; import React from 'react'; import Router from 'koa-router'; import fs from 'fs'; import koaStatic from 'koa-static'; import path from 'path'; import { renderToString } from 'react-dom/server'; // 配置文件 const config = { port: 3030 }; // 实例化 koa const app = new Koa(); // 静态资源 app.use( koaStatic(path.join(__dirname, '../build'), { maxage: 365 * 24 * 60 * 1000, index: 'root' // 这里配置不要写成'index'就可以了,因为在访问localhost:3030时,不能让服务默认去加载index.html文件,这里很容易掉进坑。 }) ); // 设置路由 app.use( new Router() .get('*', async (ctx, next) => { ctx.response.type = 'html'; //指定content type let shtml = ''; await new Promise((resolve, reject) => { fs.readFile(path.join(__dirname, '../build/index.html'), 'utfa8', function(err, data) { if (err) { reject(); return console.log(err); } shtml = data; resolve(); }); }); // 替换掉 {{root}} 为我们生成后的HTML ctx.response.body = shtml.replace('{{root}}', renderToString(<App />)); }) .routes() ); app.listen(config.port, function() { console.log('服务器启动,监听 port: ' + config.port + ' running~'); });
config-overrides.js 因为我们用的是create-react-app,这里使用react-app-rewired去改下webpack的配置。因为执行npm run build的时候会自动给资源加了hash值,而这个hash值,我们在asset-require-hook的时候去掉了hash值,配置里面需要改下,不然会出现图片不显示的问题,这里也是一个坑,要注意下。
module.exports = { webpack: function(config, env) { // ...add your webpack config // console.log(JSON.stringify(config)); // 去掉hash值,解决asset-require-hook资源问题 config.module.rules.forEach(d => { d.oneOf && d.oneOf.forEach(e => { if (e && e.options && e.options.name) { e.options.name = e.options.name.replace('[hash:8].', ''); } }); }); return config; } };
好了,所有的代码就这些了,是不是很简单了?我们koa2读取的静态资源是 build目录下面的。先执行npm run build打包项目,再执行node ./server 启动服务端项目。看下http://localhost:3030页面的HTML代码检查下:
总结
相信这篇文章是最简单的react服务器渲染案例了,这里交出github地址:https://github.com/mtsee/react-koa2-ssr
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
稳了!魔兽国服回归的3条重磅消息!官宣时间再确认!
昨天有一位朋友在大神群里分享,自己亚服账号被封号之后居然弹出了国服的封号信息对话框。
这里面让他访问的是一个国服的战网网址,com.cn和后面的zh都非常明白地表明这就是国服战网。
而他在复制这个网址并且进行登录之后,确实是网易的网址,也就是我们熟悉的停服之后国服发布的暴雪游戏产品运营到期开放退款的说明。这是一件比较奇怪的事情,因为以前都没有出现这样的情况,现在突然提示跳转到国服战网的网址,是不是说明了简体中文客户端已经开始进行更新了呢?
更新日志
- 小骆驼-《草原狼2(蓝光CD)》[原抓WAV+CUE]
- 群星《欢迎来到我身边 电影原声专辑》[320K/MP3][105.02MB]
- 群星《欢迎来到我身边 电影原声专辑》[FLAC/分轨][480.9MB]
- 雷婷《梦里蓝天HQⅡ》 2023头版限量编号低速原抓[WAV+CUE][463M]
- 群星《2024好听新歌42》AI调整音效【WAV分轨】
- 王思雨-《思念陪着鸿雁飞》WAV
- 王思雨《喜马拉雅HQ》头版限量编号[WAV+CUE]
- 李健《无时无刻》[WAV+CUE][590M]
- 陈奕迅《酝酿》[WAV分轨][502M]
- 卓依婷《化蝶》2CD[WAV+CUE][1.1G]
- 群星《吉他王(黑胶CD)》[WAV+CUE]
- 齐秦《穿乐(穿越)》[WAV+CUE]
- 发烧珍品《数位CD音响测试-动向效果(九)》【WAV+CUE】
- 邝美云《邝美云精装歌集》[DSF][1.6G]
- 吕方《爱一回伤一回》[WAV+CUE][454M]