NoteZ_技术博客 NoteZ_技术博客
🏠 首页
  • 📚 Web技术
  • 📋 Npm笔记
  • 📑 Markdown
  • 📄 Git笔记
  • 📝 Nginx文档
  • 📓 Linux文档
  • 📖 技术文档
  • 📜 其他文档
  • 🧊 NodeJs
  • 🎡 Express
  • 🔥 Rust
  • 🎉 Koa2
  • 🍃 MongoDB
  • 🐬 MySql
  • 🥦 Oracle
  • 🍁 Python
  • 🍄 JavaScript
  • 🌰 CSS
  • 🧄 HTML
  • 🥑 Canvas
  • 🌽 Nuxt
  • 🍆 React
  • 🥜 Vue
  • 🧅 TypeScript
  • 🌶️ AI
  • 📘 分类
  • 📗 标签
  • 📙 归档
⚜️ 在线编辑 (opens new window)
  • 📁 站点收藏
  • 📦 前端组件库
  • 📊 数据可视化
  • 🌈 开源插件
  • 🎗️ 关于我
  • 🔗 友情链接
GitHub (opens new window)

NoteZ_技术博客

前端界的小学生
🏠 首页
  • 📚 Web技术
  • 📋 Npm笔记
  • 📑 Markdown
  • 📄 Git笔记
  • 📝 Nginx文档
  • 📓 Linux文档
  • 📖 技术文档
  • 📜 其他文档
  • 🧊 NodeJs
  • 🎡 Express
  • 🔥 Rust
  • 🎉 Koa2
  • 🍃 MongoDB
  • 🐬 MySql
  • 🥦 Oracle
  • 🍁 Python
  • 🍄 JavaScript
  • 🌰 CSS
  • 🧄 HTML
  • 🥑 Canvas
  • 🌽 Nuxt
  • 🍆 React
  • 🥜 Vue
  • 🧅 TypeScript
  • 🌶️ AI
  • 📘 分类
  • 📗 标签
  • 📙 归档
⚜️ 在线编辑 (opens new window)
  • 📁 站点收藏
  • 📦 前端组件库
  • 📊 数据可视化
  • 🌈 开源插件
  • 🎗️ 关于我
  • 🔗 友情链接
GitHub (opens new window)
  • Express

  • Koa2

  • MongoDB

  • MySql

  • NodeJs

    • 2021 年值得使用的 Node.js 框架
    • CentOS 7上安装 Node.js 的 4 种方法
    • Linux 下安装 NodeJs 以及版本的升级和降级
    • node 中 path 模块 normalize 函数格式化路径
    • Node 中 使用 fs.stat() 读取文件状态
    • Node 中使用 compressing 压缩文件夹为 zip 文件
    • Node 中的实现 EventEmitter (event)方法
    • Node 代理 Demo 示例
    • node 判断文件或文件夹是否存在
    • node 复制文件的五种方式
    • Node 常用模块之 fs-extra
    • node 操作 mongodb 数据库备份与还原-示例代码
    • node 获取请求 ip
    • Node 读取和写入 json 文件
    • Node 递归或 mkdirp 创建多层目录
    • Node 遍历目录输出树形文件目录结构
    • Node(publish-sftp)命令上传本地文件到服务器
    • Node.js 中使用 compressing 实现zip文件的解压,解决文件名中文乱码
    • node.js 读取文件目录下的所有读取文件目录
    • Node.js把前台传来的 base64 码转成图片
    • nodeJs + vueJs 实现大文件分片上传
    • NodeJs 使用 jszip 或者 zip-dir 压缩文件夹(zip)
    • NodeJs 利用 jszip 压缩文件、文件夹,以及解压压缩文件中的文件
    • NodeJs 实现简单 WebSocket 即时通讯
    • NodeJs 框架 Express 的两种使用方式
    • NodeJs与Nginx获取客户端真实IP方法
    • NodeJs之大文件断点下载
    • NodeJS使用node-fetch下载文件并显示下载进度
    • NodeJs将图片进行压缩生成缩略图
    • nodejs递归读取所有文件
    • Node中复制文件的四种方法总结
    • node中的读取流createReadStream、写入流createWriteStream和管道流pipe
    • Node开发笔记
    • Node递归创建多层目录并写入文件
    • Node递归创建文件夹
    • PM2配置文件的使用、管理多个Node.js项目
    • 使用 koa-generator 快速搭建 Koa2 项目
    • 使用 pm2 部署 nodejs 项目
    • 使用 supervisor 自动重启 NodeJs 提高开发效率
    • 使用node反向代理解决跨域问题
    • 使用node实现保存(上传)图片的功能
    • 使用Node搭建超高压缩比的图片(webp)压缩服务
    • 前端部署从静态到node再到负载均衡
      • 前言
      • 准备工作
      • 静态站点的部署
        • 上传文件到服务器
        • 编写网站的 conf
        • 测试
      • node 项目的部署
        • 安装 pm2
        • 用 pm2 启动项目
        • nginx 代理绑定域名
      • 前端负载均衡部署
        • 一、应用服务器搭建服务站点
        • 通过 scp 或 rsync 命令将源码上传到 Centos2 服务器
        • ssh 进入 Centos2 服务器
        • 安装 pm2
        • pm2 启动站点
        • 二、搭建 Nginx Server
        • 三、实现负载均衡
        • 四、测试
        • 五、负载均衡的几种策略
      • 最后
      • 参考资料
    • 在 NodeJs 中使用 compressing 压缩和解压缩文件或文件夹
    • 在 nodejs 执行 shell 指令
    • 基于 Node 生成 vue 模板文件
    • 如何使用 Nodejs 备份 MongoDB 数据
    • 如何使用NodeJs实现base64和png文件相互转换
    • 如何在 Linux 服务器上使用 Nodejs 连接远程 Oracle 数据库
    • 混淆、编译 Node.js 源代码的几种方法
    • Node.js包之archiver压缩打包文件或目录为zip格式
    • node.js获取目录内所有文件大小总和
    • NodeJs 中使用 resize-img 制作缩略图
    • nodeJS中的http模块基本介绍
    • nodejs图片处理工具gm用法
    • node使用ffmpeg将swf转mp4 截取mp4视频第一帧为jpg图片
    • 如何在Node.js中执行shell命令
    • NodeJs 中复制(拷贝)文件或文件夹的多种方式
    • 在 NodeJs 中使用 archiver 压缩超大文件夹
    • Node.js 中使用 ssh2-sftp-client 上传文件并实时获取速率大小和进度
    • Node.js 中使用 ssh2-sftp-client 上传文件到服务器示例
    • node 使用 ssh2-sftp-client 实现 FTP 的文件上传和下载功能
    • ssh2-sftp-client 上传文件夹时获取上传速度和文件夹大小
    • 基于 node 使用 UDP 上传文件示例
    • 利用Node.js监控文件变化并使用sftp上传到服务器
    • 利用 Node 监控文件夹或文件夹变化可用的 npm 包汇总
    • NodeJS获取当前目录、运行文件所在目录、运行文件的上级目录
    • Node与GLIBC_2.27不兼容解决方案
  • Oracle

  • Rust

  • Python

  • 后端开发
  • NodeJs
NoteZ
2022-09-13
目录

前端部署从静态到node再到负载均衡

# 前言

相信很多前端同学对 vue 或 react 的开发很熟悉了,也知道如何去打包生成一个生产环境的包,但对于生产环境的部署可能有些同学了解比较少。小公司可能都是后端帮忙部署了,大公司会有专门的运维同学部署,对于生产环境的部署工作有些同学接触的不多,所以这次来分享和总结下前端项目部署相关的实战经验:从静态站点的部署,到 node 项目的部署,再到负载均衡的部署,顺便也会分享一下提高部署效率的脚本和方法。

# 准备工作

  • 一台或多台服务器或虚拟机。
  • 一份 vue 或 react 项目的打包后的文件。
  • 一份 node 项目的源码。

# 静态站点的部署

静态站点的部署指的是前端的 html/css/js 资源的部署,如 vue 或 react 打包后生成的 html,css 和 js 文件,我们将这些文件上传到服务器后,通过 Nginx 将这些资源暴露到公网上。

# 上传文件到服务器

就是将文件人工将打包后的文件拷贝到服务器上,这个很简单,但如果每次都是人工拷贝,部署效率未免会低了一些,所以建议使用一些脚本或工具。在大公司,一般对服务器权限控制得很严格,可能需要各种跳板机或动态密码等,而大公司一般都有专门的运维人员或者 CI/CD 工具。

小公司可能相对自由一些,可以允许个人直接 ssh 连接服务器,此时可以配合使用rsync或scp命令(Linux 或 Mac 系统)来一键上传文件到服务器上,给部署提效。在这里分享一下以前使用过的部署脚本,在前端项目根目录新建一个名为deploy.sh的文件:

#!/bin/bash

functiondeploy() {
  # 测试服务器
  test_host="root@test_server_ip"
  # 生产服务器
  prod_host="root@prod_server_ip"
  project_path="/srv/YourProject"

  if [ "$1" == "prod" ]; then
    target="$prod_host:$project_path"
  else
    target="$test_host:$project_path"
  fi

  rsync -azcuP ./dist/ --exclude node_modules --exclude coverage --exclude .env --exclude .nyc_output --exclude .git "$target"
  echo"deploy to $target"
}

deploy $@
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

以上脚本的意思是将/dist目录下的所有文件,上传到对应服务器的/srv/YourProject目录下。测试环境的部署是直接在根目录运行./deploy.sh,该命令会将/dist目录直接上传到root@test_server_ip服务器上;

生产环境的部署是在后面加一个参数./deploy.sh prod,这样可以实现多环境部署。更进一步的做法是将运行脚本的命令直接写进package.json中,如:

"scripts": {
  "build": "vue-cli-service build --mode staging",
  "deploy": "npm run build && ./deploy.sh",
  "deploy:prod": "npm run build && ./deploy.sh prod"
},
1
2
3
4
5

这样,通过npm run deploy命令就可以实现直接打包并部署到测试环境了。如果你的公司目前还在用人工拷贝或 FTP 工具这种低效的部署方式,不妨试一下用上面的脚本来提效哦。

注意

PS:由于 rsync 命令只在 Linux 或 Mac 才有,所以只有开发环境是 Linux 或 Mac 的用户才可以运行哦,Windows 用户是没法跑这个命令的。

# 编写网站的 conf

上传文件到服务器后,就可以着手配置 nginx 了。一般 nginx 的配置都会放在/etc/nginx/conf.d目录下,我们在该目录新建一个test.conf作为该项目的配置:

server {
    listen       80;
    server_name your-domain.com; # 域名
    location / {
        root   /srv/YourProject; # 网站源码根目录
        index index.html;
    }
    location /api {
        proxy_pass http://localhost:8080; # 反向代理后端接口地址
    }
}
1
2
3
4
5
6
7
8
9
10
11

一般来说,静态站点只需配置以上几个就可以了,

  • server_name表示域名,需要先解析到服务器的公网 ip;
  • root表示服务器中代码所在的位置,
  • index指明了默认的处理文件是index.html;
  • location /api是反向代理后端服务(这里假设了后端服务部署在本地 8080 端口),即your-domain.com/api的请求都会转发到http://localhost:8080上,一般用该方法可以完美解决前端跨域的问题。

修改 nginx 的 conf 后需要 reload 一下 nginx 服务:nginx -s reload

# 测试

如果上一步配置的域名是已经解析到服务器 ip 了的,就可以直接在公网上通过访问域名来访问你的站点了。如果不是,可以修改一下本机的 host 文件,使得配置的域名可以在本机访问;或者通过http://localhost来访问。

# node 项目的部署

node 项目在开发时可以用node app.js这样的命令来启动服务,但在服务器上如果使用这个命令,退出服务器后 node 进程就停止了,所以需要借助可以让 node 进程 keep alive 的工具。现在一般都是用pm2。

# 安装 pm2

npm install -g pm2
pm2 的一些常用命令:

pm2 start app.js              # 启动app.js应用程序
pm2 start app.js -i 4         # cluster mode 模式启动4个app.js的应用实例     # 4个应用程序会自动进行负载均衡
pm2 start app.js --name="api" # 启动应用程序并命名为 "api"
pm2 start app.js --watch      # 当文件变化时自动重启应用
pm2 start script.sh           # 启动 bash 脚本
pm2 list                      # 列表 PM2 启动的所有的应用程序
pm2 monit                     # 显示每个应用程序的CPU和内存占用情况
pm2 show [app-name]           # 显示应用程序的所有信息
pm2 logs                      # 显示所有应用程序的日志
pm2 logs [app-name]           # 显示指定应用程序的日志
pm2 flush
pm2 stop all                  # 停止所有的应用程序
pm2 stop 0                    # 停止 id为 0的指定应用程序
pm2 restart all               # 重启所有应用
pm2 reload all                # 重启 cluster mode下的所有应用
pm2 gracefulReload all        # Graceful reload all apps in cluster mode
pm2 delete all                # 关闭并删除所有应用
pm2 delete 0                  # 删除指定应用 id 0
pm2 scale api 10              # 把名字叫api的应用扩展到10个实例
pm2 reset [app-name]          # 重置重启数量
pm2 startup                   # 创建开机自启动命令
pm2 save                      # 保存当前应用列表
pm2 resurrect                 # 重新加载保存的应用列表
pm2 update                    # Save processes, kill PM2 and restore processes
pm2 generate                  # Generate a sample json configuration file
pm2 deploy app.json prod setup    # Setup "prod" remote server
pm2 deploy app.json prod          # Update "prod" remote server
pm2 deploy app.json prod revert 2 # Revert "prod" remote server by 2
pm2 module:generate [name]    # Generate sample module with name [name]
pm2 install pm2-logrotate     # Install module (here a log rotation system)
pm2 uninstall pm2-logrotate   # Uninstall module
pm2 publish                   # Increment version, git push and npm publish
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# 用 pm2 启动项目

一般来说,我们可以直接使用pm2 start app.js --name="my-project"这样的命令来启动 node 项目,但是这样手打的命令会不好管理,所以我们一般会在 node 项目的根目录下新建一个pm2.json文件来指定 pm2 启动时的参数,如:

{
  "name": "my-project",
  "script": "./server/index.js",
  "instances": 2,
  "cwd": ".",
  "exec_mode" : "cluster"
}
1
2
3
4
5
6
7

name 表示 pm2 进程的名称,script 表示启动的文件入口,instances 表示启动的示例数量(一般建议数值不大于服务器处理器的核数),cmd 表示应用程序所在的目录。

我们在服务器启动 node 项目时就可以直接pm2 start pm2.json。

# nginx 代理绑定域名

node 项目使用 pm2 运行后,只是运行在服务器的某个端口,如http://server_ip:3000,如果该服务需要通过域名直接访问,则还需要用 nginx 代理到 80 端口。在/etc/nginx/conf.d新建一个my-project.conf(文件命名随意哈,一般可以用网站域名.conf):

server {
    listen       80;
    server_name  your-domain.com;
    location / {
        proxy_pass http://localhost:3000;
    }
}
1
2
3
4
5
6
7

这是最简单的一个配置了,可以根据实际情况加一些参数哈。做完以上的步骤就完成了一个 node 项目的部署啦。

# 前端负载均衡部署

相信很少同学可以接触到负载均衡的部署了,当一个项目的访问量已经大到需要使用负载均衡的时候,一般都会有专门的运维同学去搞了。负载均衡说白了就是将大量的并发请求分担到多个服务器上。

负载均衡的架构有很多种,项目的架构是一个不断演进的过程,采用哪种负载均衡的架构需要具体问题具体分析,所以本文不会讲什么时候适合用哪种架构(笔者也不会,笑),接下来将会分享实战如何用 Nginx 从零搭建一个经典的负载均衡架构案例。

图片Nginx Server是直接暴露在最前端的机器,当用户发起请求,首先到达的是Nginx服务器,然后Nginx服务器再将请求通过某种算法分发到各个二级服务器上(图中的Centos2,Centos3,Centos4),此时Nginx Server充当的就是一个负载均衡的机器(Load Balancer)。

笔者手上没有这么多的服务器,为了更完整地演示,所以现在借助 VirtualBox 建立四个虚拟机来模拟四个服务器(当然条件确实限制时可以用同一个服务器的四个端口来代替)。

图片笔者新建了四个 Centos8 系统的虚拟机,用以假设 4 台服务器,对外都有独立 ip(假设分别是 192.168.0.1,2,3,4)。如前面的架构图所示,Centos1 将会是作为Nginx Server,充当最前端的负载均衡服务器,而其余的Centos2,Centos3,Centos4作为应用服务器,为用户提供真正的服务。接下来咱们一步一步去搭建这个系统。

# 一、应用服务器搭建服务站点

万丈高楼平地起,咱们首先得先搭建一个能对外的服务,这个服务可以是一个网站也可以是一个接口。为了简单起见,我们就直接起一个koa的Hello World,同时为了后面验证负载均衡的效果,每台机器上部署的代码都稍微改一下文案,如:Hello Centos2,Hello Centos3,Hello Centos4,这样方便后面验证用户的请求是被分发到了哪一台服务器。

koa 的 demo 站点已经为大家准备好了:koa-loadbalance[1]。

我们这里以Centos2(192.168.0.2)(ip 是虚构的)这台虚拟机为例,将会用pm2部署 koa 站点在该虚拟机上。

# 通过 scp 或 rsync 命令将源码上传到 Centos2 服务器

还记得上面的deploy.sh脚本吗?如果你添加了脚本在项目中,就可以npm run deploy直接部署到服务器上了。demo 源码中有这个脚本,大家可以改一下里面实际的 ip,再执行命令哈。

# ssh 进入 Centos2 服务器

ssh root@192.168.0.2
1

安装 node 环境

curl -sL https://rpm.nodesource.com/setup_13.x | sudo bash -
sudo yum install nodejs
1
2

可以在这里[2]看当前 node 有哪些版本,选最新的就行,现在是 13。

# 安装 pm2

npm i pm2 -g
1

# pm2 启动站点

在项目根目录执行:

pm2 start pm2.json
1

pm2 list 检查一下项目启动情况 ,同时用curl localhost:3000看返回值:

同理,按上面步骤给Centos3和Centos4服务器都将服务部署起来。(记得改一下index.js中的Hello XXX方便后面验证)。不出意外的话,我们的网站就分别运行在三台服务器的 3000 端口了:

192.168.0.2:3000 ==> Hello Centos2
192.168.0.3:3000 ==> Hello Centos3
192.168.0.4:3000 ==> Hello Centos4
1
2
3

:::info 有同学可能会问,为什么 Centos2,Centos3,Centos4 不用装 Nginx 的?在实际操作中,应用服务器其实是不用暴露在公网上的,它们与负载均衡服务器只需通过内网直接连接就可以了,这样更安全;而我们的站点又是 Node 项目,本身就可以提供 Web 服务,所以不用再装一个 Nginx 进行代理或转发了。 :::

# 二、搭建 Nginx Server

nginx 的安装方法:Nginx Install[3]。

在Centos1安装好 nginx 就可以了。

# 三、实现负载均衡

一开始还没了解过负载均衡时可能会觉得很难完全不知道是怎么配的,然后接下来你会发现超级简单,因为只需要 nginx 一个配置就可以了:upstream。

集群所有节点 我们将上面已经部署好的Centos2,Centos3,Centos4集群起来,nginx 配置类似下面这样:

upstream APPNAME {
    server host1:port;
    server host2:port;
}
1
2
3
4

APPNAME可以自定义,一般是项目名。在/etc/nginx/conf.d新建一个upstream.conf:

upstream koa-loadbalance {
    server 192.168.0.2:3000;
    server 192.168.0.3:3000;
    server 192.168.0.4:3000;
}
1
2
3
4
5

这样,我们已经将三台服务器集成为了http://koa-loadbalance的一个集群,下一步会使用它。

配置对外的站点 接下来是配置一个面向用户的网站了,我们假设网站会使用www.a.com这个域名,在/etc/nginx/conf.d下新建a.com.conf:

server {
    listen 80;
    server_name www.a.com;
    charset utf-8;
    location / {
        proxy_pass http://koa-loadbalance; # 这里是上面集群的名称
    }
}
1
2
3
4
5
6
7
8

配置结束后记得nginx -s reload重启一下,这样就完成负载均衡的配置了。

# 四、测试

如果你的域名是真实的且已经解析到 nginx 服务器,则此时可以直接通过域名访问了。由于笔者这里用的是虚拟机,公网不可访问,所以这里配置一下宿主机的 host,使得www.a.com指向centos1服务器,然后在浏览器打开www.a.com就可以测试我们的负载均衡网站啦。Mac 系统上是sudo vi /etc/hosts,在最后面添加一行:

# IP是Centos1的ip
192.168.0.1 www.a.com
1
2

图片我们在浏览器访问www.a.com,可以看到随着不断刷新,服务器返回了不同的Hello CentosX,说明我们的请求被分发到三台服务器上了,负载均衡的配置生效啦。

# 五、负载均衡的几种策略

nginx 的 upstream 可以设置很多种负载均衡的策略,以下介绍几个常用的策略。

轮询(默认):每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器 down 掉,能自动剔除。

upstream test {
    server 192.168.0.2:3000;
    server 192.168.0.3:3000;
    server 192.168.0.4:3000;
}
1
2
3
4
5

指定权重 weight:指定轮询几率,weight 和访问比率成正比,用于后端服务器性能不均的情况。

upstream test {
    server 192.168.0.2:3000 weight=5;
    server 192.168.0.3:3000 weight=10;
    server 192.168.0.4:3000 weight=20;
}
1
2
3
4
5

ip_hash:每个请求按访问 ip 的 hash 结果分配,这样每个访客固定访问一个后端服务器,可以解决 session 的问题。

upstream test {
    ip_hash;
    server 192.168.0.2:3000;
    server 192.168.0.3:3000;
    server 192.168.0.4:3000;
}
1
2
3
4
5
6

fair(第三方):按后端服务器的响应时间来分配请求,响应时间短的优先分配。

upstream test {
    server 192.168.0.2:3000;
    server 192.168.0.3:3000;
    server 192.168.0.4:3000;
    fair;
}
1
2
3
4
5
6

url_hash(第三方):按访问 url 的 hash 结果来分配请求,使每个 url 定向到同一个(对应的)后端服务器,后端服务器为缓存时比较有效。

upstream test {
    server 192.168.0.2:3000;
    server 192.168.0.3:3000;
    server 192.168.0.4:3000;
    hash $request_uri;
    hash_method crc32;
}
1
2
3
4
5
6
7

更多的策略请参考:ngx_http_upstream_module[4],根据实际情况使用上面的这些策略,没有特别需求就使用默认的轮询方式也可以。

# 最后

从静态站点到 node 站点,再到负载均衡,相信看完本文大家对整个前端的部署体系都有了一个比较全面的了解。特别是负载均衡,平时接触得少总觉得特别复杂,其实看完了会觉得很简单。更高级一些的部署可能会用上 Docker 或 k8s 的集群了,这个就留待后面再说啦。

对于部署方式的提效,本文也分享了一个使用rsync命令的脚步,配合package.json的 script,可以做到一个命令就完成部署的动作。

当然,该做法也还是有很大的优化空间的,真正好用的部署方式应该是持续集成,通过 Jenkins 或其他工具实现自动化部署,代码 push 上去就自动构建和部署了。如果你的公司还在用最原始的部署方式,不妨加把劲多探索一些这些更爽更溜的操作啦。

# 参考资料

[1] koa-loadbalance: https://github.com/fengxianqi/front_end-demos/tree/master/src/koa-loadbalance

[2] 这里: https://github.com/nodesource/distributions/tree/master/rpm

[3] Nginx Install: http://nginx.org/en/linux_packages.html#RHEL-CentOS

[4] ngx_http_upstream_module: http://nginx.org/en/docs/http/ngx_http_upstream_module.html#example

链接:https://mp.weixin.qq.com/s/ppxPF0HDKOnlkKs3bN_DlQ

#NodeJs
上次更新: 2024/01/30, 00:35:17
使用Node搭建超高压缩比的图片(webp)压缩服务
在 NodeJs 中使用 compressing 压缩和解压缩文件或文件夹

← 使用Node搭建超高压缩比的图片(webp)压缩服务 在 NodeJs 中使用 compressing 压缩和解压缩文件或文件夹→

最近更新
01
Gitea数据备份与还原
03-10
02
Linux 中使用 rsync 同步文件目录教程
03-10
03
Linux 使用 rsync 互相传输同步文件的简单步骤
03-08
更多文章>
Theme by Vdoing | Copyright © 2019-2025 NoteZ,All rights reserved | 冀ICP备2021027292号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式