1. 为什么要尝试SSR实践
- 项目有SEO需求
- 更适应于项目需求频繁变更,若采用传统的后端模版会使开发成本会更大,可以减轻后端开发成员负担
- 加快单页面应用首屏加载速度
- 方便前端接口服务端转发及独立部署
2. Next.js的使用
Next.js在SSR方面表现便捷而优越,这里选择其作为SSR技术框架,点击前往next.js官网
2.1 安装
yarn create next-app --typescript
2.2 脚手架默认添加的命令
yarn dev # 开发环境命令
yarn build # 打包
yarn start # 生产环境命令
2.3 初步尝试感悟
create next app的使用与create react app大致相同,此外Next还提供了一些内置组件如route、link、image、head、server等;如需添加项目配置,可编辑根目录next.config.js。
2.4 添加Less支持、Ant Mobile 支持
注:笔者使用的版本next 12+、 Ant Mobile 5+
添加依赖
# less需要用到的库
yarn add less less-loader next-with-less
# Ant Mobile需要用的库
yarn add antd-mobile@next
yarn add -D next-transpile-modules next-images
编辑根目录next.config.js如下
/** @type {import('next').NextConfig} */
const withLess = require("next-with-less")
const withImages = require('next-images')
const withTM = require('next-transpile-modules')([
'antd-mobile',
]);
// const lessToJS = require('less-vars-to-js');
// const themeVariables = lessToJS(fs.readFileSync(path.resolve(__dirname, './static/modifyVars.less'), 'utf8'))
module.exports = withLess({
lessLoaderOptions: {
// modifyVars: themeVariables, // make your antd custom effective
lessOptions: {
modifyVars: {
"@primary-color": "#ff0000",
}
},
},
reactStrictMode: true,
env: {
customKey: 'hello'
},
...withTM(withImages()),
});
vscode 引入less文件虽然开发环境可用,但是build会报错无法识别less后缀文件,而且import行会出现红色下划线,强迫症锦囊: 在项目目录/node_modules/next/types/global.d.ts中新增代码如下:
declare module '*.module.less' {
const classes: { readonly [key: string]: string }
export default classes
}
2.5 添加Antd
安装依赖,笔者安装时的最新版本:4.17.3
yarn add antd
默认是没有样式的,需要在pages/_app.ts中全局引入样式
import 'antd/dist/antd.css'
2.6 实现接口代理
接口代理常用来解决跨域问题以及提高安全性问题,接口代理需要启用Node服务,这里使用express作为服务框架
安装依赖
yarn add express http-proxy-middleware axios
在根目录添加server.js,添加内容如下
const express = require('express')
const next = require('next')
const { createProxyMiddleware } = require('http-proxy-middleware')
const devProxy = {
'/api/': {
target: 'http://xx.xx.xx.xx:8080',
pathRewrite: {
'^/api': ''
},
changeOrigin: true
}
}
const prodProxy = {
'/api/': {
target: 'http://xx.xx.xx.xx:8080',
pathRewrite: {
'^/api': ''
},
changeOrigin: true
}
}
const port = process.env.PORT || 3000
const hostanme = '0.0.0.0'
const dev = process.env.NODE_ENV !== 'production'
const app = next({
dev
})
const handle = app.getRequestHandler()
app.prepare()
.then(() => {
const server = express()
if (dev) {
!!devProxy && Object.keys(devProxy).forEach(context => {
server.use(createProxyMiddleware(context, devProxy[context]))
})
} else {
!!prodProxy && Object.keys(devProxy).forEach(context => {
server.use(createProxyMiddleware(context, prodProxy[context]))
})
}
server.all('*', (req, res) => {
handle(req, res)
})
server.listen(port, hostanme, err => {
if (err) {
throw err
}
console.log(`> Ready on http://${hostanme}:${port}`)
})
})
.catch((err) => {
console.log('An error occurred, unable to start the server')
console.log('发生错误,无法启动服务器')
console.log(err)
})
配置启用命令,在package.json新增内容如下
{
...
"scripts": {
"server": "node server.js",
"start": "next start",
"build": "next build",
"lint": "next lint"
},
...
}
接口代理使用
import axios from "axios";
const fetchData = async () => {
const res = await axios.get('/api/day1/student?id=1');
if(!res) return;
Toast.show({
icon: 'success',
content: res.data,
})
}
启用服务(yarn server)即可愉快的进行编码了
2.7 使用pm2守护进程
使用node启用服务需要占用一个终端窗口,而且很容易挂掉服务,这里推荐pm2用来守护进程
安装依赖
yarn global add pm2@latest
切换到项目根目录执行如下命令,即可开启守护进程
pm2 start server.js
# 默认在3000端口开启服务
添加pm2配置文件,在项目根目录执行pm2 ecosystem会生成一个ecosystem.config.js文件,编辑内容如下
module.exports = {
apps: [{
name: "demo02", // 应用名
script: 'server.js', // 入口文件
watch: false,
// 配置环境变量
env: {
"PORT": 3001,
"NODE_ENV": "development",
},
env_dev: {
"PORT": 3001,
"NODE_ENV": "development"
},
env_production: {
"PORT": 3000,
"NODE_ENV": "production",
}
}],
deploy: {
// 部署生产环境
production: {
user: 'root', // 服务器登录用户
host: '122.xx.xx.xx', // 服务器地址
ref: 'origin/main', // git发布分支
repo: 'https://github.com/debugksir/ssr_next_demo.git', // 项目git库地址
path: '/data/www/ssr_next', // 服务器路径
'pre-deploy-local': '', // 部署前本地命令
'post-deploy': 'git pull origin main && yarn start',
// 'post-deploy': 'git pull origin main && yarn && yarn build && pm2 reload ecosystem.config.js --env production',
'pre-setup': '' // 启动前命令
}
}
};
集成到项目命令集中,修改package.json如下,至此就可以使用yarn start来在服务器上部署生产服务啦
{
...
"scripts": {
"server": "node server.js",
"start": "yarn && next build && pm2 reload ecosystem.config.js --env production",
"deploy": "pm2 deploy ecosystem.config.js production",
"build": "next build",
"lint": "next lint"
},
...
}
yarn server : 开发模式
yarn start : 服务测一键部署
yarn deploy : 开发者测一键部署
到这里就可以使用以上命令来一键开发与部署自己的服务了
常用pm2命令如下:
pm2 -h # 查看帮助文档
pm2 list # 列举pm2启动的服务列表
pm2 start xxx # 启动服务
pm2 restart xxx # 重启服务
pm2 stop id # 停止服务
pm2 delete|del id # 停止并删除服务
pm2 kill # 杀掉所有进程
查看pm2 日志: pm2 logs
查看服务仪表盘:pm2 monit 或者 pm2 monitor 或者 pm2 plus
pm2生成配置文件: pm2 ecosystem
pm2启动命令: pm2 reload ecosystem.config.js --env development
pm2启动命令: pm2 reload ecosystem.config.js --env production (切记生产环境启动之前需要build)
使用Nginx
由于Nginx更擅长处理网络请求、反向代理、负载均衡、资源压缩等,所以使用Nginx处理它擅长的事物。点击查看在Mac安装Nginx
配置Nginx反向代理到Node服务
server {
listen 8080;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
proxy_pass http://127.0.0.1:3000; # 反向代理到node服务
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
配置好后重启Nginx(nginx -s reload),访问 http://本机ip:8080 查看结果吧!
如果是linux系统启动命令为(service nginx reload 或者 systemctl reload nginx.service)
补充
在nginx配置文件中添加如下代码开启静态资源代理:
location /_next/ {
alias /data/www/ssr_next/source/.next/;
autoindex on;
}
开启压缩开关:
gzip on;
完整的nginx配置
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
gzip on;
server {
listen 8080;
location / {
proxy_pass http://127.0.0.1:3000;
}
location /_next/ {
alias /data/www/ssr_next/source/.next/;
autoindex on;
}
}
}
源码:
点击查看源码