Keepalived + LVS (DR) + Nginx + NFS 高可用 Web 集群部署实战手册

一、项目背景

1.1 业务场景与核心需求

在企业数字化转型进程中,Web 服务作为业务对外交付的核心载体,其高可用性、高并发承载能力、数据一致性是保障业务稳定运行的关键要素。以电商平台、企业官网、在线教育等典型场景为例,需满足以下核心诉求:

  • 高可用保障:Web 服务需实现 7×24 小时不间断运行,规避服务器宕机、网络中断等单点故障引发的业务中断问题;尤其在促销、峰值访问时段,服务不可用将直接造成经济损失或用户流失。
  • 高并发承载:面对日均 PV 从 10 万级向百万级增长的访问量,单台 Web 服务器的 CPU、内存、网络带宽易形成性能瓶颈;需通过负载均衡分摊请求压力,确保 95% 的请求响应时间<1 秒。
  • 数据一致性:Web 服务涉及的静态资源(HTML、CSS、图片、视频)与动态业务数据(用户上传文件、订单记录),需在多台 Web 服务器间共享,避免 "不同服务器展示内容不一致" 的业务异常。
  • 易维护与扩展性:业务增长过程中,需支持快速新增 Web 节点以扩展集群能力,同时降低运维复杂度,避免重复部署资源、手动同步配置的低效操作。

1.2 传统架构的痛点与局限

在采用「Keepalived + LVS (DR) + Nginx + NFS」方案前,多数企业采用 "单 Web 服务器" 或 "简单负载均衡" 架构,存在以下难以解决的问题:

1.2.1 单点故障风险高
  • 传统 "单台 Nginx 服务器 + 本地存储" 架构中,服务器硬件故障(硬盘损坏、电源故障)或软件异常(Nginx 进程崩溃)会直接导致服务完全不可用;MTTR(平均恢复时间)依赖人工干预,通常超过 30 分钟,无法满足业务连续性要求。
  • 即便采用 "2 台 Nginx 服务器 + 简单 DNS 轮询",若其中一台服务器宕机,DNS 缓存会导致部分用户仍被解析至故障节点;且 DNS 轮询无法感知服务器负载状态,易出现 "故障节点持续接收请求" 或 "高负载节点被分配更多请求" 的问题。
1.2.2 并发承载能力不足
  • 单台 Nginx 服务器受限于 CPU 核心数(如 4 核 8G 服务器仅能稳定承载 2000-3000 并发连接),访问量峰值超过阈值时,会出现请求排队、页面超时、503 错误等问题。
  • 仅通过 "增加服务器数量" 扩展时,缺乏高效的负载均衡机制,无法将请求合理分配至各节点,导致资源浪费(部分服务器空闲)与性能瓶颈(部分服务器过载)并存。
1.2.3 数据共享与一致性难题
  • 多台 Nginx 服务器采用 "本地存储静态资源" 时,需通过 rsync 等脚本定期同步资源,但同步延迟易导致 "用户访问不同节点看到不同版本内容"(如首页图片更新后,部分节点仍展示旧图)。
  • 动态数据(用户上传的头像、订单附件)若存储在本地,无法在多节点间共享,引发 "用户在 A 节点上传文件后,切换至 B 节点无法查看" 的业务异常。
1.2.4 运维效率低下
  • 每台 Web 服务器需单独部署 Nginx 配置、静态资源、业务代码,新增节点时运维人员需重复操作,耗时且易出错(如配置文件漏改、资源版本不一致)。
  • 缺乏统一的资源管理机制,静态资源更新(如 CSS 样式调整、图片替换)时需逐台服务器修改,运维成本随节点数量增加呈线性上升。

1.3 技术方案的选型逻辑

针对上述痛点,需构建 "高可用负载均衡 + 共享存储 + Web 服务集群" 一体化架构,「Keepalived + LVS (DR) + Nginx + NFS」组合的选型逻辑如下:

1.3.1 解决高可用与负载均衡
  • LVS(Direct Routing 模式)作为四层负载均衡器,具备超高并发承载能力(单机可支撑 10 万 + 并发连接);DR 模式避免 "请求回程流量" 占用带宽,保障转发效率。
  • Keepalived 通过 VRRP 协议实现 LVS 主备高可用,主节点故障时,备节点可在 1-3 秒内自动接管虚拟 IP(VIP),实现 "无感知切换",彻底消除负载均衡层单点故障。
1.3.2 保障 Web 服务稳定性
  • Nginx 作为成熟的 Web 服务器,兼容性强、配置灵活,可稳定运行 PHP、Python 等动态业务代码;同时通过 mod_cache、mod_gzip 等模块优化静态资源访问性能。
  • 多台 Nginx 组成集群,通过 LVS 分摊请求压力;单节点故障时,LVS 自动将请求转发至其他健康节点,保障服务连续性。
1.3.3 实现数据一致性与共享
  • NFS(网络文件系统)作为共享存储,将所有 Web 服务器的静态资源目录(/images、/css)与动态上传目录(/uploads)挂载至 NFS 服务器,实现 "多节点访问同一存储资源",彻底解决数据同步问题。
  • NFS 支持权限控制与读写分离(可选配置),可保障资源访问安全性与存储性能。
1.3.4 降低运维复杂度
  • 架构模块化设计,各组件职责清晰(LVS 负责转发、Nginx 负责服务、NFS 负责存储),便于故障定位与单独扩展。
  • 新增 Web 节点时,仅需安装 Nginx 并挂载 NFS 目录,无需重复部署资源,运维效率提升 80% 以上。

1.4 项目价值与预期目标

部署「Keepalived + LVS (DR) + Nginx + NFS」架构后,预期实现以下业务与技术价值:

  • 业务连续性:Web 服务可用性从 99.9% 提升至 99.99%(年均停机时间从 8.76 小时降至 52.56 分钟),核心业务场景(电商促销、在线考试)无服务中断风险。
  • 性能提升:并发承载能力从单台服务器 3000 并发提升至集群 10 万 + 并发,页面响应时间稳定在 500ms 以内,用户体验显著优化。
  • 运维效率:资源部署与更新效率提升 80%,新增节点时间从 2 小时缩短至 15 分钟,减少重复人工操作。
  • 扩展性:支持 Web 节点与 NFS 存储独立扩展(如新增 Nginx 节点提升并发、扩容 NFS 存储容量),满足业务 3-5 年增长需求。

二、项目实践

2.1 项目环境

主机名 net1 net8 net2 VIP 服务器角色
client2 10.1.1.21 外部客户端
router 10.1.1.20 10.1.8.20 10.1.2.20 出口路由器
client1 10.1.8.21 内部客户端
web1 10.1.8.11 10.1.2.11 10.1.8.100 Web 服务器
web2 10.1.8.12 10.1.2.12 10.1.8.100 Web 服务器
web3 10.1.8.13 10.1.2.13 10.1.8.100 Web 服务器
ha1 10.1.8.14 10.1.8.100 HA 和 LVS 服务器
ha2 10.1.8.15 10.1.8.100 HA 和 LVS 服务器
nfs 10.1.2.100 存储服务器
网络说明
  1. 所有主机网卡命名规则:第一块网卡为 ens32,第二块为 ens33,第三块为 ens34(注:原文笔误 "第二块网卡名为 ens34",已修正表述)。

  2. 网卡模式:默认第一块网卡为 NAT 模式,第二块为 Host-Only 模式。

  3. 网关配置规则:

    • 10.1.1.0/24 网段网关为 10.1.1.20;
    • 10.1.2.0/24 网段网关为 10.1.2.20;
    • 10.1.8.0/24 网段网关为 10.1.8.20;
    • 路由器自身网关为 10.1.8.2。

2.2 基础配置

基础配置包含主机名、IP 地址、网关的标准化配置,以下为网关配置的参考命令(需根据不同网段适配):

bash 复制代码
## 10.1.1.0/24 网段网关配置(适配对应网卡)
nmcli connection modify ens33 ipv4.gateway 10.1.1.20
nmcli connection up ens33

## 10.1.2.0/24 网段网关配置(适配对应网卡)
nmcli connection modify ens33 ipv4.gateway 10.1.2.20
nmcli connection up ens33

## 10.1.8.0/24 网段网关配置(适配对应网卡)
nmcli connection modify ens33 ipv4.gateway 10.1.8.20
nmcli connection up ens33

2.3 路由器(router)配置

路由器核心作用是开启 IP 转发与防火墙伪装,保障跨网段通信正常:

bash 复制代码
## 开启IP转发功能(永久生效)
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
## 备选方案:修改现有配置项
# sed -i "s/ip_forward=0/ip_forward=1/g" /etc/sysctl.conf
sysctl -p  # 加载配置生效

# 启动防火墙并开启地址伪装(保障NAT转发)
systemctl enable firewalld.service --now
firewall-cmd --add-masquerade  # 临时生效
firewall-cmd --add-masquerade --permanent  # 永久生效

配置免密登录-(参考)

bash 复制代码
[root@client ~]# 
# 关闭SSH主机密钥检查(避免交互提示)
echo 'StrictHostKeyChecking no' >> /etc/ssh/ssh_config
# 生成RSA密钥对(无密码)
ssh-keygen -t rsa -f .ssh/id_rsa -N ''
# 批量分发公钥至目标节点(需替换server列表为实际主机名)
for host in server server{1..5}; do ssh-copy-id root@$host; done
# 同步SSH配置至目标节点
for host in server server{1..5}; do scp /etc/ssh/ssh_config root@$host:/etc/ssh/ssh_config; done
# 验证免密登录(执行hostname命令)
for host in server server{1..5}; do ssh root@$host hostname; done

## 同步/etc/hosts文件(统一主机名解析)
for host in server server{1..5}; do scp /etc/hosts root@$host:/etc/hosts; done

2.5 NFS 服务器配置

NFS 服务器用于提供共享存储,保障所有 Web 节点资源一致性,操作步骤如下:

bash 复制代码
## 安装NFS服务依赖包
[root@nfs ~ 10:52:45]# yum install -y nfs-utils

## 准备共享目录与测试文件
[root@nfs ~ 11:37:01]# mkdir /usr/share/nginx/html -p  # 创建Nginx默认站点目录
[root@nfs ~ 11:37:16]# echo Welcome to www.liu.cloud > /usr/share/nginx/html/index.html  # 写入测试内容

## 配置NFS共享规则(仅允许10.1.2.0/24网段读写、同步访问)
[root@nfs ~ 11:37:27]# echo '/usr/share/nginx/html 10.1.2.0/24(rw,sync)' >> /etc/exports

## 启用并启动NFS服务
[root@nfs ~ 11:37:40]# systemctl enable nfs-server.service --now
Created symlink from /etc/systemd/system/multi-user.target.wants/nfs-server.service to /usr/lib/systemd/system/nfs-service

2.6 Web 节点(web1/web2/web3)基础配置

Web 节点核心部署 Nginx 服务,先完成基础安装与测试,后续挂载 NFS 共享目录:

bash 复制代码
## Web1节点配置
[root@web1 ~ 11:37:44]# yum install -y nginx  # 安装Nginx
[root@web1 ~ 11:38:03]# echo Welcome to $(hostname) > /usr/share/nginx/html/index.html  # 写入节点标识
[root@web1 ~ 11:38:59]# systemctl enable nginx.service --now  # 开机自启并启动服务
Created symlink from /etc/systemd/system/multi-user.target.wants/nginx.service to /usr/lib/systemd/system/nginx.service.

## Web2节点配置
[root@web2 ~ 11:27:56]# yum install -y nginx
[root@web2 ~ 11:38:16]# echo Welcome to $(hostname) > /usr/share/nginx/html/index.html 
[root@web2 ~ 11:38:54]# systemctl enable nginx.service --now
Created symlink from /etc/systemd/system/multi-user.target.wants/nginx.service to /usr/lib/systemd/system/nginx.service.

## Web3节点配置
[root@web3 ~ 11:26:17]# yum install -y nginx
[root@web3 ~ 11:38:30]# echo Welcome to $(hostname) > /usr/share/nginx/html/index.html 
[root@web3 ~ 11:38:48]# systemctl enable nginx.service --now
Created symlink from /etc/systemd/system/multi-user.target.wants/nginx.service to /usr/lib/systemd/system/nginx.service.

## 验证Web节点基础访问(内部客户端测试)
[root@client1 ~ 11:31:04]# curl 10.1.8.11
Welcome to web1.liu.cloud
[root@client1 ~ 11:39:20]# curl 10.1.8.12
Welcome to web2.liu.cloud
[root@client1 ~ 11:39:22]# curl 10.1.8.13
Welcome to web3.liu.cloud

2.7 LVS-RS(Web 节点)配置

所有 Web 节点需配置虚拟网卡与 ARP 参数,适配 LVS DR 模式的网络转发规则:

bash 复制代码
## 步骤1:添加dummy虚拟网卡并配置VIP(10.1.8.100/32)
# Web1节点
[root@web1 ~ 11:39:00]# nmcli connection add type dummy ifname dummy con-name dummy ipv4.method manual ipv4.addresses 10.1.8.100/32
连接 "dummy" (1ca3da5d-9d7f-420a-9c12-a15265658a33) 已成功添加。
[root@web1 ~ 11:44:55]# nmcli connection up dummy
连接已成功激活(D-Bus 活动路径:/org/freedesktop/NetworkManager/ActiveConnection/12)。

# Web2节点
[root@web2 ~ 11:38:55]# nmcli connection add type dummy ifname dummy con-name dummy ipv4.method manual ipv4.addresses 10.1.8.100/32
连接 "dummy" (159f18d9-dc94-419f-8aa1-2c5ab1426ffd) 已成功添加。
[root@web2 ~ 11:45:06]# nmcli connection up dummy
连接已成功激活(D-Bus 活动路径:/org/freedesktop/NetworkManager/ActiveConnection/10)。

# Web3节点
[root@web3 ~ 11:38:49]# nmcli connection add type dummy ifname dummy con-name dummy ipv4.method manual ipv4.addresses 10.1.8.100/32
连接 "dummy" (66182c41-8a9a-4f9a-88a5-247b881a0d54) 已成功添加。
[root@web3 ~ 11:45:14]# nmcli connection up dummy
连接已成功激活(D-Bus 活动路径:/org/freedesktop/NetworkManager/ActiveConnection/10)。

## 步骤2:配置ARP参数(关闭dummy网卡ARP解析,避免VIP冲突)
# Web1节点
[root@web1 ~ 11:44:57]# cat >> /etc/sysctl.conf << EOF
> net.ipv4.conf.all.arp_ignore = 1
> net.ipv4.conf.all.arp_announce = 2
> net.ipv4.conf.dummy.arp_ignore = 1
> net.ipv4.conf.dummy.arp_announce = 2
> EOF
[root@web1 ~ 11:45:43]# sysctl -p  # 加载ARP配置
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
net.ipv4.conf.dummy.arp_ignore = 1
net.ipv4.conf.dummy.arp_announce = 2

# Web2节点
[root@web2 ~ 11:45:08]# cat >> /etc/sysctl.conf << EOF
> net.ipv4.conf.all.arp_ignore = 1
> net.ipv4.conf.all.arp_announce = 2
> net.ipv4.conf.dummy.arp_ignore = 1
> net.ipv4.conf.dummy.arp_announce = 2
> EOF
[root@web2 ~ 11:45:36]# sysctl -p
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
net.ipv4.conf.dummy.arp_ignore = 1
net.ipv4.conf.dummy.arp_announce = 2

# Web3节点
[root@web3 ~ 11:45:15]# cat >> /etc/sysctl.conf << EOF
> net.ipv4.conf.all.arp_ignore = 1
> net.ipv4.conf.all.arp_announce = 2
> net.ipv4.conf.dummy.arp_ignore = 1
> net.ipv4.conf.dummy.arp_announce = 2
> EOF
[root@web3 ~ 11:45:31]# sysctl -p
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
net.ipv4.conf.dummy.arp_ignore = 1
net.ipv4.conf.dummy.arp_announce = 2

2.8 Web 节点 NFS 挂载配置

将 Web 节点的 Nginx 站点目录挂载至 NFS 共享目录,实现资源统一管理:

bash 复制代码
## Web1节点NFS挂载(时间戳:12:24:45 - 12:25:42)
[root@web1 ~ 12:24:45]# yum install -y nfs-utils  # 安装NFS客户端
[root@web1 ~ 12:24:52]# echo '10.1.2.100:/usr/share/nginx/html /usr/share/nginx/html nfs defaults 0 0' >> /etc/fstab  # 写入挂载规则
[root@web1 ~ 12:25:15]# systemctl daemon-reload  # 重新加载系统配置
[root@web1 ~ 12:25:32]# mount -a  # 执行挂载
[root@web1 ~ 12:25:42]# df /usr/share/nginx/html  # 验证挂载结果
文件系统                            1K-块    已用     可用 已用% 挂载点
10.1.2.100:/usr/share/nginx/html 52403200 1641472 50761728    4% /usr/share/nginx/html

## Web2节点NFS挂载(时间戳:11:45:55 - 12:25:47)
[root@web2 ~ 11:45:55]# yum install -y nfs-utils
[root@web2 ~ 12:24:56]# echo '10.1.2.100:/usr/share/nginx/html /usr/share/nginx/html nfs defaults 0 0' >> /etc/fstab
[root@web2 ~ 12:25:20]# systemctl daemon-reload
[root@web2 ~ 12:25:27]# mount -a
[root@web2 ~ 12:25:47]# df /usr/share/nginx/html
文件系统                            1K-块    已用     可用 已用% 挂载点
10.1.2.100:/usr/share/nginx/html 52403200 1641472 50761728    4% /usr/share/nginx/html

## Web3节点NFS挂载(时间戳:11:45:59 - 12:25:54)
[root@web3 ~ 11:45:59]# yum install -y nfs-utils
[root@web3 ~ 12:25:01]# echo '10.1.2.100:/usr/share/nginx/html /usr/share/nginx/html nfs defaults 0 0' >> /etc/fstab
[root@web3 ~ 12:25:11]# systemctl daemon-reload
[root@web3 ~ 12:25:36]# mount -a
[root@web3 ~ 12:25:54]# df /usr/share/nginx/html
文件系统                            1K-块    已用     可用 已用% 挂载点
10.1.2.100:/usr/share/nginx/html 52403200 1641472 50761728    4% /usr/share/nginx/html

2.9 HA 和 LVS-DS 配置(ha1/ha2)

2.9.1 ha1 节点(主节点)配置
bash 复制代码
## 步骤1:安装依赖包(时间戳:11:28:03)
[root@ha1 ~ 11:28:03]# yum install -y keepalived ipvsadm

## 步骤2:备份并编辑Keepalived配置文件(时间戳:11:46:54 - 11:47:11)
[root@ha1 ~ 11:46:54]# cp /etc/keepalived/keepalived.conf {,.bak}  # 备份原有配置
[root@ha1 ~ 11:47:11]# vim /etc/keepalived/keepalived.conf  # 编辑配置文件
bash 复制代码
! Configuration File for keepalived

global_defs {
   router_id ha1
}

vrrp_instance web {
    state MASTER
    interface ens33
    virtual_router_id 51
    priority 110
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        10.1.8.100/24
    }
}

virtual_server 10.1.8.100 80 {
    delay_loop 6
    lb_algo rr
    lb_kind DR
    protocol TCP

    real_server 10.1.8.11 80 {
        weight 1
        TCP_CHECK {
            connect_timeout 3
            retry 3
            delay_before_retry 3
        }
    }
    real_server 10.1.8.12 80 {
        weight 2
        TCP_CHECK {
            connect_timeout 3
            retry 3
            delay_before_retry 3
        }
    }
    real_server 10.1.8.13 80 {
        weight 2
        TCP_CHECK {
            connect_timeout 3
            retry 3
            delay_before_retry 3
        }
    }
}
bash 复制代码
## 步骤3:启动Keepalived服务(时间戳:12:15:35)
[root@ha1 ~ 12:15:35]# systemctl enable keepalived.service --now
Created symlink from /etc/systemd/system/multi-user.target.wants/keepalived.service to /usr/lib/systemd/system/k
2.9.2 ha2 节点(备节点)配置
bash 复制代码
## 步骤1:安装依赖包(时间戳:10:30:56)
[root@ha2 ~ 10:30:56]# yum install -y keepalived ipvsadm

## 步骤2:备份并编辑Keepalived配置文件(时间戳:12:16:27 - 12:16:57)
[root@ha2 ~ 12:16:27]# cp /etc/keepalived/keepalived.conf {,.bak}
[root@ha2 ~ 12:16:57]# vim /etc/keepalived/keepalived.conf
bash 复制代码
! Configuration File for keepalived

global_defs {
   router_id ha2
}

vrrp_instance web {
    state BACKUP
    interface ens33
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        10.1.8.100/24
    }
}

virtual_server 10.1.8.100 80 {
    delay_loop 6
    lb_algo rr
    lb_kind DR
    protocol TCP

    real_server 10.1.8.11 80 {
        weight 1
        TCP_CHECK {
            connect_timeout 3
            retry 3
            delay_before_retry 3
        }
    }
    real_server 10.1.8.12 80 {
        weight 1
        TCP_CHECK {
            connect_timeout 3
            retry 3
            delay_before_retry 3
        }
    }
    real_server 10.1.8.13 80 {
        weight 1
        TCP_CHECK {
            connect_timeout 3
            retry 3
            delay_before_retry 3
        }
    }
}
bash 复制代码
## 步骤3:启动Keepalived服务(时间戳:12:23:03)
[root@ha2 ~ 12:23:03]# systemctl enable keepalived.service --now
Created symlink from /etc/systemd/system/multi-user.target.wants/keepalived.service to /usr/lib/systemd/system/keepalived.service.

2.10 集群测试

2.10.1 功能性测试

验证集群基础访问能力,通过客户端持续访问 VIP,确认服务正常:

bash 复制代码
# 内部客户端测试
[root@client1 ~ 12:31:21]# while true ;do curl -s http://10.1.8.100;sleep 1;done
Welcome to www.liu.cloud
Welcome to www.liu.cloud

......
# 外部客户端测试
[root@client2 ~ 10:31:34]# while true ;do curl -s http://10.1.8.100;sleep 1;done
Welcome to www.liu.cloud
Welcome to www.liu.cloud
Welcome to www.liu.cloud
Welcome to www.liu.cloud
......
2.10.2 高可用测试

持续监控集群可用性,验证主备节点切换无感知:

bash 复制代码
[root@client2 ~ 12:42:08]# while true ;do curl -s http://10.1.8.100;sleep 1;done
Welcome to www.liu.cloud
......

测试1:停止 ha1 上 keepalived 服务。

bash 复制代码
[root@ha1 ~ 12:30:30]# systemctl stop keepalived.service

结果:客户端无感知故障,正常访问集群。

测试2:恢复 ha1 上 keepalived 服务。

bash 复制代码
[root@ha1 ~ 12:33:10]# systemctl start keepalived.service

结果:客户端无感知故障,正常访问集群。

2.10.3 负载均衡测试

验证 LVS 健康检查与节点上下线自动识别能力:

测试1:停止 web2 上 nginx 服务,监控客户端访问情况。

bash 复制代码
[root@web2 ~ 12:26:22]#  systemctl stop nginx

结果:输出结果中间会出现空白,大概 15 秒,LVS 将 web2 从后端虚拟主机中剔除。

测试2:启动 web2 上 nginx 服务,监控客户端访问情况。

bash 复制代码
[root@web2 ~ 12:34:25]#  systemctl start nginx

结果:大概 5 秒,LVS将web2加入后端虚拟主机中。

相关推荐
和你看星星1 小时前
我把代码排查流程做成了一个 Codex Skill
前端
excel1 小时前
AI 冲击下的前端发展指引:从工具到价值的重塑
前端
文心快码BaiduComate1 小时前
提升组织级AI Coding质量:电商搜索项目实践
前端·后端·程序员
excel1 小时前
AI 时代前端转型:模型训练才是未来的核心竞争力
前端
放下华子我只抽RuiKe52 小时前
FastAPI 全栈后端(四):认证与授权
开发语言·前端·javascript·python·深度学习·react.js·fastapi
持敬chijing2 小时前
Web渗透之前后端漏洞-文件包含漏洞
前端·安全·web安全·网络安全·网络攻击模型·安全威胁分析
CV艺术家2 小时前
前端免费高效的接入天气组件(天气网),控制组件的样式
前端
hunterandroid2 小时前
RecyclerView 进阶:DiffUtil 与列表更新
前端
_codeOH2 小时前
Vue 3 vs React 19:框架还在卷,核心原理就这些
前端·vue.js