1. 为什么要尝试SSR实践

  • 项目有SEO需求
  • 更适应于项目需求频繁变更,若采用传统的后端模版会使开发成本会更大,可以减轻后端开发成员负担
  • 加快单页面应用首屏加载速度
  • 方便前端接口服务端转发及独立部署

2. Next.js的使用

Next.js在SSR方面表现便捷而优越,这里选择其作为SSR技术框架,点击前往next.js官网

2.1 安装

  1. yarn create next-app --typescript

2.2 脚手架默认添加的命令

  1. yarn dev # 开发环境命令
  2. yarn build # 打包
  3. 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+

添加依赖

  1. # less需要用到的库
  2. yarn add less less-loader next-with-less
  3. # Ant Mobile需要用的库
  4. yarn add antd-mobile@next
  5. yarn add -D next-transpile-modules next-images

编辑根目录next.config.js如下

  1. /** @type {import('next').NextConfig} */
  2. const withLess = require("next-with-less")
  3. const withImages = require('next-images')
  4. const withTM = require('next-transpile-modules')([
  5. 'antd-mobile',
  6. ]);
  7. // const lessToJS = require('less-vars-to-js');
  8. // const themeVariables = lessToJS(fs.readFileSync(path.resolve(__dirname, './static/modifyVars.less'), 'utf8'))
  9. module.exports = withLess({
  10. lessLoaderOptions: {
  11. // modifyVars: themeVariables, // make your antd custom effective
  12. lessOptions: {
  13. modifyVars: {
  14. "@primary-color": "#ff0000",
  15. }
  16. },
  17. },
  18. reactStrictMode: true,
  19. env: {
  20. customKey: 'hello'
  21. },
  22. ...withTM(withImages()),
  23. });

vscode 引入less文件虽然开发环境可用,但是build会报错无法识别less后缀文件,而且import行会出现红色下划线,强迫症锦囊: 在项目目录/node_modules/next/types/global.d.ts中新增代码如下:

  1. declare module '*.module.less' {
  2. const classes: { readonly [key: string]: string }
  3. export default classes
  4. }

2.5 添加Antd

安装依赖,笔者安装时的最新版本:4.17.3

  1. yarn add antd

默认是没有样式的,需要在pages/_app.ts中全局引入样式

  1. import 'antd/dist/antd.css'

2.6 实现接口代理

接口代理常用来解决跨域问题以及提高安全性问题,接口代理需要启用Node服务,这里使用express作为服务框架

安装依赖

  1. yarn add express http-proxy-middleware axios

在根目录添加server.js,添加内容如下

  1. const express = require('express')
  2. const next = require('next')
  3. const { createProxyMiddleware } = require('http-proxy-middleware')
  4. const devProxy = {
  5. '/api/': {
  6. target: 'http://xx.xx.xx.xx:8080',
  7. pathRewrite: {
  8. '^/api': ''
  9. },
  10. changeOrigin: true
  11. }
  12. }
  13. const prodProxy = {
  14. '/api/': {
  15. target: 'http://xx.xx.xx.xx:8080',
  16. pathRewrite: {
  17. '^/api': ''
  18. },
  19. changeOrigin: true
  20. }
  21. }
  22. const port = process.env.PORT || 3000
  23. const hostanme = '0.0.0.0'
  24. const dev = process.env.NODE_ENV !== 'production'
  25. const app = next({
  26. dev
  27. })
  28. const handle = app.getRequestHandler()
  29. app.prepare()
  30. .then(() => {
  31. const server = express()
  32. if (dev) {
  33. !!devProxy && Object.keys(devProxy).forEach(context => {
  34. server.use(createProxyMiddleware(context, devProxy[context]))
  35. })
  36. } else {
  37. !!prodProxy && Object.keys(devProxy).forEach(context => {
  38. server.use(createProxyMiddleware(context, prodProxy[context]))
  39. })
  40. }
  41. server.all('*', (req, res) => {
  42. handle(req, res)
  43. })
  44. server.listen(port, hostanme, err => {
  45. if (err) {
  46. throw err
  47. }
  48. console.log(`> Ready on http://${hostanme}:${port}`)
  49. })
  50. })
  51. .catch((err) => {
  52. console.log('An error occurred, unable to start the server')
  53. console.log('发生错误,无法启动服务器')
  54. console.log(err)
  55. })

配置启用命令,在package.json新增内容如下

  1. {
  2. ...
  3. "scripts": {
  4. "server": "node server.js",
  5. "start": "next start",
  6. "build": "next build",
  7. "lint": "next lint"
  8. },
  9. ...
  10. }

接口代理使用

  1. import axios from "axios";
  2. const fetchData = async () => {
  3. const res = await axios.get('/api/day1/student?id=1');
  4. if(!res) return;
  5. Toast.show({
  6. icon: 'success',
  7. content: res.data,
  8. })
  9. }

启用服务(yarn server)即可愉快的进行编码了

2.7 使用pm2守护进程

使用node启用服务需要占用一个终端窗口,而且很容易挂掉服务,这里推荐pm2用来守护进程

安装依赖

  1. yarn global add pm2@latest

切换到项目根目录执行如下命令,即可开启守护进程

  1. pm2 start server.js
  2. # 默认在3000端口开启服务

添加pm2配置文件,在项目根目录执行pm2 ecosystem会生成一个ecosystem.config.js文件,编辑内容如下

  1. module.exports = {
  2. apps: [{
  3. name: "demo02", // 应用名
  4. script: 'server.js', // 入口文件
  5. watch: false,
  6. // 配置环境变量
  7. env: {
  8. "PORT": 3001,
  9. "NODE_ENV": "development",
  10. },
  11. env_dev: {
  12. "PORT": 3001,
  13. "NODE_ENV": "development"
  14. },
  15. env_production: {
  16. "PORT": 3000,
  17. "NODE_ENV": "production",
  18. }
  19. }],
  20. deploy: {
  21. // 部署生产环境
  22. production: {
  23. user: 'root', // 服务器登录用户
  24. host: '122.xx.xx.xx', // 服务器地址
  25. ref: 'origin/main', // git发布分支
  26. repo: 'https://github.com/debugksir/ssr_next_demo.git', // 项目git库地址
  27. path: '/data/www/ssr_next', // 服务器路径
  28. 'pre-deploy-local': '', // 部署前本地命令
  29. 'post-deploy': 'git pull origin main && yarn start',
  30. // 'post-deploy': 'git pull origin main && yarn && yarn build && pm2 reload ecosystem.config.js --env production',
  31. 'pre-setup': '' // 启动前命令
  32. }
  33. }
  34. };

集成到项目命令集中,修改package.json如下,至此就可以使用yarn start来在服务器上部署生产服务啦

  1. {
  2. ...
  3. "scripts": {
  4. "server": "node server.js",
  5. "start": "yarn && next build && pm2 reload ecosystem.config.js --env production",
  6. "deploy": "pm2 deploy ecosystem.config.js production",
  7. "build": "next build",
  8. "lint": "next lint"
  9. },
  10. ...
  11. }

yarn server : 开发模式
yarn start : 服务测一键部署
yarn deploy : 开发者测一键部署
到这里就可以使用以上命令来一键开发与部署自己的服务了

常用pm2命令如下:

  1. pm2 -h # 查看帮助文档
  2. pm2 list # 列举pm2启动的服务列表
  3. pm2 start xxx # 启动服务
  4. pm2 restart xxx # 重启服务
  5. pm2 stop id # 停止服务
  6. pm2 delete|del id # 停止并删除服务
  7. pm2 kill # 杀掉所有进程
  8. 查看pm2 日志: pm2 logs
  9. 查看服务仪表盘:pm2 monit 或者 pm2 monitor 或者 pm2 plus
  10. pm2生成配置文件: pm2 ecosystem
  11. pm2启动命令: pm2 reload ecosystem.config.js --env development
  12. pm2启动命令: pm2 reload ecosystem.config.js --env production (切记生产环境启动之前需要build)

使用Nginx

由于Nginx更擅长处理网络请求、反向代理、负载均衡、资源压缩等,所以使用Nginx处理它擅长的事物。点击查看在Mac安装Nginx

配置Nginx反向代理到Node服务

  1. server {
  2. listen 8080;
  3. server_name localhost;
  4. #charset koi8-r;
  5. #access_log logs/host.access.log main;
  6. location / {
  7. root html;
  8. index index.html index.htm;
  9. proxy_pass http://127.0.0.1:3000; # 反向代理到node服务
  10. }
  11. #error_page 404 /404.html;
  12. # redirect server error pages to the static page /50x.html
  13. #
  14. error_page 500 502 503 504 /50x.html;
  15. location = /50x.html {
  16. root html;
  17. }
  18. }

配置好后重启Nginx(nginx -s reload),访问 http://本机ip:8080 查看结果吧!
如果是linux系统启动命令为(service nginx reload 或者 systemctl reload nginx.service)

补充

在nginx配置文件中添加如下代码开启静态资源代理:

  1. location /_next/ {
  2. alias /data/www/ssr_next/source/.next/;
  3. autoindex on;
  4. }

开启压缩开关:
gzip on;

完整的nginx配置

  1. #user nobody;
  2. worker_processes 1;
  3. #error_log logs/error.log;
  4. #error_log logs/error.log notice;
  5. #error_log logs/error.log info;
  6. #pid logs/nginx.pid;
  7. events {
  8. worker_connections 1024;
  9. }
  10. http {
  11. include mime.types;
  12. default_type application/octet-stream;
  13. sendfile on;
  14. keepalive_timeout 65;
  15. gzip on;
  16. server {
  17. listen 8080;
  18. location / {
  19. proxy_pass http://127.0.0.1:3000;
  20. }
  21. location /_next/ {
  22. alias /data/www/ssr_next/source/.next/;
  23. autoindex on;
  24. }
  25. }
  26. }

源码:

点击查看源码