npm常用命令系统介绍

npm常用命令系统介绍

  • npm help
  • npm init
    • package.json 文件
    • package.json 文件属性说明
    • 默认 package.json 文件--参数[-yes|-y]
    • 设置 package.json 中字段的默认值
    • package-lock.json 文件
  • npm [config|c]
    • 设置源
  • npm [install|i]
    • 可选参数:
    • 全局安装的特性
  • 包的删除
    • npm uninstall(un/rm)
    • npm rm -rf node_modules/
    • npm prune
  • 查看包的信息
    • `npm view `
    • `npm info `
    • `npm show `
    • npm outdated
    • npm list
    • npm update
      • 实战
        • 执行更新命令
        • 问题描述
        • 解决方案
  • `npm repo `
  • npm run [command]
  • npm link
  • 版本语义
    • 符号

之前介绍过一些常用的 npm 命令,如果是初始的使用上文已经够用

这里是第二次整理常用的 npm 的命令,一个是对 npm 的理解更加深入,同时也是一些不常用命令的学习,更加是一些内容的融会贯通

npm help

可以通过该命令查看所有 npm 支持的命令,并且当你的 node 版本升级的时候,也可以通过该命令最直接了解到命令的变化,终端输出内容:

npm <command>Usage:npm install        install all the dependencies in your project
npm install <foo>  add the <foo> dependency to your project
npm test           run this project command tests
npm run <foo>      run the script named <foo>
npm <command> -h   quick help on <command>
npm -l             display usage info for all commands
npm help <term>    search for help on <term> (in a browser)
npm help npm       more involved overview (in a browser)#所有支持的命令
All commands:access, adduser, audit, bugs, cache, ci, completion,config, dedupe, deprecate, diff, dist-tag, docs, doctor,edit, exec, explain, explore, find-dupes, fund, get, help,hook, init, install, install-ci-test, install-test, link,ll, login, logout, ls, org, outdated, owner, pack, ping,pkg, prefix, profile, prune, publish, query, rebuild, repo,restart, root, run-script, search, set, shrinkwrap, star,stars, start, stop, team, test, token, uninstall, unpublish,unstar, update, version, view, whoamiSpecify configs in the ini-formatted file:C:\Users\<username>\.npmrc
or on the command line via: npm <command> --key=valueMore configuration info: npm help config
Configuration fields: npm help 7 confignpm@9.5.0 D:\<npm address>

以上命令也输出了每个命令的简要介绍

npm init

package.json 文件

命令用于生成 package.json,而对于现在前端开发,我们常使用 package.json 管理引入的各种包,因此需要初始化该文件

输入该命令后会提示输入相关信息,最终生成 package.json 文件

package name: (test) package
version: (1.0.0)
description: test init command
entry point: (index.js)
test command: test
git repository: https://xxxx.www.com
keywords: test init
author:
license: (ISC)

package.json 文件

{"name": "package","version": "1.0.0","description": "test init command","main": "index.js","scripts": {"test": "test"},"repository": {"type": "git","url": "https://xxxx.www.com"},"keywords": ["test", "init"],"author": "","license": "ISC"
}

package.json 文件属性说明

关键字描述默认值
默认关键字
name项目名称
version版本号1.0.0
description描述信息默认 ‘’
main入口文件index.js
scripts可执行命令npm run []
keywords关键字[]
author作者
license遵循的协议ISC
bin可执行命令
其它
bugs当前目录 bug 信息{url,email}
os支持操作系统‘os’: [‘linux’]
engines项目使用的引擎{‘node’: ‘>=0.10.3 <15’}
homepage当前目录首页地址
dependencies线上环境需要的包
devDependencies仅开发/测试时需要的包
private为 true,拒绝发布
  • bugs
    • url bug 的追踪地址
    • email bug 的反馈 email
  • os
    • “os”: [“darwin”,“linux”]支持的操作系统
    • “os”: ["!linux"]不支持的操作系统

默认 package.json 文件–参数[-yes|-y]

npm init -yes
#或者
npm init -y

与无[-yes|-y] 参数不同,当选则-yes 或者-y 参数时,不需要根据提示输入信息,会直接创建默认的 package.json

设置 package.json 中字段的默认值

#设置
npm set init-author-name '<username>'
#获取
npm get  init-author-name
#删除
npm config delete init-author-name

npm set init.author.name方法已经弃用,现在是用init-author-name替代

通过设置了 author 字段后,npm init 的提示语句中不会再让你输入 author,而会默认使用上述值

其实上述命令是 npm config 命令的简写,详见npm config

package-lock.json 文件

用于锁定 package.json 中下载的包的真实版本,及这些包所依赖的包

之所以 package.json 与 package-lock.json 中包版本不同是因为执行 npm install 时 若没有 package-lock.json 文件,package.json 文件中包名前的^,~等符号决定了初始化具体包的规则,例如初始化当前时间点该包的补丁版本的最新版本,而在不同时间初始化,例如 3 个月前和当前时间的最新版本可能已经发生变化所以就会导致真实初始化后同一个包在 package-lock.json 中可能版本不同

npm [config|c]

命令中 config 可以简写为c,通过运行 npm help config 可以在联网的浏览器中打开npm config命令的详细介绍

#<key>=<value> 可以写成 <key> <value>,两种方式均可,配置属性值
npm config set <key>=<value> [<key>=<value> ...]
#获取配置的属性值
npm config get [<key> [<key> ...]]
#删除配置的属性值
npm config delete <key> [<key> ...]
#列出所有配置的相关信息--全局安装位置 用户配置信息与文件路径等
npm config list [--json]
#打开配置文件,并可查看全文与修改
npm config edit
npm config fix

我们一致可设置属性有init-author-name,其它可设置的属性包括哪些?
发现通过执行npm c edit将会打开 npm 的配置文件,文件中列出了所有的可配置选项

打开的文件是.npmrc,该文件是通过;表示注释,我们可以通过该文件查看到曾经配置的属性,也可以查看所有可以配置的选项

;;;;
;.npmrc配置文件的位置
; npm userconfig file: C:\Users\<username>\.npmrc
; this is a simple ini-formatted file
; lines that start with semi-colons are comments
; run `npm help 7 config` for documentation of the various options
;
; Configs like `@scope:registry` map a scope to a given registry url.
;
; Configs like `//<hostname>/:_authToken` are auth that is restricted
; to the registry host specified.;你曾经配置过的属性
cache=D:\PG\nodejs\node_cache
home=https://npm.taobao.org
init-author-email=<username>@qq.com
init-author-name=<username>
prefix=D:\PG\nodejs\node_global
registry=https://registry.npm.taobao.org/
strict-ssl=false;;;;
; all available options shown below with default values
;;;;;以下都是可以配置的属性
; _auth=null
; access=null
; all=false
; allow-same-version=false
; also=null
; audit=true
; audit-level=null
; auth-type=web
; before=null
; bin-links=true
; browser=null
; ca=null
; cache=C:\Users\<username>\AppData\Local/npm-cache
; cache-max=null
; cache-min=0
; cafile=null
; call=
; cert=null
; ci-name=null
; cidr=null
; color=true
; commit-hooks=true
; depth=null
; description=true
; dev=false
;
; diff-ignore-all-space=false
; diff-name-only=false
; diff-no-prefix=false
; diff-dst-prefix=b/
; diff-src-prefix=a/
; diff-text=false
; diff-unified=3
; dry-run=false
; editor=C:\Windows\notepad.exe
; engine-strict=false
; fetch-retries=2
; fetch-retry-factor=10
; fetch-retry-maxtimeout=60000
; fetch-retry-mintimeout=10000
; fetch-timeout=300000
; force=false
; foreground-scripts=false
; format-package-lock=true
; fund=true
; git=git
; git-tag-version=true
; global=false
; globalconfig=
; global-style=false
; heading=npm
; https-proxy=null
; if-present=false
; ignore-scripts=false
;
; include-staged=false
; include-workspace-root=false
; init-author-email=//作者邮箱
; init-author-name=//作者姓名
; init-author-url=
; init-license=ISC//package.json遵循证书规范
; init-module=~/.npm-init.js
; init-version=1.0.0//package.json初始化版本号
; init.author.email=//该方式弃用
; init.author.name=//该方式弃用
; init.author.url=//该方式弃用
; init.license=ISC//该方式弃用
; init.module=~/.npm-init.js//该方式弃用
; init.version=1.0.0//该方式弃用
; install-links=false
; install-strategy=hoisted
; json=false
; key=null
; legacy-bundling=false
; legacy-peer-deps=false
; link=false
; local-address=null
; location=user
; lockfile-version=null
; loglevel=notice
; logs-dir=null
; logs-max=10
; long=false
; maxsockets=15
; message=%s
; node-options=null
; noproxy=
; offline=false
;
; omit-lockfile-registry-resolved=false
; only=null
; optional=null
; otp=null
;
; package-lock=true
; package-lock-only=false
; pack-destination=.
; parseable=false
; prefer-offline=false
; prefer-online=false
; prefix=//全局安装地址
; preid=
; production=null
; progress=true
; provenance=false
; proxy=null
; read-only=false
; rebuild-bundle=true
; registry=https://registry.npmjs.org/
; replace-registry-host=npmjs
; save=true
; save-bundle=false
; save-dev=false
; save-exact=false
; save-optional=false
; save-peer=false
; save-prefix=^
; save-prod=false
; scope=
; script-shell=null
; searchexclude=
; searchlimit=20
; searchopts=
; searchstaleness=900
; shell=C:\Windows\system32\cmd.exe
; shrinkwrap=true
; sign-git-commit=false
; sign-git-tag=false
; strict-peer-deps=false
; strict-ssl=true
; tag=latest
; tag-version-prefix=v
; timing=false
; tmp=C:\Users\<username>\AppData\Local\Temp
; umask=0
; unicode=true
; update-notifier=true
; usage=false
; user-agent=npm/{npm-version} node/{node-version} {platform} {arch} workspaces/{workspaces} {ci}
; userconfig=~/.npmrc
; version=false
; versions=false
; viewer=browser
; which=null
;
; workspaces=null
; workspaces-update=true
; yes=null

设置源

npm config set registry <url>

同时登陆与设置源:

npm login --registry=http://192.168.1.1:8080/repository/npm-all/

回车后输入刚才新建用户的用户和密码和邮箱

npm [install|i]

npm i 是安装包的命令,i 是 install 的简写

  • npm install:安装当前命令所在路径中 package.json 中所有的包
  • npm install URL:本地指定路径包安装
  • npm install [packagename]@[version] :指定版本安装

可选参数:

  • 参数–save
    • npm i [packagename]等价于 npm i [packagename] --save
    • 若执行命令路径中没有 node_module 与 package.json,会自动生成
    • 没有参数默认是安装在当前项目的 node_module 中
    • 添加该包到 package.json 文件 的 dependencies 属性中
  • 参数-D
    • 若执行命令路径中没有 node_module 与 package.json,会自动生成
    • 安装到当前项目的 node_module 中
    • 添加该包到 package.json 中的 devDependencies 属性
  • 参数-g
    • 表示包是全局安装
    • 如果不知道全局安装的位置,可以 通过npm get prefix获取
  • 参数–production
    • 只初始化线上需要的包,即 dependencies 中的包,devDependencies 中的忽略

全局安装的特性

一般项目或软件的包是安装在命令的执行路径中的 node_modules 文件夹中,是需要被当前项目所使用的,一般 node_modules 提供 npm 与 node 之间的连接,例如:没有安装包的时候运行项目无法识别对应包中的方法

而工具包需要全局安装,全局安装的包,可以在任何位置执行它的命令;一般全局安装的时候会将全局安装包的命令添加到系统命令中去,并列出这些命令;

全局安装工具包主要是为了系统能识别该工具包的命令,并且执行该命令,例如 create-react-app 工具包,是个脚手架,是为了创建项目,但是项目创建完成后并不会用到该工具包!!!被项目使用的包必须安装到项目中,否则,一旦其他人使用该项目,却没有全局安装该包就会导致报错

包的删除

npm uninstall(un/rm)

卸载安装的包

npm uninstall [packagename]

如果事后发现之前安装的包用不到了最好卸载,也是排除其他人初始化的时候,安装不必要的包

npm rm -rf node_modules/

删除所有安装的包

npm prune

删除无用的软件包
npm 会拿你的 package.json 和 node_modules 目录进行比对,然后把那些在 package.json 中没有而 node_modules 存在的引用列出

还有那些你没有手动添加到 package.json 或者是执行 npm install [package] 然后在 package.json 中删除的包,都会被删掉。

npm prune [--production] :如果设置参数–production,并且环境变量是 production,则 devDependence 会全部被删除

查看包的信息

npm view <packagename>

查看 packagename 的最新信息,你可以用于确定你项目中的包是否是最新版本(查看远端 NPM 服务器上最新的版本信息),是否需要更新并可查看当前包的依赖包等

npm info <packagename>

查看相关信息,展示内容同npm view

npm show <packagename>

查看相关信息,展示内容同npm view

npm show <packagename> versions

可以直接查看包当前最新版本信息

npm outdated

列出将要过期的列表,以及可更新内容:

npm outdated [--depth=null|number]

  • depth 递归层次,表明查看过期包的层次

npm list

列出所有的依赖项以及依赖项的依赖项,包括子依赖项等

npm list --depth=0 查看可更新列表

npm update

  • npm list --depth=0 查看可更新列表
  • npm update
    • 更新命令所在执行目录下 package.json 文件中的可更新文件
    • npm update 更新具体包

如果 package.json 的包使用的是确切表示法,该命令不会更新包,只有^ 或 ~等非确切符号修饰的包会更新

实战

执行更新命令

npm update 更新 node_modules 中的包

问题描述

更新命令执行后 package-lock.json 版本更新
package.json 版本并没有同步更新

解决方案
  1. 全局安装 npm-check-updates
  2. 执行命令更新 package.json 文件-- ncu -u

如果使用 npm update,该命令不能够先执行,否则会导致无法执行 npm update,因为文件中的版本会变成最新的

如果先执行了 ncu -u,导致文件中的版本会变成最新的,此时如果删除 package-lock.json 文件或直接执行npm install,可以更新 node_modules 中的包

在这里插入图片描述

但是该命令也有些问题,因为可能出现不兼容!!

npm repo <packagename>

跳转到 git 相关地址

npm run [command]

在 package.json 中 script 中的命令,可以执行命令:

npm run [command]//==npm [command]npx:npm executenpx eslint --initeslint --init

初始化配置文件,以上均可 node 版本 v14.13.0

npm help scripts 查看特殊命令

pre[command]//在 command 命令之前执行的命令
[command]//执行command 命令
post[command]//在 command 命令之后执行的命令

可以围绕 command 提供 pre[command] 与 post[command] 相关钩子

npm link

对于希望能够被全局识别的项目命令,可以使用该命令,该命令用于创建与该项目相关的可以被全局识别符号链接

版本语义

[major].[minor].[patch]主要是用于表示依赖项的表示法,并在前面加上特殊字符,例如^/ ~

  • major 是主版本号,如果项目出现不兼容性等大的变更会变更 major
  • minor 是次版本号,如果项目出现功能性变更一般会增加 minor
  • patch 是修补版本号,如果是修复一些 bug 等小的变更升级 patch

符号

  • 无符号
    • 示例:“ora”: “4.0.5”
    • 安装确切的版本号 4.0.5
  • ^
    • 示例:“ora”: “^4.0.5”
    • 安装主版本号 4.x.y 的最新版本
  • ~
    • 示例:“ora”: “~4.0.5”
    • ~ install 安装次级版本 4.0.x 最新版本,也即任意修补版本
  • >|>=|<|<=
    • 示例:“ora”: “>4.0.5”
    • 大于 4.0.5 的版本号
  • -
    • 示例:“ora”: “4.0.5-4.1.5”
    • 等价于 version1< 版本号 < version2 ,版本号介于两者之间
  • ||
    • 示例:“ora”: “4.0.5||4.1.5”
    • 选择版本号 4.0.5 或者 4.1.5
  • *
    • 示例:“ora”: “*”
    • 匹配任意版本号
  • ""
    • 示例:“ora”: “”
    • 等价于*,匹配任意版本号
  • *
    • 示例:“ora”: “*”
    • 匹配任意版本号
  • git或者user/repo
    • 示例:“ora”: “https://abc.com/bvv.gz”
    • 匹配 Dependencies 地址
  • tag
    • 示例:“ora”: “latest”
    • 匹配标签对应的版本号,标签是 latest,alpha,beta
  • path
    • 示例:“ora”: “file:…/”
    • 匹配对应本地地址的包

如果没有 package-lock.json 文件,初始化的时间不同,安装时间包如果有新的版本,根据以上符号,可能安装不同版本的包,而 lock 文件则是为了锁定安装包的版本,确保即使包出现修补版本的更新,也按照 lock 中的版本进行安装

该特殊字符的功能可以通过网站体现出来

npm i <packagename>@latest 安装最新的版本

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/84739.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue3 - 实现动态获取菜单路由和按钮权限控制指令

GitHub Demo 地址 在线预览 前言 关于动态获取路由已在这里给出方案 Vue - vue-admin-template模板项目改造&#xff1a;动态获取菜单路由 这里是在此基础上升级成vue3和ts&#xff0c;数据和网络请求是通过mock实现的 具体代码请看demo!!! 本地权限控制&#xff0c;具体是通过…

(总目录)springboot - 实现zip文件上传并对zip文件解压, 包含上传oss

全文目录,一步到位 1.本文概述1.1 本文简介 2. 功能实现2.1 统一文件校验2.2 普通(多)文件上传[服务器]2.2.1 controller层2.2.2 service层2.2.3 业务impl实现类2.2.4 FileIOUtils工具包代码 2.3 zip文件的解压2.4 图片文件的压缩2.5 oss文件后端上传2.6 oss文件前端上传2.7 后…

matlab读写json文件

Background 通常&#xff0c;在matlab中使用mat文件进行数据存储。MAT文件是MATLAB中用来存储数据的二进制文件格式。MAT文件可以包含各种数据类型&#xff0c;包括数字、矩阵、向量、结构体、字符和函数等。但是&#xff0c;当和其他语言有交互时&#xff0c;mat文件会不太方便…

golang实现远程控制主机

文章目录 ssh原理使用golang远程下发命令使用golang远程传输文件 ssh原理 说到ssh原理个人觉得解释最全的一张图是这张华为画的 Connection establishment 这一步就是建立tcp连接 version negotiation 这一步是ssh客户端(连接者)和被ssh服务端(连接者)进行协议的交换&#xf…

Jetpack Compose 的简单 MVI 框架

Jetpack Compose 的简单 MVI 框架 在 Jetpack Compose 应用程序中管理状态的一种简单方法 选择正确的架构是至关重要的&#xff0c;因为架构变更后期代价高昂。MVP已被MVVM和MVI取代&#xff0c;而MVI更受欢迎。MVI通过强制实施结构化的状态管理方法&#xff0c;只在reducer中…

控制台日志打印console的封装,加入美化、行显示与打印开关,支持node.js环境

控制台日志打印console的封装&#xff0c;加入美化、行显示与打印开关&#xff0c;支持node.js环境 为什么要写这个&#xff1f; 封装这个控制台日志打印工具&#xff0c;主要是在项目中自己做的SDK需要提供给其他开发人员使用&#xff0c;加入了日志美化和打印打开&#xff…

【数据结构】顺序表与ArrayList

作者主页&#xff1a;paper jie 的博客 本文作者&#xff1a;大家好&#xff0c;我是paper jie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 本文录入于《JAVA数据结构》专栏&#xff0c;本专栏是针对于大学生&#xff0c;编程小白精心打造的。笔者用重金(时间和精…

【云计算】虚拟私有云 VPC

虚拟私有云 VPC 1.前言1.1 基本介绍1.2 VPC 的作用1.3 VPC 的适用人群 2.VPC 基本概念2.1 VPC 相关基本概念2.2 其他相关基本概念 3.VPC 通信场景3.1 VPC 内部互通3.2 VPC 间互通3.2.1 对等连接3.2.2 Transit Gateway 或者云联网 3.3 访问 Internet3.3.1 Internet 网关3.3.2 NA…

【HCIE】04.网络安全技术

端口隔离 在同一VLAN中可以隔离二层与三层通信&#xff0c;让同VLAN内的设备可以通信或者不可以通信。 定义一个端口隔离组&#xff0c;在一个组内无法互访&#xff0c;不在一个组里面可以进行互访 port-isolate enable group1 //使能端口隔离功能 port-isolate mdoe all //全…

B : DS顺序表--连续操作

Description 建立顺序表的类&#xff0c;属性包括&#xff1a;数组、实际长度、最大长度&#xff08;设定为1000&#xff09; 该类具有以下成员函数&#xff1a; 构造函数&#xff1a;实现顺序表的初始化。 插入多个数据的multiinsert(int i, int n, int item[])函数&#…

Unity 开发人员转CGE(castle Game engine)城堡游戏引擎指导手册

Unity 开发人员的城堡游戏引擎概述 一、简介2. Unity相当于什么GameObject&#xff1f;3. 如何设计一个由多种资产、生物等组成的关卡&#xff1f;4. 在哪里放置特定角色的代码&#xff08;例如生物、物品&#xff09;&#xff1f;Unity 中“向 GameObject 添加 MonoBehaviour”…

Vue3大屏项目实现数字跳动的效果

一、vue-count-to组件&#xff1a; 1、安装&#xff1a; npm install vue3-count-to --save 2、使用&#xff1a; <template><BaseCountTo:startVal"startVal":endVal"endVal":duration"duration":decimals"decimals":pr…

基于复旦微的FMQL45T900全国产化ARM核心模块(100%国产化)

TES745D是一款基于上海复旦微电子FMQL45T900的全国产化ARM核心板。该核心板将复旦微的FMQL45T900&#xff08;与XILINX的XC7Z045-2FFG900I兼容&#xff09;的最小系统集成在了一个87*117mm的核心板上&#xff0c;可以作为一个核心模块&#xff0c;进行功能性扩展&#xff0c;能…

Redis 五大类型源码及底层实现

面试题&#xff1a; 谈谈Redis数据类型的底层数据结构&#xff1a; SDS动态字符串双向链表玉缩列表ziplist哈希表hashtable跳表kiplist整数集合intset快速列表quicklist紧凑列表listpack Redis源代码的核心部分 官网&#xff1a;GitHub - redis/redis: Redis is an in-memory…

在已知的二维坐标里找到最接近的点

一、业务场景 最近在研发的项目&#xff0c;在做可视化层&#xff0c;在全球地图上&#xff0c;对我们的国家的陆地地图经纬度按照步长为1的间隔做了二维处理。在得到一组整数的点位信息后&#xff0c;需要将我们已有的数据库数据(业务项目)按照地址的经纬度&#xff0c;映射到…

大数据Flink(八十三):SQL语法的DML:With、SELECT WHERE、SELECT DISTINCT 子句

文章目录 SQL语法的DML:With、SELECT & WHERE、SELECT DISTINCT 子句 一、DML:With 子句

本地Docker Registry远程连接,为你带来高效便捷的镜像管理体验!

Linux 本地 Docker Registry本地镜像仓库远程连接 文章目录 Linux 本地 Docker Registry本地镜像仓库远程连接1. 部署Docker Registry2. 本地测试推送镜像3. Linux 安装cpolar4. 配置Docker Registry公网访问地址5. 公网远程推送Docker Registry6. 固定Docker Registry公网地址…

jmeterbeanshell调用jsonpath获取对应值

1.jmeter 新建线程组、Java Request、BeanShell Assertion、View Results Tree 2、在BeanShell Assertion中贴入代码&#xff1a; import org.apache.jmeter.extractor.json.jsonpath.JSONManager; import java.util.List; JSONManager js new JSONManager(); String jsonStr…

电商项目高级篇-01 elasticsearch

电商项目高级篇-01 elasticsearch 1、linux下安装elasticsearch和可视化工具2、docker设置虚拟机开机启动和容器开机启动3、elasticsearch的curd3.1、新增、更新3.2、查询 1、linux下安装elasticsearch和可视化工具 将安装好jdk1.8和tomcat的centos7下安装elasticsearch dock…

贝叶斯神经网络 BBB 学习中遇到的一些问题

这里写目录标题 贝叶斯公式模型概率的公式1/n 形式的贝叶斯公式 全概率公式全概率公式的积分形式 后验推理后验预测分布 posterior predictive distributionKL 散度 平均场 VIBayes by Backprop 代码重新参数化 贝叶斯公式 模型概率的公式 一开始看了这个 https://zhuanlan.z…