资讯动态

基于Verdaccio搭建企业级私有npm仓库:从原理到实践

发布时间:2026/8/11 20:33:39 来源:尧图企业网站定制
1. 项目概述与核心价值最近在带几个前端团队做内网项目遇到一个挺典型的问题项目依赖的第三方包有的因为网络问题下载慢如蜗牛有的因为公司安全策略压根连不上外网还有的包版本更新后突然不兼容搞得整个开发流程磕磕绊绊。每次npm install都像开盲盒运气好几分钟运气差一下午就搭进去了。后来我们决定干脆自己动手在本地局域网里搭一个 npm 仓库。这个“npm本地仓库”学名叫私有 npm registry你可以把它理解成公司内部的“软件包超市”。所有经过审核、项目必需的 npm 包都会缓存或发布到这个内部的超市里。开发者从此不用再千里迢迢跑去 npm 官方源或者淘宝镜像站下载直接从内部超市取货速度飞快而且完全可控。这不仅仅是解决网络问题更是前端工程化、团队协作和项目安全中非常关键的一环。它能统一团队的依赖版本避免“在我机器上是好的”这种尴尬能对内部开发的公共组件库进行私有化管理在完全离线或严格的内网环境下它更是不可或缺的基础设施。接下来我会基于 Verdaccio 这个目前最流行、最轻量的方案带你从零开始一步步搭建一个稳定、易用的本地 npm 仓库并分享我们在实际运维中踩过的坑和积累的经验。2. 工具选型与方案设计2.1 为什么选择 Verdaccio市面上能搭建私有 npm registry 的工具不少比如sinopia已停止维护、cnpmjs.org、甚至直接用npm官方的企业版。我们最终选择 Verdaccio主要基于以下几点考量零配置开箱即用Verdaccio 的默认配置就能满足大部分私有仓库的需求你几乎不需要修改任何文件安装后启动服务就能用。这对于快速搭建和后期维护来说省心太多。轻量级资源友好它本身就是一个 Node.js 应用无需数据库默认使用文件系统存储包和用户信息内存和 CPU 占用都很低在一台普通的开发机甚至虚拟机里就能跑得很顺畅。代理缓存机制这是它的核心功能。当用户请求一个包时Verdaccio 会先检查本地存储是否有缓存。如果没有它会自动从配置的上游源如 npm 官方源、淘宝镜像下载并缓存到本地下次请求时就直接返回本地缓存。这个特性完美解决了外网包下载慢和不稳定的问题。完善的权限与发布管理支持用户注册、登录、分组权限控制。你可以设置哪些包是只读的仅缓存哪些用户可以发布特定范围的包如my-company/*非常适合团队内部共享组件库。活跃的社区与插件生态Verdaccio 社区活跃更新及时并且支持通过插件扩展认证、存储、UI 主题等功能有很强的定制能力。相比之下cnpmjs.org功能更强大但配置也更复杂更适合超大型团队或公有镜像站。而 npm 企业版是商业软件需要付费。对于绝大多数中小型团队和项目Verdaccio 在易用性和功能性上取得了最佳平衡。2.2 整体架构与工作流程在动手之前我们先理清整个系统是如何运转的。一个典型的 Verdaccio 本地仓库架构包含以下角色Verdaccio 服务运行在服务器上的核心程序提供包查询、下载、缓存、发布等所有 API 和 Web 界面。上游源 (Upstream Registry)通常是https://registry.npmjs.org/或https://registry.npmmirror.com/淘宝镜像。当本地没有缓存时Verdaccio 会向这里请求包。开发者客户端团队成员的开发机器。他们的npm或yarn、pnpm等客户端需要配置为指向本地的 Verdaccio 服务地址。内部私有包团队自己开发的、不想公开发布的 npm 包其作用域scope通常类似my-team/*发布到本地仓库。工作流程如下开发者执行npm install react。npm客户端向配置好的本地 Verdaccio 地址发起请求。Verdaccio 检查本地存储发现没有react包的缓存。Verdaccio 向上游源如淘宝镜像请求react包下载并缓存到本地文件系统。Verdaccio 将react包返回给开发者客户端完成安装。当另一位开发者同样安装react时Verdaccio 发现本地已有缓存便直接返回速度极快。开发者开发了一个组件包my-team/button执行npm publish。客户端将包发布到本地 Verdaccio。Verdaccio 将其存储在本地后续其他团队成员安装my-team/button时直接从本地获取。这个流程确保了公共包的快速获取和私有包的安全管理。3. 环境准备与安装部署3.1 服务器环境准备Verdaccio 是 Node.js 应用所以首先需要准备 Node.js 环境。我们以一台干净的 Linux 服务器如 CentOS 7/8 或 Ubuntu 20.04/22.04为例。第一步安装 Node.js不建议使用系统自带的、版本陈旧的 Node.js。推荐使用nvmNode Version Manager来安装和管理 Node.js 版本这样切换版本非常方便。# 1. 安装 nvm curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash # 或者使用 gitee 镜像加速 # bash -c $(curl -fsSL https://gitee.com/RubyMetric/nvm-cn/raw/main/install.sh) # 2. 使 nvm 生效或重新登录终端 source ~/.bashrc # 如果是 ~/.zshrc则 source ~/.zshrc # 3. 安装一个长期支持版本如 Node.js 18 nvm install 18 nvm use 18 nvm alias default 18 # 设置默认版本 # 4. 验证安装 node -v # 应输出 v18.x.x npm -v # 应输出 9.x.x 或 10.x.x注意生产环境建议使用 LTS长期支持版本如 Node.js 18 或 20它们在稳定性和安全性上更有保障。避免使用最新的奇数版本如 19, 21。第二步检查端口占用Verdaccio 默认使用4873端口。确保该端口没有被其他程序占用。sudo lsof -i :4873 # 或者 sudo netstat -tlnp | grep 4873如果端口被占用你需要停止相关服务或者在后续配置中为 Verdaccio 修改默认端口。3.2 安装与启动 Verdaccio安装 Verdaccio 非常简单使用 npm 全局安装即可。# 使用 npm 全局安装 verdaccio npm install -g verdaccio6 --registryhttps://registry.npmmirror.com # 安装完成后验证版本 verdaccio --version安装完成后直接运行verdaccio命令即可启动服务。verdaccio你会看到类似下面的输出说明服务已成功启动并显示了配置文件路径、存储路径、插件路径以及服务监听的地址默认是http://localhost:4873。warn --- config file - /home/user/.config/verdaccio/config.yaml warn --- Plugin successfully loaded: verdaccio-htpasswd warn --- Plugin successfully loaded: verdaccio-audit warn --- http address - http://localhost:4873/ - verdaccio/6.0.6此时在浏览器中打开http://你的服务器IP:4873就能看到 Verdaccio 的 Web 管理界面了。页面上会显示已缓存的包列表初始为空以及登录/注册按钮。实操心得第一次启动时Verdaccio 会在用户主目录下的.config/verdaccio文件夹中生成默认的配置文件config.yaml和存储目录storage。这个启动方式适合本地测试。但对于生产环境我们需要让它以后台服务如 systemd 服务的形式运行并处理开机自启、日志管理等问题。3.3 配置 Systemd 服务生产环境为了让 Verdaccio 稳定运行在服务器上我们将其配置为 systemd 服务。第一步创建专用用户可选但推荐为了安全不建议使用 root 用户运行 Verdaccio。sudo useradd -r -s /sbin/nologin verdaccio第二步创建 systemd 服务文件sudo vim /etc/systemd/system/verdaccio.service将以下内容写入文件注意修改ExecStart中verdaccio命令的路径可以通过which verdaccio命令查找和User、Group为你创建的用户。[Unit] DescriptionVerdaccio npm registry Afternetwork.target [Service] Typesimple # 关键找到 verdaccio 的实际安装路径 ExecStart/usr/bin/verdaccio # 如果上面路径不对可以用 which verdaccio 查看 # ExecStart/home/your-username/.nvm/versions/node/v18.20.2/bin/verdaccio Userverdaccio Groupverdaccio Restarton-failure RestartSec10 # 可选设置环境变量如指定配置文件路径 # EnvironmentVERDACCIO_CONFIG_PATH/opt/verdaccio/config.yaml # 安全相关设置 NoNewPrivilegestrue PrivateTmptrue ProtectSystemstrict ReadWritePaths/path/to/verdaccio/storage # 改为你的实际存储路径 [Install] WantedBymulti-user.target第三步调整目录权限并启动服务# 假设 verdaccio 的存储目录在 /home/verdaccio/.config/verdaccio/storage # 需要将该目录的所有权给 verdaccio 用户 sudo chown -R verdaccio:verdaccio /home/verdaccio/.config/verdaccio # 重新加载 systemd 配置 sudo systemctl daemon-reload # 启动 verdaccio 服务 sudo systemctl start verdaccio # 设置开机自启 sudo systemctl enable verdaccio # 查看服务状态和日志 sudo systemctl status verdaccio sudo journalctl -u verdaccio -f # 实时查看日志现在Verdaccio 已经作为一个稳定的后台服务运行了。你可以通过http://服务器IP:4873访问 Web 界面。4. 核心配置详解与优化默认配置虽然能用但为了适应团队需求和安全规范我们通常需要调整配置文件config.yaml。它的默认路径是~/.config/verdaccio/config.yaml。4.1 基础网络与上游源配置首先我们需要让服务能被局域网内其他机器访问并配置一个更快的上游镜像源。# 存储所有包的目录 storage: ./storage # Web UI 相关配置 web: title: My Company Private NPM Registry # 可以在这里设置 logo 等 # 认证插件默认使用 htpasswd 文件管理用户 auth: htpasswd: file: ./htpasswd # 最大用户数-1 表示不限制 max_users: -1 # 上游源配置这是核心 uplinks: npmjs: url: https://registry.npmjs.org/ # 缓存时间单位分钟 cache: true max_fails: 10 timeout: 100s maxage: 10m # 代理请求失败时的重试策略 fail_timeout: 5m taobao: url: https://registry.npmmirror.com/ cache: true max_fails: 10 timeout: 30s # 国内源可以设置更短的超时 maxage: 10m # 包访问权限配置 packages: # 作用域包如 my-company/*只有认证用户能发布所有人可读 my-company/*: access: $all publish: $authenticated unpublish: $authenticated proxy: npmjs taobao # 可以代理多个上游源按顺序尝试 # 其他所有包*所有人可读但只有认证用户能发布通常用于内部临时包 **: access: $all publish: $authenticated unpublish: $authenticated # 关键配置代理使得本地没有的包去上游拉取 proxy: npmjs taobao # 服务器监听配置 listen: 0.0.0.0:4873 # 修改这里从 localhost 改为 0.0.0.0监听所有网卡 # 日志输出配置 logs: - {type: stdout, format: pretty, level: http} # - {type: file, path: verdaccio.log, level: info} # 也可以输出到文件关键改动解释listen: 0.0.0.0:4873这是最重要的一步。默认localhost只能本机访问改为0.0.0.0后服务器会监听所有网络接口允许局域网内其他机器通过服务器IP访问。uplinks我们定义了两个上游源npmjs官方源和taobao淘宝镜像。在packages的proxy字段中可以引用它们。Verdaccio 会按顺序尝试直到成功。将taobao放在后面可以作为官方源的备用加速国内下载。packages这里定义了包的访问策略。my-company/*这是我们内部包的作用域。access: $all表示所有人包括未登录用户可以安装。publish: $authenticated表示只有登录用户才能发布。这符合内部组件库的管理需求。**匹配所有其他包。同样配置了代理这样当请求react、lodash等公共包时Verdaccio 会去上游拉取并缓存。4.2 用户认证与安全管理默认使用htpasswd文件管理用户虽然简单但适合中小团队。每次通过 Web 界面注册的用户密码会经过加密后存储在这个文件中。创建第一个管理员用户首先需要安装htpasswd工具通常来自apache2-utils或httpd-tools包。# Ubuntu/Debian sudo apt-get install apache2-utils # CentOS/RHEL sudo yum install httpd-tools生成密码文件并添加用户。# 切换到 verdaccio 配置目录 cd ~/.config/verdaccio # 使用 htpasswd 创建文件并添加用户 ‘admin’会提示输入密码 htpasswd -c ./htpasswd admin # 后续添加其他用户不要用 -c 参数否则会覆盖文件 # htpasswd ./htpasswd developer1修改config.yaml中的max_users: 100可以限制注册用户数或者设置为-1关闭 Web 注册完全由管理员通过htpasswd命令管理用户这样更安全。注意事项htpasswd文件方式在用户量很大时管理不便。如果需要对接公司的 LDAP/AD 或 OAuth2如 GitLab、GitHubVerdaccio 有丰富的社区插件如verdaccio-auth-ldap,verdaccio-oauth2可以实现配置会稍复杂一些。4.3 存储与缓存优化默认的文件系统存储对于小规模团队完全足够。但如果你担心磁盘 I/O 性能或者包数量极多可以考虑以下优化更换存储路径将storage配置到一个高性能或大容量的磁盘分区。storage: /data/verdaccio/storage记得修改 systemd 服务文件中的ReadWritePaths路径并给对应目录赋予verdaccio用户权限。使用外部存储插件Verdaccio 支持 Amazon S3、Google Cloud Storage 等插件可以将包文件存储到对象存储中。这对于分布式团队或需要高可用性的场景很有用。缓存策略调整uplinks配置中的maxage控制缓存的有效期。对于版本非常稳定的基础包如lodash可以适当延长。对于频繁更新的包如某些工具的 CLI可以缩短。但请注意maxage过后Verdaccio 会向上游源检查元数据metadata如果版本没变并不会重新下载整个 tarball所以对带宽影响不大。修改配置后需要重启 Verdaccio 服务使配置生效sudo systemctl restart verdaccio5. 客户端配置与使用指南服务端搭好了接下来需要让团队成员的开发机客户端能够使用这个私有仓库。5.1 永久配置 npm 客户端最常用的方法是修改 npm 的全局配置将 registry 指向我们的私有仓库。# 将默认 registry 设置为你的私有仓库地址 npm config set registry http://your-server-ip:4873/ # 为特定的作用域包如 my-company/*也设置 registry # 这能确保安装内部包时一定走私有仓库 npm config set my-company:registry http://your-server-ip:4873/ # 查看配置是否生效 npm config get registry npm config get my-company:registry这些命令会修改用户主目录下的.npmrc文件。配置完成后所有npm install、npm publish命令默认都会使用你搭建的私有仓库。5.2 项目级配置与 .npmrc 文件对于公司项目更推荐将配置写在项目根目录的.npmrc文件中并提交到代码仓库。这样能保证所有开发者、CI/CD 环境都使用完全一致的仓库配置。在项目根目录创建.npmrc文件内容如下# 设置默认 registry registryhttp://your-server-ip:4873/ # 为内部作用域包单独设置 registry确保优先级 my-company:registryhttp://your-server-ip:4873/ # 如果需要认证发布包时可以在这里添加 token不推荐明文存储密码 # //your-server-ip:4873/:_authTokenyour-npm-token安全提示不要在项目.npmrc或全局.npmrc中明文写入用户名密码或_authToken。对于需要认证的操作如npm publish最好通过npm login命令交互式登录token 会安全地存储在本地。5.3 使用流程安装与发布1. 安装公共包配置好 registry 后安装公共包和以前没有任何区别。npm install react npm install lodash4.17.21第一次安装时Verdaccio 会从上游源下载并缓存。你可以在 Web 界面的“包列表”中看到新缓存的包。团队其他成员再次安装时速度就是局域网内网速度了。2. 登录与发布私有包假设你开发了一个内部组件包my-company/ui-button。首先在项目目录下登录私有仓库npm login --registryhttp://your-server-ip:4873按照提示输入用户名、密码和邮箱在 Web 界面注册的或管理员创建的。登录成功后凭证会保存在本地~/.npmrc中。然后在组件包目录下执行发布# 确保 package.json 中的 name 是 my-company/ui-button npm publish发布成功后这个包就存储在 Verdaccio 服务器的storage目录下了。其他团队成员配置好 registry 后可以直接npm install my-company/ui-button进行安装。3. 使用 yarn 或 pnpm如果你的团队使用yarn或pnpm配置方式类似。yarn:yarn config set registry http://your-server-ip:4873/或者在项目.yarnrc.yml中配置npmRegistryServer: http://your-server-ip:4873/ npmScopes: my-company: npmRegistryServer: http://your-server-ip:4873/pnpm:pnpm config set registry http://your-server-ip:4873/5.4 处理常见客户端报错在配置过程中你可能会遇到一些客户端报错这里列举两个最常见的错误1npm ERR! code E401/npm ERR! 401 Unauthorized原因尝试发布包或安装私有包时未登录或登录凭证已过期/无效。解决执行npm login重新登录。检查.npmrc文件中是否有错误的 token 或旧配置可以尝试npm logout后再登录。错误2npm ERR! code ETIMEDOUT/npm ERR! network connect ETIMEDOUT原因客户端无法连接到你的 Verdaccio 服务器地址。解决确认服务器 IP 和端口4873是否正确。在客户端机器上用telnet your-server-ip 4873或curl -v http://your-server-ip:4873测试网络连通性。检查服务器防火墙是否放行了 4873 端口。# CentOS 7 使用 firewalld sudo firewall-cmd --permanent --add-port4873/tcp sudo firewall-cmd --reload # Ubuntu 使用 ufw sudo ufw allow 4873/tcp确认 Verdaccio 服务正在运行且监听在0.0.0.0。6. 高级运维与故障排查6.1 监控与日志分析维护一个服务查看日志是基本功。Verdaccio 的日志默认输出到 stdout被 systemd 的 journal 捕获我们可以方便地查看。# 查看实时日志 sudo journalctl -u verdaccio -f # 查看最近100行日志 sudo journalctl -u verdaccio -n 100 # 查看指定时间段的日志 sudo journalctl -u verdaccio --since 2024-01-01 --until 2024-01-02日志级别可以在config.yaml的logs部分调整。level: info会记录一般操作信息level: http会记录所有 HTTP 请求信息量很大有助于调试但日志文件增长快生产环境建议用info。典型的错误日志分析http -- 401认证失败检查用户密码或 token。http -- 404请求的包在本地和上游都不存在。error--- request error: getaddrinfo ENOTFOUND registry.npmjs.org无法解析上游源域名检查服务器网络/DNS。客户端一直卡住无响应可能是 Verdaccio 在向上游拉取一个很大的包如 webpack 的某些版本网络慢导致超时。可以检查服务器带宽或考虑预缓存常用大包。6.2 数据备份与清理备份最重要的数据就是storage目录存放所有包的压缩文件和config.yaml、htpasswd文件。定期将它们打包备份即可。# 简单备份示例 tar -czf verdaccio-backup-$(date %Y%m%d).tar.gz ~/.config/verdaccio/清理随着时间推移缓存包会占用大量磁盘空间。Verdaccio 本身没有自动清理旧版本包的功能。你可以手动删除storage目录下对应包文件夹中的旧版本.tgz文件。务必谨慎操作建议先备份。使用社区插件如verdaccio-clean-storage它可以基于时间或版本数量策略自动清理。写一个定时任务脚本定期清理超过一定时间未被访问的包缓存。可以通过分析storage目录下文件的访问时间atime来实现但这需要文件系统支持并启用 atime 更新。6.3 性能调优与高可用考虑对于几十人的开发团队默认配置的单机 Verdaccio 性能完全足够。如果遇到性能瓶颈可以考虑使用反向代理在 Verdaccio 前面加一个 Nginx 或 Caddy。好处可以配置 SSL/TLSHTTPS、负载均衡如果你部署了多个 Verdaccio 实例、缓存静态资源、压缩传输、统一域名端口等。简单 Nginx 配置示例server { listen 80; server_name npm.my-company.com; # 重定向到 HTTPS推荐 return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name npm.my-company.com; ssl_certificate /path/to/your/cert.pem; ssl_certificate_key /path/to/your/key.pem; location / { proxy_pass http://localhost:4873; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 重要对于上传大文件publish需要调整超时和 body 大小 client_max_body_size 100M; proxy_connect_timeout 600; proxy_send_timeout 600; proxy_read_timeout 600; send_timeout 600; } }配置后客户端就可以通过https://npm.my-company.com访问了记得把客户端的 registry 地址也更新为此域名。集群化对于超大规模团队可以考虑 Verdaccio 集群。但这涉及到共享存储如 S3和负载均衡复杂度较高。一个更简单的“准高可用”方案是部署两个 Verdaccio 实例共享同一个storage目录通过 NFS 或云存储前面用 Nginx 做负载均衡。这样即使一个实例宕机服务也不中断。6.4 常见问题排查速查表问题现象可能原因排查步骤与解决方案Web 界面能访问但npm install失败1. 客户端.npmrc配置错误。2. 防火墙/安全组阻止了 4873 端口。3. 客户端网络代理干扰。1.npm config get registry检查配置。2. 在客户端telnet 服务器IP 4873测试端口。3. 检查客户端 npm 代理设置npm config get proxy如有则清空npm config delete proxy。npm publish返回 401/4031. 未登录或登录过期。2. 用户无该作用域包的发布权限。3. 包名与config.yaml中packages权限不匹配。1. 执行npm login重新登录。2. 检查config.yaml确认该用户或所在组有publish权限。3. 确认要发布的包名如my-company/xxx匹配配置文件中的模式。安装包速度极慢且 Web 界面看不到新包1. Verdaccio 正在从慢速的上游源首次下载。2. 上游源网络不通。1. 首次安装大型包如webpack时耐心等待或让一位同事先装。2. 检查config.yaml的uplinks配置将taobao镜像源放在前面。在服务器上curl测试上游源连通性。Verdaccio 服务启动失败1. 端口被占用。2.config.yaml语法错误。3. 存储目录权限错误。1.sudo lsof -i :4873查杀占用进程或改端口。2. 用yamllint或在线工具检查 YAML 语法。3. 检查storage目录及其父目录对运行用户如verdaccio是否有读写权限。磁盘空间快速耗尽缓存包过多未清理旧版本。1. 定期手动清理storage目录。2. 考虑使用存储插件将包存到对象存储。3. 实施清理脚本或使用清理插件。7. 延伸应用与最佳实践搭建好基础服务只是第一步要让私有仓库真正提升团队效率还需要一些最佳实践。1. 内部包发布规范为团队内部的my-company/*作用域包制定发布规范版本号严格遵循 SemVer语义化版本号规范。Changlog使用standard-version或conventional-changelog工具自动生成更新日志。CI/CD 集成将npm publish集成到 GitLab CI/CD 或 GitHub Actions 中在打 Tag 时自动发布版本。文档要求每个内部包必须有清晰的 README说明用途、API、开发指南。2. 关键公共包预缓存对于项目依赖的关键、体积大的公共包如webpack、babel/core、react-dom等可以在服务器上手动触发一次安装将其预缓存到本地避免团队成员首次安装时等待。# 在服务器上临时将 npm registry 指向自己如果还没改全局配置 npm --registry http://localhost:4873 install webpack5 # 或者更优雅的方式在 Verdaccio 服务器上使用 curl 模拟请求 # curl -X GET http://localhost:4873/webpack3. 与 CI/CD 集成在 Jenkins、GitLab Runner 等 CI/CD 环境中同样需要配置 npm registry。在 Runner 的全局环境变量中设置NPM_CONFIG_REGISTRYhttp://your-npm-registry。或者在构建脚本中显式地使用--registry参数npm install --registryhttp://your-npm-registry。对于需要发布包的流水线可以使用npm的自动化 Token 进行认证通过npm token create生成并将 Token 存储在 CI/CD 系统的 Secret 变量中避免明文密码。4. 定期审计与安全扫描私有仓库里的包同样可能存在安全漏洞。可以定期在服务器上使用npm audit命令配置好 registry对缓存的关键包进行扫描。或者使用专业的软件成分分析SCA工具对接私有仓库进行持续监控。考虑启用 Verdaccio 的verdaccio-audit插件默认已加载它可以在包被访问时进行基本的审计。5. 文档与推广最后别忘了为你的私有仓库编写一份简洁的使用文档告诉团队成员仓库的访问地址URL。如何配置客户端的 npm/yarn/pnpm。如何登录和发布内部包。遇到问题的联系人和排查步骤。一个稳定、高效的本地 npm 仓库就像为前端团队铺设了一条“内部高速公路”它能显著提升开发体验、保障构建稳定性、并促进代码复用。从我们团队的实际经验来看花一天时间搭建和磨合换来的是长期开发效率的提升和依赖管理的井然有序这笔投入非常值得。刚开始可能会遇到一些小问题但一旦流程跑顺你会发现大家再也回不去那个被网络和版本问题困扰的时代了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价