深入学习SSR|深入学习SSR , NextJS 一起重构 掘金!

前言 逃不开的SSR,大掘金就是使用Nuxt的SSR,本篇文章对SPA单页面应用的的一些SEO痛点进行总结,再简单实现一下掘金PC界面,学习一下Next,冲!
所以 一位程序员的职业生涯大约十年,只有人寿命的十分之一。前端项目只是你生活工作的一部分,而你却是它的全部,你是他的灵魂。请放下长时间的游戏、工作时的摸鱼。多学习来以最完美的状态好好陪你项目!
正文 主页讲解 单页面 加载 和 SEO 问题。 掘金PC还有许多功能等你开发
知识点

  • SSR & SSG & CSR
  • 预渲染 Prerender
  • 手动配置 SSR
  • Next 还原掘金PC端
    CSR & SSR & SSG
    CSR客户端渲染(Client Side Rendering)
  • CSR 渲染流程
深入学习SSR|深入学习SSR , NextJS 一起重构 掘金!
文章图片

SSR 服务端渲染(Server Side Rendering)
  • 是指将单页应用(SPA)在服务器端渲染成 HTML 片段,发送到浏览器,然后交由浏览器为其绑定状态与事件,成为完全可交互页面的过程。(PS:本文中的 SSR 内容都是围绕同构应用来讲的)
  • SSR 渲染流程:
深入学习SSR|深入学习SSR , NextJS 一起重构 掘金!
文章图片

服务端只负责首次“渲染”(真正意义上,只有浏览器才能渲染页面,服务端其实是生成 HTML 内容),然后返回给客户端,客户端接管页面交互(事件绑定等逻辑),之后客户端路由切换时,直接通过 JS 代码来显示对应的内容,不再需要服务端渲染(只有页面刷新时会需要)
SSG 静态页面生成(Static Stie Generation)
  • 解决白屏问题、SEO问题。但无法生成用户相关内容(所以用户请求的结果都相同)。请求发生的过程中直接生成静态的HTML文件。(缺点以生成的文件难以达到自动更新,往往设置时间戳或者定时清理文件)
为什么要使用SSR
这个概念想必大家都知道
  • 解决首屏渲染速度延迟问题
  • 解决SEO搜索引擎抓取问题
不妨看下下面的图
深入学习SSR|深入学习SSR , NextJS 一起重构 掘金!
文章图片

这是CSR 的页面源码
深入学习SSR|深入学习SSR , NextJS 一起重构 掘金!
文章图片

这是SSR 的页面源码
为什么出现上述缺点,无非没有真实的HTML等导致
这就是为何要使用SSR的原因了,其实不用SSR使用预渲染也能解决SEO问题,但无法解决首屏渲染白屏的问题,从而导致用户体验的欠佳
预渲染 Prerender
  • 简单解释,用户浏览器请求和SEO的蜘蛛爬去分别转发到不同的地址
深入学习SSR|深入学习SSR , NextJS 一起重构 掘金!
文章图片

以Node方式实现是这样的
const prerender = require("prerender"); const server = prerender({ port: 5011 //更改prerender服务器端口 }); //使用prerender插件 server.use(prerender.removeScriptTags()); server.start();

其他可以参考 https://github.com/prerender/prerender#readme
手动配置 SSR
配置 SSR 前你需要一些 Webpack基础 可以看下我的文章, 从零学习Webpack,当然你也可以看官网
webpack.base.js
const path = require("path"); module.exports = { mode: "development", watch: true, resolve: { alias: { "@": path.resolve(__dirname, "src") }, extensions: [".js", ".jsx", ".css"] }, module: { rules: [ { test: /\.jsx?/, exclude: /node_modules/, use: [ { loader: "babel-loader", options: { presets: ["@babel/preset-react"] } } ] } ] } };

webpack.server.js
const path = require("path"); const baseConfig = require("./webpack.base"); const merge = require("webpack-merge"); const nodeExternals = require("webpack-node-externals"); const serverConfig = { devtool: "none", entry: "./src/server", target: "node", output: { filename: "server.js", path: path.resolve(__dirname, "./dist"), publicPath: "/" }, externals: [nodeExternals()], module: { rules: [ { test: /\.css$/, use: ["isomorphic-style-loader", "css-loader?modules"] }, { test: /\.(png)|(jpg)|(gif)$/, use: [ { loader: "file-loader", options: { name: "img/[name].[hash:5].[ext]", emitFile: false } } ] } ] } }; module.exports = merge(baseConfig, serverConfig);

上面配置的服务端的 webpack 客户端同理,配置好入口,避免服务端代码在客户端运行
client
import React from "react"; import ReactDOM from "react-dom"; import App from "./App"; ReactDOM.hydrate(, document.getElementById("root"));

官网推荐在使用服务端渲染时 调用 ReactDOM.hydrate 方法
而服务端代码责负责将DOM转成HTML字符串
express 和 koa 中间件
import React from "react"; import App from "./App"; import ReactDom from "react-dom/server"; import getHTML from "./getHTML"; export default (req, res) => { const context = {}; const componentHTML = ReactDom.renderToString(); const html = getHTML(componentHTML); res.send(html); };

这就是手动配置 SSR 的基本过程,不够详细是为了让你们思考。
还有接下来介绍的Next js 是一款比较成熟的 React 服务端渲染框架,并不需要你手动配.
Next 还原掘金PC端
看看官网 Nextjs
  • 路由
  • SSR 和 SSG
  • Redux
  • Styles
  • Config
从上面 入手带你 学会具备掘金的开发功能
约定式路由 深入学习SSR|深入学习SSR , NextJS 一起重构 掘金!
文章图片

这个应该不用怎么介绍, 根据文件目录结构生成 路由 ,当然应该也可以手动配置
这可以直接集成 中间件反向代理
// 图中 [...args].js// Next.js API route support: https://nextjs.org/docs/api-routes/introduction const { createProxyMiddleware } = require('http-proxy-middleware'); export default createProxyMiddleware({ target: 'http://localhost:3300', changeOrigin: true, pathRewrite: { '^/api': '', }, }); // 解决转发Post问题 export const config = { api: { bodyParser: false, }, };

SSR 和 SSG 在Next 中使用 getServerSideProps (SSR)
这个方法只在server端执行 ,有一个context上下文,有如下基本参数
  • params:如果此页面使用动态路由,则params包含路由参数。如果页面名称是[id].js,params则将看起来像{ - id: ... }。要了解更多信息,请查看动态路由文档。
  • req:HTTP IncomingMessage对象。
  • res:HTTP响应对象。
  • query:代表查询字符串的对象。
  • preview:preview表示true页面是否处于预览模式,false否则。请参阅预览模式文档。
  • previewData:设置的预览数据setPreviewData。请参阅预览模式文档。
  • resolvedUrl:请求网址的规范化版本,该版本会剥离_next/data客户端转换的前缀,并包含原始查询值。
/ 每次请求到达后都会运行 // 仅在服务器端运行 // req, res, query export async function getServerSideProps({ query }: any) { const res = await Request.fetchAritcles(); return { props: { articles: res.list, total: res.list.length, }, }; }

一般发送接口请求服务,可以看下上面获取文章详情的部分代码,学会可以实现掘金列表
深入学习SSR|深入学习SSR , NextJS 一起重构 掘金!
文章图片

getStaticProps (SSG)
该方法参数基本 和 getServerSideProps 一样
和SSG概念一样直接生成 单独的 html 文件
export async function getStaticProps(context) { const res = await fetch(`https://.../data`) const data = https://www.it610.com/article/await res.json()if (!data) { return { notFound: true, } }return { props: { data }, // will be passed to the page component as props } }

Redux
集成 Redux 和基本的 React 项目相同,唯一的问题是需要判断 是否浏览器防止仓库重新构建和方法不存在问题
比如这样
import { createStore, applyMiddleware } from "redux"; import reducer from "./reducers"; import thunk from "redux-thunk"; import { composeWithDevTools } from "redux-devtools-extension"; import isBrowser from "../util/isBrowser"; let store; /** * 创建仓库的函数 * 该函数保证,如果是服务器端,每一次调用产生一个新的仓库 * 如果是客户端,每一次调用返回同一个仓库 * @param {*} initialState 仓库的初始值 */ export default function(initialState) { if (isBrowser()) { //客户端 if (!store) { store = create(initialState); } return store; //返回已有仓库 } return create(initialState); }function create(initialState) { return createStore( reducer, initialState, composeWithDevTools(applyMiddleware(thunk)) ); }

只构造一个仓库
掌握这项基本技能能实现基本的登录状态管理
深入学习SSR|深入学习SSR , NextJS 一起重构 掘金!
文章图片

Styles
  • 构建够有一个 全局的 globe.css
  • 样式使用的是css module
  • 集成scss 继续往下翻一翻 看配置
掌握这些还不够,还有很多坑等着你 服务端执行顺序
  • _app getServerSideProps()
  • page getServerSideProps()
  • _document getServerSideProps()
  • _app constructor()
  • _app render()
  • page constructor()
  • page render()
  • _document constructor()
  • _document render()
客户端执行顺序(首次打开页面)
  • _app constructor()
  • _app render()
  • page constructor()
  • page render()
路由跳转执行顺序
  • _app getServerSideProps()
  • page getServerSideProps()
  • _app render()
  • page constructor()
  • page render()
_app.js
import { Provider } from 'react-redux'; import Layout from '@/layout/default'; import '../styles/globals.scss'; import 'antd/dist/antd.min.css'; import store from '../store/index'; function MyApp({ Component, pageProps }) { return ( ); }export default MyApp;

提供了页面的propscomponent 便于构建模板等
假如你有些地方不需要 SSR 你得这样
// User 不使用 ssr const User = dynamic(import('./user'), { ssr: false, });

Config
我使用不多介绍一下,更多可以看官网
const path = require('path'); // 接入 scss module.exports = { sassOptions: { includePaths: [path.join(__dirname, 'styles')], }, }; // 接入 typescript module.exports = { typescript: { // !! WARN !! // Dangerously allow production builds to successfully complete even if // your project has type errors. // !! WARN !! ignoreBuildErrors: true, }, }; // 配置webpack module.exports = { pageExtensions: ['jsx', 'js', 'ts', 'tsx'], }; module.exports = { webpack: (config, { isServer }) => { config.resolve.alias['@'] = path.resolve(__dirname, './src'); return config; }, };

这些大概可以满足开发
【深入学习SSR|深入学习SSR , NextJS 一起重构 掘金!】掘金Pc 功能靠你们了 冲!
总结
  • Next 学习成本低,简单易用,无需自己配SSR,Github使用多
  • Next 缺点显而易见,规范多,按照规则,还得封装许多方法(小问题
  • 了解SSR 、 SSG 、 CSR 、预渲染
  • 掌握 Next 框架 学会开发 掘金PC 技术

    推荐阅读