前言
逃不开的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
服务端渲染(Server Side Rendering)
- 是指将单页应用(SPA)在服务器端渲染成 HTML 片段,发送到浏览器,然后交由浏览器为其绑定状态与事件,成为完全可交互页面的过程。(PS:本文中的 SSR 内容都是围绕同构应用来讲的)
- SSR 渲染流程:
服务端只负责首次“渲染”(真正意义上,只有浏览器才能渲染页面,服务端其实是生成 HTML 内容),然后返回给客户端,客户端接管页面交互(事件绑定等逻辑),之后客户端路由切换时,直接通过 JS 代码来显示对应的内容,不再需要服务端渲染(只有页面刷新时会需要)
SSG
静态页面生成(Static Stie Generation)
- 解决白屏问题、SEO问题。但无法生成用户相关内容(所以用户请求的结果都相同)。请求发生的过程中直接生成静态的HTML文件。(缺点以生成的文件难以达到自动更新,往往设置时间戳或者定时清理文件)
为什么要使用SSR
这个概念想必大家都知道
- 解决首屏渲染速度延迟问题
- 解决SEO搜索引擎抓取问题
不妨看下下面的图
这是CSR 的页面源码
这是SSR 的页面源码
为什么出现上述缺点,无非没有真实的HTML等导致
这就是为何要使用SSR的原因了,其实不用SSR使用预渲染也能解决SEO问题,但无法解决首屏渲染白屏的问题,从而导致用户体验的欠佳
预渲染 Prerender
- 简单解释,用户浏览器请求和SEO的蜘蛛爬去分别转发到不同的地址
以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
从上面 入手带你 学会具备掘金的开发功能
约定式路由
这个应该不用怎么介绍, 根据文件目录结构生成 路由 ,当然应该也可以手动配置
这可以直接集成 中间件反向代理
// 图中 [...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,
},
};
}
一般发送接口请求服务,可以看下上面获取文章详情的部分代码,学会可以实现掘金列表
getStaticProps (SSG)
该方法参数基本 和 getServerSideProps 一样
和SSG概念一样直接生成 单独的 html
文件
export async function getStaticProps(context) {
const res = await fetch(`https://.../data`)
const data = 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))
);
}
只构造一个仓库
掌握这项基本技能能实现基本的登录状态管理
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;
提供了页面的props
和 component
便于构建模板等
假如你有些地方不需要 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;
},
};
这些大概可以满足开发
掘金Pc 功能靠你们了 冲!
总结
- Next 学习成本低,简单易用,无需自己配SSR,Github使用多
- Next 缺点显而易见,规范多,按照规则,还得封装许多方法(
小问题
) - 了解SSR 、 SSG 、 CSR 、预渲染
- 掌握 Next 框架 学会开发 掘金PC 技术