掌握npm:前端开发必备的包管理工具 – wiki基地


掌握 npm:前端开发必备的包管理工具

引言:现代前端开发的基石

在互联网技术飞速发展的今天,前端开发早已不再是当年简单的网页制作。随着单页应用(SPA)、复杂交互、模块化、组件化以及构建工具的兴起,现代前端项目变得日益庞大和复杂。一个典型的现代前端项目,往往依赖于成百上千个外部库和模块,例如 React、Vue、Angular 等框架,Webpack、Vite 等构建工具,Babel 用于代码转换,Lodash、jQuery 等工具库,以及各种 UI 组件库、状态管理库、网络请求库等等。

手动管理这些依赖,不仅工作量巨大且极易出错,而且难以保证团队成员之间、甚至同一项目在不同时间点构建环境的一致性。想象一下,你需要在项目中引入一个日历组件,然后一个数据可视化库,再加一个动画库……你需要手动下载它们的代码,解决它们各自的依赖问题(可能这些库又依赖于其他库),处理版本冲突,并确保它们在项目中的可用性。这无疑是一场噩梦。

正是在这样的背景下,包管理工具应运而生,成为了现代前端开发的基石。而在众多包管理工具中,npm(Node Package Manager)无疑是最为广泛使用和最具影响力的一个。掌握 npm,不仅仅是学会几个命令,更是理解现代前端项目组织、构建和协作的核心逻辑。它连接了全球数百万开发者构建的开源模块,为前端工程师提供了前所未有的开发效率和能力。

本文将深入探讨 npm 的方方面面,从其基本概念、核心功能,到常用命令、高级用法以及最佳实践,帮助你全面掌握这个前端开发必备的利器。

第一部分:什么是 npm?为何它如此重要?

1. npm 的定义与起源

npm 全称 Node Package Manager,正如其名,它是 Node.js 的包管理器。虽然 Node.js 最初是为了在服务器端运行 JavaScript 而创建的,但其模块化思想和事件驱动的异步 I/O 模型,以及随之而来的 npm 包管理器,极大地推动了 JavaScript 生态系统的发展,包括前端领域。npm 使得开发者可以方便地查找、下载、安装、管理和发布 JavaScript 包(Package),这些包通常就是我们所说的模块或库。

npm 主要包含两部分:
* 命令行工具 (CLI): 安装在你的本地计算机上,用于与 npm 注册表进行交互、管理本地项目的依赖等。
* npm 注册表 (Registry): 一个巨大的在线数据库,存储着成千上万个开源的 JavaScript 包。这是开发者分享和获取代码的主要平台。

2. npm 解决了哪些问题?

npm 的出现和普及,彻底改变了前端开发的流程和模式,主要解决了以下核心问题:

  • 依赖管理自动化: npm 允许你在项目的配置文件中声明项目所需的所有外部依赖(库、框架、工具等)及其版本范围。通过简单的命令,npm 就能自动下载这些依赖及其子依赖,无需手动寻找和下载。
  • 模块复用与分享: npm 注册表提供了一个中心化的平台,开发者可以将自己编写的通用模块发布上去,供全球其他开发者使用。这极大地促进了代码的复用,避免了“重复造轮子”。
  • 版本控制与一致性: npm 配合项目配置文件,可以精确控制项目使用的各个依赖的版本。通过锁定依赖版本,可以确保团队所有成员、以及不同构建环境都能使用完全相同的依赖版本,从而避免“在我电脑上没问题”的问题。
  • 项目初始化与标准化: npm 提供了项目初始化的命令,可以快速生成标准的项目结构和配置文件,为项目建立统一规范。
  • 自动化工作流程: npm 允许在配置文件中定义脚本命令,用于执行各种开发任务,如代码编译、打包、测试、部署、启动开发服务器等。这使得项目的构建和开发流程可以高度自动化和标准化。
  • 广阔的生态系统: npm 注册表是目前世界上最大的软件注册表之一,拥有海量的开源模块。几乎任何你想要的功能或解决的问题,都可能已经在 npm 上找到了现成的解决方案。

第二部分:入门 npm – 安装与基本使用

1. 安装 Node.js (附带 npm)

由于 npm 是 Node.js 的包管理器,因此安装 Node.js 时通常会同时安装 npm。

访问 Node.js 官方网站 (https://nodejs.org/),下载适合你操作系统的安装包并进行安装。通常推荐下载 LTS (长期支持) 版本,它更稳定。

安装完成后,打开终端或命令行工具,运行以下命令来验证 Node.js 和 npm 是否安装成功,并查看其版本:

bash
node -v
npm -v

如果看到版本号输出,说明安装成功。

2. 初始化项目 (npm init)

在一个新的项目文件夹中,你通常会做的第一步就是初始化 npm 项目。这会生成一个 package.json 文件,它是项目的核心配置文件。

进入项目文件夹,运行:

bash
npm init

npm 会引导你填写一些项目信息,如项目名称、版本、描述、入口文件、作者、许可证等。你可以一路按回车键使用默认值(项目名称默认为文件夹名),或者在初始化时加上 -y 参数,跳过问答环节,使用默认值快速生成 package.json 文件:

bash
npm init -y

生成的 package.json 文件大致如下:

json
{
"name": "my-frontend-project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

这个文件将记录项目的元数据、依赖信息以及可执行的脚本命令。

第三部分:npm 的核心概念 – package.jsonnode_modulespackage-lock.json

理解这三个文件/文件夹是掌握 npm 的关键。

1. package.json:项目的“身份证”与配置中心

package.json 文件是 npm 项目的灵魂。它是一个 JSON 格式的文件,包含了关于项目的几乎所有重要信息:

  • name: 项目/包的名称。在 npm 注册表上发布时,名称必须是唯一的(除非是 Scoped Packages)。建议使用小写字母和连字符。
  • version: 项目/包的版本号。遵循 Semantic Versioning (SemVer) 规范(主版本号.次版本号.修订号),用于管理版本兼容性。
  • description: 项目的简要描述。
  • main: 项目的入口文件,当其他人在 Node.js 环境中 require()import 你的包时,会首先加载这个文件。
  • scripts: 一个对象,定义了一系列可执行的脚本命令的快捷方式。这是自动化开发工作流程的核心。例如,你可以定义 startbuildtest 等脚本。
  • keywords: 关键词数组,有助于其他开发者在 npm 注册表上搜索到你的包。
  • author: 作者信息。
  • license: 项目的许可证信息,指定了其他人使用你的代码的权限。
  • dependencies: 一个对象,列出了项目在生产环境运行时所需的依赖包及其版本范围。例如,你的 React 项目会在这里列出 reactreact-dom
  • devDependencies: 一个对象,列出了项目在开发和构建环境所需的依赖包及其版本范围。这些包在项目最终部署到生产环境时不是必需的。例如,Webpack、Babel、ESLint、Jest 等通常会被列在这里。
  • peerDependencies: 列出项目所兼容的依赖包版本。这通常用于库开发,表明你的库需要与其宿主环境(如特定版本的 React)兼容。
  • engines: 指定项目所需的 Node.js 和 npm 版本范围。

理解 dependenciesdevDependencies 的区别非常重要。简单来说,如果一个依赖只在开发、构建、测试阶段需要,就放在 devDependencies;如果项目在生产环境运行时也需要它(例如一个前端框架、一个数据请求库),就放在 dependencies

2. node_modules:依赖的“巢穴”

当你通过 npm 安装依赖时,npm 会在项目根目录下创建一个 node_modules 文件夹。所有通过 npm 安装的包及其依赖的包,都会被下载并放置在这个文件夹中。

node_modules 文件夹的特点:

  • 嵌套结构 (早期) / 平铺结构 (现代): 早期的 npm 版本会严格按照依赖树的层级在 node_modules 中创建嵌套文件夹。这可能导致非常深的文件路径和大量的重复模块。现代 npm (v3+) 倾向于使用平铺结构 (flattened structure),将尽可能多的模块直接放在项目根目录下的 node_modules 中,只有当不同模块依赖于同一个包的不同版本时,才会出现嵌套。这解决了路径过深和模块重复的问题。
  • 文件数量庞大: 即使是一个看似简单的项目,其依赖树也可能非常复杂,导致 node_modules 文件夹包含成千上万个文件和文件夹,占用大量磁盘空间。
  • 不应提交到版本控制: node_modules 文件夹是根据 package.jsonpackage-lock.json 自动生成的,因此绝不应该将其提交到 Git 等版本控制系统中。你应该在 .gitignore 文件中忽略 node_modules 文件夹。其他开发者克隆你的项目后,只需要运行 npm install 命令,npm 就会根据 package.jsonpackage-lock.json 自动下载和生成 node_modules 文件夹。

3. package-lock.json:锁定依赖版本的“快照”

package-lock.json 文件(或早期的 npm-shrinkwrap.json)是由 npm 自动生成和维护的,它记录了项目中所有依赖包(包括其子依赖)被安装时的确切版本号、下载地址以及哈希值

为什么需要 package-lock.json

package.json 中的版本号通常允许一个范围(例如 ^1.0.0 意味着兼容 1.x.x 但不升到 2.0.0)。这意味着即使 package.json 不变,如果某个依赖发布了新的兼容版本,不同时间运行 npm install 可能会安装到不同的子版本。在团队协作或 CI/CD 环境中,这可能导致构建结果的不一致性,甚至引入新的 bug。

package-lock.json precisely locks down the entire dependency tree. 当你运行 npm install 时:

  • 如果 package-lock.json 存在,npm 会优先根据 package-lock.json 中记录的精确版本和结构来安装依赖,这保证了无论何时何地运行 npm install,都能得到完全相同的 node_modules 结构。
  • 如果 package-lock.json 不存在,或者 package.json 中的依赖范围允许的新版本发布,npm 会根据 package.json 中的规则安装最新的兼容版本,并更新或生成 package-lock.json 文件来反映这次安装的结果。

因此,package-lock.json 是确保项目构建一致性的关键,它必须被提交到版本控制系统。

第四部分:npm 常用命令详解

掌握 npm 的核心命令是高效开发的关键。

1. 安装依赖 (npm install)

  • 安装 package.json 中声明的所有依赖:
    在项目根目录下运行 npm install (或简写为 npm i)。npm 会读取 package.json 文件,并根据 package-lock.json (如果存在) 或 package.json 的规则下载所有 dependenciesdevDependencies 中列出的包到 node_modules 文件夹。同时,它会创建或更新 package-lock.json

    “`bash
    npm install

    npm i
    “`

    注意: 在克隆一个新项目后,第一次运行 npm install 是必须的步骤。

  • 安装特定的包:
    安装一个包并将其添加到 dependencies 中:

    “`bash
    npm install

    或简写

    npm i

    默认行为,等同于 –save (从 npm 5.0 开始)

    “`

    安装一个包并将其添加到 devDependencies 中:

    “`bash
    npm install –save-dev

    或简写

    npm i -D
    “`

    安装一个包并将其添加到 peerDependencies 中:

    “`bash
    npm install –save-peer

    或简写

    npm i -P
    “`

  • 安装指定版本的包:
    安装特定版本:

    “`bash
    npm install @

    例如:

    npm install [email protected]
    “`

    安装最新版本:

    bash
    npm install <package-name>@latest

    安装预发布版本 (如 beta, alpha, rc):

    bash
    npm install <package-name>@next

  • 全局安装包:
    有些工具包(如 webpack、create-react-app、vue-cli)通常需要全局安装,以便在系统的任何位置都能使用其命令行工具。

    “`bash
    npm install -g

    例如:

    npm install -g webpack
    ``
    *注意:* 现代前端开发中,更推荐将构建工具等作为项目的
    devDependencies安装,并使用npm runnpx` 来执行它们,以避免不同项目依赖不同版本全局工具的问题。

  • 从 Git 仓库安装:
    你可以直接从 Git 仓库安装一个包:

    “`bash
    npm install

    例如:

    npm install git+https://github.com/user/repo.git
    “`

  • 从本地路径安装:
    安装本地文件系统中的一个包:

    bash
    npm install <path/to/local/folder>

2. 卸载依赖 (npm uninstall)

卸载一个包并从 package.json 中移除对应的条目:

“`bash
npm uninstall

卸载并从 dependencies 移除 (默认)

也可以使用 –save 或 -S 明确指定

npm uninstall –save-dev

或简写

npm uninstall -D

卸载并从 devDependencies 移除

npm uninstall -g

全局卸载

“`

卸载命令会同时更新 package.jsonpackage-lock.json,并从 node_modules 中移除对应的文件。

3. 更新依赖 (npm update)

更新项目中的依赖包到满足 package.json 中版本范围的最新版本,并更新 package-lock.json

bash
npm update

更新指定的包:

bash
npm update <package-name>

npm update 会根据 package.json 中指定的版本范围进行更新。例如,如果 package.json 中是 "lodash": "^4.17.21",运行 npm update lodash 可能会将 lodash 更新到 4.x.x 的最新版本,但不会更新到 5.x.x。

要检查哪些包有更新版本(可能超出 package.json 中的版本范围),可以使用 npm outdated 命令。

4. 检查过期依赖 (npm outdated)

列出所有已安装的包中,有新版本可用的包。它会显示当前的安装版本、期望的版本范围(根据 package.json)以及可用的最新版本。

bash
npm outdated

输出通常会包含几列信息:包名、当前版本 (Current)、package.json 中指定的版本范围 (Wanted)、满足版本范围的最新版本 (Latest)。

5. 列出已安装的依赖 (npm list)

查看项目当前安装的依赖树:

“`bash
npm list

或简写

npm ls
“`

由于依赖树可能很深,输出会非常详细。你可以限制显示的深度:

“`bash
npm list –depth=0

只显示顶级依赖 (直接在 package.json 中列出的)

“`

查看指定包的信息:

bash
npm list <package-name>

6. 运行脚本 (npm run)

package.json 中的 scripts 字段是自动化开发流程的核心。通过 npm run <script-name> 命令可以执行这些脚本。

例如,如果你的 package.json 中有:

json
"scripts": {
"start": "webpack serve --open",
"build": "webpack --mode production",
"test": "jest",
"lint": "eslint src/"
}

你可以运行:

bash
npm run start # 启动开发服务器
npm run build # 执行生产环境打包
npm run test # 运行测试
npm run lint # 执行代码检查

对于 starttestinstallversion 等少数几个命令,如果它们在 scripts 中定义了,你可以省略 run,直接执行 npm startnpm test 等。

npm run 命令的一个重要特性是,它会将 node_modules/.bin 目录添加到系统的 PATH 环境变量中(仅对当前脚本执行过程有效)。这意味着你可以在脚本中直接使用安装在项目本地 devDependencies 中的可执行文件,而无需全局安装它们(例如,你可以直接在脚本中使用 webpackjesteslint 命令,即使它们没有全局安装)。

7. 执行包自带的可执行文件 (npx)

npx (npm package runner) 是 npm 5.2 版本引入的工具,它极大地简化了执行安装在项目本地 node_modules/.bin 目录下的可执行文件。

假设你想运行一个项目本地安装的 eslint 命令来检查文件:

传统方式 (使用 npm run 或手动指定路径):

“`bash
npm run lint — src/some-file.js # 如果 lint 脚本是 eslint src/

或者

./node_modules/.bin/eslint src/some-file.js
“`

使用 npx

bash
npx eslint src/some-file.js

npx 会查找当前项目或父级目录的 node_modules/.bin 目录,找到 eslint 可执行文件并运行它。如果找不到,npx 还会询问你是否要临时下载并运行该包(而不会将其安装到你的项目依赖中),这对于运行一些一次性工具非常方便,比如 create-react-app

bash
npx create-react-app my-app

这会临时下载 create-react-app 包并执行它来创建项目,而无需你全局安装 create-react-app

npx 是一个非常实用的工具,推荐在执行本地安装的命令时优先使用它。

8. 查找包 (npm search)

在 npm 注册表上搜索包:

“`bash
npm search

例如:

npm search react component
“`

9. 查看包信息 (npm viewnpm info)

查看指定包的详细信息,如版本、依赖、维护者等:

“`bash
npm view

npm info “`

10. 发布包 (npm publish)

如果你开发了一个可复用的模块,可以将其发布到 npm 注册表,供其他开发者使用。这需要你在 npm 官网注册账号并登录 (npm login)。

在包含 package.json 的包根目录下运行:

bash
npm publish

首次发布需要填写一些信息,如包名必须是唯一的。如果是 Scoped Package (@your-scope/your-package),默认发布为私有包,除非使用 --access public 选项。

11. 审计安全漏洞 (npm audit)

检查项目依赖中是否存在已知的安全漏洞:

bash
npm audit

npm 会分析你的依赖树,并报告发现的漏洞及其建议的修复措施(通常是升级受影响的依赖包)。

“`bash
npm audit fix

尝试自动修复报告的漏洞 (通常是升级到不受影响的最小版本)

npm audit fix –force

强制修复,可能包含重大版本变更

“`

第五部分:高级概念与最佳实践

1. Semantic Versioning (SemVer) 语义化版本

理解 SemVer 对于管理依赖版本至关重要。版本号格式通常为 主版本号.次版本号.修订号 (MAJOR.MINOR.PATCH)。

  • MAJOR (主版本号): 当你做了不兼容的 API 修改时,增加主版本号。
  • MINOR (次版本号): 当你以兼容的方式增加了新功能时,增加次版本号。
  • PATCH (修订号): 当你做了兼容的 Bug 修复时,增加修订号。

package.json 中,版本号前通常会加上符号,表示允许的版本范围:

  • ^ (脱字号/插入符): 兼容主版本号不变的次版本号和修订号更新。例如 ^1.2.3 允许安装 1.2.3、1.2.4、1.3.0 等,但不允许 2.0.0。这是 npm install 安装依赖时的默认前缀。
  • ~ (波浪号): 兼容次版本号不变的修订号更新。例如 ~1.2.3 允许安装 1.2.3、1.2.4 等,但不允许 1.3.0 或 2.0.0。
  • > < = >= <= : 比较操作符。
  • *"" : 允许安装任何版本 (不推荐,风险高)。
  • - : 版本范围,例如 1.0.0 - 2.0.0
  • || : 组合范围,例如 ^1.0.0 || ^2.0.0

package-lock.json 则会锁定到 SemVer 范围内的某个确切版本。

2. Scoped Packages (作用域包)

Scoped Packages 以 @scope-name/package-name 的形式命名,例如 @angular/core@vant/weapp。它们解决了包名冲突的问题,并允许组织或个人在自己的命名空间下发布一系列相关的包。

  • 未指定作用域的包默认发布到全局公共注册表。
  • 指定作用域的包默认发布为私有包,只有付费用户才能发布和使用私有包。但你也可以使用 npm publish --access public 将 Scoped Package 发布为公共包。

3. 在 CI/CD 环境中使用 npm ci

在持续集成/持续部署 (CI/CD) 流水线中,为了确保构建的稳定性和可重复性,强烈建议使用 npm ci (clean install) 命令代替 npm install

bash
npm ci

npm ci 的特点:

  • 必须依赖 package-lock.json: 它会严格按照 package-lock.json 文件中记录的精确版本和结构来安装依赖。如果 package.jsonpackage-lock.json 不匹配,它会报错而不是修改 package-lock.json
  • 删除 node_modules 后重新安装: 它会先删除已存在的 node_modules 文件夹,然后从零开始安装。这保证了环境的干净。
  • 速度通常比 npm install 快: 因为它跳过了版本解析和依赖树计算的过程,直接按照 package-lock.json 安装。

npm ci 是为自动化环境设计的,确保每次构建都使用完全一致的依赖版本,极大地提高了构建的可靠性。

4. 缓存 (Cache)

npm 会在本地缓存下载的包文件,以便下次安装相同版本的包时可以直接使用缓存,而无需重新下载,这可以显著提高安装速度。

你可以通过以下命令管理缓存:

bash
npm cache clean --force # 清理缓存 (旧版本使用 npm cache clean)
npm cache verify # 验证缓存的完整性 (新版本推荐)

缓存的位置因操作系统而异,可以通过 npm config get cache 查看。

5. 配置 (Config)

npm 有很多配置选项,可以用来改变其行为,比如设置代理、修改默认安装路径、配置注册表地址等。

bash
npm config list # 列出所有配置
npm config get <key> # 获取指定配置的值
npm config set <key> <value> # 设置配置

例如,设置 npm 使用淘宝镜像源以加速下载:

bash
npm config set registry https://registry.npmmirror.com/

要恢复默认注册表:

bash
npm config delete registry

6. .npmrc 文件

除了全局配置,你还可以在项目根目录下创建 .npmrc 文件来设置项目特定的 npm 配置。这些配置会覆盖全局配置。这对于团队协作非常有用,可以将一些项目相关的配置(如私有注册表地址)放在项目中,确保团队成员使用一致的配置。

第六部分:npm 在前端开发工作流中的应用

掌握 npm 不仅仅是了解命令,更重要的是将其融入日常开发工作流:

  1. 项目启动: npm init -y 初始化项目,创建 package.json
  2. 引入依赖: npm install react react-dom 安装框架,npm install webpack webpack-cli -D 安装构建工具。依赖会被添加到 package.json,并生成 package-lock.json
  3. 定义脚本:package.jsonscripts 中定义 startbuildtest 等脚本。
  4. 日常开发: 运行 npm start 启动开发服务器。
  5. 构建与测试: 运行 npm run build 进行打包,npm run test 运行测试。
  6. 代码检查: 运行 npm run lint 执行代码规范检查。
  7. 版本控制:package.jsonpackage-lock.json 提交到 Git,忽略 node_modules
  8. 团队协作: 其他成员克隆仓库后,运行 npm install 来安装所有依赖。
  9. CI/CD: 在自动化构建环境中使用 npm ci 来安装依赖,确保构建环境一致。
  10. 更新维护: 定期运行 npm outdated 检查可更新的依赖,使用 npm update 或手动指定版本更新,并运行 npm audit 检查安全漏洞。

第七部分:常见问题与故障排除

  • node_modules 文件夹过大: 这是正常现象。理解它的大小并正确使用 .gitignore 忽略它即可。如果遇到问题,尝试删除 node_modulespackage-lock.json,然后重新运行 npm install
  • 安装速度慢: 尝试切换到国内镜像源,如淘宝镜像 (npm config set registry https://registry.npmmirror.com/)。检查网络连接。
  • 依赖版本冲突: package-lock.json 大部分情况下能解决这个问题。如果仍然出现问题,可能是因为 package.json 中的版本范围过于宽松,或者存在复杂的 peerDependencies 冲突。尝试手动指定问题依赖的精确版本,或者使用 npm update 结合 npm list 分析依赖树。
  • 安装过程中报错: 仔细阅读错误信息。常见的错误可能与网络问题、权限问题(在某些系统上全局安装可能需要 sudo,但不推荐)、Node.js/npm 版本不兼容、或者某个依赖包自身的安装脚本出错有关。尝试以管理员身份运行(不推荐长期使用)、清理缓存、更新 npm 自身 (npm install -g npm)。
  • npm run 脚本找不到命令: 确认该命令对应的包已经安装在项目的 dependenciesdevDependencies 中,并且该包的可执行文件位于 node_modules/.bin 目录下。确保你正在项目根目录运行 npm run 命令。

结论:npm 的未来与替代者

虽然 npm 是目前最流行的 JavaScript 包管理器,但它也面临一些挑战和竞争。Yarn(由 Facebook 开发)和 pnpm(性能至上)是两个值得关注的替代者。Yarn 在早期版本中针对 npm 的性能和可靠性问题做出了改进(如并行安装、离线模式、更严格的 lockfile),而 pnpm 则采用了独特的基于内容可寻址存储的方式,极大地优化了磁盘空间的占用和安装速度,并更严格地处理幽灵依赖(phantom dependencies)问题。

这三个工具在功能上大同小异,都围绕着 package.json 和 lockfile 工作,安装命令也非常相似 (yarn add vs npm install, pnpm add vs npm install)。npm 自身也在不断发展,在后续版本中吸收了 Yarn 和 pnpm 的许多优点,如 package-lock.json 的引入、npm ci 命令、性能优化等。

对于前端开发者而言,掌握 npm 仍然是必备技能,因为它拥有最庞大的用户群和生态系统,大多数项目默认使用 npm。了解 Yarn 和 pnpm 可以作为进阶,根据项目或团队需求选择最合适的工具。

总而言之,npm 不仅是一个简单的命令行工具,更是现代前端开发工作流的核心。它通过自动化依赖管理、促进模块复用、标准化项目结构和构建流程,极大地提高了开发效率和项目质量。深入理解并熟练运用 npm 的各种功能和最佳实践,将使你成为一个更高效、更专业的现代前端工程师。从现在开始,让 npm 成为你开发旅程中不可或缺的伙伴吧!


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部