[{"_path":"/docs/tools","_draft":false,"_partial":false,"_empty":false,"title":"基础工具","description":"命令行，代码编辑器还有源代码管理是开发工作日常必备的工具，无论我们开发什么类型的应用，都会用到这几样工具。这些也是我们要在宁皓独立开发者训练营中用到的几样工具。","excerpt":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"基础工具"},"children":[{"type":"text","value":"基础工具"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"命令行，代码编辑器还有源代码管理是开发工作日常必备的工具，无论我们开发什么类型的应用，都会用到这几样工具。这些也是我们要在宁皓独立开发者训练营中用到的几样工具。"}]},{"type":"element","tag":"h2","props":{"id":"命令行"},"children":[{"type":"text","value":"命令行"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"做开发的时候需要用到大量的工具，有些带图形界面，有些不带图形界面。这些不带图形界面的工具，需要通过命令行界面执行文字命令来使用这些工具，命令行界面就是 command line interface，简称为 cli，要在命令行界面下输入的命令通常就是要运行的这个命令行工具的名字。创建应用、安装项目依赖，启动应用的开发服务，做源代码管理，远程连接管理服务器等等，这些任务一般都需要在命令行界面下完成。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在我们的电脑的操作系统里面都有一些命令行工具，可以通过系统里提供的命令行界面使用它们，比如 Windows 系统里的命令提示符，macOS 系统里的终端都属于命令行界面，通过它们可以执行系统里的命令行工具。在做开发的时候，我们会额外再安装一些命令行工具，这些工具也可以在命令行界面下使用。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"作为开发者我们要尽快熟悉这种在命令行界面下的工作方式，其实很简单没什么难度，就是输入要执行的命令，这些命令一般都支持一些选项与参数，通过这些东西可以配置要执行的命令具体要做的事情。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"可以先从最基本的处理文件与目录的命令开始学习，比如改变当前所在的位置，列出目录里的资源，创建、编辑、删除文件与目录，修改文件与目录的权限等等。要查看系统里的一个目录里的资源，如果通过图形界面完成这个操作，只需要浏览到这个目录的位置，然后双击打开它就可以了。如果是在命令行界面下完成这个动作，你需要执行改变工作目录的命令，进入到想要查看的目录，然后再执行列出命令列出这个目录里的资源。"}]},{"type":"element","tag":"h2","props":{"id":"代码编辑器"},"children":[{"type":"text","value":"代码编辑器"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"代码编辑器就是编写项目代码用的工具，这种工具有很多品种，有些擅长编写某种特定类型的应用，有些是比较通用的，可以编写任何类型的应用，其实任何具有文本编辑功能的东西都可以作为代码编辑器。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"代码编辑器是 code editor，还有一种东西叫 IDE，指的是 integrated development environment，也就是集成开发环境，或者叫综合开发环境，这种东西不但包含了代码编写功能，它还提供了开发某种类型的应用需要的一些配套工具，比如应用的运行环境，自动测试还有编译等等。代码编辑器与 IDE 之间的界限现在越来越模糊了，因为很多代码编辑器都支持安装额外的插件扩展自身的功能，通过这些插件或者叫扩展，也可以配置一套开发某种类型的应用的 IDE。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"推荐大家可以先选择一款通用型的代码编辑器， VSCode 现在就是一个不错的选择，开源、免费、可定制、可扩展。"}]},{"type":"element","tag":"h2","props":{"id":"源代码管理"},"children":[{"type":"text","value":"源代码管理"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"对项目做了一些修改，比如创建了新的文件，编写了新的代码，定义了新的功能，或者修复了 Bug，改进了某个功能等等，完成以后，我们可能希望要保存一下项目当前的这个状态，这样以后遇到问题的时候，可以查看项目当初保存的那个状态，可以把项目恢复到这个状态。要保存项目的当前状态，可以复制整个项目，以后出了问题，想要恢复项目的时候，可以用这个复制品替代正在开发的项目。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"保存项目状态更好的方法是使用源代码管理工具或者叫版本控制工具。我们也必须要对开发的项目做 “源代码管理”，source control management，或者叫 “版本控制”，version control，它们指的是一个意思。Git 是现在最常用的一种源代码管理或者叫版本控制工具，Git 也是每一位开发者必须会用的工具之一，也必然会用到它。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"用源代码管理工具可以更好的管理项目的状态，每次对项目做了一些修改以后，想要记录或者保存一下项目的这个修改之后的状态，就可以使用源代码管理工具做一次提交（commit），这样我们以后可以浏览项目的提交历史记录，可以查看每一次提交对项目都做了什么，是谁做了这次提交，为什么要做这次提交，这次提交都修改了项目的哪些文件哪几行代码等等。或者可以重做这次提交对项目做的所有修改，甚至可以把项目恢复到这次提交所保存的状态上。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"这些必要的工具我们只需要先熟悉一下，理解它们的作用就行了，我们的目的并不是成为使用工具的大师，而是去创造应用，提供服务，解决问题。不需要花太多精力来挑选工具、定制工具，也不需要用太多时间专门训练工具的使用技巧，可以在实际的开发工作中不断地积累必要的技巧。"}]}]},"navigation":false,"body":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"基础工具"},"children":[{"type":"text","value":"基础工具"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"命令行，代码编辑器还有源代码管理是开发工作日常必备的工具，无论我们开发什么类型的应用，都会用到这几样工具。这些也是我们要在宁皓独立开发者训练营中用到的几样工具。"}]},{"type":"element","tag":"h2","props":{"id":"命令行"},"children":[{"type":"text","value":"命令行"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"做开发的时候需要用到大量的工具，有些带图形界面，有些不带图形界面。这些不带图形界面的工具，需要通过命令行界面执行文字命令来使用这些工具，命令行界面就是 command line interface，简称为 cli，要在命令行界面下输入的命令通常就是要运行的这个命令行工具的名字。创建应用、安装项目依赖，启动应用的开发服务，做源代码管理，远程连接管理服务器等等，这些任务一般都需要在命令行界面下完成。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在我们的电脑的操作系统里面都有一些命令行工具，可以通过系统里提供的命令行界面使用它们，比如 Windows 系统里的命令提示符，macOS 系统里的终端都属于命令行界面，通过它们可以执行系统里的命令行工具。在做开发的时候，我们会额外再安装一些命令行工具，这些工具也可以在命令行界面下使用。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"作为开发者我们要尽快熟悉这种在命令行界面下的工作方式，其实很简单没什么难度，就是输入要执行的命令，这些命令一般都支持一些选项与参数，通过这些东西可以配置要执行的命令具体要做的事情。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"可以先从最基本的处理文件与目录的命令开始学习，比如改变当前所在的位置，列出目录里的资源，创建、编辑、删除文件与目录，修改文件与目录的权限等等。要查看系统里的一个目录里的资源，如果通过图形界面完成这个操作，只需要浏览到这个目录的位置，然后双击打开它就可以了。如果是在命令行界面下完成这个动作，你需要执行改变工作目录的命令，进入到想要查看的目录，然后再执行列出命令列出这个目录里的资源。"}]},{"type":"element","tag":"h2","props":{"id":"代码编辑器"},"children":[{"type":"text","value":"代码编辑器"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"代码编辑器就是编写项目代码用的工具，这种工具有很多品种，有些擅长编写某种特定类型的应用，有些是比较通用的，可以编写任何类型的应用，其实任何具有文本编辑功能的东西都可以作为代码编辑器。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"代码编辑器是 code editor，还有一种东西叫 IDE，指的是 integrated development environment，也就是集成开发环境，或者叫综合开发环境，这种东西不但包含了代码编写功能，它还提供了开发某种类型的应用需要的一些配套工具，比如应用的运行环境，自动测试还有编译等等。代码编辑器与 IDE 之间的界限现在越来越模糊了，因为很多代码编辑器都支持安装额外的插件扩展自身的功能，通过这些插件或者叫扩展，也可以配置一套开发某种类型的应用的 IDE。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"推荐大家可以先选择一款通用型的代码编辑器， VSCode 现在就是一个不错的选择，开源、免费、可定制、可扩展。"}]},{"type":"element","tag":"h2","props":{"id":"源代码管理"},"children":[{"type":"text","value":"源代码管理"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"对项目做了一些修改，比如创建了新的文件，编写了新的代码，定义了新的功能，或者修复了 Bug，改进了某个功能等等，完成以后，我们可能希望要保存一下项目当前的这个状态，这样以后遇到问题的时候，可以查看项目当初保存的那个状态，可以把项目恢复到这个状态。要保存项目的当前状态，可以复制整个项目，以后出了问题，想要恢复项目的时候，可以用这个复制品替代正在开发的项目。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"保存项目状态更好的方法是使用源代码管理工具或者叫版本控制工具。我们也必须要对开发的项目做 “源代码管理”，source control management，或者叫 “版本控制”，version control，它们指的是一个意思。Git 是现在最常用的一种源代码管理或者叫版本控制工具，Git 也是每一位开发者必须会用的工具之一，也必然会用到它。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"用源代码管理工具可以更好的管理项目的状态，每次对项目做了一些修改以后，想要记录或者保存一下项目的这个修改之后的状态，就可以使用源代码管理工具做一次提交（commit），这样我们以后可以浏览项目的提交历史记录，可以查看每一次提交对项目都做了什么，是谁做了这次提交，为什么要做这次提交，这次提交都修改了项目的哪些文件哪几行代码等等。或者可以重做这次提交对项目做的所有修改，甚至可以把项目恢复到这次提交所保存的状态上。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"这些必要的工具我们只需要先熟悉一下，理解它们的作用就行了，我们的目的并不是成为使用工具的大师，而是去创造应用，提供服务，解决问题。不需要花太多精力来挑选工具、定制工具，也不需要用太多时间专门训练工具的使用技巧，可以在实际的开发工作中不断地积累必要的技巧。"}]}],"toc":{"title":"","searchDepth":2,"depth":2,"links":[{"id":"命令行","depth":2,"text":"命令行"},{"id":"代码编辑器","depth":2,"text":"代码编辑器"},{"id":"源代码管理","depth":2,"text":"源代码管理"}]}},"_type":"markdown","_id":"content:docs:1.tools:0.index.md","_source":"content","_file":"docs/1.tools/0.index.md","_extension":"md"},{"_path":"/docs/tools/cli","_draft":false,"_partial":false,"_empty":false,"title":"命令行界面","description":"在宁皓独立开发者训练营中，有些训练任务需要在命令行界面下完成。Windows 用户可以安装一个 Cmder，macOS 用户可以使用系统自带的终端作为命令行界面。","excerpt":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"命令行界面"},"children":[{"type":"text","value":"命令行界面"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在宁皓独立开发者训练营中，有些训练任务需要在命令行界面下完成。Windows 用户可以安装一个 Cmder，macOS 用户可以使用系统自带的终端作为命令行界面。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在训练任务中需要使用在命令行界面下做的一些事情："}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"使用命令创建应用"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"启动应用在本地的开发服务"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"安装项目需要的功能包"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"执行命令快速创建应用需要的各种零部件"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"远程连接管理服务器"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"部署服务端应用"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"对开发的项目做源代码管理"}]}]},{"type":"element","tag":"h2","props":{"id":"cmderwindows"},"children":[{"type":"text","value":"Cmder（Windows）"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Cmder 是一款在 Windows 系统中使用的命令行界面。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"我们需要安装一个完整版的 Cmder 作为在 Windows 系统中使用的命令行界面工具。当您在训练视频中听到 “打开终端”，如果您的电脑的操作系统用的是 Windows，就可以打开 cmder。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://cmder.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://cmder.net/"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"Cmder 截图","src":"/images/docs/tools/cmd/cmder.png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"安装与使用"},"children":[{"type":"text","value":"安装与使用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 cmder 官方下载完整版的 cmder，将解压之后得到的 cmder 目录放在系统的 Program Files 目录中，然后可以在桌面上创建一个 cmder.exe 的快捷方式。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 cmder，可以右键点击 cmder 或是它的快捷方式，然后选择以 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"管理员身份运行"}]},{"type":"text","value":"，打开以后可以点击右下角的绿色加号小图标，新建一个 Bash as admin 类型的标签窗口，在这个标签窗口输入要执行的命令。"}]},{"type":"element","tag":"h3","props":{"id":"配置文件"},"children":[{"type":"text","value":"配置文件"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"因为我们会选择使用 Bash as admin 类型的 cmder，对应的配置文件应该是 cmder/config/user_profile.sh，也就是在这个文件里的配置会影响 Bash as admin 类型的 cmder。暂时我们只需要知道这个文件的存在即可。"}]},{"type":"element","tag":"h2","props":{"id":"终端macos"},"children":[{"type":"text","value":"终端（macOS）"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"终端（Terminal）是 macOS 系统中自带的一款命令行界面。在训练视频中，执行命令的时候一般都会在终端下面完成，如果您用的操作系统是 Windows，可以使用 Cmder 代替终端。"}]},{"type":"element","tag":"h3","props":{"id":"使用终端"},"children":[{"type":"text","value":"使用终端"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"终端是 macOS 系统自带的工具，不需要额外安装，在系统的启动台或者使用聚焦搜索，都可以打开终端，打开以后就可以在上面执行文字命令了。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"打开终端","src":"/images/docs/tools/cmd/open-terminal.png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"通过聚焦搜索找到终端"}]}]},{"type":"element","tag":"h3","props":{"id":"偏好设置"},"children":[{"type":"text","value":"偏好设置"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"通过终端的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"偏好设置"}]},{"type":"text","value":"，你可以设置终端使用的主题、字体字号、光标的样式等等。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开终端，在终端菜单下面可以打开编好设置（command + ,），然后在描述文件标签的下面，选择使用 Homebrew，字体是 Monaco 18，光标用的是下划线。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"终端的偏好设置","src":"/images/docs/tools/cmd/terminal-profile.png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"终端的偏好设置"}]}]},{"type":"element","tag":"h3","props":{"id":"配置文件-1"},"children":[{"type":"text","value":"配置文件"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"通过用户主目录下的 .zshrc 与 .zprofile 这两个文件可以修改终端的相关配置。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"因为文件的名字里是以点开头的，这种文件在系统里默认会被隐藏起来，也就是即使你打开了文件所在的目录，也可能看不到 .zshrc 或 .zprofile 这两个文件。在终端可以使用 vi 或 code（VSCode 编辑器里带的一个命令行工具） 命令打开并编辑这两个文件。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"使用 vi 命令编辑"}]}]},{"type":"element","tag":"code","props":{"code":"vi ~/.zprofile\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"vi ~/.zprofile\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令会用 vi 编辑器打开当前登录用户主目录下的 .zprofile 文件。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"使用 code 命令编辑"}]}]},{"type":"element","tag":"code","props":{"code":"code ~/.zprofile\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"code ~/.zprofile\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令会使用 VSCode 编辑器打开当前登录用户主目录下的 .zprofile 这个文件。"}]},{"type":"element","tag":"h3","props":{"id":"修改提示符"},"children":[{"type":"text","value":"修改提示符"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开终端，在光标左边的东西就是提示符，默认这个提示符看起来可能像下面这样："}]},{"type":"element","tag":"code","props":{"code":"wanghao@wanghaodeMacBook ~ %\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"wanghao@wanghaodeMacBook ~ %\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在提示符里会显示当前登录到系统中的用户（wanghao）与电脑的名字（wanghaodeMacBook），还有当前所在的目录（~），另外在结尾还有一个 % 符号。在训练营的视频内容中，终端的提示符是一个简单的 → 符号，如果你希望使用同样的提示符，可以通过修改 ~/.zshrc 这个文件。"}]},{"type":"element","tag":"code","props":{"code":"vi ~/.zshrc\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"vi ~/.zshrc\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"vi"}]},{"type":"text","value":" 是一个在命令行界面下使用的文本编辑器，它后面是要编辑的文件的位置，如果文件不存在就会新创建一个。打开以后，先按一下小写的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"i"}]},{"type":"text","value":" 按键，这会打开编辑器的编辑模式，这样就可以编辑文件内容了，在文件中添加下面这行代码："}]},{"type":"element","tag":"code","props":{"code":"PROMPT='→ '\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"PROMPT='→ '\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"上面这行配置的就是终端的命令提示符。完成编辑以后，按一下 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"esc"}]},{"type":"text","value":" 退出编辑模式，再输入 "},{"type":"element","tag":"code-inline","props":{},"children":[{"type":"text","value":":wq"}]},{"type":"text","value":" 并按下回车*，*这样会保存并且退出文件，注意要在英文状态下输入这些命令。修改了配置文件以后，需要重启终端才能生效，或者可以执行一下 srouce ~/.zshrc，这时你会发现命令提示符就会是我们修改之后的样子了。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"修改了描述文件与提示符之后的终端","src":"/images/docs/tools/cmd/terminal-prompt.png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"修改了描述文件与提示符之后的终端"}]}]},{"type":"element","tag":"h2","props":{"id":"常用命令"},"children":[{"type":"text","value":"常用命令"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"下面是几个经常会用到的命令行工具。"}]},{"type":"element","tag":"h3","props":{"id":"pwd输出当前位置"},"children":[{"type":"text","value":"pwd（输出当前位置）"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"pwd"}]},{"type":"text","value":" （print working directory）命令可以查看当前所在的位置。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"pwd"}]},{"type":"text","value":" ，得到的结果是一个路径，这个路径是用目录名字加斜线表示的*，*通过这个路径我们就可以知道自己当前所在的位置，这个位置其实就是系统里的某个目录。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"→ pwd\n/Users/wanghao/desktop\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"→ pwd\n/Users/wanghao/desktop\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行 pwd 命令，显示当前所在位置是 /Users/wanghao/desktop。"}]},{"type":"element","tag":"h3","props":{"id":"ls列出资源"},"children":[{"type":"text","value":"ls（列出资源）"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"ls（list）命令可以列出指定位置里的资源，也就是列出某个位置里包含的文件与目录。"}]},{"type":"element","tag":"h4","props":{"id":"列出当前位置里的资源"},"children":[{"type":"text","value":"列出当前位置里的资源"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行 ls 命令，如果不指定具体位置，就会列出当前所在位置里的东西。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"→ ls\nREADME.md    dist        package.json    src ...\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"→ ls\nREADME.md    dist        package.json    src ...\n"}]}]}]},{"type":"element","tag":"h4","props":{"id":"列出指定位置里的资源"},"children":[{"type":"text","value":"列出指定位置里的资源"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 ls 命令的后面可以加上一个具体的位置，这样可以列出这个位置里包含的资源。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"→ ls ~/desktop/xuanwu\nREADME.md    dist        package.json    src ...\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"→ ls ~/desktop/xuanwu\nREADME.md    dist        package.json    src ...\n"}]}]}]},{"type":"element","tag":"h4","props":{"id":"列出资源的详细信息"},"children":[{"type":"text","value":"列出资源的详细信息"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果想列出资源更详细的信息，可以配合使用 l（长格式） 与 a（所有） 这两个选项。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"→ ls -la\ntotal 832\ndrwxr-xr-x   20 wanghao  staff     640 Feb 25 17:31 .\ndrwxr-xr-x@  59 wanghao  staff    1888 Apr 26 10:50 ..\ndrwxr-xr-x   12 wanghao  staff     384 Apr 26 10:55 .git\n-rw-r--r--    1 wanghao  staff     283 Mar  7 22:15 .gitignore\n-rw-r--r--    1 wanghao  staff     315 Mar  7 22:15 README.md\ndrwxr-xr-x@   8 wanghao  staff     256 Feb  5 09:41 dist\ndrwxr-xr-x  860 wanghao  staff   27520 Jan 24 09:01 node_modules\n-rw-r--r--    1 wanghao  staff    1580 May  5 09:45 package.json\ndrwxr-xr-x   22 wanghao  staff     704 May  5 09:45 src\n...\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"→ ls -la\ntotal 832\ndrwxr-xr-x   20 wanghao  staff     640 Feb 25 17:31 .\ndrwxr-xr-x@  59 wanghao  staff    1888 Apr 26 10:50 ..\ndrwxr-xr-x   12 wanghao  staff     384 Apr 26 10:55 .git\n-rw-r--r--    1 wanghao  staff     283 Mar  7 22:15 .gitignore\n-rw-r--r--    1 wanghao  staff     315 Mar  7 22:15 README.md\ndrwxr-xr-x@   8 wanghao  staff     256 Feb  5 09:41 dist\ndrwxr-xr-x  860 wanghao  staff   27520 Jan 24 09:01 node_modules\n-rw-r--r--    1 wanghao  staff    1580 May  5 09:45 package.json\ndrwxr-xr-x   22 wanghao  staff     704 May  5 09:45 src\n...\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 l 选项会用长格式显示资源，每一行是一个资源（文件或目录），每一栏分别表示不同的东西，比如 drwxr-xr-x 表示的是资源的权限，wanghao 是资源的拥有者，staff 表示资源所属的用户组。使用了 a 这个选项以后会列出所有资源（包含隐藏文件），比如 .git 与 .gitignore，这两个文件的名字是以点开头的，在不使用 a 选项的情况下，列出资源时是不会显示这种文件的。"}]},{"type":"element","tag":"h3","props":{"id":"cd改变当前位置"},"children":[{"type":"text","value":"cd（改变当前位置）"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"cd（change directory）命令可以改变当前位置，也就是进入到系统的某个目录的下面。"}]},{"type":"element","tag":"h4","props":{"id":"进入到某个位置"},"children":[{"type":"text","value":"进入到某个位置"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 cd 命令的后面加上一个具体的路径，可以进入到这个指定的位置。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"cd ~/desktop\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cd ~/desktop\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"~/desktop 是一个路径，这里的 ~（"},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"波浪号"}]},{"type":"text","value":"）指的位置就是用户的主目录。执行上面这行命令可以进入到用户主目录下面的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"desktop"}]},{"type":"text","value":" 这个目录里面，这个目录里的东西默认会出现在你的电脑桌面上。 确定当前所在位置可以用 pwd 命令，列出当前位置里的资源可以执行 ls 命令。"}]},{"type":"element","tag":"h4","props":{"id":"回到上一级目录"},"children":[{"type":"text","value":"回到上一级目录"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 cd 命令的后面加上 ../，可以进入到当前目录的上一级目录。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"cd ../\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cd ../\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"../"}]},{"type":"text","value":"（"},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"两个点加一条斜线"}]},{"type":"text","value":"）表示的是上一级目录，比如当前位置是在 /Users/wanghao/desktop*，*对于这个位置来说，它的上一级目录就是 /Users/wanghao。这样如果执行 cd ../ 以后，用 pwd 输出当前的位置，得到的结果会显示当前是在 /Users/wanghao 这个位置上。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"../../"}]},{"type":"text","value":" 表示的是上一级目录的上一级目录， "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"./"}]},{"type":"text","value":" 一个点加一条斜线表示的是当前目录。"}]},{"type":"element","tag":"h3","props":{"id":"mkdir创建目录"},"children":[{"type":"text","value":"mkdir（创建目录）"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"mkdir（make directory）命令可以创建目录。"}]},{"type":"element","tag":"h4","props":{"id":"在当前位置创建目录"},"children":[{"type":"text","value":"在当前位置创建目录"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 mkdir 命令的后面加上要创建的目录的名字，可以创建一个目录。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"mkdir xuanwu\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"mkdir xuanwu\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"上面命令会在当前目录下创建一个叫 xuanwu 的目录。"}]},{"type":"element","tag":"h4","props":{"id":"在指定位置创建目录"},"children":[{"type":"text","value":"在指定位置创建目录"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 mkdir 命令的后面可以加上要创建的目录的具体位置。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"mkdir -p ~/desktop/projects/xuanwu\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"mkdir -p ~/desktop/projects/xuanwu\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"注意上面我们在命令里用了一个 p 选项，如果不用这个选项必须要保证新目录的父目录是存在的，也就是在桌面上应该有个 projects 目录，这样才能正常地创建 xuanwu 这个目录，如果 projects 这个目录不存在，执行命令时就会报错：No such file or directory。用了 p 选项以后，如果需要的目录不存在就会自动创建需要的目录。"}]},{"type":"element","tag":"h3","props":{"id":"cp复制"},"children":[{"type":"text","value":"cp（复制）"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"cp（copy）命令可以复制文件或目录。"}]},{"type":"element","tag":"h4","props":{"id":"复制文件"},"children":[{"type":"text","value":"复制文件"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行 cp 命令的时候需要提供想要复制的文件与复制到的位置。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"cp assets/example.env .env\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cp assets/example.env .env\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"上面这行命令会复制当前位置里的 assets 目录里的 example.env 这个文件，复制到的位置是当前目录，名字叫 .env。也就是在当前目录下应该会有一个 .env 文件，这个文件就是 assets/example.env 的复制品。"}]},{"type":"element","tag":"h4","props":{"id":"复制目录"},"children":[{"type":"text","value":"复制目录"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"用 cp 命令复制目录的时候需要加上 R 选项，这样可以复制指定的目录以及它里面包含的所有东西。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"cp -R config ../config_bak\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cp -R config ../config_bak\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这个命令会复制当前位置里的 config 这个目录（包含目录里的所有文件与目录），将复制品放在上一级目录里，名字是 config_bak。"}]},{"type":"element","tag":"h3","props":{"id":"rm删除"},"children":[{"type":"text","value":"rm（删除）"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"rm（remove）命令可以删除文件或目录。"}]},{"type":"element","tag":"h4","props":{"id":"删除文件"},"children":[{"type":"text","value":"删除文件"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行 rm 命令删除文件时，需要提供要删除的文件。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"rm package-lock.json\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"rm package-lock.json\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这个命令会将当前目录下的 package-lock.json 这个文件删除掉。"}]},{"type":"element","tag":"h4","props":{"id":"删除目录"},"children":[{"type":"text","value":"删除目录"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"用 rm 命令删除目录时可以提供 r（递归） 与 f（强制） 这两个选项，这样会强制将指定目录以及它里面包含的全部东西一块删除掉。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"rm -rf ~/desktop/nid-node/node_modules\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"rm -rf ~/desktop/nid-node/node_modules\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这个命令，会强制将桌面上的 nid-node 目录里的 node_modules 这个目录以及它里面包含的东西全部都删除掉。"}]},{"type":"element","tag":"h3","props":{"id":"vi命令行编辑器"},"children":[{"type":"text","value":"vi（命令行编辑器）"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"vi（visual）是一个在命令行界面下使用的编辑器，可以用它编辑或创建文本文件。"}]},{"type":"element","tag":"h4","props":{"id":"打开编辑器"},"children":[{"type":"text","value":"打开编辑器"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 vi 命令的后面加上要编辑的文件的具体位置，执行以后就会用 vi 编辑器打开指定的文件。如果要编辑当前目录下的文件，可以直接输入文件的名字。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"vi ~/desktop/xuanwu/README.md\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"vi ~/desktop/xuanwu/README.md\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"用 vi 编辑的文件可以不存在，但要保证文件所在目录是存在的，如果目录不存在，保存文件时就会报错：Can't open file for writing。所以在用 vi 编辑或创建文件之前，先保存文件目录是存在的，如果不存在，可以使用 mkdir 命令创建所需目录。"}]},{"type":"element","tag":"h4","props":{"id":"编辑文件"},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"编辑文件"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 vi 编辑文件内容，需要打开编辑模式，按一下 i 按键可以打开编辑模式，这时窗口底部会显示 — INSERT —，完成编辑以后按 esc 按键可以退出编辑模式。"}]},{"type":"element","tag":"h4","props":{"id":"保存文件并退出"},"children":[{"type":"text","value":"保存文件并退出"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"编辑了文件内容以后，如果想要保存对文件的修改并退出 vi 编辑器，先确定当前在正常模式下，按 esc 可以退出其它模式回到正常模式，然后执行："}]},{"type":"element","tag":"code","props":{"code":":wq\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":":wq\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"w 表示写入，q 表示退出，输入 "},{"type":"element","tag":"code-inline","props":{},"children":[{"type":"text","value":":wq"}]},{"type":"text","value":" 以后按下回车，这样就会保存对文件的修改，然后关掉 vi 编辑器。"}]},{"type":"element","tag":"h4","props":{"id":"放弃修改直接强制退出"},"children":[{"type":"text","value":"放弃修改直接强制退出"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果不想保存对文件的修改，打算强制退出 vi 编辑器，可以执行："}]},{"type":"element","tag":"code","props":{"code":":q!\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":":q!\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"q 表示退出，! 表示强制。"}]},{"type":"element","tag":"h4","props":{"id":"翻页查看文件"},"children":[{"type":"text","value":"翻页查看文件"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"按 ctrl + f 按键可以向下翻页查看文件，按 ctrl + b 按键可以向上翻页查看文件。"}]},{"type":"element","tag":"h4","props":{"id":"搜索文件内容"},"children":[{"type":"text","value":"搜索文件内容"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在正常模式下，输入 /，后面加上要搜索的关键词，按下回车，可以搜索并定位到当前文件包含指定关键词的地方。按 n 可以定位到下一个匹配的地方，按 N 可以定位到上一个匹配的地方。"}]},{"type":"element","tag":"h3","props":{"id":"cat输出文件内容"},"children":[{"type":"text","value":"cat（输出文件内容）"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"cat（concatenate）命令可以将多个文件合并成一个文件，也可以输出指定文件里的内容。"}]},{"type":"element","tag":"h4","props":{"id":"输出文件内容"},"children":[{"type":"text","value":"输出文件内容"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 cat 命令的后面提供要查看内容的文件，可以输出这个文件里的内容。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"cat ~/.ssh/id_rsa.pub\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cat ~/.ssh/id_rsa.pub\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这行命令会输出在用户主目录下的 .ssh 目录里的 id_rsa.pub 这个文件里的内容。"}]},{"type":"element","tag":"h2","props":{"id":"相关概念"},"children":[{"type":"text","value":"相关概念"}]},{"type":"element","tag":"h3","props":{"id":"终端"},"children":[{"type":"text","value":"终端"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"终端就是一种命令行界面工具，当您在训练营中听到或看到 “在终端” 或 “打开终端” 时，Windows 用户可以打开 cmder，macOS 用户可以打开系统自带的终端。"}]},{"type":"element","tag":"h3","props":{"id":"用户主目录"},"children":[{"type":"text","value":"用户主目录"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在操作系统里的用户一般都有一个主目录，这个目录的位置在不同的操作系统里是不一样的，比如在 macOS 系统里，wanghao 这个用户的主目录的位置应该是 /Users/wanghao，在 Ubuntu 系统里，wanghao 这个用户的主目录应该是在 /home/wanghao。"}]},{"type":"element","tag":"h4","props":{"id":"进入用户主目录"},"children":[{"type":"text","value":"进入用户主目录"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端可以使用 ~（波浪号）表示当前用户的主目录所在的位置，比如在 macOS 系统里，当前登录的用户如果是 wanghao，这个 ~ 表示的位置应该就是 /Users/wanghao，所以进入到这个位置，可以执行："}]},{"type":"element","tag":"code","props":{"code":"cd ~\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cd ~\n"}]}]}]},{"type":"element","tag":"h3","props":{"id":"根目录"},"children":[{"type":"text","value":"根目录"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"根目录指的是某个地方的第一级别的目录。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"比如一个项目的位置是 ~/desktop/nid-node，对于这个项目来说，它的根目录指的就是 ~/desktop/nid-node。如果我们在这个项目的根目录下创建了一个 README.md 文件，那这个文件的位置应该就是 ~/desktop/nid-node/README.md。"}]},{"type":"element","tag":"h4","props":{"id":"系统根目录"},"children":[{"type":"text","value":"系统根目录"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 macOS 与 Linux 类型的操作系统里，系统的根目录指的是 / 这个位置，比如执行 cd / 命令，会进入到系统根目录的下面。/Users 这个路径，可以描述为系统根目录下面的 Users 这个目录。"}]},{"type":"element","tag":"h3","props":{"id":"路径"},"children":[{"type":"text","value":"路径"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"路径可以用来表示在操作系统里的一个目录或文件的位置，路径由目录与斜线组成，如果是表示文件的路径，在路径中还会包含文件的名字。"}]},{"type":"element","tag":"h4","props":{"id":"目录的路径"},"children":[{"type":"text","value":"目录的路径"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"表示目录的路径由目录的名字与斜线组成。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"/Users/wanghao/desktop/xuanwu\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"/Users/wanghao/desktop/xuanwu\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"上面这个路径从系统的根目录开始，这个路径指的就是系统根目录下的 Users 里面的 wanghao 里的 desktop 目录里的 xuanwu 这个目录。"}]},{"type":"element","tag":"h4","props":{"id":"文件的路径"},"children":[{"type":"text","value":"文件的路径"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"表示文件的路径由目录的名字、斜线与文件组成。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"/Users/wanghao/desktop/xuanwu/src/main.ts\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"/Users/wanghao/desktop/xuanwu/src/main.ts\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"上面这个路径从系统的根目录开始，这个路径指的就是系统根目录下的 Users 里面的 wanghao 里的 desktop 目录里的 xuanwu 这个目录里的 src 目录里的 main.ts 这个文件。也就是 main.ts 这个文件在 src 目录的下面，src 在 xuanwu 的下面，xuanwu 在 desktop 目录里，desktop 在 wanghao 这个目录里，wanghao 在 Users 这个目录里，Users 这个目录在系统的根目录的下面。"}]},{"type":"element","tag":"h4","props":{"id":"相对路径"},"children":[{"type":"text","value":"相对路径"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"相对路径指的是相对于当前位置的路径。相对路径表示的实际位置，会随着当前位置的变化而变化。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"src/main.ts\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"src/main.ts\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"相对路径不以系统根目录（/）开始。描述上面这个相对路径，可以说当前目录下的 src 目录里的 main.ts。如果当前位置是 /Users/wanghao/desktop/xuanwu，相对于这个位置，上面这个路径在系统中的实际位置应该就是："}]},{"type":"element","tag":"code","props":{"code":"/Users/wanghao/desktop/xuanwu/src/main.ts\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"/Users/wanghao/desktop/xuanwu/src/main.ts\n"}]}]}]},{"type":"element","tag":"h4","props":{"id":"绝对路径"},"children":[{"type":"text","value":"绝对路径"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"绝对路径指的就是一个目录或文件在系统中的实际位置。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"/Users/wanghao/desktop\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"/Users/wanghao/desktop\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"绝对路径会从系统的根目录开始。描述上面这个绝对路径，可以说在系统根目录下的 Users 里的 wanghao 里的 desktop 这个目录。"}]},{"type":"element","tag":"h3","props":{"id":"环境变量"},"children":[{"type":"text","value":"环境变量"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"环境变量（environment variables）就是在系统环境中存在的一些变量，程序可以读取这些环境变量并获取到它们的值。"}]},{"type":"element","tag":"h4","props":{"id":"查看环境变量"},"children":[{"type":"text","value":"查看环境变量"}]},{"type":"element","tag":"code","props":{"code":"printenv\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"printenv\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端执行 printenv 命令可以输出当前已有的一些环境变量以及它们对应的值。环境变量的名字一般全部使用大写字母，等号右边是环境变量对应的值。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"LANG=zh_CN.UTF-8\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"LANG=zh_CN.UTF-8\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"LANG 是环境变量的名字，zh*CN.UTF-8 是 LANG 这个环境变量对应的值。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"在 Windows 系统中通过 Cmder 执行 printenv 命令","src":"/images/docs/tools/cmd/cmder-printenv.png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 Windows 系统中通过 Cmder 执行 printenv 命令"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"在 macOS 系统中通过终端执行 printenv 命令","src":"/images/docs/tools/cmd/terminal-printenv.png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"*在 macOS 系统中通过终端执行 printenv 命令_"}]},{"type":"element","tag":"h4","props":{"id":"设置环境变量"},"children":[{"type":"text","value":"设置环境变量"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端，执行 export 命令可以添加环境变量，被添加的环境变量只能在当前这个终端标签里使用。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"export MACHINE_NAME=玄武\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"export MACHINE_NAME=玄武\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令会添加一个环境变量叫 MACHINE*NAME，它的值是 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"玄武"}]},{"type":"text","value":"。这样在当前这个终端标签里运行的程序，都可以读取到 MACHINE_NAME 这个环境变量，得到的值就是 *玄武_ 。执行 printenv 可以确定一下是否包含了 MACHINE_NAME 这个环境变量。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"关掉这个终端标签，重新再打开，之前设置的环境变量就不见了。如果你希望每次打开终端的时候自动设置一些环境变量，可以将设置环境变量用的命令添加到终端的配置文件里，在 macOS 系统里就是 ~/.zprofile，Windows 系统里是 cmder/config/user-profile.sh。"}]},{"type":"element","tag":"h3","props":{"id":"path"},"children":[{"type":"text","value":"PATH"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"PATH 是一个环境变量，它的值是一组路径，在终端执行命令的时候，会在这些路径里查找是否包含要执行的命令程序。"}]},{"type":"element","tag":"h4","props":{"id":"输出-path-的值"},"children":[{"type":"text","value":"输出 PATH 的值"}]},{"type":"element","tag":"code","props":{"code":"printenv PATH\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"printenv PATH\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"得到的结果应该是一组路径，不同的路径之间会用冒号分隔开，在这些路径里都会包含一些可以执行的命令程序。"}]},{"type":"element","tag":"code","props":{"code":"/usr/local/bin:/usr/bin:/bin: ...\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"/usr/local/bin:/usr/bin:/bin: ...\n"}]}]}]},{"type":"element","tag":"h4","props":{"id":"设置-path-的值"},"children":[{"type":"text","value":"设置 PATH 的值"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"安装了软件以后，可能会包含一些命令行工具，如果想在终端直接使用它们，需要将包含这些命令行工具的目录添加到 PATH 里。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"export PATH=$PATH:/Applications/flutter/bin\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"export PATH=$PATH:/Applications/flutter/bin\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端的配置文件里添加上面这行代码，结果就是在 PATH 这个环境变量里添加了一个新的路径，位置是 /Applications/flutter/bin，也就是在这个位置里包含的命令行工具，以后都可以直接在终端执行。这里的 $PATH 引用的是原来的 PATH 这个环境变量的值。"}]},{"type":"element","tag":"h2","props":{"id":"钥匙"},"children":[{"type":"text","value":"钥匙"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"钥匙（keys）是加密解密用的东西（文本文件），在训练营中，向远程仓库推送代码的时候，登录管理远程服务器的时候都会用到在本地电脑里存放的钥匙。钥匙一般都成对出现，一个密钥（private key）对应一个公钥（public key）。我们可以给当前登录到系统的用户生成一对钥匙，其中密钥是私密的，决对不能交给别人，但是公钥可以放心交到其它地方使用。"}]},{"type":"element","tag":"h3","props":{"id":"生成钥匙对"},"children":[{"type":"text","value":"生成钥匙对"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"当前用户的钥匙对会放在用户主目录下的 .ssh 这个目录里，你可以先确定一下是否已经为当前用户生成了钥匙对。"}]},{"type":"element","tag":"code","props":{"code":"ls ~/.ssh\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"ls ~/.ssh\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令，如果发现用户主目录下的 .ssh 里面，有 id_rsa（密钥） 与 id_rsa.pub（公钥） 这两个文件，说明当前用户已经拥有了钥匙对。如果没有这两个文件，可以执行 ssh-keygen 命令生成它们。"}]},{"type":"element","tag":"code","props":{"code":"ssh-keygen\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"ssh-keygen\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端，执行 ssh-keygen 命令，并一路回车，完成以后就会在用户主目录下的 .ssh 目录里生成一对钥匙。这个命令只需要执行一次，重复执行会覆盖掉之前生成的钥匙对。"}]},{"type":"element","tag":"h3","props":{"id":"查看公钥文件内容"},"children":[{"type":"text","value":"查看公钥文件内容"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在配置三方服务使用钥匙验证身份的时候，我们需要复制用户的公钥文件里的内容。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"查看公钥文件里的内容可以执行："}]},{"type":"element","tag":"code","props":{"code":"cat ~/.ssh/id_rsa.pub\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cat ~/.ssh/id_rsa.pub\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"输出的内容看起来像这样："}]},{"type":"element","tag":"code","props":{"code":"ssh-rsa AAAAB3NzaC1yc2EAAX...\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"ssh-rsa AAAAB3NzaC1yc2EAAX...\n"}]}]}]},{"type":"element","tag":"h2","props":{"id":"技巧"},"children":[{"type":"text","value":"技巧"}]},{"type":"element","tag":"h3","props":{"id":"知道命令来自哪里"},"children":[{"type":"text","value":"知道命令来自哪里"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果你想知道一个命令行工具到底在哪儿，可以用 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"which"}]},{"type":"text","value":" 命令，比如我想知道 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"ls"}]},{"type":"text","value":" 这个命令行工具的位置，可以执行："}]},{"type":"element","tag":"code","props":{"code":"which ls\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"which ls\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"得到的结果就是 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"ls"}]},{"type":"text","value":" 这个命令行工具的位置，具体在哪取决于你的操作系统。在 macOS 系统里，这个工具是在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"/bin/ls"}]},{"type":"text","value":" 。在 Windows 系统里，如果你用的是 bash 类型的 Cmder，会显示这个工具的位置是 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"/usr/bin/ls"}]},{"type":"text","value":" 。"},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"usr"}]},{"type":"text","value":" 一般表示的是 user，也就是用户的意思，"},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"bin"}]},{"type":"text","value":" 这个目录里通常包含的是一些可以执行的程序。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"这个 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"/usr/bin"}]},{"type":"text","value":" 应该是 Cmder 设置的一个目录的别名，因为在 Windows 系统里并没有这个地方，实际的位置应该是 Cmder 里的某个目录的下面。在我的系统里，"},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"/usr/bin"}]},{"type":"text","value":" 真正的位置是 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"/C/Program\\ Files/cmder/vendor/git-for-windows/usr/bin"}]},{"type":"text","value":" ，进入到这个目录的下面，列出目录里的资源，你会发现一些 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":".exe"}]},{"type":"text","value":" 后缀的文件。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 Windows 系统里，带这种 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":".exe"}]},{"type":"text","value":" 后缀的文件就是可执行的程序。在后面我们会用到这里面的一些命令行工具，比如远程连接服务器用的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"ssh"}]},{"type":"text","value":" ，生成密钥文件用的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"ssh-keygen"}]},{"type":"text","value":" 等等。Windows 系统默认都不带这些东西，这也是我们为什么要用 Cmder 来代替 Windows 系统自带的命令行界面。"}]},{"type":"element","tag":"h3","props":{"id":"获取命令的帮助信息"},"children":[{"type":"text","value":"获取命令的帮助信息"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果你了解某个命令行工具的用法，可以在命令的后面加上 help 选项，比如："}]},{"type":"element","tag":"code","props":{"code":"ls --help\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"ls --help\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果命令不提供 help 这个选项，可以试着用 man（manual） 命令查看命令的使用说明，比如："}]},{"type":"element","tag":"code","props":{"code":"man ls\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"man ls\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"命令的说明文档可能会分页显示，按 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"f"}]},{"type":"text","value":" 键可以向后翻页，按 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"b"}]},{"type":"text","value":" 键可以向前翻页，按 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"q"}]},{"type":"text","value":" 键可以退出文档。"}]}]},"body":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"命令行界面"},"children":[{"type":"text","value":"命令行界面"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在宁皓独立开发者训练营中，有些训练任务需要在命令行界面下完成。Windows 用户可以安装一个 Cmder，macOS 用户可以使用系统自带的终端作为命令行界面。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在训练任务中需要使用在命令行界面下做的一些事情："}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"使用命令创建应用"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"启动应用在本地的开发服务"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"安装项目需要的功能包"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"执行命令快速创建应用需要的各种零部件"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"远程连接管理服务器"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"部署服务端应用"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"对开发的项目做源代码管理"}]}]},{"type":"element","tag":"h2","props":{"id":"cmderwindows"},"children":[{"type":"text","value":"Cmder（Windows）"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Cmder 是一款在 Windows 系统中使用的命令行界面。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"我们需要安装一个完整版的 Cmder 作为在 Windows 系统中使用的命令行界面工具。当您在训练视频中听到 “打开终端”，如果您的电脑的操作系统用的是 Windows，就可以打开 cmder。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://cmder.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://cmder.net/"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"Cmder 截图","src":"/images/docs/tools/cmd/cmder.png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"安装与使用"},"children":[{"type":"text","value":"安装与使用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 cmder 官方下载完整版的 cmder，将解压之后得到的 cmder 目录放在系统的 Program Files 目录中，然后可以在桌面上创建一个 cmder.exe 的快捷方式。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 cmder，可以右键点击 cmder 或是它的快捷方式，然后选择以 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"管理员身份运行"}]},{"type":"text","value":"，打开以后可以点击右下角的绿色加号小图标，新建一个 Bash as admin 类型的标签窗口，在这个标签窗口输入要执行的命令。"}]},{"type":"element","tag":"h3","props":{"id":"配置文件"},"children":[{"type":"text","value":"配置文件"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"因为我们会选择使用 Bash as admin 类型的 cmder，对应的配置文件应该是 cmder/config/user_profile.sh，也就是在这个文件里的配置会影响 Bash as admin 类型的 cmder。暂时我们只需要知道这个文件的存在即可。"}]},{"type":"element","tag":"h2","props":{"id":"终端macos"},"children":[{"type":"text","value":"终端（macOS）"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"终端（Terminal）是 macOS 系统中自带的一款命令行界面。在训练视频中，执行命令的时候一般都会在终端下面完成，如果您用的操作系统是 Windows，可以使用 Cmder 代替终端。"}]},{"type":"element","tag":"h3","props":{"id":"使用终端"},"children":[{"type":"text","value":"使用终端"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"终端是 macOS 系统自带的工具，不需要额外安装，在系统的启动台或者使用聚焦搜索，都可以打开终端，打开以后就可以在上面执行文字命令了。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"打开终端","src":"/images/docs/tools/cmd/open-terminal.png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"通过聚焦搜索找到终端"}]}]},{"type":"element","tag":"h3","props":{"id":"偏好设置"},"children":[{"type":"text","value":"偏好设置"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"通过终端的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"偏好设置"}]},{"type":"text","value":"，你可以设置终端使用的主题、字体字号、光标的样式等等。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开终端，在终端菜单下面可以打开编好设置（command + ,），然后在描述文件标签的下面，选择使用 Homebrew，字体是 Monaco 18，光标用的是下划线。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"终端的偏好设置","src":"/images/docs/tools/cmd/terminal-profile.png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"终端的偏好设置"}]}]},{"type":"element","tag":"h3","props":{"id":"配置文件-1"},"children":[{"type":"text","value":"配置文件"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"通过用户主目录下的 .zshrc 与 .zprofile 这两个文件可以修改终端的相关配置。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"因为文件的名字里是以点开头的，这种文件在系统里默认会被隐藏起来，也就是即使你打开了文件所在的目录，也可能看不到 .zshrc 或 .zprofile 这两个文件。在终端可以使用 vi 或 code（VSCode 编辑器里带的一个命令行工具） 命令打开并编辑这两个文件。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"使用 vi 命令编辑"}]}]},{"type":"element","tag":"code","props":{"code":"vi ~/.zprofile\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"vi ~/.zprofile\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令会用 vi 编辑器打开当前登录用户主目录下的 .zprofile 文件。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"使用 code 命令编辑"}]}]},{"type":"element","tag":"code","props":{"code":"code ~/.zprofile\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"code ~/.zprofile\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令会使用 VSCode 编辑器打开当前登录用户主目录下的 .zprofile 这个文件。"}]},{"type":"element","tag":"h3","props":{"id":"修改提示符"},"children":[{"type":"text","value":"修改提示符"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开终端，在光标左边的东西就是提示符，默认这个提示符看起来可能像下面这样："}]},{"type":"element","tag":"code","props":{"code":"wanghao@wanghaodeMacBook ~ %\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"wanghao@wanghaodeMacBook ~ %\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在提示符里会显示当前登录到系统中的用户（wanghao）与电脑的名字（wanghaodeMacBook），还有当前所在的目录（~），另外在结尾还有一个 % 符号。在训练营的视频内容中，终端的提示符是一个简单的 → 符号，如果你希望使用同样的提示符，可以通过修改 ~/.zshrc 这个文件。"}]},{"type":"element","tag":"code","props":{"code":"vi ~/.zshrc\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"vi ~/.zshrc\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"vi"}]},{"type":"text","value":" 是一个在命令行界面下使用的文本编辑器，它后面是要编辑的文件的位置，如果文件不存在就会新创建一个。打开以后，先按一下小写的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"i"}]},{"type":"text","value":" 按键，这会打开编辑器的编辑模式，这样就可以编辑文件内容了，在文件中添加下面这行代码："}]},{"type":"element","tag":"code","props":{"code":"PROMPT='→ '\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"PROMPT='→ '\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"上面这行配置的就是终端的命令提示符。完成编辑以后，按一下 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"esc"}]},{"type":"text","value":" 退出编辑模式，再输入 "},{"type":"element","tag":"code-inline","props":{},"children":[{"type":"text","value":":wq"}]},{"type":"text","value":" 并按下回车*，*这样会保存并且退出文件，注意要在英文状态下输入这些命令。修改了配置文件以后，需要重启终端才能生效，或者可以执行一下 srouce ~/.zshrc，这时你会发现命令提示符就会是我们修改之后的样子了。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"修改了描述文件与提示符之后的终端","src":"/images/docs/tools/cmd/terminal-prompt.png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"修改了描述文件与提示符之后的终端"}]}]},{"type":"element","tag":"h2","props":{"id":"常用命令"},"children":[{"type":"text","value":"常用命令"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"下面是几个经常会用到的命令行工具。"}]},{"type":"element","tag":"h3","props":{"id":"pwd输出当前位置"},"children":[{"type":"text","value":"pwd（输出当前位置）"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"pwd"}]},{"type":"text","value":" （print working directory）命令可以查看当前所在的位置。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"pwd"}]},{"type":"text","value":" ，得到的结果是一个路径，这个路径是用目录名字加斜线表示的*，*通过这个路径我们就可以知道自己当前所在的位置，这个位置其实就是系统里的某个目录。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"→ pwd\n/Users/wanghao/desktop\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"→ pwd\n/Users/wanghao/desktop\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行 pwd 命令，显示当前所在位置是 /Users/wanghao/desktop。"}]},{"type":"element","tag":"h3","props":{"id":"ls列出资源"},"children":[{"type":"text","value":"ls（列出资源）"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"ls（list）命令可以列出指定位置里的资源，也就是列出某个位置里包含的文件与目录。"}]},{"type":"element","tag":"h4","props":{"id":"列出当前位置里的资源"},"children":[{"type":"text","value":"列出当前位置里的资源"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行 ls 命令，如果不指定具体位置，就会列出当前所在位置里的东西。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"→ ls\nREADME.md    dist        package.json    src ...\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"→ ls\nREADME.md    dist        package.json    src ...\n"}]}]}]},{"type":"element","tag":"h4","props":{"id":"列出指定位置里的资源"},"children":[{"type":"text","value":"列出指定位置里的资源"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 ls 命令的后面可以加上一个具体的位置，这样可以列出这个位置里包含的资源。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"→ ls ~/desktop/xuanwu\nREADME.md    dist        package.json    src ...\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"→ ls ~/desktop/xuanwu\nREADME.md    dist        package.json    src ...\n"}]}]}]},{"type":"element","tag":"h4","props":{"id":"列出资源的详细信息"},"children":[{"type":"text","value":"列出资源的详细信息"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果想列出资源更详细的信息，可以配合使用 l（长格式） 与 a（所有） 这两个选项。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"→ ls -la\ntotal 832\ndrwxr-xr-x   20 wanghao  staff     640 Feb 25 17:31 .\ndrwxr-xr-x@  59 wanghao  staff    1888 Apr 26 10:50 ..\ndrwxr-xr-x   12 wanghao  staff     384 Apr 26 10:55 .git\n-rw-r--r--    1 wanghao  staff     283 Mar  7 22:15 .gitignore\n-rw-r--r--    1 wanghao  staff     315 Mar  7 22:15 README.md\ndrwxr-xr-x@   8 wanghao  staff     256 Feb  5 09:41 dist\ndrwxr-xr-x  860 wanghao  staff   27520 Jan 24 09:01 node_modules\n-rw-r--r--    1 wanghao  staff    1580 May  5 09:45 package.json\ndrwxr-xr-x   22 wanghao  staff     704 May  5 09:45 src\n...\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"→ ls -la\ntotal 832\ndrwxr-xr-x   20 wanghao  staff     640 Feb 25 17:31 .\ndrwxr-xr-x@  59 wanghao  staff    1888 Apr 26 10:50 ..\ndrwxr-xr-x   12 wanghao  staff     384 Apr 26 10:55 .git\n-rw-r--r--    1 wanghao  staff     283 Mar  7 22:15 .gitignore\n-rw-r--r--    1 wanghao  staff     315 Mar  7 22:15 README.md\ndrwxr-xr-x@   8 wanghao  staff     256 Feb  5 09:41 dist\ndrwxr-xr-x  860 wanghao  staff   27520 Jan 24 09:01 node_modules\n-rw-r--r--    1 wanghao  staff    1580 May  5 09:45 package.json\ndrwxr-xr-x   22 wanghao  staff     704 May  5 09:45 src\n...\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 l 选项会用长格式显示资源，每一行是一个资源（文件或目录），每一栏分别表示不同的东西，比如 drwxr-xr-x 表示的是资源的权限，wanghao 是资源的拥有者，staff 表示资源所属的用户组。使用了 a 这个选项以后会列出所有资源（包含隐藏文件），比如 .git 与 .gitignore，这两个文件的名字是以点开头的，在不使用 a 选项的情况下，列出资源时是不会显示这种文件的。"}]},{"type":"element","tag":"h3","props":{"id":"cd改变当前位置"},"children":[{"type":"text","value":"cd（改变当前位置）"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"cd（change directory）命令可以改变当前位置，也就是进入到系统的某个目录的下面。"}]},{"type":"element","tag":"h4","props":{"id":"进入到某个位置"},"children":[{"type":"text","value":"进入到某个位置"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 cd 命令的后面加上一个具体的路径，可以进入到这个指定的位置。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"cd ~/desktop\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cd ~/desktop\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"~/desktop 是一个路径，这里的 ~（"},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"波浪号"}]},{"type":"text","value":"）指的位置就是用户的主目录。执行上面这行命令可以进入到用户主目录下面的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"desktop"}]},{"type":"text","value":" 这个目录里面，这个目录里的东西默认会出现在你的电脑桌面上。 确定当前所在位置可以用 pwd 命令，列出当前位置里的资源可以执行 ls 命令。"}]},{"type":"element","tag":"h4","props":{"id":"回到上一级目录"},"children":[{"type":"text","value":"回到上一级目录"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 cd 命令的后面加上 ../，可以进入到当前目录的上一级目录。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"cd ../\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cd ../\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"../"}]},{"type":"text","value":"（"},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"两个点加一条斜线"}]},{"type":"text","value":"）表示的是上一级目录，比如当前位置是在 /Users/wanghao/desktop*，*对于这个位置来说，它的上一级目录就是 /Users/wanghao。这样如果执行 cd ../ 以后，用 pwd 输出当前的位置，得到的结果会显示当前是在 /Users/wanghao 这个位置上。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"../../"}]},{"type":"text","value":" 表示的是上一级目录的上一级目录， "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"./"}]},{"type":"text","value":" 一个点加一条斜线表示的是当前目录。"}]},{"type":"element","tag":"h3","props":{"id":"mkdir创建目录"},"children":[{"type":"text","value":"mkdir（创建目录）"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"mkdir（make directory）命令可以创建目录。"}]},{"type":"element","tag":"h4","props":{"id":"在当前位置创建目录"},"children":[{"type":"text","value":"在当前位置创建目录"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 mkdir 命令的后面加上要创建的目录的名字，可以创建一个目录。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"mkdir xuanwu\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"mkdir xuanwu\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"上面命令会在当前目录下创建一个叫 xuanwu 的目录。"}]},{"type":"element","tag":"h4","props":{"id":"在指定位置创建目录"},"children":[{"type":"text","value":"在指定位置创建目录"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 mkdir 命令的后面可以加上要创建的目录的具体位置。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"mkdir -p ~/desktop/projects/xuanwu\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"mkdir -p ~/desktop/projects/xuanwu\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"注意上面我们在命令里用了一个 p 选项，如果不用这个选项必须要保证新目录的父目录是存在的，也就是在桌面上应该有个 projects 目录，这样才能正常地创建 xuanwu 这个目录，如果 projects 这个目录不存在，执行命令时就会报错：No such file or directory。用了 p 选项以后，如果需要的目录不存在就会自动创建需要的目录。"}]},{"type":"element","tag":"h3","props":{"id":"cp复制"},"children":[{"type":"text","value":"cp（复制）"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"cp（copy）命令可以复制文件或目录。"}]},{"type":"element","tag":"h4","props":{"id":"复制文件"},"children":[{"type":"text","value":"复制文件"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行 cp 命令的时候需要提供想要复制的文件与复制到的位置。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"cp assets/example.env .env\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cp assets/example.env .env\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"上面这行命令会复制当前位置里的 assets 目录里的 example.env 这个文件，复制到的位置是当前目录，名字叫 .env。也就是在当前目录下应该会有一个 .env 文件，这个文件就是 assets/example.env 的复制品。"}]},{"type":"element","tag":"h4","props":{"id":"复制目录"},"children":[{"type":"text","value":"复制目录"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"用 cp 命令复制目录的时候需要加上 R 选项，这样可以复制指定的目录以及它里面包含的所有东西。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"cp -R config ../config_bak\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cp -R config ../config_bak\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这个命令会复制当前位置里的 config 这个目录（包含目录里的所有文件与目录），将复制品放在上一级目录里，名字是 config_bak。"}]},{"type":"element","tag":"h3","props":{"id":"rm删除"},"children":[{"type":"text","value":"rm（删除）"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"rm（remove）命令可以删除文件或目录。"}]},{"type":"element","tag":"h4","props":{"id":"删除文件"},"children":[{"type":"text","value":"删除文件"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行 rm 命令删除文件时，需要提供要删除的文件。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"rm package-lock.json\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"rm package-lock.json\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这个命令会将当前目录下的 package-lock.json 这个文件删除掉。"}]},{"type":"element","tag":"h4","props":{"id":"删除目录"},"children":[{"type":"text","value":"删除目录"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"用 rm 命令删除目录时可以提供 r（递归） 与 f（强制） 这两个选项，这样会强制将指定目录以及它里面包含的全部东西一块删除掉。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"rm -rf ~/desktop/nid-node/node_modules\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"rm -rf ~/desktop/nid-node/node_modules\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这个命令，会强制将桌面上的 nid-node 目录里的 node_modules 这个目录以及它里面包含的东西全部都删除掉。"}]},{"type":"element","tag":"h3","props":{"id":"vi命令行编辑器"},"children":[{"type":"text","value":"vi（命令行编辑器）"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"vi（visual）是一个在命令行界面下使用的编辑器，可以用它编辑或创建文本文件。"}]},{"type":"element","tag":"h4","props":{"id":"打开编辑器"},"children":[{"type":"text","value":"打开编辑器"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 vi 命令的后面加上要编辑的文件的具体位置，执行以后就会用 vi 编辑器打开指定的文件。如果要编辑当前目录下的文件，可以直接输入文件的名字。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"vi ~/desktop/xuanwu/README.md\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"vi ~/desktop/xuanwu/README.md\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"用 vi 编辑的文件可以不存在，但要保证文件所在目录是存在的，如果目录不存在，保存文件时就会报错：Can't open file for writing。所以在用 vi 编辑或创建文件之前，先保存文件目录是存在的，如果不存在，可以使用 mkdir 命令创建所需目录。"}]},{"type":"element","tag":"h4","props":{"id":"编辑文件"},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"编辑文件"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 vi 编辑文件内容，需要打开编辑模式，按一下 i 按键可以打开编辑模式，这时窗口底部会显示 — INSERT —，完成编辑以后按 esc 按键可以退出编辑模式。"}]},{"type":"element","tag":"h4","props":{"id":"保存文件并退出"},"children":[{"type":"text","value":"保存文件并退出"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"编辑了文件内容以后，如果想要保存对文件的修改并退出 vi 编辑器，先确定当前在正常模式下，按 esc 可以退出其它模式回到正常模式，然后执行："}]},{"type":"element","tag":"code","props":{"code":":wq\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":":wq\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"w 表示写入，q 表示退出，输入 "},{"type":"element","tag":"code-inline","props":{},"children":[{"type":"text","value":":wq"}]},{"type":"text","value":" 以后按下回车，这样就会保存对文件的修改，然后关掉 vi 编辑器。"}]},{"type":"element","tag":"h4","props":{"id":"放弃修改直接强制退出"},"children":[{"type":"text","value":"放弃修改直接强制退出"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果不想保存对文件的修改，打算强制退出 vi 编辑器，可以执行："}]},{"type":"element","tag":"code","props":{"code":":q!\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":":q!\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"q 表示退出，! 表示强制。"}]},{"type":"element","tag":"h4","props":{"id":"翻页查看文件"},"children":[{"type":"text","value":"翻页查看文件"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"按 ctrl + f 按键可以向下翻页查看文件，按 ctrl + b 按键可以向上翻页查看文件。"}]},{"type":"element","tag":"h4","props":{"id":"搜索文件内容"},"children":[{"type":"text","value":"搜索文件内容"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在正常模式下，输入 /，后面加上要搜索的关键词，按下回车，可以搜索并定位到当前文件包含指定关键词的地方。按 n 可以定位到下一个匹配的地方，按 N 可以定位到上一个匹配的地方。"}]},{"type":"element","tag":"h3","props":{"id":"cat输出文件内容"},"children":[{"type":"text","value":"cat（输出文件内容）"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"cat（concatenate）命令可以将多个文件合并成一个文件，也可以输出指定文件里的内容。"}]},{"type":"element","tag":"h4","props":{"id":"输出文件内容"},"children":[{"type":"text","value":"输出文件内容"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 cat 命令的后面提供要查看内容的文件，可以输出这个文件里的内容。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"cat ~/.ssh/id_rsa.pub\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cat ~/.ssh/id_rsa.pub\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这行命令会输出在用户主目录下的 .ssh 目录里的 id_rsa.pub 这个文件里的内容。"}]},{"type":"element","tag":"h2","props":{"id":"相关概念"},"children":[{"type":"text","value":"相关概念"}]},{"type":"element","tag":"h3","props":{"id":"终端"},"children":[{"type":"text","value":"终端"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"终端就是一种命令行界面工具，当您在训练营中听到或看到 “在终端” 或 “打开终端” 时，Windows 用户可以打开 cmder，macOS 用户可以打开系统自带的终端。"}]},{"type":"element","tag":"h3","props":{"id":"用户主目录"},"children":[{"type":"text","value":"用户主目录"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在操作系统里的用户一般都有一个主目录，这个目录的位置在不同的操作系统里是不一样的，比如在 macOS 系统里，wanghao 这个用户的主目录的位置应该是 /Users/wanghao，在 Ubuntu 系统里，wanghao 这个用户的主目录应该是在 /home/wanghao。"}]},{"type":"element","tag":"h4","props":{"id":"进入用户主目录"},"children":[{"type":"text","value":"进入用户主目录"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端可以使用 ~（波浪号）表示当前用户的主目录所在的位置，比如在 macOS 系统里，当前登录的用户如果是 wanghao，这个 ~ 表示的位置应该就是 /Users/wanghao，所以进入到这个位置，可以执行："}]},{"type":"element","tag":"code","props":{"code":"cd ~\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cd ~\n"}]}]}]},{"type":"element","tag":"h3","props":{"id":"根目录"},"children":[{"type":"text","value":"根目录"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"根目录指的是某个地方的第一级别的目录。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"比如一个项目的位置是 ~/desktop/nid-node，对于这个项目来说，它的根目录指的就是 ~/desktop/nid-node。如果我们在这个项目的根目录下创建了一个 README.md 文件，那这个文件的位置应该就是 ~/desktop/nid-node/README.md。"}]},{"type":"element","tag":"h4","props":{"id":"系统根目录"},"children":[{"type":"text","value":"系统根目录"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 macOS 与 Linux 类型的操作系统里，系统的根目录指的是 / 这个位置，比如执行 cd / 命令，会进入到系统根目录的下面。/Users 这个路径，可以描述为系统根目录下面的 Users 这个目录。"}]},{"type":"element","tag":"h3","props":{"id":"路径"},"children":[{"type":"text","value":"路径"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"路径可以用来表示在操作系统里的一个目录或文件的位置，路径由目录与斜线组成，如果是表示文件的路径，在路径中还会包含文件的名字。"}]},{"type":"element","tag":"h4","props":{"id":"目录的路径"},"children":[{"type":"text","value":"目录的路径"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"表示目录的路径由目录的名字与斜线组成。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"/Users/wanghao/desktop/xuanwu\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"/Users/wanghao/desktop/xuanwu\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"上面这个路径从系统的根目录开始，这个路径指的就是系统根目录下的 Users 里面的 wanghao 里的 desktop 目录里的 xuanwu 这个目录。"}]},{"type":"element","tag":"h4","props":{"id":"文件的路径"},"children":[{"type":"text","value":"文件的路径"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"表示文件的路径由目录的名字、斜线与文件组成。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"/Users/wanghao/desktop/xuanwu/src/main.ts\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"/Users/wanghao/desktop/xuanwu/src/main.ts\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"上面这个路径从系统的根目录开始，这个路径指的就是系统根目录下的 Users 里面的 wanghao 里的 desktop 目录里的 xuanwu 这个目录里的 src 目录里的 main.ts 这个文件。也就是 main.ts 这个文件在 src 目录的下面，src 在 xuanwu 的下面，xuanwu 在 desktop 目录里，desktop 在 wanghao 这个目录里，wanghao 在 Users 这个目录里，Users 这个目录在系统的根目录的下面。"}]},{"type":"element","tag":"h4","props":{"id":"相对路径"},"children":[{"type":"text","value":"相对路径"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"相对路径指的是相对于当前位置的路径。相对路径表示的实际位置，会随着当前位置的变化而变化。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"src/main.ts\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"src/main.ts\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"相对路径不以系统根目录（/）开始。描述上面这个相对路径，可以说当前目录下的 src 目录里的 main.ts。如果当前位置是 /Users/wanghao/desktop/xuanwu，相对于这个位置，上面这个路径在系统中的实际位置应该就是："}]},{"type":"element","tag":"code","props":{"code":"/Users/wanghao/desktop/xuanwu/src/main.ts\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"/Users/wanghao/desktop/xuanwu/src/main.ts\n"}]}]}]},{"type":"element","tag":"h4","props":{"id":"绝对路径"},"children":[{"type":"text","value":"绝对路径"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"绝对路径指的就是一个目录或文件在系统中的实际位置。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"/Users/wanghao/desktop\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"/Users/wanghao/desktop\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"绝对路径会从系统的根目录开始。描述上面这个绝对路径，可以说在系统根目录下的 Users 里的 wanghao 里的 desktop 这个目录。"}]},{"type":"element","tag":"h3","props":{"id":"环境变量"},"children":[{"type":"text","value":"环境变量"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"环境变量（environment variables）就是在系统环境中存在的一些变量，程序可以读取这些环境变量并获取到它们的值。"}]},{"type":"element","tag":"h4","props":{"id":"查看环境变量"},"children":[{"type":"text","value":"查看环境变量"}]},{"type":"element","tag":"code","props":{"code":"printenv\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"printenv\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端执行 printenv 命令可以输出当前已有的一些环境变量以及它们对应的值。环境变量的名字一般全部使用大写字母，等号右边是环境变量对应的值。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"LANG=zh_CN.UTF-8\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"LANG=zh_CN.UTF-8\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"LANG 是环境变量的名字，zh*CN.UTF-8 是 LANG 这个环境变量对应的值。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"在 Windows 系统中通过 Cmder 执行 printenv 命令","src":"/images/docs/tools/cmd/cmder-printenv.png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 Windows 系统中通过 Cmder 执行 printenv 命令"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"在 macOS 系统中通过终端执行 printenv 命令","src":"/images/docs/tools/cmd/terminal-printenv.png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"*在 macOS 系统中通过终端执行 printenv 命令_"}]},{"type":"element","tag":"h4","props":{"id":"设置环境变量"},"children":[{"type":"text","value":"设置环境变量"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端，执行 export 命令可以添加环境变量，被添加的环境变量只能在当前这个终端标签里使用。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"export MACHINE_NAME=玄武\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"export MACHINE_NAME=玄武\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令会添加一个环境变量叫 MACHINE*NAME，它的值是 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"玄武"}]},{"type":"text","value":"。这样在当前这个终端标签里运行的程序，都可以读取到 MACHINE_NAME 这个环境变量，得到的值就是 *玄武_ 。执行 printenv 可以确定一下是否包含了 MACHINE_NAME 这个环境变量。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"关掉这个终端标签，重新再打开，之前设置的环境变量就不见了。如果你希望每次打开终端的时候自动设置一些环境变量，可以将设置环境变量用的命令添加到终端的配置文件里，在 macOS 系统里就是 ~/.zprofile，Windows 系统里是 cmder/config/user-profile.sh。"}]},{"type":"element","tag":"h3","props":{"id":"path"},"children":[{"type":"text","value":"PATH"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"PATH 是一个环境变量，它的值是一组路径，在终端执行命令的时候，会在这些路径里查找是否包含要执行的命令程序。"}]},{"type":"element","tag":"h4","props":{"id":"输出-path-的值"},"children":[{"type":"text","value":"输出 PATH 的值"}]},{"type":"element","tag":"code","props":{"code":"printenv PATH\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"printenv PATH\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"得到的结果应该是一组路径，不同的路径之间会用冒号分隔开，在这些路径里都会包含一些可以执行的命令程序。"}]},{"type":"element","tag":"code","props":{"code":"/usr/local/bin:/usr/bin:/bin: ...\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"/usr/local/bin:/usr/bin:/bin: ...\n"}]}]}]},{"type":"element","tag":"h4","props":{"id":"设置-path-的值"},"children":[{"type":"text","value":"设置 PATH 的值"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"安装了软件以后，可能会包含一些命令行工具，如果想在终端直接使用它们，需要将包含这些命令行工具的目录添加到 PATH 里。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"export PATH=$PATH:/Applications/flutter/bin\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"export PATH=$PATH:/Applications/flutter/bin\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端的配置文件里添加上面这行代码，结果就是在 PATH 这个环境变量里添加了一个新的路径，位置是 /Applications/flutter/bin，也就是在这个位置里包含的命令行工具，以后都可以直接在终端执行。这里的 $PATH 引用的是原来的 PATH 这个环境变量的值。"}]},{"type":"element","tag":"h2","props":{"id":"钥匙"},"children":[{"type":"text","value":"钥匙"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"钥匙（keys）是加密解密用的东西（文本文件），在训练营中，向远程仓库推送代码的时候，登录管理远程服务器的时候都会用到在本地电脑里存放的钥匙。钥匙一般都成对出现，一个密钥（private key）对应一个公钥（public key）。我们可以给当前登录到系统的用户生成一对钥匙，其中密钥是私密的，决对不能交给别人，但是公钥可以放心交到其它地方使用。"}]},{"type":"element","tag":"h3","props":{"id":"生成钥匙对"},"children":[{"type":"text","value":"生成钥匙对"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"当前用户的钥匙对会放在用户主目录下的 .ssh 这个目录里，你可以先确定一下是否已经为当前用户生成了钥匙对。"}]},{"type":"element","tag":"code","props":{"code":"ls ~/.ssh\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"ls ~/.ssh\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令，如果发现用户主目录下的 .ssh 里面，有 id_rsa（密钥） 与 id_rsa.pub（公钥） 这两个文件，说明当前用户已经拥有了钥匙对。如果没有这两个文件，可以执行 ssh-keygen 命令生成它们。"}]},{"type":"element","tag":"code","props":{"code":"ssh-keygen\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"ssh-keygen\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端，执行 ssh-keygen 命令，并一路回车，完成以后就会在用户主目录下的 .ssh 目录里生成一对钥匙。这个命令只需要执行一次，重复执行会覆盖掉之前生成的钥匙对。"}]},{"type":"element","tag":"h3","props":{"id":"查看公钥文件内容"},"children":[{"type":"text","value":"查看公钥文件内容"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在配置三方服务使用钥匙验证身份的时候，我们需要复制用户的公钥文件里的内容。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"查看公钥文件里的内容可以执行："}]},{"type":"element","tag":"code","props":{"code":"cat ~/.ssh/id_rsa.pub\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cat ~/.ssh/id_rsa.pub\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"输出的内容看起来像这样："}]},{"type":"element","tag":"code","props":{"code":"ssh-rsa AAAAB3NzaC1yc2EAAX...\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"ssh-rsa AAAAB3NzaC1yc2EAAX...\n"}]}]}]},{"type":"element","tag":"h2","props":{"id":"技巧"},"children":[{"type":"text","value":"技巧"}]},{"type":"element","tag":"h3","props":{"id":"知道命令来自哪里"},"children":[{"type":"text","value":"知道命令来自哪里"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果你想知道一个命令行工具到底在哪儿，可以用 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"which"}]},{"type":"text","value":" 命令，比如我想知道 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"ls"}]},{"type":"text","value":" 这个命令行工具的位置，可以执行："}]},{"type":"element","tag":"code","props":{"code":"which ls\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"which ls\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"得到的结果就是 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"ls"}]},{"type":"text","value":" 这个命令行工具的位置，具体在哪取决于你的操作系统。在 macOS 系统里，这个工具是在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"/bin/ls"}]},{"type":"text","value":" 。在 Windows 系统里，如果你用的是 bash 类型的 Cmder，会显示这个工具的位置是 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"/usr/bin/ls"}]},{"type":"text","value":" 。"},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"usr"}]},{"type":"text","value":" 一般表示的是 user，也就是用户的意思，"},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"bin"}]},{"type":"text","value":" 这个目录里通常包含的是一些可以执行的程序。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"这个 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"/usr/bin"}]},{"type":"text","value":" 应该是 Cmder 设置的一个目录的别名，因为在 Windows 系统里并没有这个地方，实际的位置应该是 Cmder 里的某个目录的下面。在我的系统里，"},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"/usr/bin"}]},{"type":"text","value":" 真正的位置是 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"/C/Program\\ Files/cmder/vendor/git-for-windows/usr/bin"}]},{"type":"text","value":" ，进入到这个目录的下面，列出目录里的资源，你会发现一些 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":".exe"}]},{"type":"text","value":" 后缀的文件。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 Windows 系统里，带这种 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":".exe"}]},{"type":"text","value":" 后缀的文件就是可执行的程序。在后面我们会用到这里面的一些命令行工具，比如远程连接服务器用的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"ssh"}]},{"type":"text","value":" ，生成密钥文件用的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"ssh-keygen"}]},{"type":"text","value":" 等等。Windows 系统默认都不带这些东西，这也是我们为什么要用 Cmder 来代替 Windows 系统自带的命令行界面。"}]},{"type":"element","tag":"h3","props":{"id":"获取命令的帮助信息"},"children":[{"type":"text","value":"获取命令的帮助信息"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果你了解某个命令行工具的用法，可以在命令的后面加上 help 选项，比如："}]},{"type":"element","tag":"code","props":{"code":"ls --help\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"ls --help\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果命令不提供 help 这个选项，可以试着用 man（manual） 命令查看命令的使用说明，比如："}]},{"type":"element","tag":"code","props":{"code":"man ls\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"man ls\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"命令的说明文档可能会分页显示，按 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"f"}]},{"type":"text","value":" 键可以向后翻页，按 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"b"}]},{"type":"text","value":" 键可以向前翻页，按 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"q"}]},{"type":"text","value":" 键可以退出文档。"}]}],"toc":{"title":"","searchDepth":2,"depth":2,"links":[{"id":"cmderwindows","depth":2,"text":"Cmder（Windows）","children":[{"id":"安装与使用","depth":3,"text":"安装与使用"},{"id":"配置文件","depth":3,"text":"配置文件"}]},{"id":"终端macos","depth":2,"text":"终端（macOS）","children":[{"id":"使用终端","depth":3,"text":"使用终端"},{"id":"偏好设置","depth":3,"text":"偏好设置"},{"id":"配置文件-1","depth":3,"text":"配置文件"},{"id":"修改提示符","depth":3,"text":"修改提示符"}]},{"id":"常用命令","depth":2,"text":"常用命令","children":[{"id":"pwd输出当前位置","depth":3,"text":"pwd（输出当前位置）"},{"id":"ls列出资源","depth":3,"text":"ls（列出资源）"},{"id":"cd改变当前位置","depth":3,"text":"cd（改变当前位置）"},{"id":"mkdir创建目录","depth":3,"text":"mkdir（创建目录）"},{"id":"cp复制","depth":3,"text":"cp（复制）"},{"id":"rm删除","depth":3,"text":"rm（删除）"},{"id":"vi命令行编辑器","depth":3,"text":"vi（命令行编辑器）"},{"id":"cat输出文件内容","depth":3,"text":"cat（输出文件内容）"}]},{"id":"相关概念","depth":2,"text":"相关概念","children":[{"id":"终端","depth":3,"text":"终端"},{"id":"用户主目录","depth":3,"text":"用户主目录"},{"id":"根目录","depth":3,"text":"根目录"},{"id":"路径","depth":3,"text":"路径"},{"id":"环境变量","depth":3,"text":"环境变量"},{"id":"path","depth":3,"text":"PATH"}]},{"id":"钥匙","depth":2,"text":"钥匙","children":[{"id":"生成钥匙对","depth":3,"text":"生成钥匙对"},{"id":"查看公钥文件内容","depth":3,"text":"查看公钥文件内容"}]},{"id":"技巧","depth":2,"text":"技巧","children":[{"id":"知道命令来自哪里","depth":3,"text":"知道命令来自哪里"},{"id":"获取命令的帮助信息","depth":3,"text":"获取命令的帮助信息"}]}]}},"_type":"markdown","_id":"content:docs:1.tools:1.cli.md","_source":"content","_file":"docs/1.tools/1.cli.md","_extension":"md"},{"_path":"/docs/tools/vscode","_draft":false,"_partial":false,"_empty":false,"title":"代码编辑器","description":"VSCode 是在宁皓独立开发者训练营中选择使用的代码编辑器，在训练营中编写的 65000 行代码都是通过这款编辑器完成的。","excerpt":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"代码编辑器"},"children":[{"type":"text","value":"代码编辑器"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"VSCode 是在宁皓独立开发者训练营中选择使用的代码编辑器，在训练营中编写的 65000 行代码都是通过这款编辑器完成的。"}]},{"type":"element","tag":"h2","props":{"id":"vscode"},"children":[{"type":"text","value":"VSCode"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"VSCode 是一款代码编辑器，开源、免费、跨平台、可定制、可扩展。"}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://code.visualstudio.com/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://code.visualstudio.com/"}]}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"下载地址"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://code.visualstudio.com/Download","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://code.visualstudio.com/Download"}]}]}]},{"type":"element","tag":"h3","props":{"id":"安装与使用"},"children":[{"type":"text","value":"安装与使用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 VSCode 官方网站下载合适的版本，然后将其安装在系统上，无需单独练习编辑器的使用技巧，可在实际开发中不断地积累。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/vscode-main-ui.png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/vscode-bottom-ui.png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"使用命令面板"},"children":[{"type":"text","value":"使用命令面板"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"命令面板"}]},{"type":"text","value":"（command palette） 是比较常用的功能，在编辑器里能做的大部分的事情都可以在命令面板里找到并执行。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"打开命令面板的快捷键"}]}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"macOS：command + shift + p"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"Windows：ctrl + shift + p"}]}]},{"type":"element","tag":"h3","props":{"id":"使用-code-命令"},"children":[{"type":"text","value":"使用 code 命令"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"code 是 VSCode 编辑器自带的一个命令行工具，在命令行界面下，使用这个命令可以方便地打开文件与目录。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"在 PATH 中添加 code 命令"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果想在终端使用 code 命令，需要将其添加到 PATH 这个环境变量里。先打开命令面板，然后搜索并执行 install code command in PATH（在 PATH 中添加 code 命令），完成以后就可以在终端执行 code 这个命令了。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image.png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"使用 code 命令打开目录"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端执行 code 命令，后面可以加上要打开的目录或文件。"}]},{"type":"element","tag":"code","props":{"code":"code ~/desktop/nid-node\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"code ~/desktop/nid-node\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令，会用 VSCode 编辑器打开桌面上的 nid-node 这个目录。"}]},{"type":"element","tag":"h2","props":{"id":"扩展"},"children":[{"type":"text","value":"扩展"}]},{"type":"element","tag":"h3","props":{"id":"中文语言包"},"children":[{"type":"text","value":"中文语言包"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"默认 VSCode 编辑器界面上的文字是英文的，我们可以给编辑器安装额外的语言包，这样就可以改变界面上显示的文字使用的语言了。"}]},{"type":"element","tag":"h4","props":{"id":"安装中文语言包"},"children":[{"type":"text","value":"安装中文语言包"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开扩展，搜索 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"chinese"}]},{"type":"text","value":"，安装中文简体或中文繁体扩展，完成以后重启编辑器，界面上就会使用中文语言了。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(1).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 VSCode 扩展搜索 chinese 关键词"}]}]},{"type":"element","tag":"h4","props":{"id":"配置显示语言"},"children":[{"type":"text","value":"配置显示语言"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"推荐大家先熟悉一下英文界面，了解一下在编辑器里出现的各种东西的英文名，这样以后遇到问题的时候可以方便使用英文去搜索。改变界面显示的语言，可以打开命令面板，搜索并执行 Configure Display Language，选择要使用的语言，比如 en（英文） 或 zh-cn（简体中文），重新启动编辑器即可生效。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(2).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在命令面板搜索 display language 命令"}]}]},{"type":"element","tag":"h3","props":{"id":"prettier"},"children":[{"type":"text","value":"Prettier"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Prettier 可以自动排版（格式化）编写的代码，在训练营中，我们会用它排版编写的 JavaScript / TypeScript、CSS 与 Vue 组件的代码。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://prettier.io/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://prettier.io"}]}]},{"type":"element","tag":"h4","props":{"id":"安装-prettier-扩展"},"children":[{"type":"text","value":"安装 Prettier 扩展"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开 VSCode 扩展，搜索 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"prettier"}]},{"type":"text","value":"，安装一下 Prettier - Code formatter 这个扩展。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(3).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 VSCode 扩展搜索 prettier 关键词"}]}]},{"type":"element","tag":"h4","props":{"id":"配置-prettier"},"children":[{"type":"text","value":"配置 Prettier"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Prettier 扩展提供了一些配置选项，你可以在 VSCode 编辑器的设置里去修改这些选项的值。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在开发的项目的根目录下面，可以提供一个 Prettier 的配置文件，名字是 .prettierrc，文件内容的格式是 JSON，在这个文件里可以设置 Prettier 排版代码时用的一些规则。比如将 singleQuote 设置成 true，意思是在格式化代码时将双引号自动转换成单引号。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(4).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"项目根目录里的 Prettier 配置文件"}]}]},{"type":"element","tag":"h4","props":{"id":"使用-prettier"},"children":[{"type":"text","value":"使用 Prettier"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Prettier 扩展可以作为 VSCode 编辑器的一种 Formatter（格式化工具），在命令面板搜索并执行 Format Document... ，这会弹出一个格式化工具列表菜单，可以进一步选择使用 Prettier，这样就会使用 Prettier 格式化当前文件里的代码了。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(5).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在命令面板搜索 format document"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"我们可以勾选 VSCode 编辑器的 Format on save 这个配置选项，然后可以配置让指定类型的文件默认使用 Prettier 作为格式化代码用的工具， 这样当保存文件时，就会自动使用 Prettier 格式化被保存的这个文件里的代码。"}]},{"type":"element","tag":"h3","props":{"id":"vetur"},"children":[{"type":"text","value":"Vetur"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Vetur 这个扩展提供了开发 Vue 应用时需要的一些功能。在训练营中开发 Vue 应用时会用到这个扩展提供的功能。"}]},{"type":"element","tag":"h4","props":{"id":"安装-vetur-扩展"},"children":[{"type":"text","value":"安装 Vetur 扩展"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开编辑器的扩展，搜索并安装 Vetur 扩展。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(6).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 VSCode 扩展搜索 vetur 关键词"}]}]},{"type":"element","tag":"h3","props":{"id":"flutter"},"children":[{"type":"text","value":"Flutter"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Flutter 这个扩展提供了开发 Flutter 应用时需要的功能。在训练营中开发 Flutter 移动端应用时会用到这个扩展提供的功能。"}]},{"type":"element","tag":"h4","props":{"id":"安装-flutter-扩展"},"children":[{"type":"text","value":"安装 Flutter 扩展"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开编辑器的扩展，搜索并安装 Flutter 扩展。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(7).png"},"children":[]}]},{"type":"element","tag":"h2","props":{"id":"主题"},"children":[{"type":"text","value":"主题"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"VSCode 支持定制界面颜色主题与文件图标主题，这些主题也属于编辑器的扩展，所以你可以在编辑器的扩展商店里搜索并安装新的主题。"}]},{"type":"element","tag":"h3","props":{"id":"文件图标主题"},"children":[{"type":"text","value":"文件图标主题"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"文件图标主题可以改变在编辑器边栏上显示的文件与目录列表所使用的小图标。"}]},{"type":"element","tag":"h4","props":{"id":"安装-material-icon-theme"},"children":[{"type":"text","value":"安装 Material Icon Theme"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Material Icon Theme 是在训练视频中出现的 VSCode 编辑器使用的文件图标主题。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开编辑器的扩展，搜索并安装 Material Icon Theme，完成安装以后 VSCode 默认会将其设置为当前使用的文件图标主题。这款主题有几种网格，训练视频中使用的是 Classic 风格的主题。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(8).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 VSCode 扩展里搜索 material icon theme"}]}]},{"type":"element","tag":"h4","props":{"id":"设置文件图标主题"},"children":[{"type":"text","value":"设置文件图标主题"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开命令面板，搜索并执行 File Icon Theme，这会列出编辑器里可选的文件图标主题，你可以选择想要使用的主题。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(9).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在命令面板搜索 file icon theme"}]}]},{"type":"element","tag":"h3","props":{"id":"颜色主题"},"children":[{"type":"text","value":"颜色主题"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"VSCode 编辑器的颜色主题可以改变编辑器界面与代码高亮的样式。"}]},{"type":"element","tag":"h4","props":{"id":"安装-ninghao-color-theme"},"children":[{"type":"text","value":"安装 Ninghao Color Theme"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Ninghao Color Theme 在训练视频中出现的 VSCode 编辑器使用的颜色主题。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开扩展，搜索 ninghao，然后安装 Ninghao Color Theme 这个扩展，完成以后选择使用 Ninghao Dark 这款颜色主题。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(10).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 VSCode 扩展搜索 ninghao"}]}]},{"type":"element","tag":"h4","props":{"id":"设置颜色主题"},"children":[{"type":"text","value":"设置颜色主题"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开命令面板，搜索并执行 Color Theme，这会列出编辑器里的颜色主题，你可以选择自己要使用的颜色主题。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(11).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在命令面板搜索 color theme"}]}]},{"type":"element","tag":"h2","props":{"id":"配置"},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"配置"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"VSCode 支持全局范围与项目级别的配置，配置以 JSON 格式保存，放在 settings.json 文件里。"}]},{"type":"element","tag":"h3","props":{"id":"全局配置"},"children":[{"type":"text","value":"全局配置"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开命令面板，搜索并执行 Open Settings UI，这会打开设置界面，通过这个界面可以修改编辑器的全局范围的一些设置。"}]},{"type":"element","tag":"h4","props":{"id":"保存文件时格式化代码"},"children":[{"type":"text","value":"保存文件时格式化代码"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在配置搜索 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"format on save"}]},{"type":"text","value":"，勾选一下 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Editor:Format On Save"}]},{"type":"text","value":" 这个选项，这样编写了代码文件的内容并保存文件后，就会自动格式化编写的代码。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(12).png"},"children":[]}]},{"type":"element","tag":"h4","props":{"id":"typescript-导入模块时使用相对路径"},"children":[{"type":"text","value":"TypeScript 导入模块时使用相对路径"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在训练营中，我们会使用 TypeScript 语言编写应用，编辑器自动导入模块时可以使用模块的相对路径。打开编辑器的配置，然后找到 TypeScript > Preferences: Import Module Specifier 这个配置选项，将选项的值设置成 relative（相对）。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(13).png"},"children":[]}]},{"type":"element","tag":"h4","props":{"id":"settingsjson"},"children":[{"type":"text","value":"settings.json"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开命令面板，搜索并执行 Open Settings JSON，这会打开一个 settings.json 文件，文件里存放的就是 VSCode 编辑器在全局范围的配置。通过编辑器的设置界面修改的配置选项都会记录在这个文件中。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"\"editor.formatOnSave\": true\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"\"editor.formatOnSave\": true\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 settings.json 文件中搜索 formatOnSave，你会发现它的值应该是 true，因为之前通过图形界面的配置，勾选了 Editor:Format On Save 这个选项。"}]},{"type":"element","tag":"h3","props":{"id":"项目配置"},"children":[{"type":"text","value":"项目配置"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"VSCode 编辑器支持设置项目级别的配置，这些配置只会影响到当前项目。"}]},{"type":"element","tag":"h4","props":{"id":"设置项目级别的配置"},"children":[{"type":"text","value":"设置项目级别的配置"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"要设置 VSCode 编辑器的项目级别的配置，可以在项目的根目录的下面新建一个 .vscode 目录，在个目录里再新建一个 settings.json 文件。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":".vscode/settings.json"}]}]},{"type":"element","tag":"code","props":{"code":"{\n  \"editor.formatOnSave\": true,\n  \"typescript.preferences.importModuleSpecifier\": \"relative\"\n}\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"{\n  \"editor.formatOnSave\": true,\n  \"typescript.preferences.importModuleSpecifier\": \"relative\"\n}\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果在项目中添加上面这个 VSCode 编辑器配置文件，编辑器会在保存文件时格式化代码，在 TypeScript 代码文件里导入模块时会使用相对的路径。"}]},{"type":"element","tag":"h2","props":{"id":"代码片断"},"children":[{"type":"text","value":"代码片断"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"编写应用时，输入几个字母按下 tab 键就可以快速插入事先定义好的代码块，这就是在 VSCode 编辑器里的代码片断提供的功能。将经常要在项目里编写的，具有同样模式的代码块定义成代码片断。代码片断可以区分语言，也就是我们可以为某种特定的程序语言定义代码片断。代码片断可以在全局范围定义，也可以在项目级别定义。"}]},{"type":"element","tag":"h3","props":{"id":"定义项目级别的代码片断"},"children":[{"type":"text","value":"定义项目级别的代码片断"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在训练营里编写应用时，我们会使用项目级别的代码片断，这些代码片断会包含在项目的初始仓库里，所以准备好项目以后，你可以直接使用在项目里定义的代码片断。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"定义项目级别的代码片断，可以将定义代码片断的文件放在项目根目录下的 .vscode 这个目录里面，代码片断的文件名需要 .code-snippets 后缀，比如 nid-nest.code-snippets，VSCode 会自动获取在项目里定义的代码片断。"}]},{"type":"element","tag":"h3","props":{"id":"理解代码片断的定义"},"children":[{"type":"text","value":"理解代码片断的定义"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"定义代码片断的文件的内容格式是 JSON，每个代码片断需要设置它的标题，前缀（prefix）代码主体（body），在编写应用时输入代码片断的前缀字符时，VSCode 会提示可用的代码片断，按下 tab 以后就会插入当前选择的代码片断。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在定义代码片断的时候可以在设置一些编辑点，每个编辑点都有编号，还可以设置可选的占位符文字，比如 ${1:Name} ，指的就是在插入代码片断以后，第一个要编辑的地方。完成编辑以后按下 tab 会跳转到下一处编辑点，也就是 ${2}。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":".vscode/nid-nest.code-snippets"}]}]},{"type":"element","tag":"code","props":{"code":"{\n  \"CQRS: Execute command\": {\n    \"prefix\": \"ec\",\n    \"body\": [\n      \"${3}this.commandBus.execute(\",\n      \"  new ${1:Name}Command(${2})\",\n      \");\"\n    ]\n  }\n}\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"{\n  \"CQRS: Execute command\": {\n    \"prefix\": \"ec\",\n    \"body\": [\n      \"${3}this.commandBus.execute(\",\n      \"  new ${1:Name}Command(${2})\",\n      \");\"\n    ]\n  }\n}\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在上面的代码片断文件里定义了一个代码片断，标题是 CQRS: Execute command，前缀是 ec，在这个代码片断的主体里设置了三处编辑点。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"参考"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"a","props":{"href":"https://github.com/ninghao/nid-nest-starter/blob/master/.vscode/nid-nest.code-snippets","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://github.com/ninghao/nid-nest-starter/blob/master/.vscode/nid-nest.code-snippets"}]}]},{"type":"element","tag":"h3","props":{"id":"使用代码片断"},"children":[{"type":"text","value":"使用代码片断"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在编写项目的时候，输入代码片断的前缀（prefix），VSCode 就会列出相关的代码片断，选择要插入的代码片断，然后按下 tab 键就可以插入对应的代码片断了。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(14).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在项目文件里输入 ec 这个前缀，会显示可以使用 CQRS: Execute command 这个代码片断，按下 tab 键可以在当前位置插入这个代码片断。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(15).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"插入代码片断以后，可以编辑代码片断里的第一个编辑点，完成编写以后按下 tab 键可以跳转到下一处编辑器继续修改这个代码片断。"}]}]},"body":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"代码编辑器"},"children":[{"type":"text","value":"代码编辑器"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"VSCode 是在宁皓独立开发者训练营中选择使用的代码编辑器，在训练营中编写的 65000 行代码都是通过这款编辑器完成的。"}]},{"type":"element","tag":"h2","props":{"id":"vscode"},"children":[{"type":"text","value":"VSCode"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"VSCode 是一款代码编辑器，开源、免费、跨平台、可定制、可扩展。"}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://code.visualstudio.com/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://code.visualstudio.com/"}]}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"下载地址"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://code.visualstudio.com/Download","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://code.visualstudio.com/Download"}]}]}]},{"type":"element","tag":"h3","props":{"id":"安装与使用"},"children":[{"type":"text","value":"安装与使用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 VSCode 官方网站下载合适的版本，然后将其安装在系统上，无需单独练习编辑器的使用技巧，可在实际开发中不断地积累。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/vscode-main-ui.png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/vscode-bottom-ui.png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"使用命令面板"},"children":[{"type":"text","value":"使用命令面板"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"命令面板"}]},{"type":"text","value":"（command palette） 是比较常用的功能，在编辑器里能做的大部分的事情都可以在命令面板里找到并执行。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"打开命令面板的快捷键"}]}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"macOS：command + shift + p"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"Windows：ctrl + shift + p"}]}]},{"type":"element","tag":"h3","props":{"id":"使用-code-命令"},"children":[{"type":"text","value":"使用 code 命令"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"code 是 VSCode 编辑器自带的一个命令行工具，在命令行界面下，使用这个命令可以方便地打开文件与目录。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"在 PATH 中添加 code 命令"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果想在终端使用 code 命令，需要将其添加到 PATH 这个环境变量里。先打开命令面板，然后搜索并执行 install code command in PATH（在 PATH 中添加 code 命令），完成以后就可以在终端执行 code 这个命令了。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image.png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"使用 code 命令打开目录"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端执行 code 命令，后面可以加上要打开的目录或文件。"}]},{"type":"element","tag":"code","props":{"code":"code ~/desktop/nid-node\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"code ~/desktop/nid-node\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令，会用 VSCode 编辑器打开桌面上的 nid-node 这个目录。"}]},{"type":"element","tag":"h2","props":{"id":"扩展"},"children":[{"type":"text","value":"扩展"}]},{"type":"element","tag":"h3","props":{"id":"中文语言包"},"children":[{"type":"text","value":"中文语言包"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"默认 VSCode 编辑器界面上的文字是英文的，我们可以给编辑器安装额外的语言包，这样就可以改变界面上显示的文字使用的语言了。"}]},{"type":"element","tag":"h4","props":{"id":"安装中文语言包"},"children":[{"type":"text","value":"安装中文语言包"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开扩展，搜索 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"chinese"}]},{"type":"text","value":"，安装中文简体或中文繁体扩展，完成以后重启编辑器，界面上就会使用中文语言了。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(1).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 VSCode 扩展搜索 chinese 关键词"}]}]},{"type":"element","tag":"h4","props":{"id":"配置显示语言"},"children":[{"type":"text","value":"配置显示语言"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"推荐大家先熟悉一下英文界面，了解一下在编辑器里出现的各种东西的英文名，这样以后遇到问题的时候可以方便使用英文去搜索。改变界面显示的语言，可以打开命令面板，搜索并执行 Configure Display Language，选择要使用的语言，比如 en（英文） 或 zh-cn（简体中文），重新启动编辑器即可生效。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(2).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在命令面板搜索 display language 命令"}]}]},{"type":"element","tag":"h3","props":{"id":"prettier"},"children":[{"type":"text","value":"Prettier"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Prettier 可以自动排版（格式化）编写的代码，在训练营中，我们会用它排版编写的 JavaScript / TypeScript、CSS 与 Vue 组件的代码。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://prettier.io/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://prettier.io"}]}]},{"type":"element","tag":"h4","props":{"id":"安装-prettier-扩展"},"children":[{"type":"text","value":"安装 Prettier 扩展"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开 VSCode 扩展，搜索 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"prettier"}]},{"type":"text","value":"，安装一下 Prettier - Code formatter 这个扩展。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(3).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 VSCode 扩展搜索 prettier 关键词"}]}]},{"type":"element","tag":"h4","props":{"id":"配置-prettier"},"children":[{"type":"text","value":"配置 Prettier"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Prettier 扩展提供了一些配置选项，你可以在 VSCode 编辑器的设置里去修改这些选项的值。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在开发的项目的根目录下面，可以提供一个 Prettier 的配置文件，名字是 .prettierrc，文件内容的格式是 JSON，在这个文件里可以设置 Prettier 排版代码时用的一些规则。比如将 singleQuote 设置成 true，意思是在格式化代码时将双引号自动转换成单引号。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(4).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"项目根目录里的 Prettier 配置文件"}]}]},{"type":"element","tag":"h4","props":{"id":"使用-prettier"},"children":[{"type":"text","value":"使用 Prettier"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Prettier 扩展可以作为 VSCode 编辑器的一种 Formatter（格式化工具），在命令面板搜索并执行 Format Document... ，这会弹出一个格式化工具列表菜单，可以进一步选择使用 Prettier，这样就会使用 Prettier 格式化当前文件里的代码了。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(5).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在命令面板搜索 format document"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"我们可以勾选 VSCode 编辑器的 Format on save 这个配置选项，然后可以配置让指定类型的文件默认使用 Prettier 作为格式化代码用的工具， 这样当保存文件时，就会自动使用 Prettier 格式化被保存的这个文件里的代码。"}]},{"type":"element","tag":"h3","props":{"id":"vetur"},"children":[{"type":"text","value":"Vetur"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Vetur 这个扩展提供了开发 Vue 应用时需要的一些功能。在训练营中开发 Vue 应用时会用到这个扩展提供的功能。"}]},{"type":"element","tag":"h4","props":{"id":"安装-vetur-扩展"},"children":[{"type":"text","value":"安装 Vetur 扩展"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开编辑器的扩展，搜索并安装 Vetur 扩展。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(6).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 VSCode 扩展搜索 vetur 关键词"}]}]},{"type":"element","tag":"h3","props":{"id":"flutter"},"children":[{"type":"text","value":"Flutter"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Flutter 这个扩展提供了开发 Flutter 应用时需要的功能。在训练营中开发 Flutter 移动端应用时会用到这个扩展提供的功能。"}]},{"type":"element","tag":"h4","props":{"id":"安装-flutter-扩展"},"children":[{"type":"text","value":"安装 Flutter 扩展"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开编辑器的扩展，搜索并安装 Flutter 扩展。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(7).png"},"children":[]}]},{"type":"element","tag":"h2","props":{"id":"主题"},"children":[{"type":"text","value":"主题"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"VSCode 支持定制界面颜色主题与文件图标主题，这些主题也属于编辑器的扩展，所以你可以在编辑器的扩展商店里搜索并安装新的主题。"}]},{"type":"element","tag":"h3","props":{"id":"文件图标主题"},"children":[{"type":"text","value":"文件图标主题"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"文件图标主题可以改变在编辑器边栏上显示的文件与目录列表所使用的小图标。"}]},{"type":"element","tag":"h4","props":{"id":"安装-material-icon-theme"},"children":[{"type":"text","value":"安装 Material Icon Theme"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Material Icon Theme 是在训练视频中出现的 VSCode 编辑器使用的文件图标主题。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开编辑器的扩展，搜索并安装 Material Icon Theme，完成安装以后 VSCode 默认会将其设置为当前使用的文件图标主题。这款主题有几种网格，训练视频中使用的是 Classic 风格的主题。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(8).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 VSCode 扩展里搜索 material icon theme"}]}]},{"type":"element","tag":"h4","props":{"id":"设置文件图标主题"},"children":[{"type":"text","value":"设置文件图标主题"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开命令面板，搜索并执行 File Icon Theme，这会列出编辑器里可选的文件图标主题，你可以选择想要使用的主题。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(9).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在命令面板搜索 file icon theme"}]}]},{"type":"element","tag":"h3","props":{"id":"颜色主题"},"children":[{"type":"text","value":"颜色主题"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"VSCode 编辑器的颜色主题可以改变编辑器界面与代码高亮的样式。"}]},{"type":"element","tag":"h4","props":{"id":"安装-ninghao-color-theme"},"children":[{"type":"text","value":"安装 Ninghao Color Theme"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Ninghao Color Theme 在训练视频中出现的 VSCode 编辑器使用的颜色主题。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开扩展，搜索 ninghao，然后安装 Ninghao Color Theme 这个扩展，完成以后选择使用 Ninghao Dark 这款颜色主题。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(10).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 VSCode 扩展搜索 ninghao"}]}]},{"type":"element","tag":"h4","props":{"id":"设置颜色主题"},"children":[{"type":"text","value":"设置颜色主题"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开命令面板，搜索并执行 Color Theme，这会列出编辑器里的颜色主题，你可以选择自己要使用的颜色主题。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(11).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在命令面板搜索 color theme"}]}]},{"type":"element","tag":"h2","props":{"id":"配置"},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"配置"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"VSCode 支持全局范围与项目级别的配置，配置以 JSON 格式保存，放在 settings.json 文件里。"}]},{"type":"element","tag":"h3","props":{"id":"全局配置"},"children":[{"type":"text","value":"全局配置"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开命令面板，搜索并执行 Open Settings UI，这会打开设置界面，通过这个界面可以修改编辑器的全局范围的一些设置。"}]},{"type":"element","tag":"h4","props":{"id":"保存文件时格式化代码"},"children":[{"type":"text","value":"保存文件时格式化代码"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在配置搜索 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"format on save"}]},{"type":"text","value":"，勾选一下 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Editor:Format On Save"}]},{"type":"text","value":" 这个选项，这样编写了代码文件的内容并保存文件后，就会自动格式化编写的代码。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(12).png"},"children":[]}]},{"type":"element","tag":"h4","props":{"id":"typescript-导入模块时使用相对路径"},"children":[{"type":"text","value":"TypeScript 导入模块时使用相对路径"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在训练营中，我们会使用 TypeScript 语言编写应用，编辑器自动导入模块时可以使用模块的相对路径。打开编辑器的配置，然后找到 TypeScript > Preferences: Import Module Specifier 这个配置选项，将选项的值设置成 relative（相对）。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(13).png"},"children":[]}]},{"type":"element","tag":"h4","props":{"id":"settingsjson"},"children":[{"type":"text","value":"settings.json"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开命令面板，搜索并执行 Open Settings JSON，这会打开一个 settings.json 文件，文件里存放的就是 VSCode 编辑器在全局范围的配置。通过编辑器的设置界面修改的配置选项都会记录在这个文件中。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"\"editor.formatOnSave\": true\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"\"editor.formatOnSave\": true\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 settings.json 文件中搜索 formatOnSave，你会发现它的值应该是 true，因为之前通过图形界面的配置，勾选了 Editor:Format On Save 这个选项。"}]},{"type":"element","tag":"h3","props":{"id":"项目配置"},"children":[{"type":"text","value":"项目配置"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"VSCode 编辑器支持设置项目级别的配置，这些配置只会影响到当前项目。"}]},{"type":"element","tag":"h4","props":{"id":"设置项目级别的配置"},"children":[{"type":"text","value":"设置项目级别的配置"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"要设置 VSCode 编辑器的项目级别的配置，可以在项目的根目录的下面新建一个 .vscode 目录，在个目录里再新建一个 settings.json 文件。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":".vscode/settings.json"}]}]},{"type":"element","tag":"code","props":{"code":"{\n  \"editor.formatOnSave\": true,\n  \"typescript.preferences.importModuleSpecifier\": \"relative\"\n}\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"{\n  \"editor.formatOnSave\": true,\n  \"typescript.preferences.importModuleSpecifier\": \"relative\"\n}\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果在项目中添加上面这个 VSCode 编辑器配置文件，编辑器会在保存文件时格式化代码，在 TypeScript 代码文件里导入模块时会使用相对的路径。"}]},{"type":"element","tag":"h2","props":{"id":"代码片断"},"children":[{"type":"text","value":"代码片断"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"编写应用时，输入几个字母按下 tab 键就可以快速插入事先定义好的代码块，这就是在 VSCode 编辑器里的代码片断提供的功能。将经常要在项目里编写的，具有同样模式的代码块定义成代码片断。代码片断可以区分语言，也就是我们可以为某种特定的程序语言定义代码片断。代码片断可以在全局范围定义，也可以在项目级别定义。"}]},{"type":"element","tag":"h3","props":{"id":"定义项目级别的代码片断"},"children":[{"type":"text","value":"定义项目级别的代码片断"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在训练营里编写应用时，我们会使用项目级别的代码片断，这些代码片断会包含在项目的初始仓库里，所以准备好项目以后，你可以直接使用在项目里定义的代码片断。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"定义项目级别的代码片断，可以将定义代码片断的文件放在项目根目录下的 .vscode 这个目录里面，代码片断的文件名需要 .code-snippets 后缀，比如 nid-nest.code-snippets，VSCode 会自动获取在项目里定义的代码片断。"}]},{"type":"element","tag":"h3","props":{"id":"理解代码片断的定义"},"children":[{"type":"text","value":"理解代码片断的定义"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"定义代码片断的文件的内容格式是 JSON，每个代码片断需要设置它的标题，前缀（prefix）代码主体（body），在编写应用时输入代码片断的前缀字符时，VSCode 会提示可用的代码片断，按下 tab 以后就会插入当前选择的代码片断。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在定义代码片断的时候可以在设置一些编辑点，每个编辑点都有编号，还可以设置可选的占位符文字，比如 ${1:Name} ，指的就是在插入代码片断以后，第一个要编辑的地方。完成编辑以后按下 tab 会跳转到下一处编辑点，也就是 ${2}。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":".vscode/nid-nest.code-snippets"}]}]},{"type":"element","tag":"code","props":{"code":"{\n  \"CQRS: Execute command\": {\n    \"prefix\": \"ec\",\n    \"body\": [\n      \"${3}this.commandBus.execute(\",\n      \"  new ${1:Name}Command(${2})\",\n      \");\"\n    ]\n  }\n}\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"{\n  \"CQRS: Execute command\": {\n    \"prefix\": \"ec\",\n    \"body\": [\n      \"${3}this.commandBus.execute(\",\n      \"  new ${1:Name}Command(${2})\",\n      \");\"\n    ]\n  }\n}\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在上面的代码片断文件里定义了一个代码片断，标题是 CQRS: Execute command，前缀是 ec，在这个代码片断的主体里设置了三处编辑点。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"参考"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"a","props":{"href":"https://github.com/ninghao/nid-nest-starter/blob/master/.vscode/nid-nest.code-snippets","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://github.com/ninghao/nid-nest-starter/blob/master/.vscode/nid-nest.code-snippets"}]}]},{"type":"element","tag":"h3","props":{"id":"使用代码片断"},"children":[{"type":"text","value":"使用代码片断"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在编写项目的时候，输入代码片断的前缀（prefix），VSCode 就会列出相关的代码片断，选择要插入的代码片断，然后按下 tab 键就可以插入对应的代码片断了。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(14).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在项目文件里输入 ec 这个前缀，会显示可以使用 CQRS: Execute command 这个代码片断，按下 tab 键可以在当前位置插入这个代码片断。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/vscode/image(15).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"插入代码片断以后，可以编辑代码片断里的第一个编辑点，完成编写以后按下 tab 键可以跳转到下一处编辑器继续修改这个代码片断。"}]}],"toc":{"title":"","searchDepth":2,"depth":2,"links":[{"id":"vscode","depth":2,"text":"VSCode","children":[{"id":"安装与使用","depth":3,"text":"安装与使用"},{"id":"使用命令面板","depth":3,"text":"使用命令面板"},{"id":"使用-code-命令","depth":3,"text":"使用 code 命令"}]},{"id":"扩展","depth":2,"text":"扩展","children":[{"id":"中文语言包","depth":3,"text":"中文语言包"},{"id":"prettier","depth":3,"text":"Prettier"},{"id":"vetur","depth":3,"text":"Vetur"},{"id":"flutter","depth":3,"text":"Flutter"}]},{"id":"主题","depth":2,"text":"主题","children":[{"id":"文件图标主题","depth":3,"text":"文件图标主题"},{"id":"颜色主题","depth":3,"text":"颜色主题"}]},{"id":"配置","depth":2,"text":"配置","children":[{"id":"全局配置","depth":3,"text":"全局配置"},{"id":"项目配置","depth":3,"text":"项目配置"}]},{"id":"代码片断","depth":2,"text":"代码片断","children":[{"id":"定义项目级别的代码片断","depth":3,"text":"定义项目级别的代码片断"},{"id":"理解代码片断的定义","depth":3,"text":"理解代码片断的定义"},{"id":"使用代码片断","depth":3,"text":"使用代码片断"}]}]}},"_type":"markdown","_id":"content:docs:1.tools:2.vscode.md","_source":"content","_file":"docs/1.tools/2.vscode.md","_extension":"md"},{"_path":"/docs/tools/git","_draft":false,"_partial":false,"_empty":false,"title":"源代码管理","description":"Git 是宁皓独立开发者训练营选择使用的源代码管理工具，我们要使用 Git 对开发的应用项目做源代码管理。","excerpt":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"源代码管理"},"children":[{"type":"text","value":"源代码管理"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Git 是宁皓独立开发者训练营选择使用的源代码管理工具，我们要使用 Git 对开发的应用项目做源代码管理。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在训练任务中需要使用 Git 做的一些事情："}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"克隆远程仓库"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"初始化本地仓库"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"创建并切换分支"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"完成任务后做提交"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"合并分支"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"将本地提交推送到远程仓库"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在训练营里有一组任务会专门练习使用 Git，您可以跟随训练任务一步一步去准备 Git，修改配置，做提交，创建与合并分支，使用远程仓库等等。此文可以辅助您准备好必要的工具，介绍一下工作流程，再熟悉一下相关的关键概念。"}]},{"type":"element","tag":"h2","props":{"id":"git"},"children":[{"type":"text","value":"Git"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Git 是一种源代码管理工具，也可以说是版本控制工具。使用 Git 对开发的项目做源代码管理，是任何开发者都必备的一项重要的技能。Git 是一个命令行工具，也就是你需要在命令行界面下通过执行 git 命令完成要做的事情。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://git-scm.com/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://git-scm.com/"}]}]},{"type":"element","tag":"h3","props":{"id":"安装"},"children":[{"type":"text","value":"安装"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Windows 用户安装的 cmder 里面自带 Git，macOS 系统里也会自带 Git，如果需要使用更新版本的 Git，可以到它的官网下载并安装。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"确定在系统里是否已经安装了 Git，可以在终端执行："}]},{"type":"element","tag":"code","props":{"code":"git --help\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git --help\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令如果能看到帮助信息，说明在系统里已经安装了 Git。"}]},{"type":"element","tag":"h3","props":{"id":"配置"},"children":[{"type":"text","value":"配置"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 Git 做提交时，会在提交中记录做这次提交的用户是谁，这些用户相关的信息可以通过 Git 的全局配置来设置。"}]},{"type":"element","tag":"h4","props":{"id":"配置全局用户名"},"children":[{"type":"text","value":"配置全局用户名"}]},{"type":"element","tag":"code","props":{"code":"git config --global user.name \"wanghao\"\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git config --global user.name \"wanghao\"\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"将 wanghao 替换成您自己的用户名。"}]},{"type":"element","tag":"h4","props":{"id":"配置全局用户邮箱"},"children":[{"type":"text","value":"配置全局用户邮箱"}]},{"type":"element","tag":"code","props":{"code":"git config --global user.email \"wanghao@ninghao.net\"\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git config --global user.email \"wanghao@ninghao.net\"\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"将 "},{"type":"element","tag":"a","props":{"href":"mailto:wanghao@ninghao.net"},"children":[{"type":"text","value":"wanghao@ninghao.net"}]},{"type":"text","value":" 替换成您自己的邮箱地址。"}]},{"type":"element","tag":"h4","props":{"id":"查看配置信息"},"children":[{"type":"text","value":"查看配置信息"}]},{"type":"element","tag":"code","props":{"code":"git config --list\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git config --list\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这个命令会列出 Git 当前使用的配置信息，可以确定一下 user"},{"type":"element","tag":"a","props":{"href":"http://user.name/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":".name"}]},{"type":"text","value":" 与 user.email 这两个配置的值。"}]},{"type":"element","tag":"h2","props":{"id":"仓库"},"children":[{"type":"text","value":"仓库"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在项目里创建一个仓库（repository），就可以对项目做源代码管理了。Git 会把它需要的东西都存储在这个仓库里，这个仓库其实就是一个名字是 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":".git"}]},{"type":"text","value":" 的目录，Git 帮我们管理这个目录里的东西，所以通常我们不需要关心仓库里到底都放了些什么，需要做什么都可以通过执行 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"git"}]},{"type":"text","value":" 命令完成。"}]},{"type":"element","tag":"h3","props":{"id":"创建仓库"},"children":[{"type":"text","value":"创建仓库"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在项目里创建一个仓库，可以执行 git init 命令，它会在项目里初始化一个代码仓库，也就是在项目里创建一个 .git 目录，里面会包含一些必要的东西。"}]},{"type":"element","tag":"code","props":{"code":"git init\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git init\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在项目所在目录的下面，执行上面这行命令会给项目创建一个仓库，这个命令只需要执行一次。"}]},{"type":"element","tag":"h3","props":{"id":"删除仓库"},"children":[{"type":"text","value":"删除仓库"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果想重新对项目做源代码管理，可以把项目当前的仓库删除掉，然后再重新执行 git init 命令初始化一个新的仓库。"}]},{"type":"element","tag":"code","props":{"code":"rm -rf ~/desktop/xb2-node/.git\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"rm -rf ~/desktop/xb2-node/.git\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这行命令会将桌面上的 xb2-node 这个项目的仓库删除掉。"}]},{"type":"element","tag":"h2","props":{"id":"提交"},"children":[{"type":"text","value":"提交"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"提交（commit）可以保存项目当前的状态。在项目里创建了新的文件或是修改了文件里的代码，这其实都是在改变项目的状态，如果你想保存一下项目当前的状态，就可以对项目做一次提交，提交时可以设置一条信息，记录并说明一下为什么要做这次提交，方便以后可以随时查阅。"}]},{"type":"element","tag":"h3","props":{"id":"提交的流程"},"children":[{"type":"text","value":"提交的流程"}]},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"查看状态"}]},{"type":"text","value":"。执行 git status 命令，查看一下项目当前都做了哪些修改。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"添加修改"}]},{"type":"text","value":"。执行 git add 命令可以添加要包含在此次提交里的修改。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"确定提交"}]},{"type":"text","value":"。执行 git commit 命令确定提交。"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"git status\ngit add .\ngit commit -m '修正环境变量名称'\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git status\ngit add .\ngit commit -m '修正环境变量名称'\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"分别执行上面这几行命令会对项目做一次提交。git add . 会把当前项目里的修改全部添加到暂存区准备提交，然后执行 git commit 命令确定提交，用 m 选项给这次提交添加一条描述信息。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在本地使用带图形化界面的 Git 软件，可以更方便地做提交，比如使用 VSCode 编辑器自带的源代码管理功能，或者使用 Sourcetree。"}]},{"type":"element","tag":"h3","props":{"id":"查看提交历史"},"children":[{"type":"text","value":"查看提交历史"}]},{"type":"element","tag":"code","props":{"code":"git log\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git log\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这个命令可以查看项目的提交历史，每条历史记录都会显示提交的 ID（commit）、作者（Author）、日期（Date）、还有提交信息。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"commit 4e6a75bfc61d9bbbc1a0b3f2ab3ed3f75e6ae5ad\nAuthor: wanghao <wanghao@ninghao.net>\nDate:   Sat Apr 23 08:06:49 2022 +0800\n\n    修正环境变量名称\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"commit 4e6a75bfc61d9bbbc1a0b3f2ab3ed3f75e6ae5ad\nAuthor: wanghao <wanghao@ninghao.net>\nDate:   Sat Apr 23 08:06:49 2022 +0800\n\n    修正环境变量名称\n"}]}]}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"ID"}]},{"type":"text","value":"：4e6a75bfc61d9bbbc1a0b3f2ab3ed3f75e6ae5ad"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"作者"}]},{"type":"text","value":"：wanghao <"},{"type":"element","tag":"a","props":{"href":"mailto:wanghao@ninghao.net"},"children":[{"type":"text","value":"wanghao@ninghao.net"}]},{"type":"text","value":">"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"日期"}]},{"type":"text","value":"：Sat Apr 23 08:06:49 2022 +0800"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"信息"}]},{"type":"text","value":"：修正环境变量名称"}]}]},{"type":"element","tag":"h3","props":{"id":"提交的作用"},"children":[{"type":"text","value":"提交的作用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"提交会记录对项目所做的修改，保存项目的一个状态，通过提交的 ID 可以引用这次提交。我们可以查看在这次提交里包含的修改，比如修改了项目里的哪些文件，哪几行代码等等。也可以检出（checkout）这次提交，查看当初项目在做这次提交时的样子。还可以还原（revert）这次提交对项目做的修改，或者将项目重置（reset）到做这次提交时的状态。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"提交是不能被修改的，比如提交的作者、日期或者包含在提交里的修改，所有这些东西都是不能修改的，也就是一但确定了提交以后，就无法再修改这次提交了。我们可以重做最后一次做的提交，可以还原之前做的提交，这需要做一次新的提交保存还原之后的状态。"}]},{"type":"element","tag":"h2","props":{"id":"分支"},"children":[{"type":"text","value":"分支"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"分支（branch）是实践想法，添加新功能，修复 Bug 的好地方。在开发应用时，做这些事情之前都可以创建新的分支，然后在新的分支上去开发新功能或者修复 Bug 等等，无论我们怎么折腾都不会影响到其它分支上的项目。如果成功了，可以将在这些分支上做的提交合并到项目的主分支上，如果想法不成立或是新功能很失败，可以删除这些分支。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image.png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"master--main"},"children":[{"type":"text","value":"master / main"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"给项目初始化了一个仓库以后，默认会创建一个 master 分支，这个分支跟我们自己创建的分支并没有什么区别，只不过按习惯我们会将其称之为主分支。"}]},{"type":"element","tag":"h3","props":{"id":"查看分支"},"children":[{"type":"text","value":"查看分支"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行 git branch 命令可以列出项目里的分支，名字前面带 * 号的分支是项目当前所在的分支。"}]},{"type":"element","tag":"code","props":{"code":"git branch\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git branch\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"查看全部分支"}]}]},{"type":"element","tag":"code","props":{"code":"git branch -a\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git branch -a\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果想显示全部的分支（包含远程分支），可以在命令里使用 a 选项。"}]},{"type":"element","tag":"h3","props":{"id":"创建分支"},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"创建分支"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在开发项目时我们可以任意创建需要的分支，创建分支的时候需要指定一个起始提交，这样新创建的分支上就会包含这次起始提交以及在这次提交之前做的所有的提交。在每条分支上可以包含只属于自己的提交，这些提交会在创建分支时指定的那个起始提交之后。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"git branch payment\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git branch payment\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这个命令会创建一个新的分支，名字叫 payment，创建这个分支时并没有特别指定这个分支的起始提交，所以默认会基于当前分支的最后一次提交创建这个分支。假设当前分支是 master，在这个分支上的最后一次提交是 “添加 README.md”，这样在新创建的 payment 分支的提交历史里，最后一次提交也会是 “添加 README.md” 。"}]},{"type":"element","tag":"h3","props":{"id":"切换分支"},"children":[{"type":"text","value":"切换分支"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 git checkout 命令可以切换项目当前的分支。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"git checkout payment\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git checkout payment\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令会将项目当前分支切换到 payment，执行 git branch 命令可以查看项目当前的分支。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"创建新分支并切换到这个分支上"}]}]},{"type":"element","tag":"code","props":{"code":"git checkout -b payment\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git checkout -b payment\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果要切换到的分支并不存在，可以在命令里使用 b 选项，这样可以创建新的分支并且切换到这个新的分支上。"}]},{"type":"element","tag":"h3","props":{"id":"合并分支历史"},"children":[{"type":"text","value":"合并分支/历史"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"用 git merge 命令可以合并分支，也可以说是合并提交历史。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"git checkout master\ngit merge payment\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git checkout master\ngit merge payment\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"假设当前是在 payment 这个分支上，之前在这个分支上做了一些新的提交，现在打算将这些提交合并到 master 分支上，可以先将当前分支切换到 master，然后执行 git merge 合并 payment 分支，这样在 payment 分支上做的那些提交就会合并到 master 分支上了。"}]},{"type":"element","tag":"h3","props":{"id":"删除分支"},"children":[{"type":"text","value":"删除分支"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 git branch 命令，外加 d 选项可以将不再需要的分支删除掉。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"git branch -d payment\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git branch -d payment\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的这个命令会将 payment 这个分支删除掉。注意我们不能删除当前所在的分支，比如想要删除 payment 分支，需要先将当前分支切换到其它的分支上，然后再删除它。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"强制删除分支"}]}]},{"type":"element","tag":"code","props":{"code":"git branch -D payment\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git branch -D payment\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"要删除的分支包含尚未合并的提交也是无法删除的，如果要强制删除这样的分支，需要使用大写的 D 选项。"}]},{"type":"element","tag":"h2","props":{"id":"忽略"},"children":[{"type":"text","value":"忽略"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 .gitignore 文件里可以列出要忽略不做源代码管理的东西。"}]},{"type":"element","tag":"h3","props":{"id":"gitignore"},"children":[{"type":"text","value":".gitignore"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在项目里创建了一个仓库以后，默认 Git 会跟踪这个目录里所有的文件。不过在项目里有些东西是不用做源代码管理的，比如一些日志文件，用户上传的文件，系统自动生成的一些文件等等，这些东西可以在 .gitignore ** 文件里说明一下。这个文件一般要放在项目根目录的下面。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":".gitignore 文件内容示例"}]}]},{"type":"element","tag":"code","props":{"code":"# compiled output\n/dist\n/node_modules\n\n# Logs\nlogs\n*.log\nnpm-debug.log*\n\n# OS\n.DS_Store\n\n# IDE - VSCode\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"# compiled output\n/dist\n/node_modules\n\n# Logs\nlogs\n*.log\nnpm-debug.log*\n\n# OS\n.DS_Store\n\n# IDE - VSCode\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 .gitignore 文件中，带 # 号前缀的是注释内容。"}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"*/dist，*指的是 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"dist"}]},{"type":"text","value":" 目录，这样就会忽略掉这个目录里的所有的文件。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"**.log，*指的是忽略掉所有文件名里带 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":".log"}]},{"type":"text","value":" 后缀的文件，* 表示所有。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":".vscode/"}]},{"type":"text","value":"，指的是*忽略掉 *.vscode* 目录里的所有的文件。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"!.vscode/settings.json，指的是"}]},{"type":"text","value":"不忽略 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":".vscode/settings.json"}]},{"type":"text","value":" 文件，! 号表示否定。"}]}]},{"type":"element","tag":"h3","props":{"id":"忽略已被-git-跟踪的文件"},"children":[{"type":"text","value":"忽略已被 Git 跟踪的文件"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":".gitignore 文件的作用就是告诉 Git，项目里哪些文件不需要做源代码管理。不过有一种情况是例外，就是如果文件已经被 Git 跟踪了，就是已经做了源代码管理，这时如果在 .gitignore 文件里列出这些东西，Git 仍然会对这些东西做源代码管理。如果想忽略这些东西，可以执行下面这几行命令："}]},{"type":"element","tag":"code","props":{"code":"git rm -r --cached .\ngit add .\ngit commit -am \"删除忽略的文件\"\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git rm -r --cached .\ngit add .\ngit commit -am \"删除忽略的文件\"\n"}]}]}]},{"type":"element","tag":"h2","props":{"id":"远程"},"children":[{"type":"text","value":"远程"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 Git 语境下，远程（remote）指的是远程仓库。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"项目有本地仓库，也可以给它添加一个远程仓库，这需要先在远程仓库的服务商那里为项目创建一个远程仓库，然后将其添加到本地项目里，这样就可以将本地仓库上传（push）到远程仓库里了，也可以从远程仓库那里拉取（pull）最新的提交。远程仓库有点像是项目的备份与合作中心，其实本质上远程仓库与本地仓库没什么区别，所以可以把它想成是在网络其它地方的一个仓库。"}]},{"type":"element","tag":"h3","props":{"id":"远程仓库服务商"},"children":[{"type":"text","value":"远程仓库服务商"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"a","props":{"href":"https://github.com/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"Github"}]},{"type":"text","value":" 与 "},{"type":"element","tag":"a","props":{"href":"https://coding.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"Coding"}]},{"type":"text","value":" 都提供远程仓库服务，你可以选择使用其中任意一个。"}]},{"type":"element","tag":"h3","props":{"id":"用-ssh-key-验证身份"},"children":[{"type":"text","value":"用 SSH key 验证身份"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在远程仓库服务端那里注册了帐户以后，可以配置使用 SSH key 的方式验证身份，这样以后将本地仓库推送到远程仓库时，就可以通过本地的钥匙验证用户身份，也就是不再需要输入在远程仓库服务商那里注册的用户名与密码了。使用 SSH Key 的方式验证身份，就是将本地用户主目录下存放的公钥文件里的内容，添加到远程仓库服务商那里。"}]},{"type":"element","tag":"h4","props":{"id":"输出本地公钥文件内容"},"children":[{"type":"text","value":"输出本地公钥文件内容"}]},{"type":"element","tag":"code","props":{"code":"cat ~/.ssh/id_rsa.pub\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cat ~/.ssh/id_rsa.pub\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的这行命令可以输出用户主目录下的 .ssh 目录里的 id_rsa.pub 文件里的内容。复制一下输出的这个文件里的内容。"}]},{"type":"element","tag":"h4","props":{"id":"配置-github-帐户的-ssh-key"},"children":[{"type":"text","value":"配置 Github 帐户的 SSH key"}]},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"登录 "},{"type":"element","tag":"a","props":{"href":"https://github.com/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"Github"}]},{"type":"text","value":"。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"打开用户的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Settings"}]},{"type":"text","value":"（设置）。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"打开设置页面边栏上的 "},{"type":"element","tag":"a","props":{"href":"https://github.com/settings/keys","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"SSH and GPG keys"}]},{"type":"text","value":"。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"SSH keys"}]},{"type":"text","value":" 右侧的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"New SSH key"}]},{"type":"text","value":"。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Title"}]},{"type":"text","value":" 里随便输入一个标题，将复制的用户公钥文件内容粘贴到 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Key"}]},{"type":"text","value":" 里面。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Add SSH key"}]},{"type":"text","value":"，确定添加。"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(1).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 Github 账户设置添加 SSH Keys"}]}]},{"type":"element","tag":"h4","props":{"id":"配置-coding-帐户的-ssh-key"},"children":[{"type":"text","value":"配置 Coding 帐户的 SSH key"}]},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"登录 "},{"type":"element","tag":"a","props":{"href":"https://coding.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"Coding"}]},{"type":"text","value":"。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"打开 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"个人账户设置"}]},{"type":"text","value":"。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"打开 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"SSH 公钥"}]},{"type":"text","value":"。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"新增公钥"}]},{"type":"text","value":"。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"公钥名称"}]},{"type":"text","value":" 里输入一个名字，然后将复制的用户公钥文件里的内容放在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"公钥内容"}]},{"type":"text","value":" 里。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"选择 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"公钥有效期"}]},{"type":"text","value":"。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"确认"}]},{"type":"text","value":"，确定添加公钥。"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(2).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 Coding 个人账户设置添加 SSH 公钥"}]}]},{"type":"element","tag":"h3","props":{"id":"创建远程仓库"},"children":[{"type":"text","value":"创建远程仓库"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在远程仓库服务商那里为项目创建一个远程仓库。"}]},{"type":"element","tag":"h4","props":{"id":"在-github-创建远程仓库"},"children":[{"type":"text","value":"在 Github 创建远程仓库"}]},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"登录到 Github。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"点击右上角+号图标，选择 "},{"type":"element","tag":"a","props":{"href":"https://github.com/new","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"New repository"}]}]},{"type":"text","value":"。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"输入 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Repository name"}]},{"type":"text","value":"（仓库名称）。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"选择 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Public"}]},{"type":"text","value":"（公开） 或 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Private"}]},{"type":"text","value":"（私有）。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Create repository"}]},{"type":"text","value":"（创建仓库）。"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 Github 创建远程仓库时，仓库的名字在你的账户里应该是唯一的，也就是不能有重名的仓库。创建仓库时可以选择这个仓库是公开还是私有，如果选择 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Public"}]},{"type":"text","value":"（公开），任何人都能够查看或克隆你的项目仓库。如果选择 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Private"}]},{"type":"text","value":"（私有），默认只有你自己可以使用这个仓库。你可以通过配置仓库，邀请他人共同开发项目。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(3).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 Github 创建仓库"}]}]},{"type":"element","tag":"h4","props":{"id":"在-coding-创建远程仓库"},"children":[{"type":"text","value":"在 Coding 创建远程仓库"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 Coding 创建远程仓库的流程与 Github 略有不同，首先你需要创建一个项目，然后在这个项目里再去创建代码仓库。"}]},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"登录 Coding。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"选择并打开项目（创建新的项目）。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"打开项目的代码仓库，点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"创建代码仓库"}]},{"type":"text","value":"。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"设置 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"仓库名称、仓库描述"}]},{"type":"text","value":"，选择 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"是否开源"}]},{"type":"text","value":"。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"完成创建"}]},{"type":"text","value":"。"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(4).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 Coding 创建代码仓库"}]}]},{"type":"element","tag":"h4","props":{"id":"远程仓库地址"},"children":[{"type":"text","value":"远程仓库地址"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"无论是在 Github 还是在 Coding 上创建的仓库，都提供两种类型的仓库地址，一种是 HTTPS，一种是 SSH。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"这两种仓库地址唯一不同的地方就是验证身份的方式，如果使用 HTTPS 类型的地址，就需要通过用户名与密码完成身份验证，如果选择使用 SSH 类型的仓库地址，可以通过 SSH 钥匙完成身份验证。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(5).png"},"children":[]},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(6).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Github 仓库地址"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(7).png"},"children":[]},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(8).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Coding 仓库地址"}]}]},{"type":"element","tag":"h3","props":{"id":"管理项目的远程仓库"},"children":[{"type":"text","value":"管理项目的远程仓库"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"创建了远程仓库以后，可以将它添加到项目里使用。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在项目里可以随便添加远程仓库，其实就是给要添加的远程起个名字，再设置一个对应的仓库地址，以后可以使用给远程起的名字引用对应的远程仓库。"}]},{"type":"element","tag":"h4","props":{"id":"添加远程"},"children":[{"type":"text","value":"添加远程"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 git remote add 命令可以给项目添加远程。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"git remote add origin git@github.com:wanghao8080/xb2-node.git\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git remote add origin git@github.com:wanghao8080/xb2-node.git\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端，项目所在目录的下面执行上面这行命令可以给项目添加一个名字是 origin 的远程，对应的地址就是之前我在 Github 上创建的一个仓库的地址。origin 是一个远程的命名惯例，并没有特别的意思，你可以随意命名远程。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"项目可以同时拥有多个远程："}]},{"type":"element","tag":"code","props":{"code":"git remote add coding git@e.coding.net:ninghao/xb2/xb2-node.git\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git remote add coding git@e.coding.net:ninghao/xb2/xb2-node.git\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令会给项目添加一个名字是 coding 的远程，对应的地址是我在 Coding 创建的一个代码仓库的地址。"}]},{"type":"element","tag":"h4","props":{"id":"查看远程"},"children":[{"type":"text","value":"查看远程"}]},{"type":"element","tag":"code","props":{"code":"git remote -v\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git remote -v\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行 git remote 可以列出项目的远程，配合 v 选项可以显示更详细的信息。"}]},{"type":"element","tag":"h4","props":{"id":"删除远程"},"children":[{"type":"text","value":"删除远程"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"要删除项目里的远程，可以使用 git remote remove 命令。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"git remote remove coding\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git remote remove coding\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这行命令会将项目里的名字是 coding 的这个远程删除掉。"}]},{"type":"element","tag":"h3","props":{"id":"使用远程仓库"},"children":[{"type":"text","value":"使用远程仓库"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"为项目添加了远程以后，我们可以将本地项目推送到指定的远程仓库里，也可以从某个远程那里获取到最新的提交。"}]},{"type":"element","tag":"h4","props":{"id":"推送"},"children":[{"type":"text","value":"推送"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 git push 可以将本地的东西推送到指定的远程那里。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用这个命令的时候要指定远程的名字，还有要推送的东西，一般就是一个本地分支的名字。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"git push origin master\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git push origin master\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令会将本地的 master 分支推送到 origin 这个远程，这个 origin 指的具体是哪里，要看当初添加这个 origin 远程的时候设置的远程仓库的地址是什么。成功以后可以到远程仓库页面那里刷新一下，应该会出现项目的代码，远程里也会包含一个 master 分支。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"以后在本地的 master 分支上有了新的提交，都可以执行 git push 命令，将这些新做的东西推送到 origin 这个远程。"}]},{"type":"element","tag":"h4","props":{"id":"拉取"},"children":[{"type":"text","value":"拉取"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 git pull 命令可以将远程上的新东西拉取到本地。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"git pull origin\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git pull origin\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令可以将 origin 这个远程里的新东西下载下来并合并到本地。这个命令的功能相当于先用 git fetch 从远程那里下载最新的东西，然后用 git merge 将这些新东西合并到本地。"}]},{"type":"element","tag":"h4","props":{"id":"克隆"},"children":[{"type":"text","value":"克隆"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 git clone 命令可以将远程仓库克隆到本地。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"cd ~/desktop\ngit clone https://github.com/ninghao/xb2-node.git\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cd ~/desktop\ngit clone https://github.com/ninghao/xb2-node.git\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这两行命令，会将一个远程仓库克隆到本地电脑的桌面上，默认会放在 xb2-node 这个目录的下面。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"git clone 只需要执行一次，如果克隆的是自己创建的远程仓库或者有权限管理的远程仓库，可以使用 SSH 类型的地址，如果克隆的是别人的代码仓库，可以选择使用 HTTPS 类型的仓库地址。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"完成以后在本地应该就会出现一个跟远程仓库里一模一样的项目了。项目里会包含一个 origin 远程，指向的就是当时克隆的那个远程仓库。"}]},{"type":"element","tag":"h4","props":{"id":"基于远程分支创建本地分支"},"children":[{"type":"text","value":"基于远程分支创建本地分支"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"克隆完成以后，默认在项目里会有一个 master 本地分支，如果你想使用远程里的其它的分支，需要基于远程分支创建一个本地的分支。首先可以执行 git branch -a 查看当前所有的分支，你应该会发现一些 remotes/origin/* 这种名字的分支，这些就是在远程仓库里的分支。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"git branch audit remotes/origin/audit\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git branch audit remotes/origin/audit\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"上面这行命令会基于 remotes/origin/audit 这个远程分支创建一个名字是 audit 的本地分支。这样你就可以切换到 audit 这个本地分支了，在这个分支上的项目跟远程里的那个 audit 分支上的项目是一样的。"}]},{"type":"element","tag":"h3","props":{"id":"协作"},"children":[{"type":"text","value":"协作"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"远程仓库为项目开发提供了一个合作中心，我们可以邀请他人成为项目的协作者，这样大家就可以一起开发项目了。每个人在本地都有一份项目的仓库，大家可以在本地进行开发，然后将新做的提交推送到远程仓库，团队的协作者可以从远程拉取他人新做的这些提交。"}]},{"type":"element","tag":"h4","props":{"id":"为-github-仓库添加协作者"},"children":[{"type":"text","value":"为 Github 仓库添加协作者"}]},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"在远程仓库页面，打开 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Settings"}]},{"type":"text","value":"（设置）。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"打开边栏菜单里的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Collaborators"}]},{"type":"text","value":"（协作者）。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Add people"}]},{"type":"text","value":"（添加），搜索要添加的协作者的用户名并选择。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"被选择的用户会收到一封邮件，对方确认以后就会正式成为项目的协作者。"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(9).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Github 仓库设置下的 Collaborators"}]}]},{"type":"element","tag":"h4","props":{"id":"为-coding-仓库添加协作者"},"children":[{"type":"text","value":"为 Coding 仓库添加协作者"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 Coding 里需要在项目级别添加项目的成员，然后设置成员的权限。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(10).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Coding 项目添加成员的界面"}]}]},{"type":"element","tag":"h2","props":{"id":"图形界面"},"children":[{"type":"text","value":"图形界面"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在本地开发时可以使用带图形界面的 Git 软件，比如 Sourcetree，或者使用 VSCode 自带的源代码管理功能。"}]},{"type":"element","tag":"h3","props":{"id":"sourcetree"},"children":[{"type":"text","value":"Sourcetree"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Sourcetree 是一款带图形界面的 Git 软件，也就是使用它我们可以通过图形界面完成需要执行 git 命令完成的事情，比如做提交、查看提交历史、创建分支、推送与拉取等等。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"这款软件可以辅助你完成一些训练任务，比如我们在《小白的开发之路：Node.js 服务端应用开发实践》中开发了一个"},{"type":"element","tag":"a","props":{"href":"https://github.com/ninghao/xb2-node.git","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"Node 应用"}]},{"type":"text","value":"，每完成一个任务我都会做一次提交，你可以将这个项目克隆到本地，然后使用 Sourcetree 打开这个本地仓库，这样就可以查看项目的提交历史，可以检查每一次提交对项目都做了哪些修改。你还可以很方便的检出（checkout）到任意一次提交，这样你在 VSCode 编辑器那里看到的项目，就是我做完这次提交时的样子。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(11).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Sourcetree 软件屏幕截图"}]}]},{"type":"element","tag":"h3","props":{"id":"vscode"},"children":[{"type":"text","value":"VSCode"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"VSCode 编辑器自带源代码管理功能。"}]},{"type":"element","tag":"h4","props":{"id":"使用-vscode-源代码管理"},"children":[{"type":"text","value":"使用 VSCode 源代码管理"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"点击活动栏上的源代码管理小图标可以打开源代码管理功能，或者使用快捷键 ctrl + shift + G。点击源代码管理右侧的 ... 小图标会弹出一个源代码管理相关功能的菜单，或者也可以在命令面板里搜索执行 Git 相关的命令。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(12).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"VSCode 编辑器源代码管理"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(13).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 VSCode 命令面板里搜索 Git"}]}]},{"type":"element","tag":"h4","props":{"id":"使用-vscode-源代码管理做提交"},"children":[{"type":"text","value":"使用 VSCode 源代码管理做提交"}]},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"修改项目。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"打开 VSCode 源代码管理。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"添加在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"更改"}]},{"type":"text","value":" 下面列出的修改，或者点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"更改"}]},{"type":"text","value":" 右侧的 + 号添加全部修改。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"添加的修改会在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"暂存的更改"}]},{"type":"text","value":" 下面出现，这些就是要包含在本次提交里的更改。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"在文本框里输入提交日志，然后按下 command + enter 或者点击源代码管理右侧的对号小图标确定提交。"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(14).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"VSCode 源代码管理功能"}]}]}]},"body":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"源代码管理"},"children":[{"type":"text","value":"源代码管理"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Git 是宁皓独立开发者训练营选择使用的源代码管理工具，我们要使用 Git 对开发的应用项目做源代码管理。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在训练任务中需要使用 Git 做的一些事情："}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"克隆远程仓库"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"初始化本地仓库"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"创建并切换分支"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"完成任务后做提交"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"合并分支"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"将本地提交推送到远程仓库"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在训练营里有一组任务会专门练习使用 Git，您可以跟随训练任务一步一步去准备 Git，修改配置，做提交，创建与合并分支，使用远程仓库等等。此文可以辅助您准备好必要的工具，介绍一下工作流程，再熟悉一下相关的关键概念。"}]},{"type":"element","tag":"h2","props":{"id":"git"},"children":[{"type":"text","value":"Git"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Git 是一种源代码管理工具，也可以说是版本控制工具。使用 Git 对开发的项目做源代码管理，是任何开发者都必备的一项重要的技能。Git 是一个命令行工具，也就是你需要在命令行界面下通过执行 git 命令完成要做的事情。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://git-scm.com/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://git-scm.com/"}]}]},{"type":"element","tag":"h3","props":{"id":"安装"},"children":[{"type":"text","value":"安装"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Windows 用户安装的 cmder 里面自带 Git，macOS 系统里也会自带 Git，如果需要使用更新版本的 Git，可以到它的官网下载并安装。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"确定在系统里是否已经安装了 Git，可以在终端执行："}]},{"type":"element","tag":"code","props":{"code":"git --help\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git --help\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令如果能看到帮助信息，说明在系统里已经安装了 Git。"}]},{"type":"element","tag":"h3","props":{"id":"配置"},"children":[{"type":"text","value":"配置"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 Git 做提交时，会在提交中记录做这次提交的用户是谁，这些用户相关的信息可以通过 Git 的全局配置来设置。"}]},{"type":"element","tag":"h4","props":{"id":"配置全局用户名"},"children":[{"type":"text","value":"配置全局用户名"}]},{"type":"element","tag":"code","props":{"code":"git config --global user.name \"wanghao\"\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git config --global user.name \"wanghao\"\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"将 wanghao 替换成您自己的用户名。"}]},{"type":"element","tag":"h4","props":{"id":"配置全局用户邮箱"},"children":[{"type":"text","value":"配置全局用户邮箱"}]},{"type":"element","tag":"code","props":{"code":"git config --global user.email \"wanghao@ninghao.net\"\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git config --global user.email \"wanghao@ninghao.net\"\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"将 "},{"type":"element","tag":"a","props":{"href":"mailto:wanghao@ninghao.net"},"children":[{"type":"text","value":"wanghao@ninghao.net"}]},{"type":"text","value":" 替换成您自己的邮箱地址。"}]},{"type":"element","tag":"h4","props":{"id":"查看配置信息"},"children":[{"type":"text","value":"查看配置信息"}]},{"type":"element","tag":"code","props":{"code":"git config --list\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git config --list\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这个命令会列出 Git 当前使用的配置信息，可以确定一下 user"},{"type":"element","tag":"a","props":{"href":"http://user.name/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":".name"}]},{"type":"text","value":" 与 user.email 这两个配置的值。"}]},{"type":"element","tag":"h2","props":{"id":"仓库"},"children":[{"type":"text","value":"仓库"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在项目里创建一个仓库（repository），就可以对项目做源代码管理了。Git 会把它需要的东西都存储在这个仓库里，这个仓库其实就是一个名字是 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":".git"}]},{"type":"text","value":" 的目录，Git 帮我们管理这个目录里的东西，所以通常我们不需要关心仓库里到底都放了些什么，需要做什么都可以通过执行 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"git"}]},{"type":"text","value":" 命令完成。"}]},{"type":"element","tag":"h3","props":{"id":"创建仓库"},"children":[{"type":"text","value":"创建仓库"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在项目里创建一个仓库，可以执行 git init 命令，它会在项目里初始化一个代码仓库，也就是在项目里创建一个 .git 目录，里面会包含一些必要的东西。"}]},{"type":"element","tag":"code","props":{"code":"git init\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git init\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在项目所在目录的下面，执行上面这行命令会给项目创建一个仓库，这个命令只需要执行一次。"}]},{"type":"element","tag":"h3","props":{"id":"删除仓库"},"children":[{"type":"text","value":"删除仓库"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果想重新对项目做源代码管理，可以把项目当前的仓库删除掉，然后再重新执行 git init 命令初始化一个新的仓库。"}]},{"type":"element","tag":"code","props":{"code":"rm -rf ~/desktop/xb2-node/.git\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"rm -rf ~/desktop/xb2-node/.git\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这行命令会将桌面上的 xb2-node 这个项目的仓库删除掉。"}]},{"type":"element","tag":"h2","props":{"id":"提交"},"children":[{"type":"text","value":"提交"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"提交（commit）可以保存项目当前的状态。在项目里创建了新的文件或是修改了文件里的代码，这其实都是在改变项目的状态，如果你想保存一下项目当前的状态，就可以对项目做一次提交，提交时可以设置一条信息，记录并说明一下为什么要做这次提交，方便以后可以随时查阅。"}]},{"type":"element","tag":"h3","props":{"id":"提交的流程"},"children":[{"type":"text","value":"提交的流程"}]},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"查看状态"}]},{"type":"text","value":"。执行 git status 命令，查看一下项目当前都做了哪些修改。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"添加修改"}]},{"type":"text","value":"。执行 git add 命令可以添加要包含在此次提交里的修改。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"确定提交"}]},{"type":"text","value":"。执行 git commit 命令确定提交。"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"git status\ngit add .\ngit commit -m '修正环境变量名称'\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git status\ngit add .\ngit commit -m '修正环境变量名称'\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"分别执行上面这几行命令会对项目做一次提交。git add . 会把当前项目里的修改全部添加到暂存区准备提交，然后执行 git commit 命令确定提交，用 m 选项给这次提交添加一条描述信息。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在本地使用带图形化界面的 Git 软件，可以更方便地做提交，比如使用 VSCode 编辑器自带的源代码管理功能，或者使用 Sourcetree。"}]},{"type":"element","tag":"h3","props":{"id":"查看提交历史"},"children":[{"type":"text","value":"查看提交历史"}]},{"type":"element","tag":"code","props":{"code":"git log\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git log\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这个命令可以查看项目的提交历史，每条历史记录都会显示提交的 ID（commit）、作者（Author）、日期（Date）、还有提交信息。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"commit 4e6a75bfc61d9bbbc1a0b3f2ab3ed3f75e6ae5ad\nAuthor: wanghao <wanghao@ninghao.net>\nDate:   Sat Apr 23 08:06:49 2022 +0800\n\n    修正环境变量名称\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"commit 4e6a75bfc61d9bbbc1a0b3f2ab3ed3f75e6ae5ad\nAuthor: wanghao <wanghao@ninghao.net>\nDate:   Sat Apr 23 08:06:49 2022 +0800\n\n    修正环境变量名称\n"}]}]}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"ID"}]},{"type":"text","value":"：4e6a75bfc61d9bbbc1a0b3f2ab3ed3f75e6ae5ad"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"作者"}]},{"type":"text","value":"：wanghao <"},{"type":"element","tag":"a","props":{"href":"mailto:wanghao@ninghao.net"},"children":[{"type":"text","value":"wanghao@ninghao.net"}]},{"type":"text","value":">"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"日期"}]},{"type":"text","value":"：Sat Apr 23 08:06:49 2022 +0800"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"信息"}]},{"type":"text","value":"：修正环境变量名称"}]}]},{"type":"element","tag":"h3","props":{"id":"提交的作用"},"children":[{"type":"text","value":"提交的作用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"提交会记录对项目所做的修改，保存项目的一个状态，通过提交的 ID 可以引用这次提交。我们可以查看在这次提交里包含的修改，比如修改了项目里的哪些文件，哪几行代码等等。也可以检出（checkout）这次提交，查看当初项目在做这次提交时的样子。还可以还原（revert）这次提交对项目做的修改，或者将项目重置（reset）到做这次提交时的状态。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"提交是不能被修改的，比如提交的作者、日期或者包含在提交里的修改，所有这些东西都是不能修改的，也就是一但确定了提交以后，就无法再修改这次提交了。我们可以重做最后一次做的提交，可以还原之前做的提交，这需要做一次新的提交保存还原之后的状态。"}]},{"type":"element","tag":"h2","props":{"id":"分支"},"children":[{"type":"text","value":"分支"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"分支（branch）是实践想法，添加新功能，修复 Bug 的好地方。在开发应用时，做这些事情之前都可以创建新的分支，然后在新的分支上去开发新功能或者修复 Bug 等等，无论我们怎么折腾都不会影响到其它分支上的项目。如果成功了，可以将在这些分支上做的提交合并到项目的主分支上，如果想法不成立或是新功能很失败，可以删除这些分支。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image.png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"master--main"},"children":[{"type":"text","value":"master / main"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"给项目初始化了一个仓库以后，默认会创建一个 master 分支，这个分支跟我们自己创建的分支并没有什么区别，只不过按习惯我们会将其称之为主分支。"}]},{"type":"element","tag":"h3","props":{"id":"查看分支"},"children":[{"type":"text","value":"查看分支"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行 git branch 命令可以列出项目里的分支，名字前面带 * 号的分支是项目当前所在的分支。"}]},{"type":"element","tag":"code","props":{"code":"git branch\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git branch\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"查看全部分支"}]}]},{"type":"element","tag":"code","props":{"code":"git branch -a\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git branch -a\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果想显示全部的分支（包含远程分支），可以在命令里使用 a 选项。"}]},{"type":"element","tag":"h3","props":{"id":"创建分支"},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"创建分支"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在开发项目时我们可以任意创建需要的分支，创建分支的时候需要指定一个起始提交，这样新创建的分支上就会包含这次起始提交以及在这次提交之前做的所有的提交。在每条分支上可以包含只属于自己的提交，这些提交会在创建分支时指定的那个起始提交之后。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"git branch payment\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git branch payment\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这个命令会创建一个新的分支，名字叫 payment，创建这个分支时并没有特别指定这个分支的起始提交，所以默认会基于当前分支的最后一次提交创建这个分支。假设当前分支是 master，在这个分支上的最后一次提交是 “添加 README.md”，这样在新创建的 payment 分支的提交历史里，最后一次提交也会是 “添加 README.md” 。"}]},{"type":"element","tag":"h3","props":{"id":"切换分支"},"children":[{"type":"text","value":"切换分支"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 git checkout 命令可以切换项目当前的分支。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"git checkout payment\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git checkout payment\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令会将项目当前分支切换到 payment，执行 git branch 命令可以查看项目当前的分支。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"创建新分支并切换到这个分支上"}]}]},{"type":"element","tag":"code","props":{"code":"git checkout -b payment\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git checkout -b payment\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果要切换到的分支并不存在，可以在命令里使用 b 选项，这样可以创建新的分支并且切换到这个新的分支上。"}]},{"type":"element","tag":"h3","props":{"id":"合并分支历史"},"children":[{"type":"text","value":"合并分支/历史"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"用 git merge 命令可以合并分支，也可以说是合并提交历史。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"git checkout master\ngit merge payment\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git checkout master\ngit merge payment\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"假设当前是在 payment 这个分支上，之前在这个分支上做了一些新的提交，现在打算将这些提交合并到 master 分支上，可以先将当前分支切换到 master，然后执行 git merge 合并 payment 分支，这样在 payment 分支上做的那些提交就会合并到 master 分支上了。"}]},{"type":"element","tag":"h3","props":{"id":"删除分支"},"children":[{"type":"text","value":"删除分支"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 git branch 命令，外加 d 选项可以将不再需要的分支删除掉。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"git branch -d payment\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git branch -d payment\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的这个命令会将 payment 这个分支删除掉。注意我们不能删除当前所在的分支，比如想要删除 payment 分支，需要先将当前分支切换到其它的分支上，然后再删除它。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"强制删除分支"}]}]},{"type":"element","tag":"code","props":{"code":"git branch -D payment\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git branch -D payment\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"要删除的分支包含尚未合并的提交也是无法删除的，如果要强制删除这样的分支，需要使用大写的 D 选项。"}]},{"type":"element","tag":"h2","props":{"id":"忽略"},"children":[{"type":"text","value":"忽略"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 .gitignore 文件里可以列出要忽略不做源代码管理的东西。"}]},{"type":"element","tag":"h3","props":{"id":"gitignore"},"children":[{"type":"text","value":".gitignore"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在项目里创建了一个仓库以后，默认 Git 会跟踪这个目录里所有的文件。不过在项目里有些东西是不用做源代码管理的，比如一些日志文件，用户上传的文件，系统自动生成的一些文件等等，这些东西可以在 .gitignore ** 文件里说明一下。这个文件一般要放在项目根目录的下面。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":".gitignore 文件内容示例"}]}]},{"type":"element","tag":"code","props":{"code":"# compiled output\n/dist\n/node_modules\n\n# Logs\nlogs\n*.log\nnpm-debug.log*\n\n# OS\n.DS_Store\n\n# IDE - VSCode\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"# compiled output\n/dist\n/node_modules\n\n# Logs\nlogs\n*.log\nnpm-debug.log*\n\n# OS\n.DS_Store\n\n# IDE - VSCode\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 .gitignore 文件中，带 # 号前缀的是注释内容。"}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"*/dist，*指的是 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"dist"}]},{"type":"text","value":" 目录，这样就会忽略掉这个目录里的所有的文件。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"**.log，*指的是忽略掉所有文件名里带 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":".log"}]},{"type":"text","value":" 后缀的文件，* 表示所有。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":".vscode/"}]},{"type":"text","value":"，指的是*忽略掉 *.vscode* 目录里的所有的文件。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"!.vscode/settings.json，指的是"}]},{"type":"text","value":"不忽略 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":".vscode/settings.json"}]},{"type":"text","value":" 文件，! 号表示否定。"}]}]},{"type":"element","tag":"h3","props":{"id":"忽略已被-git-跟踪的文件"},"children":[{"type":"text","value":"忽略已被 Git 跟踪的文件"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":".gitignore 文件的作用就是告诉 Git，项目里哪些文件不需要做源代码管理。不过有一种情况是例外，就是如果文件已经被 Git 跟踪了，就是已经做了源代码管理，这时如果在 .gitignore 文件里列出这些东西，Git 仍然会对这些东西做源代码管理。如果想忽略这些东西，可以执行下面这几行命令："}]},{"type":"element","tag":"code","props":{"code":"git rm -r --cached .\ngit add .\ngit commit -am \"删除忽略的文件\"\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git rm -r --cached .\ngit add .\ngit commit -am \"删除忽略的文件\"\n"}]}]}]},{"type":"element","tag":"h2","props":{"id":"远程"},"children":[{"type":"text","value":"远程"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 Git 语境下，远程（remote）指的是远程仓库。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"项目有本地仓库，也可以给它添加一个远程仓库，这需要先在远程仓库的服务商那里为项目创建一个远程仓库，然后将其添加到本地项目里，这样就可以将本地仓库上传（push）到远程仓库里了，也可以从远程仓库那里拉取（pull）最新的提交。远程仓库有点像是项目的备份与合作中心，其实本质上远程仓库与本地仓库没什么区别，所以可以把它想成是在网络其它地方的一个仓库。"}]},{"type":"element","tag":"h3","props":{"id":"远程仓库服务商"},"children":[{"type":"text","value":"远程仓库服务商"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"a","props":{"href":"https://github.com/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"Github"}]},{"type":"text","value":" 与 "},{"type":"element","tag":"a","props":{"href":"https://coding.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"Coding"}]},{"type":"text","value":" 都提供远程仓库服务，你可以选择使用其中任意一个。"}]},{"type":"element","tag":"h3","props":{"id":"用-ssh-key-验证身份"},"children":[{"type":"text","value":"用 SSH key 验证身份"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在远程仓库服务端那里注册了帐户以后，可以配置使用 SSH key 的方式验证身份，这样以后将本地仓库推送到远程仓库时，就可以通过本地的钥匙验证用户身份，也就是不再需要输入在远程仓库服务商那里注册的用户名与密码了。使用 SSH Key 的方式验证身份，就是将本地用户主目录下存放的公钥文件里的内容，添加到远程仓库服务商那里。"}]},{"type":"element","tag":"h4","props":{"id":"输出本地公钥文件内容"},"children":[{"type":"text","value":"输出本地公钥文件内容"}]},{"type":"element","tag":"code","props":{"code":"cat ~/.ssh/id_rsa.pub\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cat ~/.ssh/id_rsa.pub\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的这行命令可以输出用户主目录下的 .ssh 目录里的 id_rsa.pub 文件里的内容。复制一下输出的这个文件里的内容。"}]},{"type":"element","tag":"h4","props":{"id":"配置-github-帐户的-ssh-key"},"children":[{"type":"text","value":"配置 Github 帐户的 SSH key"}]},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"登录 "},{"type":"element","tag":"a","props":{"href":"https://github.com/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"Github"}]},{"type":"text","value":"。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"打开用户的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Settings"}]},{"type":"text","value":"（设置）。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"打开设置页面边栏上的 "},{"type":"element","tag":"a","props":{"href":"https://github.com/settings/keys","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"SSH and GPG keys"}]},{"type":"text","value":"。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"SSH keys"}]},{"type":"text","value":" 右侧的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"New SSH key"}]},{"type":"text","value":"。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Title"}]},{"type":"text","value":" 里随便输入一个标题，将复制的用户公钥文件内容粘贴到 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Key"}]},{"type":"text","value":" 里面。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Add SSH key"}]},{"type":"text","value":"，确定添加。"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(1).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 Github 账户设置添加 SSH Keys"}]}]},{"type":"element","tag":"h4","props":{"id":"配置-coding-帐户的-ssh-key"},"children":[{"type":"text","value":"配置 Coding 帐户的 SSH key"}]},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"登录 "},{"type":"element","tag":"a","props":{"href":"https://coding.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"Coding"}]},{"type":"text","value":"。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"打开 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"个人账户设置"}]},{"type":"text","value":"。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"打开 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"SSH 公钥"}]},{"type":"text","value":"。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"新增公钥"}]},{"type":"text","value":"。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"公钥名称"}]},{"type":"text","value":" 里输入一个名字，然后将复制的用户公钥文件里的内容放在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"公钥内容"}]},{"type":"text","value":" 里。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"选择 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"公钥有效期"}]},{"type":"text","value":"。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"确认"}]},{"type":"text","value":"，确定添加公钥。"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(2).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 Coding 个人账户设置添加 SSH 公钥"}]}]},{"type":"element","tag":"h3","props":{"id":"创建远程仓库"},"children":[{"type":"text","value":"创建远程仓库"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在远程仓库服务商那里为项目创建一个远程仓库。"}]},{"type":"element","tag":"h4","props":{"id":"在-github-创建远程仓库"},"children":[{"type":"text","value":"在 Github 创建远程仓库"}]},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"登录到 Github。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"点击右上角+号图标，选择 "},{"type":"element","tag":"a","props":{"href":"https://github.com/new","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"New repository"}]}]},{"type":"text","value":"。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"输入 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Repository name"}]},{"type":"text","value":"（仓库名称）。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"选择 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Public"}]},{"type":"text","value":"（公开） 或 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Private"}]},{"type":"text","value":"（私有）。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Create repository"}]},{"type":"text","value":"（创建仓库）。"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 Github 创建远程仓库时，仓库的名字在你的账户里应该是唯一的，也就是不能有重名的仓库。创建仓库时可以选择这个仓库是公开还是私有，如果选择 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Public"}]},{"type":"text","value":"（公开），任何人都能够查看或克隆你的项目仓库。如果选择 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Private"}]},{"type":"text","value":"（私有），默认只有你自己可以使用这个仓库。你可以通过配置仓库，邀请他人共同开发项目。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(3).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 Github 创建仓库"}]}]},{"type":"element","tag":"h4","props":{"id":"在-coding-创建远程仓库"},"children":[{"type":"text","value":"在 Coding 创建远程仓库"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 Coding 创建远程仓库的流程与 Github 略有不同，首先你需要创建一个项目，然后在这个项目里再去创建代码仓库。"}]},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"登录 Coding。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"选择并打开项目（创建新的项目）。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"打开项目的代码仓库，点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"创建代码仓库"}]},{"type":"text","value":"。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"设置 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"仓库名称、仓库描述"}]},{"type":"text","value":"，选择 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"是否开源"}]},{"type":"text","value":"。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"完成创建"}]},{"type":"text","value":"。"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(4).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 Coding 创建代码仓库"}]}]},{"type":"element","tag":"h4","props":{"id":"远程仓库地址"},"children":[{"type":"text","value":"远程仓库地址"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"无论是在 Github 还是在 Coding 上创建的仓库，都提供两种类型的仓库地址，一种是 HTTPS，一种是 SSH。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"这两种仓库地址唯一不同的地方就是验证身份的方式，如果使用 HTTPS 类型的地址，就需要通过用户名与密码完成身份验证，如果选择使用 SSH 类型的仓库地址，可以通过 SSH 钥匙完成身份验证。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(5).png"},"children":[]},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(6).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Github 仓库地址"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(7).png"},"children":[]},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(8).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Coding 仓库地址"}]}]},{"type":"element","tag":"h3","props":{"id":"管理项目的远程仓库"},"children":[{"type":"text","value":"管理项目的远程仓库"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"创建了远程仓库以后，可以将它添加到项目里使用。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在项目里可以随便添加远程仓库，其实就是给要添加的远程起个名字，再设置一个对应的仓库地址，以后可以使用给远程起的名字引用对应的远程仓库。"}]},{"type":"element","tag":"h4","props":{"id":"添加远程"},"children":[{"type":"text","value":"添加远程"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 git remote add 命令可以给项目添加远程。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"git remote add origin git@github.com:wanghao8080/xb2-node.git\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git remote add origin git@github.com:wanghao8080/xb2-node.git\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端，项目所在目录的下面执行上面这行命令可以给项目添加一个名字是 origin 的远程，对应的地址就是之前我在 Github 上创建的一个仓库的地址。origin 是一个远程的命名惯例，并没有特别的意思，你可以随意命名远程。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"项目可以同时拥有多个远程："}]},{"type":"element","tag":"code","props":{"code":"git remote add coding git@e.coding.net:ninghao/xb2/xb2-node.git\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git remote add coding git@e.coding.net:ninghao/xb2/xb2-node.git\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令会给项目添加一个名字是 coding 的远程，对应的地址是我在 Coding 创建的一个代码仓库的地址。"}]},{"type":"element","tag":"h4","props":{"id":"查看远程"},"children":[{"type":"text","value":"查看远程"}]},{"type":"element","tag":"code","props":{"code":"git remote -v\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git remote -v\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行 git remote 可以列出项目的远程，配合 v 选项可以显示更详细的信息。"}]},{"type":"element","tag":"h4","props":{"id":"删除远程"},"children":[{"type":"text","value":"删除远程"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"要删除项目里的远程，可以使用 git remote remove 命令。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"git remote remove coding\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git remote remove coding\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这行命令会将项目里的名字是 coding 的这个远程删除掉。"}]},{"type":"element","tag":"h3","props":{"id":"使用远程仓库"},"children":[{"type":"text","value":"使用远程仓库"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"为项目添加了远程以后，我们可以将本地项目推送到指定的远程仓库里，也可以从某个远程那里获取到最新的提交。"}]},{"type":"element","tag":"h4","props":{"id":"推送"},"children":[{"type":"text","value":"推送"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 git push 可以将本地的东西推送到指定的远程那里。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用这个命令的时候要指定远程的名字，还有要推送的东西，一般就是一个本地分支的名字。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"git push origin master\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git push origin master\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令会将本地的 master 分支推送到 origin 这个远程，这个 origin 指的具体是哪里，要看当初添加这个 origin 远程的时候设置的远程仓库的地址是什么。成功以后可以到远程仓库页面那里刷新一下，应该会出现项目的代码，远程里也会包含一个 master 分支。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"以后在本地的 master 分支上有了新的提交，都可以执行 git push 命令，将这些新做的东西推送到 origin 这个远程。"}]},{"type":"element","tag":"h4","props":{"id":"拉取"},"children":[{"type":"text","value":"拉取"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 git pull 命令可以将远程上的新东西拉取到本地。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"git pull origin\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git pull origin\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令可以将 origin 这个远程里的新东西下载下来并合并到本地。这个命令的功能相当于先用 git fetch 从远程那里下载最新的东西，然后用 git merge 将这些新东西合并到本地。"}]},{"type":"element","tag":"h4","props":{"id":"克隆"},"children":[{"type":"text","value":"克隆"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 git clone 命令可以将远程仓库克隆到本地。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"cd ~/desktop\ngit clone https://github.com/ninghao/xb2-node.git\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cd ~/desktop\ngit clone https://github.com/ninghao/xb2-node.git\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这两行命令，会将一个远程仓库克隆到本地电脑的桌面上，默认会放在 xb2-node 这个目录的下面。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"git clone 只需要执行一次，如果克隆的是自己创建的远程仓库或者有权限管理的远程仓库，可以使用 SSH 类型的地址，如果克隆的是别人的代码仓库，可以选择使用 HTTPS 类型的仓库地址。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"完成以后在本地应该就会出现一个跟远程仓库里一模一样的项目了。项目里会包含一个 origin 远程，指向的就是当时克隆的那个远程仓库。"}]},{"type":"element","tag":"h4","props":{"id":"基于远程分支创建本地分支"},"children":[{"type":"text","value":"基于远程分支创建本地分支"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"克隆完成以后，默认在项目里会有一个 master 本地分支，如果你想使用远程里的其它的分支，需要基于远程分支创建一个本地的分支。首先可以执行 git branch -a 查看当前所有的分支，你应该会发现一些 remotes/origin/* 这种名字的分支，这些就是在远程仓库里的分支。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"git branch audit remotes/origin/audit\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"git branch audit remotes/origin/audit\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"上面这行命令会基于 remotes/origin/audit 这个远程分支创建一个名字是 audit 的本地分支。这样你就可以切换到 audit 这个本地分支了，在这个分支上的项目跟远程里的那个 audit 分支上的项目是一样的。"}]},{"type":"element","tag":"h3","props":{"id":"协作"},"children":[{"type":"text","value":"协作"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"远程仓库为项目开发提供了一个合作中心，我们可以邀请他人成为项目的协作者，这样大家就可以一起开发项目了。每个人在本地都有一份项目的仓库，大家可以在本地进行开发，然后将新做的提交推送到远程仓库，团队的协作者可以从远程拉取他人新做的这些提交。"}]},{"type":"element","tag":"h4","props":{"id":"为-github-仓库添加协作者"},"children":[{"type":"text","value":"为 Github 仓库添加协作者"}]},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"在远程仓库页面，打开 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Settings"}]},{"type":"text","value":"（设置）。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"打开边栏菜单里的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Collaborators"}]},{"type":"text","value":"（协作者）。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Add people"}]},{"type":"text","value":"（添加），搜索要添加的协作者的用户名并选择。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"被选择的用户会收到一封邮件，对方确认以后就会正式成为项目的协作者。"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(9).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Github 仓库设置下的 Collaborators"}]}]},{"type":"element","tag":"h4","props":{"id":"为-coding-仓库添加协作者"},"children":[{"type":"text","value":"为 Coding 仓库添加协作者"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 Coding 里需要在项目级别添加项目的成员，然后设置成员的权限。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(10).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Coding 项目添加成员的界面"}]}]},{"type":"element","tag":"h2","props":{"id":"图形界面"},"children":[{"type":"text","value":"图形界面"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在本地开发时可以使用带图形界面的 Git 软件，比如 Sourcetree，或者使用 VSCode 自带的源代码管理功能。"}]},{"type":"element","tag":"h3","props":{"id":"sourcetree"},"children":[{"type":"text","value":"Sourcetree"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Sourcetree 是一款带图形界面的 Git 软件，也就是使用它我们可以通过图形界面完成需要执行 git 命令完成的事情，比如做提交、查看提交历史、创建分支、推送与拉取等等。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"这款软件可以辅助你完成一些训练任务，比如我们在《小白的开发之路：Node.js 服务端应用开发实践》中开发了一个"},{"type":"element","tag":"a","props":{"href":"https://github.com/ninghao/xb2-node.git","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"Node 应用"}]},{"type":"text","value":"，每完成一个任务我都会做一次提交，你可以将这个项目克隆到本地，然后使用 Sourcetree 打开这个本地仓库，这样就可以查看项目的提交历史，可以检查每一次提交对项目都做了哪些修改。你还可以很方便的检出（checkout）到任意一次提交，这样你在 VSCode 编辑器那里看到的项目，就是我做完这次提交时的样子。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(11).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Sourcetree 软件屏幕截图"}]}]},{"type":"element","tag":"h3","props":{"id":"vscode"},"children":[{"type":"text","value":"VSCode"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"VSCode 编辑器自带源代码管理功能。"}]},{"type":"element","tag":"h4","props":{"id":"使用-vscode-源代码管理"},"children":[{"type":"text","value":"使用 VSCode 源代码管理"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"点击活动栏上的源代码管理小图标可以打开源代码管理功能，或者使用快捷键 ctrl + shift + G。点击源代码管理右侧的 ... 小图标会弹出一个源代码管理相关功能的菜单，或者也可以在命令面板里搜索执行 Git 相关的命令。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(12).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"VSCode 编辑器源代码管理"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(13).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 VSCode 命令面板里搜索 Git"}]}]},{"type":"element","tag":"h4","props":{"id":"使用-vscode-源代码管理做提交"},"children":[{"type":"text","value":"使用 VSCode 源代码管理做提交"}]},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"修改项目。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"打开 VSCode 源代码管理。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"添加在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"更改"}]},{"type":"text","value":" 下面列出的修改，或者点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"更改"}]},{"type":"text","value":" 右侧的 + 号添加全部修改。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"添加的修改会在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"暂存的更改"}]},{"type":"text","value":" 下面出现，这些就是要包含在本次提交里的更改。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"在文本框里输入提交日志，然后按下 command + enter 或者点击源代码管理右侧的对号小图标确定提交。"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/tools/git/image(14).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"VSCode 源代码管理功能"}]}]}],"toc":{"title":"","searchDepth":2,"depth":2,"links":[{"id":"git","depth":2,"text":"Git","children":[{"id":"安装","depth":3,"text":"安装"},{"id":"配置","depth":3,"text":"配置"}]},{"id":"仓库","depth":2,"text":"仓库","children":[{"id":"创建仓库","depth":3,"text":"创建仓库"},{"id":"删除仓库","depth":3,"text":"删除仓库"}]},{"id":"提交","depth":2,"text":"提交","children":[{"id":"提交的流程","depth":3,"text":"提交的流程"},{"id":"查看提交历史","depth":3,"text":"查看提交历史"},{"id":"提交的作用","depth":3,"text":"提交的作用"}]},{"id":"分支","depth":2,"text":"分支","children":[{"id":"master--main","depth":3,"text":"master / main"},{"id":"查看分支","depth":3,"text":"查看分支"},{"id":"创建分支","depth":3,"text":"创建分支"},{"id":"切换分支","depth":3,"text":"切换分支"},{"id":"合并分支历史","depth":3,"text":"合并分支/历史"},{"id":"删除分支","depth":3,"text":"删除分支"}]},{"id":"忽略","depth":2,"text":"忽略","children":[{"id":"gitignore","depth":3,"text":".gitignore"},{"id":"忽略已被-git-跟踪的文件","depth":3,"text":"忽略已被 Git 跟踪的文件"}]},{"id":"远程","depth":2,"text":"远程","children":[{"id":"远程仓库服务商","depth":3,"text":"远程仓库服务商"},{"id":"用-ssh-key-验证身份","depth":3,"text":"用 SSH key 验证身份"},{"id":"创建远程仓库","depth":3,"text":"创建远程仓库"},{"id":"管理项目的远程仓库","depth":3,"text":"管理项目的远程仓库"},{"id":"使用远程仓库","depth":3,"text":"使用远程仓库"},{"id":"协作","depth":3,"text":"协作"}]},{"id":"图形界面","depth":2,"text":"图形界面","children":[{"id":"sourcetree","depth":3,"text":"Sourcetree"},{"id":"vscode","depth":3,"text":"VSCode"}]}]}},"_type":"markdown","_id":"content:docs:1.tools:3.git.md","_source":"content","_file":"docs/1.tools/3.git.md","_extension":"md"},{"_path":"/docs/env","_draft":false,"_partial":false,"_empty":false,"title":"训练/开发环境","description":"准备好必备的工具以后，可以再准备一下参与训练需要的环境，也就是搭建各种应用的开发环境。在训练内容（视频、文字教材）中，会为您演示如何在本地电脑上搭建这些开发环境，您也可以参考这组文档搭建这些环境。如果视频内容与文档内容有差异，优先选择文档内容。搭建环境遇到问题时，可以随时与训练营向导联系。","excerpt":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"训练开发环境"},"children":[{"type":"text","value":"训练/开发环境"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"准备好必备的工具以后，可以再准备一下参与训练需要的环境，也就是搭建各种应用的开发环境。在训练内容（视频、文字教材）中，会为您演示如何在本地电脑上搭建这些开发环境，您也可以参考这组文档搭建这些环境。如果视频内容与文档内容有差异，优先选择文档内容。搭建环境遇到问题时，可以随时与训练营向导联系。"}]}]},"navigation":false,"body":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"训练开发环境"},"children":[{"type":"text","value":"训练/开发环境"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"准备好必备的工具以后，可以再准备一下参与训练需要的环境，也就是搭建各种应用的开发环境。在训练内容（视频、文字教材）中，会为您演示如何在本地电脑上搭建这些开发环境，您也可以参考这组文档搭建这些环境。如果视频内容与文档内容有差异，优先选择文档内容。搭建环境遇到问题时，可以随时与训练营向导联系。"}]}],"toc":{"title":"","searchDepth":2,"depth":2,"links":[]}},"_type":"markdown","_id":"content:docs:2.env:0.index.md","_source":"content","_file":"docs/2.env/0.index.md","_extension":"md"},{"_path":"/docs/env/backend","_draft":false,"_partial":false,"_empty":false,"title":"后端开发环境","description":"Node.js 是宁皓独立开发者训练营选择使用的后端技术，我们会基于 Node.js 开发应用的后端（服务端）。","excerpt":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"后端服务端应用开发环境"},"children":[{"type":"text","value":"后端（服务端）应用开发环境"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Node.js 是宁皓独立开发者训练营选择使用的后端技术，我们会基于 Node.js 开发应用的后端（服务端）。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"先在本地电脑上安装一个 Node.js，然后再准备一下应用需要的数据服务，我们为应用选择的是 MySQL 这种数据库。还需要一些必要的客户端软件，比如调试应用接口用的 HTTP 客户端，在训练营中使用的是 Insomnia，还有连接管理数据服务用的数据库客户端，训练营中使用的是 TablePlus。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"准备好这些东西，做一些基本的配置以后，就算是准备好了后端应用开发环境，也就可以开启后端应用开发之旅了。"}]},{"type":"element","tag":"h2","props":{"id":"nodejs"},"children":[{"type":"text","value":"Node.js"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在本地电脑上安装了 Node.js 以后，就可以编写与运行 Node.js 应用了。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://nodejs.org/en/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://nodejs.org/en/"}]}]},{"type":"element","tag":"h3","props":{"id":"安装-nodejs"},"children":[{"type":"text","value":"安装 Node.js"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"到 Node.js 的官方网站，下载 LTS（长期支持） 版本的 Node.js，将其安装在电脑上，完成以后电脑就可以运行 Node.js 应用了。在训练营中开发的应用可在 v16 版本的 Node.js 里运行。"}]},{"type":"element","tag":"h3","props":{"id":"命令行工具"},"children":[{"type":"text","value":"命令行工具"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在开发 Node.js 应用时，经常需要用到一些命令行工具，这些工具一般就是在 Node.js 软件包（package）里提供的。"}]},{"type":"element","tag":"h4","props":{"id":"全局命令行工具"},"children":[{"type":"text","value":"全局命令行工具"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"有些命令行工具可以在全局范围安装，比如 Vue 或 Nest 框架提供的命令行工具，就适合将其安装在全局范围，这样就可以在任何地方使用这些工具。执行 npm install 命令的时候，配合使用 global 或 g 选项可以在全局范围安装 Node.js 软件包。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"npm install @vue/cli --global\ncd ~/desktop\nvue create nid-vue\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"npm install @vue/cli --global\ncd ~/desktop\nvue create nid-vue\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在全局范围安装了 Vue 框架的命令行工具以后，进入到桌面，然后执行 vue create 命令，这会在桌面上创建一个 Vue 项目，放在 nid-vue 这个目录里。"}]},{"type":"element","tag":"h4","props":{"id":"项目本地命令行工具"},"children":[{"type":"text","value":"项目本地命令行工具"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"有些带命令行的软件包需要将其安装在项目本地，比如在 TypeScript 这个包里面提供了一个 tsc 命令，这个包就需要安装在项目本地，因为每个项目需要的软件包的版本可能是不一样的。这种命令行工具软件包或者附带命令行工具的软件包，一般会作为项目的开发依赖，执行 npm install 命令的时候，配合使用 save-dev 或 D 选项可以将软件包作为项目的开发依赖。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"cd ~/desktop/nid-node\nnpm install typescript --save-dev\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cd ~/desktop/nid-node\nnpm install typescript --save-dev\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"先进入到项目所在目录，然后用 npm install 命令安装一个 typescript 软件包，用 save-dev 选项会将这个软件包作为项目的开发依赖。完成以后可以查看项目里的 node_modules/.bin 这个目录，在里面应该能找到 tsc 这个命令行工具。"}]},{"type":"element","tag":"h4","props":{"id":"执行项目本地命令行工具"},"children":[{"type":"text","value":"执行项目本地命令行工具"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果安装的软件包里带命令行工具，这些命令行工具一般都会放在项目的 node_modules/.bin 这个目录里。比如你想执行 typescript 这个软件包里带的 tsc 命令，需要指定这个命令行的具体位置，这个位置可以是相对的位置。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"cd ~/desktop/nid-node\n./node_modules/.bin/tsc\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cd ~/desktop/nid-node\n./node_modules/.bin/tsc\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"确定当前是在项目所在目录的下面，再执行一下给项目安装的 typescript 软件包里带的 tsc 这个命令，也就是当前目录下的 node_modules/.bin 这个目录里的 tsc。"}]},{"type":"element","tag":"h4","props":{"id":"配置-path-环境变量"},"children":[{"type":"text","value":"配置 PATH 环境变量"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果你想在项目里直接执行在安装的软件包里提供的命令行工具，可以配置一下 PATH 这个环境变量，将 ./node_modules/.bin 这个路径添加到 PATH 里。"}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"*Windows"}]},{"type":"text","value":"：cmder/config/user_profile.sh*"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"macOS"}]},{"type":"text","value":": ~/.zprofile"}]}]},{"type":"element","tag":"code","props":{"code":"export PATH=$PATH:./node_modules/.bin\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"export PATH=$PATH:./node_modules/.bin\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端的配置文件里添加上面这行代码，意思就是在 PATH 这个环境变量里添加 ./node_modules/.bin 这个路径，这样在项目所在目录的下面执行命令的时候，终端会在这个路径里查找要执行的命令行工具，也就不需要我们再输入命令的具体位置了。"}]},{"type":"element","tag":"blockquote","props":{},"children":[{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"修改了终端的配置文件以后，要重启终端，这样新做的配置才能生效。"}]}]},{"type":"element","tag":"h3","props":{"id":"软件包的安装源"},"children":[{"type":"text","value":"软件包的安装源"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"基于 Node.js 开发应用时，需要安装一些第三方提供的软件包（package），这些东西默认来自 npm 网站，如果你发现安装软件包时速度很慢，可以考虑切换使用第三方提供的安装源。"}]},{"type":"element","tag":"h4","props":{"id":"使用-nrm-管理软件包的安装源"},"children":[{"type":"text","value":"使用 nrm 管理软件包的安装源"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"借助 nrm 这个命令行工具可以方便地切换不同的安装源。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"安装 nrm 命令行工具"}]}]},{"type":"element","tag":"code","props":{"code":"npm install nrm --global\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"npm install nrm --global\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在全局范围安装 nrm 这个命令行工具。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"列出可用安装源"}]}]},{"type":"element","tag":"code","props":{"code":"nrm ls\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"nrm ls\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"返回的结果："}]},{"type":"element","tag":"code","props":{"code":"* npm -------- https://registry.npmjs.org/\n  yarn ------- https://registry.yarnpkg.com/\n  cnpm ------- http://r.cnpmjs.org/\n  taobao ----- https://registry.npm.taobao.org/\n  ...\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"* npm -------- https://registry.npmjs.org/\n  yarn ------- https://registry.yarnpkg.com/\n  cnpm ------- http://r.cnpmjs.org/\n  taobao ----- https://registry.npm.taobao.org/\n  ...\n"}]}]}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"号标注的就是当前使用的软件包安装源，默认就是 npm。"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"切换安装源"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在国内可以选择使用 taobao 这个安装源。"}]},{"type":"element","tag":"code","props":{"code":"nrm use taobao\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"nrm use taobao\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这行命令会将安装源设置成 taobao，这时再执行 nrm ls 查看安装源列表，你会发现 taobao 会变成当前使用的安装源。将软件包的安装源切换至第三方以后，如果你想把自己编写的软件包发布到 npm，需要将安装源切换回 npm。"}]},{"type":"element","tag":"h3","props":{"id":"多版本-nodejs可选"},"children":[{"type":"text","value":"多版本 Node.js（可选）"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"默认在操作系统里只能存在一个版本的 Node.js，有时我们可能需要根据不同的 Node.js 项目，使用不同版本的 Node.js，可以借助一些工具（nvm），在系统里同时安装多个版本的 Node.js，通过项目的配置或者使用命令可以切换使用不同版本的 Node.js。macOS 用户可以使用 nvm，Windows 用户可以使用 NVM for Windows。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果在系统里安装了多个版本的 Node.js，切换使用不同版本的 Node.js 以后，需要重新安装在全局范围里安装的命令行工具。"}]},{"type":"element","tag":"h4","props":{"id":"nvm"},"children":[{"type":"text","value":"nvm"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"nvm 这个工具允许在系统上同时存在多个版本的 Node.js，推荐 macOS 用户使用这个工具来管理安装在系统里的 Node.js，这样可以任意切换使用需要的 Node.js。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"项目地址"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://github.com/nvm-sh/nvm","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://github.com/nvm-sh/nvm"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"安装 nvm 命令行工具"}]}]},{"type":"element","tag":"code","props":{"code":"sudo curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"sudo curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端，执行上面这行命令会在系统里安装好 nvm 这个命令行工具。首先会下载 install.sh 这个脚本文件，里面写的就是安装 nvm 需要做的一些事情，这个文件是在 github 上提供的，所以在国内有可能会遇到无法连接的问题，在系统网络的 DNS 里添加 8.8.8.8 这个地址应该可以解决问题。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"完成安装以后，需要重新启动终端，这样就可以在终端使用 nvm 这个命令了。nvm 的安装脚本会修改终端的配置文件，比如 ~/.zshrc，你可以查看一下这个文件的内容，应该会看到类似下面这样的内容："}]},{"type":"element","tag":"code","props":{"code":"export NVM_DIR=\"$HOME/.nvm\"\n...\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"export NVM_DIR=\"$HOME/.nvm\"\n...\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"安装指定版本的 Node.js"}]}]},{"type":"element","tag":"code","props":{"code":"nvm install 16.15.0\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"nvm install 16.15.0\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令可以安装 16.15.0 版本的 Node.js。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"查看所有可用的版本"}]}]},{"type":"element","tag":"code","props":{"code":"nvm ls-remote\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"nvm ls-remote\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这行命令会列出所有可以安装使用的 Node.js。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"查看已经安装的版本"}]}]},{"type":"element","tag":"code","props":{"code":"nvm ls\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"nvm ls\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这行命令会列出当前已经安装在系统上的 Node.js，还会显示当前正在使用的 Node.js。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"设置当前使用的版本"}]}]},{"type":"element","tag":"code","props":{"code":"nvm use 16.15.0\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"nvm use 16.15.0\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令会将当前使用的 Node.js 设置成 16.15.0 这个版本。"}]},{"type":"element","tag":"h4","props":{"id":"nvm-for-windows"},"children":[{"type":"text","value":"NVM for Windows"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"项目地址"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://github.com/coreybutler/nvm-windows","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://github.com/coreybutler/nvm-windows"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"NVM for Windows 跟上面介绍的 nvm 差不多，但不是同一个项目。Windows 用户可以使用 NVM for Windows，它提供了一个安装包，下载并将其安装在电脑上，打开终端以后，就可以使用 nvm 这个命令了。"}]},{"type":"element","tag":"h3","props":{"id":"软件包编译环境"},"children":[{"type":"text","value":"软件包编译环境"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"开发 Node.js 应用会为项目安装一些软件包，大部分软件包直接就可以使用，不过有些软件包需要编译成本地的 Node 模块，这就需要我们的系统拥有合适的编译环境。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在宁皓独立开发者训练营基于 Node.js 开发后端应用时，会用到一个 bcrypt 这个软件包，为项目安装它的时候就有可能需要编译本地模块。如果在安装过程中出现类似 node-pre-gyp ERR! build error 这些的句子，说明编译本地模块的时候出了问题，也就是我们的系统暂时无法满足编译条件。"}]},{"type":"element","tag":"h4","props":{"id":"macos安装-xcode"},"children":[{"type":"text","value":"macOS：安装 XCode"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image.png"},"children":[]},{"type":"text","value":"\n在 macOS 系统中安装 XCode 这个软件以后就支持 Node.js 编译本地模块了，在 App Store 里搜索并安装 XCode，完成以后打开这个软件，应该会提示你安装一些额外的组件或者一些命令行工具，确定安装这些东西。"}]},{"type":"element","tag":"h4","props":{"id":"windows安装编译工具"},"children":[{"type":"text","value":"Windows：安装编译工具"}]},{"type":"element","tag":"code","props":{"code":"npm install --global windows-build-tools\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"npm install --global windows-build-tools\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Windows 用户可以在全局范围安装 windows-build-tools，这会在 Windows 系统中准备好编译 Node 本地模块需要的环境。"}]},{"type":"element","tag":"h2","props":{"id":"mysql"},"children":[{"type":"text","value":"MySQL"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在宁皓独立开发者训练营中，我们会用 MySQL 为应用提供数据服务。在本地电脑上安装并运行一个 MySQL 服务器（8.x），这样服务端应用就可以连接使用这个数据服务，管理应用的数据了。"}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://www.mysql.com/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://www.mysql.com/"}]}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"下载地址"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://dev.mysql.com/downloads/mysql/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://dev.mysql.com/downloads/mysql/"}]}]}]},{"type":"element","tag":"h3","props":{"id":"macos安装-mysql"},"children":[{"type":"text","value":"macOS：安装 MySQL"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 MySQL 的官方网站，选择下载 8.x 社区版的 MySQL。打开下载页面以后，Select Operating System 选择 macOS。根据电脑架构选择合适的版本，如果电脑用的是 m1 芯片，可以下载 ARM 版本，如果是 intel 处理器，可以下载 x86 版本。在系统的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"关于电脑"}]},{"type":"text","value":" 那里可以查看电脑使用的处理器。无论是哪种版本，推荐下载用 DMG 格式封闭的 MySQL，下载完成以后将其安装在系统里。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(1).png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"macos管理-mysql-服务"},"children":[{"type":"text","value":"macOS：管理 MySQL 服务"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 macOS 系统里安装了 MySQL 服务器以后，在系统的偏好设置那里可以找到 MySQL 服务的管理界面，在这里可以管理 MySQL 服务器，比如停止或启动 MySQL 服务。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(2).png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"windows安装-mysql"},"children":[{"type":"text","value":"Windows：安装 MySQL"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 MySQL 的官方网站，选择下载 8.x 社区版的 MySQL。打开下载页面以后，Select Operating System 选择 Microsoft Windows，然后点击 MySQL Installer for Windows，下载带安装向导的 MySQL（如 mysql-installer-community-8.0.29.0.msi），然后将其安装在系统里。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(3).png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"windows管理-mysql-服务"},"children":[{"type":"text","value":"Windows：管理 MySQL 服务"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 Windows 系统里安装了 MySQL 服务器以后，在系统托盘那里应该会出现一个 MySQL 小图标，通过它可以管理 MySQL 服务的启动状态。"}]},{"type":"element","tag":"h3","props":{"id":"root-用户"},"children":[{"type":"text","value":"root 用户"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"MySQL 里的 root 用户是超级管理员，拥有管理整个服务的权限，比如管理数据库与用户等等。在安装 MySQL 的过程中，应该会提示你设置这个用户的密码，稍后我们会用到这个用户与给他设置的密码连接管理 MySQL 服务。"}]},{"type":"element","tag":"h2","props":{"id":"tableplus"},"children":[{"type":"text","value":"TablePlus"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"TablePlus 是一种数据库客户端软件，用它可以连接管理不同类型的数据服务。在训练营中我们会使用 TablePlus 连接管理 MySQL 数据服务，观察应用数据仓库里的数据与结构，练习 SQL 语言等等。"}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://tableplus.com/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://tableplus.com/"}]}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"下载地址"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://tableplus.com/download","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://tableplus.com/download"}]}]}]},{"type":"element","tag":"h3","props":{"id":"连接-mysql-服务"},"children":[{"type":"text","value":"连接 MySQL 服务"}]},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"打开 TablePlus。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"点击界面底部的 Create a new connection...（创建新连接）。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"数据服务的类型选择 MySQL，然后点击 Create。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"配置连接。"},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"Name：随便填写一个连接名称。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"Ver.：版本设置成 Default。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"Host：数据服务在本地，所以主机名设置成 127.0.0.1。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"Port：MySQL 服务默认的端口号是 3306。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"User：可以填写 root。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"Password：输入 root 用户的密码。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"Database：连接的数据仓库的名字，暂时没有可以留空。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"点击 Test，测试连接，一切正常的话文本框会变绿，测试失败会出现提示。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"点击 Connect 确定打开连接。"}]}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(4).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"创建新连接，选择数据服务类型"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(5).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"连接的配置界面"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(6).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"打开连接后显示的界面"}]}]},{"type":"element","tag":"h3","props":{"id":"创建数据仓库"},"children":[{"type":"text","value":"创建数据仓库"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开之前创建的连接以后，需要再打开数据服务里的一个具体的数据仓库。点击界面标题栏上的数据仓库小图标（在 SQL 左边），这会打开 Open database 窗口，在这里会列出当前连接可以管理的数据仓库，点击 + 号小图标会显示 New Database 窗口。"}]},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"Name：数据仓库名字，比如 xb2_node。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"Encoding：数据编码格式，选择 utf8mb4。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"Collation：数据整理，选择 utf8mb4_0900_ai_ci。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"点击 OK，确定创建新的数据仓库。"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(7).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"TablePlus 创建数据仓库"}]}]},{"type":"element","tag":"h3","props":{"id":"打开数据仓库"},"children":[{"type":"text","value":"打开数据仓库"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"创建了数据仓库以后，可以在 Open database 这里列出这个新的数据仓库，选中并打开它。每次打开连接以后，都需要打开需要管理的数据仓库。我们可以编辑一下之前创建的连接，在 Database 那里输入连接以后要打开的数据仓库的名字，比如 xb2_node，这样打开连接的同时会打开 xb2_node 这个数据仓库。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(8).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"TablePlus 打开数据仓库"}]}]},{"type":"element","tag":"h3","props":{"id":"管理数据仓库"},"children":[{"type":"text","value":"管理数据仓库"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开连接并打开了数据仓库以后，如果数据仓库里拥有已经创建的数据表（Tables），在左边栏就会显示这些数据表。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"观察数据记录"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"选中某个数据表，在右边会显示这个数据表里的数据记录。调试服务端应用的时候，经常需要在 TablePlus 里观察数据表里的数据的变化，刷新（command + R） TablePlus 应用的界面可以查看最新的变化。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(9).png"},"children":[]},{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"修改数据记录"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 TablePlus 里可以直接修改数据表里的数据记录的值，也可以添加新的数据记录或者删除数据记录，确定这些操作需要保存（command + S）。被修改的数据记录与数据表会用特殊颜色标记，确定保存以后会变成正常状态。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(10).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"TablePlus 会使用特殊颜色标记被修改的数据记录与数据表了数据记录"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"数据表结构"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开数据表以后，默认会显示数据表里的 Data（数据），点击界面底部的 Structure（结构）可以查看与修改数据表的结构，修改了数据表的结构以后，需要保存（command + S）才能生效。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(11).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"TablePlus 提供的数据表结构的管理界面"}]}]},{"type":"element","tag":"h3","props":{"id":"执行-sql"},"children":[{"type":"text","value":"执行 SQL"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"点击界面顶部的标题栏里的 SQL 图标，可以打开执行 SQL 的界面，在这里可以通过执行 SQL 操作数据仓库，界面下方会显示执行的结果或查询出来的数据。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(12).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"TablePlus 提供的 SQL 界面"}]}]},{"type":"element","tag":"h2","props":{"id":"insomnia"},"children":[{"type":"text","value":"Insomnia"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Insomnia 是一种 Http 客户端软件，我们在训练营中开发应用时，在测试与调试服务端应用接口的时候会用到它。"}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://insomnia.rest/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://insomnia.rest"}]}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"下载地址"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://insomnia.rest/download","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://insomnia.rest/download"}]}]}]},{"type":"element","tag":"h3","props":{"id":"管理项目"},"children":[{"type":"text","value":"管理项目"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开 Insomnia 以后可以先创建一个 project（项目），每个项目的下面可以拥有属于自己的一些接口的设计文档（design document）或是请求集合（request collection）。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(13).png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"请求集合"},"children":[{"type":"text","value":"请求集合"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在项目下面，点击 Create 按钮，在弹出的菜单里选择 Request Collection，创建一个请求集合，每个请求集合里面可以包含一组请求。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(14).png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"管理环境"},"children":[{"type":"text","value":"管理环境"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开创建的请求集合，可以管理一下在这个集合里的环境。每个环境里都可以设置一些环境变量，在创建请求的时候可以使用这些环境变量。切换集合当前使用的环境，这些环境变量的值也会发生相应的变化。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"比如我们在请求集合里创建两个环境：开发环境与生产环境，在这两个环境里面定义一个名字是 nid_nest_api 的环境变量，它的值就是服务端应用在不同环境下的基本地址。这样如果在请求里使用了 nid_nest_api 这个环境变量，就可以得到服务端接口的基本地址了，而且在切换集合当前环境的时候，这个地址也会发生改变。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(15).png"},"children":[]},{"type":"text","value":"\n点击环境名称会弹出菜单，列出当前可用的环境，点击 Manage Environments，可以管理集合的环境，在 Sub Environments 下面新建两个环境，然后分别设置一些环境变量，数据的格式是 JSON。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(16).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"图中显示在开发环境中添加了一个 nid_nest_api，对应的值是 "},{"type":"element","tag":"a","props":{"href":"http://localhost:3000/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"http://localhost:3000"}]},{"type":"text","value":"。"}]}]},{"type":"element","tag":"h3","props":{"id":"管理请求"},"children":[{"type":"text","value":"管理请求"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开请求集合以后，可以创建一些 Folder（目录），然后在这些目录里再分别创建一些 Request（请求）。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(17).png"},"children":[]},{"type":"text","value":"\n在请求中可以使用请求集合里创建的环境里定义的环境变量，比如在上图这个 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"创建内容"}]},{"type":"text","value":" 请求中，请求的地址里用了 nid_nest_api 这个环境变量，当前这个请求集合设置使用的环境是 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"开发环境"}]},{"type":"text","value":"，所以这个环境变量的值就是在开发环境里定义的 nid_nest_api 的值，也就是 "},{"type":"element","tag":"a","props":{"href":"http://localhost/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"http://localhost"}]},{"type":"text","value":":3000。"}]}]},"body":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"后端服务端应用开发环境"},"children":[{"type":"text","value":"后端（服务端）应用开发环境"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Node.js 是宁皓独立开发者训练营选择使用的后端技术，我们会基于 Node.js 开发应用的后端（服务端）。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"先在本地电脑上安装一个 Node.js，然后再准备一下应用需要的数据服务，我们为应用选择的是 MySQL 这种数据库。还需要一些必要的客户端软件，比如调试应用接口用的 HTTP 客户端，在训练营中使用的是 Insomnia，还有连接管理数据服务用的数据库客户端，训练营中使用的是 TablePlus。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"准备好这些东西，做一些基本的配置以后，就算是准备好了后端应用开发环境，也就可以开启后端应用开发之旅了。"}]},{"type":"element","tag":"h2","props":{"id":"nodejs"},"children":[{"type":"text","value":"Node.js"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在本地电脑上安装了 Node.js 以后，就可以编写与运行 Node.js 应用了。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://nodejs.org/en/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://nodejs.org/en/"}]}]},{"type":"element","tag":"h3","props":{"id":"安装-nodejs"},"children":[{"type":"text","value":"安装 Node.js"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"到 Node.js 的官方网站，下载 LTS（长期支持） 版本的 Node.js，将其安装在电脑上，完成以后电脑就可以运行 Node.js 应用了。在训练营中开发的应用可在 v16 版本的 Node.js 里运行。"}]},{"type":"element","tag":"h3","props":{"id":"命令行工具"},"children":[{"type":"text","value":"命令行工具"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在开发 Node.js 应用时，经常需要用到一些命令行工具，这些工具一般就是在 Node.js 软件包（package）里提供的。"}]},{"type":"element","tag":"h4","props":{"id":"全局命令行工具"},"children":[{"type":"text","value":"全局命令行工具"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"有些命令行工具可以在全局范围安装，比如 Vue 或 Nest 框架提供的命令行工具，就适合将其安装在全局范围，这样就可以在任何地方使用这些工具。执行 npm install 命令的时候，配合使用 global 或 g 选项可以在全局范围安装 Node.js 软件包。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"npm install @vue/cli --global\ncd ~/desktop\nvue create nid-vue\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"npm install @vue/cli --global\ncd ~/desktop\nvue create nid-vue\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在全局范围安装了 Vue 框架的命令行工具以后，进入到桌面，然后执行 vue create 命令，这会在桌面上创建一个 Vue 项目，放在 nid-vue 这个目录里。"}]},{"type":"element","tag":"h4","props":{"id":"项目本地命令行工具"},"children":[{"type":"text","value":"项目本地命令行工具"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"有些带命令行的软件包需要将其安装在项目本地，比如在 TypeScript 这个包里面提供了一个 tsc 命令，这个包就需要安装在项目本地，因为每个项目需要的软件包的版本可能是不一样的。这种命令行工具软件包或者附带命令行工具的软件包，一般会作为项目的开发依赖，执行 npm install 命令的时候，配合使用 save-dev 或 D 选项可以将软件包作为项目的开发依赖。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"cd ~/desktop/nid-node\nnpm install typescript --save-dev\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cd ~/desktop/nid-node\nnpm install typescript --save-dev\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"先进入到项目所在目录，然后用 npm install 命令安装一个 typescript 软件包，用 save-dev 选项会将这个软件包作为项目的开发依赖。完成以后可以查看项目里的 node_modules/.bin 这个目录，在里面应该能找到 tsc 这个命令行工具。"}]},{"type":"element","tag":"h4","props":{"id":"执行项目本地命令行工具"},"children":[{"type":"text","value":"执行项目本地命令行工具"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果安装的软件包里带命令行工具，这些命令行工具一般都会放在项目的 node_modules/.bin 这个目录里。比如你想执行 typescript 这个软件包里带的 tsc 命令，需要指定这个命令行的具体位置，这个位置可以是相对的位置。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"cd ~/desktop/nid-node\n./node_modules/.bin/tsc\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cd ~/desktop/nid-node\n./node_modules/.bin/tsc\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"确定当前是在项目所在目录的下面，再执行一下给项目安装的 typescript 软件包里带的 tsc 这个命令，也就是当前目录下的 node_modules/.bin 这个目录里的 tsc。"}]},{"type":"element","tag":"h4","props":{"id":"配置-path-环境变量"},"children":[{"type":"text","value":"配置 PATH 环境变量"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果你想在项目里直接执行在安装的软件包里提供的命令行工具，可以配置一下 PATH 这个环境变量，将 ./node_modules/.bin 这个路径添加到 PATH 里。"}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"*Windows"}]},{"type":"text","value":"：cmder/config/user_profile.sh*"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"macOS"}]},{"type":"text","value":": ~/.zprofile"}]}]},{"type":"element","tag":"code","props":{"code":"export PATH=$PATH:./node_modules/.bin\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"export PATH=$PATH:./node_modules/.bin\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端的配置文件里添加上面这行代码，意思就是在 PATH 这个环境变量里添加 ./node_modules/.bin 这个路径，这样在项目所在目录的下面执行命令的时候，终端会在这个路径里查找要执行的命令行工具，也就不需要我们再输入命令的具体位置了。"}]},{"type":"element","tag":"blockquote","props":{},"children":[{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"修改了终端的配置文件以后，要重启终端，这样新做的配置才能生效。"}]}]},{"type":"element","tag":"h3","props":{"id":"软件包的安装源"},"children":[{"type":"text","value":"软件包的安装源"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"基于 Node.js 开发应用时，需要安装一些第三方提供的软件包（package），这些东西默认来自 npm 网站，如果你发现安装软件包时速度很慢，可以考虑切换使用第三方提供的安装源。"}]},{"type":"element","tag":"h4","props":{"id":"使用-nrm-管理软件包的安装源"},"children":[{"type":"text","value":"使用 nrm 管理软件包的安装源"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"借助 nrm 这个命令行工具可以方便地切换不同的安装源。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"安装 nrm 命令行工具"}]}]},{"type":"element","tag":"code","props":{"code":"npm install nrm --global\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"npm install nrm --global\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在全局范围安装 nrm 这个命令行工具。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"列出可用安装源"}]}]},{"type":"element","tag":"code","props":{"code":"nrm ls\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"nrm ls\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"返回的结果："}]},{"type":"element","tag":"code","props":{"code":"* npm -------- https://registry.npmjs.org/\n  yarn ------- https://registry.yarnpkg.com/\n  cnpm ------- http://r.cnpmjs.org/\n  taobao ----- https://registry.npm.taobao.org/\n  ...\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"* npm -------- https://registry.npmjs.org/\n  yarn ------- https://registry.yarnpkg.com/\n  cnpm ------- http://r.cnpmjs.org/\n  taobao ----- https://registry.npm.taobao.org/\n  ...\n"}]}]}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"号标注的就是当前使用的软件包安装源，默认就是 npm。"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"切换安装源"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在国内可以选择使用 taobao 这个安装源。"}]},{"type":"element","tag":"code","props":{"code":"nrm use taobao\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"nrm use taobao\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这行命令会将安装源设置成 taobao，这时再执行 nrm ls 查看安装源列表，你会发现 taobao 会变成当前使用的安装源。将软件包的安装源切换至第三方以后，如果你想把自己编写的软件包发布到 npm，需要将安装源切换回 npm。"}]},{"type":"element","tag":"h3","props":{"id":"多版本-nodejs可选"},"children":[{"type":"text","value":"多版本 Node.js（可选）"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"默认在操作系统里只能存在一个版本的 Node.js，有时我们可能需要根据不同的 Node.js 项目，使用不同版本的 Node.js，可以借助一些工具（nvm），在系统里同时安装多个版本的 Node.js，通过项目的配置或者使用命令可以切换使用不同版本的 Node.js。macOS 用户可以使用 nvm，Windows 用户可以使用 NVM for Windows。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果在系统里安装了多个版本的 Node.js，切换使用不同版本的 Node.js 以后，需要重新安装在全局范围里安装的命令行工具。"}]},{"type":"element","tag":"h4","props":{"id":"nvm"},"children":[{"type":"text","value":"nvm"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"nvm 这个工具允许在系统上同时存在多个版本的 Node.js，推荐 macOS 用户使用这个工具来管理安装在系统里的 Node.js，这样可以任意切换使用需要的 Node.js。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"项目地址"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://github.com/nvm-sh/nvm","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://github.com/nvm-sh/nvm"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"安装 nvm 命令行工具"}]}]},{"type":"element","tag":"code","props":{"code":"sudo curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"sudo curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端，执行上面这行命令会在系统里安装好 nvm 这个命令行工具。首先会下载 install.sh 这个脚本文件，里面写的就是安装 nvm 需要做的一些事情，这个文件是在 github 上提供的，所以在国内有可能会遇到无法连接的问题，在系统网络的 DNS 里添加 8.8.8.8 这个地址应该可以解决问题。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"完成安装以后，需要重新启动终端，这样就可以在终端使用 nvm 这个命令了。nvm 的安装脚本会修改终端的配置文件，比如 ~/.zshrc，你可以查看一下这个文件的内容，应该会看到类似下面这样的内容："}]},{"type":"element","tag":"code","props":{"code":"export NVM_DIR=\"$HOME/.nvm\"\n...\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"export NVM_DIR=\"$HOME/.nvm\"\n...\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"安装指定版本的 Node.js"}]}]},{"type":"element","tag":"code","props":{"code":"nvm install 16.15.0\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"nvm install 16.15.0\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令可以安装 16.15.0 版本的 Node.js。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"查看所有可用的版本"}]}]},{"type":"element","tag":"code","props":{"code":"nvm ls-remote\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"nvm ls-remote\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这行命令会列出所有可以安装使用的 Node.js。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"查看已经安装的版本"}]}]},{"type":"element","tag":"code","props":{"code":"nvm ls\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"nvm ls\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这行命令会列出当前已经安装在系统上的 Node.js，还会显示当前正在使用的 Node.js。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"设置当前使用的版本"}]}]},{"type":"element","tag":"code","props":{"code":"nvm use 16.15.0\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"nvm use 16.15.0\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令会将当前使用的 Node.js 设置成 16.15.0 这个版本。"}]},{"type":"element","tag":"h4","props":{"id":"nvm-for-windows"},"children":[{"type":"text","value":"NVM for Windows"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"项目地址"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://github.com/coreybutler/nvm-windows","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://github.com/coreybutler/nvm-windows"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"NVM for Windows 跟上面介绍的 nvm 差不多，但不是同一个项目。Windows 用户可以使用 NVM for Windows，它提供了一个安装包，下载并将其安装在电脑上，打开终端以后，就可以使用 nvm 这个命令了。"}]},{"type":"element","tag":"h3","props":{"id":"软件包编译环境"},"children":[{"type":"text","value":"软件包编译环境"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"开发 Node.js 应用会为项目安装一些软件包，大部分软件包直接就可以使用，不过有些软件包需要编译成本地的 Node 模块，这就需要我们的系统拥有合适的编译环境。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在宁皓独立开发者训练营基于 Node.js 开发后端应用时，会用到一个 bcrypt 这个软件包，为项目安装它的时候就有可能需要编译本地模块。如果在安装过程中出现类似 node-pre-gyp ERR! build error 这些的句子，说明编译本地模块的时候出了问题，也就是我们的系统暂时无法满足编译条件。"}]},{"type":"element","tag":"h4","props":{"id":"macos安装-xcode"},"children":[{"type":"text","value":"macOS：安装 XCode"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image.png"},"children":[]},{"type":"text","value":"\n在 macOS 系统中安装 XCode 这个软件以后就支持 Node.js 编译本地模块了，在 App Store 里搜索并安装 XCode，完成以后打开这个软件，应该会提示你安装一些额外的组件或者一些命令行工具，确定安装这些东西。"}]},{"type":"element","tag":"h4","props":{"id":"windows安装编译工具"},"children":[{"type":"text","value":"Windows：安装编译工具"}]},{"type":"element","tag":"code","props":{"code":"npm install --global windows-build-tools\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"npm install --global windows-build-tools\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Windows 用户可以在全局范围安装 windows-build-tools，这会在 Windows 系统中准备好编译 Node 本地模块需要的环境。"}]},{"type":"element","tag":"h2","props":{"id":"mysql"},"children":[{"type":"text","value":"MySQL"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在宁皓独立开发者训练营中，我们会用 MySQL 为应用提供数据服务。在本地电脑上安装并运行一个 MySQL 服务器（8.x），这样服务端应用就可以连接使用这个数据服务，管理应用的数据了。"}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://www.mysql.com/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://www.mysql.com/"}]}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"下载地址"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://dev.mysql.com/downloads/mysql/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://dev.mysql.com/downloads/mysql/"}]}]}]},{"type":"element","tag":"h3","props":{"id":"macos安装-mysql"},"children":[{"type":"text","value":"macOS：安装 MySQL"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 MySQL 的官方网站，选择下载 8.x 社区版的 MySQL。打开下载页面以后，Select Operating System 选择 macOS。根据电脑架构选择合适的版本，如果电脑用的是 m1 芯片，可以下载 ARM 版本，如果是 intel 处理器，可以下载 x86 版本。在系统的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"关于电脑"}]},{"type":"text","value":" 那里可以查看电脑使用的处理器。无论是哪种版本，推荐下载用 DMG 格式封闭的 MySQL，下载完成以后将其安装在系统里。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(1).png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"macos管理-mysql-服务"},"children":[{"type":"text","value":"macOS：管理 MySQL 服务"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 macOS 系统里安装了 MySQL 服务器以后，在系统的偏好设置那里可以找到 MySQL 服务的管理界面，在这里可以管理 MySQL 服务器，比如停止或启动 MySQL 服务。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(2).png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"windows安装-mysql"},"children":[{"type":"text","value":"Windows：安装 MySQL"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 MySQL 的官方网站，选择下载 8.x 社区版的 MySQL。打开下载页面以后，Select Operating System 选择 Microsoft Windows，然后点击 MySQL Installer for Windows，下载带安装向导的 MySQL（如 mysql-installer-community-8.0.29.0.msi），然后将其安装在系统里。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(3).png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"windows管理-mysql-服务"},"children":[{"type":"text","value":"Windows：管理 MySQL 服务"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 Windows 系统里安装了 MySQL 服务器以后，在系统托盘那里应该会出现一个 MySQL 小图标，通过它可以管理 MySQL 服务的启动状态。"}]},{"type":"element","tag":"h3","props":{"id":"root-用户"},"children":[{"type":"text","value":"root 用户"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"MySQL 里的 root 用户是超级管理员，拥有管理整个服务的权限，比如管理数据库与用户等等。在安装 MySQL 的过程中，应该会提示你设置这个用户的密码，稍后我们会用到这个用户与给他设置的密码连接管理 MySQL 服务。"}]},{"type":"element","tag":"h2","props":{"id":"tableplus"},"children":[{"type":"text","value":"TablePlus"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"TablePlus 是一种数据库客户端软件，用它可以连接管理不同类型的数据服务。在训练营中我们会使用 TablePlus 连接管理 MySQL 数据服务，观察应用数据仓库里的数据与结构，练习 SQL 语言等等。"}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://tableplus.com/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://tableplus.com/"}]}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"下载地址"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://tableplus.com/download","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://tableplus.com/download"}]}]}]},{"type":"element","tag":"h3","props":{"id":"连接-mysql-服务"},"children":[{"type":"text","value":"连接 MySQL 服务"}]},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"打开 TablePlus。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"点击界面底部的 Create a new connection...（创建新连接）。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"数据服务的类型选择 MySQL，然后点击 Create。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"配置连接。"},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"Name：随便填写一个连接名称。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"Ver.：版本设置成 Default。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"Host：数据服务在本地，所以主机名设置成 127.0.0.1。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"Port：MySQL 服务默认的端口号是 3306。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"User：可以填写 root。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"Password：输入 root 用户的密码。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"Database：连接的数据仓库的名字，暂时没有可以留空。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"点击 Test，测试连接，一切正常的话文本框会变绿，测试失败会出现提示。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"点击 Connect 确定打开连接。"}]}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(4).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"创建新连接，选择数据服务类型"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(5).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"连接的配置界面"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(6).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"打开连接后显示的界面"}]}]},{"type":"element","tag":"h3","props":{"id":"创建数据仓库"},"children":[{"type":"text","value":"创建数据仓库"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开之前创建的连接以后，需要再打开数据服务里的一个具体的数据仓库。点击界面标题栏上的数据仓库小图标（在 SQL 左边），这会打开 Open database 窗口，在这里会列出当前连接可以管理的数据仓库，点击 + 号小图标会显示 New Database 窗口。"}]},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"Name：数据仓库名字，比如 xb2_node。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"Encoding：数据编码格式，选择 utf8mb4。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"Collation：数据整理，选择 utf8mb4_0900_ai_ci。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"点击 OK，确定创建新的数据仓库。"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(7).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"TablePlus 创建数据仓库"}]}]},{"type":"element","tag":"h3","props":{"id":"打开数据仓库"},"children":[{"type":"text","value":"打开数据仓库"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"创建了数据仓库以后，可以在 Open database 这里列出这个新的数据仓库，选中并打开它。每次打开连接以后，都需要打开需要管理的数据仓库。我们可以编辑一下之前创建的连接，在 Database 那里输入连接以后要打开的数据仓库的名字，比如 xb2_node，这样打开连接的同时会打开 xb2_node 这个数据仓库。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(8).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"TablePlus 打开数据仓库"}]}]},{"type":"element","tag":"h3","props":{"id":"管理数据仓库"},"children":[{"type":"text","value":"管理数据仓库"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开连接并打开了数据仓库以后，如果数据仓库里拥有已经创建的数据表（Tables），在左边栏就会显示这些数据表。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"观察数据记录"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"选中某个数据表，在右边会显示这个数据表里的数据记录。调试服务端应用的时候，经常需要在 TablePlus 里观察数据表里的数据的变化，刷新（command + R） TablePlus 应用的界面可以查看最新的变化。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(9).png"},"children":[]},{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"修改数据记录"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 TablePlus 里可以直接修改数据表里的数据记录的值，也可以添加新的数据记录或者删除数据记录，确定这些操作需要保存（command + S）。被修改的数据记录与数据表会用特殊颜色标记，确定保存以后会变成正常状态。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(10).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"TablePlus 会使用特殊颜色标记被修改的数据记录与数据表了数据记录"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"数据表结构"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开数据表以后，默认会显示数据表里的 Data（数据），点击界面底部的 Structure（结构）可以查看与修改数据表的结构，修改了数据表的结构以后，需要保存（command + S）才能生效。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(11).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"TablePlus 提供的数据表结构的管理界面"}]}]},{"type":"element","tag":"h3","props":{"id":"执行-sql"},"children":[{"type":"text","value":"执行 SQL"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"点击界面顶部的标题栏里的 SQL 图标，可以打开执行 SQL 的界面，在这里可以通过执行 SQL 操作数据仓库，界面下方会显示执行的结果或查询出来的数据。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(12).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"TablePlus 提供的 SQL 界面"}]}]},{"type":"element","tag":"h2","props":{"id":"insomnia"},"children":[{"type":"text","value":"Insomnia"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Insomnia 是一种 Http 客户端软件，我们在训练营中开发应用时，在测试与调试服务端应用接口的时候会用到它。"}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://insomnia.rest/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://insomnia.rest"}]}]},{"type":"element","tag":"li","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"下载地址"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://insomnia.rest/download","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://insomnia.rest/download"}]}]}]},{"type":"element","tag":"h3","props":{"id":"管理项目"},"children":[{"type":"text","value":"管理项目"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开 Insomnia 以后可以先创建一个 project（项目），每个项目的下面可以拥有属于自己的一些接口的设计文档（design document）或是请求集合（request collection）。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(13).png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"请求集合"},"children":[{"type":"text","value":"请求集合"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在项目下面，点击 Create 按钮，在弹出的菜单里选择 Request Collection，创建一个请求集合，每个请求集合里面可以包含一组请求。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(14).png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"管理环境"},"children":[{"type":"text","value":"管理环境"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开创建的请求集合，可以管理一下在这个集合里的环境。每个环境里都可以设置一些环境变量，在创建请求的时候可以使用这些环境变量。切换集合当前使用的环境，这些环境变量的值也会发生相应的变化。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"比如我们在请求集合里创建两个环境：开发环境与生产环境，在这两个环境里面定义一个名字是 nid_nest_api 的环境变量，它的值就是服务端应用在不同环境下的基本地址。这样如果在请求里使用了 nid_nest_api 这个环境变量，就可以得到服务端接口的基本地址了，而且在切换集合当前环境的时候，这个地址也会发生改变。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(15).png"},"children":[]},{"type":"text","value":"\n点击环境名称会弹出菜单，列出当前可用的环境，点击 Manage Environments，可以管理集合的环境，在 Sub Environments 下面新建两个环境，然后分别设置一些环境变量，数据的格式是 JSON。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(16).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"图中显示在开发环境中添加了一个 nid_nest_api，对应的值是 "},{"type":"element","tag":"a","props":{"href":"http://localhost:3000/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"http://localhost:3000"}]},{"type":"text","value":"。"}]}]},{"type":"element","tag":"h3","props":{"id":"管理请求"},"children":[{"type":"text","value":"管理请求"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开请求集合以后，可以创建一些 Folder（目录），然后在这些目录里再分别创建一些 Request（请求）。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/backend/image(17).png"},"children":[]},{"type":"text","value":"\n在请求中可以使用请求集合里创建的环境里定义的环境变量，比如在上图这个 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"创建内容"}]},{"type":"text","value":" 请求中，请求的地址里用了 nid_nest_api 这个环境变量，当前这个请求集合设置使用的环境是 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"开发环境"}]},{"type":"text","value":"，所以这个环境变量的值就是在开发环境里定义的 nid_nest_api 的值，也就是 "},{"type":"element","tag":"a","props":{"href":"http://localhost/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"http://localhost"}]},{"type":"text","value":":3000。"}]}],"toc":{"title":"","searchDepth":2,"depth":2,"links":[{"id":"nodejs","depth":2,"text":"Node.js","children":[{"id":"安装-nodejs","depth":3,"text":"安装 Node.js"},{"id":"命令行工具","depth":3,"text":"命令行工具"},{"id":"软件包的安装源","depth":3,"text":"软件包的安装源"},{"id":"多版本-nodejs可选","depth":3,"text":"多版本 Node.js（可选）"},{"id":"软件包编译环境","depth":3,"text":"软件包编译环境"}]},{"id":"mysql","depth":2,"text":"MySQL","children":[{"id":"macos安装-mysql","depth":3,"text":"macOS：安装 MySQL"},{"id":"macos管理-mysql-服务","depth":3,"text":"macOS：管理 MySQL 服务"},{"id":"windows安装-mysql","depth":3,"text":"Windows：安装 MySQL"},{"id":"windows管理-mysql-服务","depth":3,"text":"Windows：管理 MySQL 服务"},{"id":"root-用户","depth":3,"text":"root 用户"}]},{"id":"tableplus","depth":2,"text":"TablePlus","children":[{"id":"连接-mysql-服务","depth":3,"text":"连接 MySQL 服务"},{"id":"创建数据仓库","depth":3,"text":"创建数据仓库"},{"id":"打开数据仓库","depth":3,"text":"打开数据仓库"},{"id":"管理数据仓库","depth":3,"text":"管理数据仓库"},{"id":"执行-sql","depth":3,"text":"执行 SQL"}]},{"id":"insomnia","depth":2,"text":"Insomnia","children":[{"id":"管理项目","depth":3,"text":"管理项目"},{"id":"请求集合","depth":3,"text":"请求集合"},{"id":"管理环境","depth":3,"text":"管理环境"},{"id":"管理请求","depth":3,"text":"管理请求"}]}]}},"_type":"markdown","_id":"content:docs:2.env:1.backend.md","_source":"content","_file":"docs/2.env/1.backend.md","_extension":"md"},{"_path":"/docs/env/frontend","_draft":false,"_partial":false,"_empty":false,"title":"前端开发环境","description":"Vue.js 是在宁皓独立开发者训练营中选择使用的前端应用框架，我们会基于 Vue.js 开发应用的前端，也就是可以通过浏览器使用的应用。","excerpt":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"前端web应用开发环境"},"children":[{"type":"text","value":"前端（Web）应用开发环境"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Vue.js 是在宁皓独立开发者训练营中选择使用的前端应用框架，我们会基于 Vue.js 开发应用的前端，也就是可以通过浏览器使用的应用。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在本地准备好基于 Vue.js 框架的应用开发环境，可以先准备一个调试应用需要的 Chrome 浏览器，在系统里装好 Node.js，再安装一下 Vue.js 框架提供的命令行工具，用这个命令行工具创建一个 Vue.js 应用项目，启动项目的开发服务，再用 VSCode 打开这个项目，就可以开启本次训练营的前端应用开发之旅了。"}]},{"type":"element","tag":"h2","props":{"id":"chrome"},"children":[{"type":"text","value":"Chrome"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在训练营中，我们会使用 Chrome 浏览器测试基于 Vue.js 开发的前端应用。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/frontend/image.png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"安装-chrome"},"children":[{"type":"text","value":"安装 Chrome"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Chrome 是 Google 公司开发的产品，在国内无法正常访问 Chrome 浏览器的官方网站，我们可以在百度搜索 Chrome 关键词，通过第三方网站下载这款浏览器。"}]},{"type":"element","tag":"h3","props":{"id":"开发者工具"},"children":[{"type":"text","value":"开发者工具"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Chrome 浏览器的开发者工具是前端工程师必备的工具之一，在训练营中，我们会在开发者工具里检查界面元素，在控制台查看输出的调试数据，在应用标签里查看本地存储等等。打开这个工具可以通过菜单 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"视图"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"开发者"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"开发者工具"}]},{"type":"text","value":"。或者使用快捷键 alt + command + I（macOS）、ctrl + shift + I（Windows）。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/frontend/image(1).png"},"children":[]}]},{"type":"element","tag":"h4","props":{"id":"界面语言"},"children":[{"type":"text","value":"界面语言"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在开发者工具 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"设置"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"偏好设置"}]},{"type":"text","value":" 里，打开 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"语言"}]},{"type":"text","value":" 这个下拉菜单，在这里可以选择开发者工具的界面使用的语言。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/frontend/image(2).png"},"children":[]}]},{"type":"element","tag":"h4","props":{"id":"元素"},"children":[{"type":"text","value":"元素"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在开发者工具的元素选项卡可以检查界面元素，比如定位元素在界面上的显示，应用在元素上的样式等等。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/frontend/image(3).png"},"children":[]}]},{"type":"element","tag":"h4","props":{"id":"控制台"},"children":[{"type":"text","value":"控制台"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"开发调试前端应用时，我们经常会使用 console.log() 在控制台上输出一些数据，在开发者工具的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"控制台"}]},{"type":"text","value":" 标签里，可以查看前端应用输出到控制台上的数据。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/frontend/image(4).png"},"children":[]}]},{"type":"element","tag":"h4","props":{"id":"应用"},"children":[{"type":"text","value":"应用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"前端应用会在浏览器的本地存储里存储一些数据，在开发者工具 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"应用"}]},{"type":"text","value":" 标签的下面，可以在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"存储"}]},{"type":"text","value":" 的下面，查看应用的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"本地存储空间"}]},{"type":"text","value":" 里的数据。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/frontend/image(5).png"},"children":[]}]},{"type":"element","tag":"h2","props":{"id":"nodejs"},"children":[{"type":"text","value":"Node.js"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在训练营中开发 Vue.js 应用时，应用的开发服务与创建应用零部件需要的命令行工具都需要用到 Node.js。在准备服务端应用开发环境的时候，我们已经在系统里准备好了 Node.js。"}]},{"type":"element","tag":"h2","props":{"id":"yarn"},"children":[{"type":"text","value":"Yarn"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"yarn 与 npm 一样，都可以作为 Node.js 项目的包管理工具。在开发 Node.js 服务端项目或者前端项目都会用到包管理工具，一般来说二选其一即可，在 NIDC（宁皓独立开发者训练营） 中，开发服务端项目的时候会使用 npm 作为包管理工具，开发基于 Vue.js 框架的前端应用时，会使用 yarn 作为项目的包管理工具。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://yarnpkg.com/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://yarnpkg.com/"}]}]},{"type":"element","tag":"h3","props":{"id":"安装-yarn"},"children":[{"type":"text","value":"安装 Yarn"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"以前需要用 npm 安装 yarn，现在 yarn 这个包管理工具包含在版本 >=16.10 的 Node.js 里，在终端执行一下："}]},{"type":"element","tag":"code","props":{"code":"corepack enable\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"corepack enable\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果使用的 Node.js 版本 <16.10，可以在全局范围安装一下 corepack："}]},{"type":"element","tag":"code","props":{"code":"npm install corepack --global\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"npm install corepack --global\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"完成以后，应该就可以使用 yarn 这个包管理工具了。"}]},{"type":"element","tag":"code","props":{"code":"yarn --help\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"yarn --help\n"}]}]}]},{"type":"element","tag":"h2","props":{"id":"vuejs"},"children":[{"type":"text","value":"Vue.js"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 NIDC（宁皓独立开发者训练营）中，我们会使用 Vue 提供的命令行工具，比如去创建全部的 Vue 项目，启动本地开发服务，编译生成在生产环境上使用的应用等等。"}]},{"type":"element","tag":"h3","props":{"id":"vue-cli-命令行工具"},"children":[{"type":"text","value":"Vue CLI 命令行工具"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在全局范围安装一下 Vue CLI 这个包，这样就可以在任何地方使用 vue 命令了。"}]},{"type":"element","tag":"code","props":{"code":"npm install @vue/cli --global\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"npm install @vue/cli --global\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端，用 npm 在全局范围安装一个叫 @vue/cli 的东西，这个 @vue/cli 是软件包的名字，Vue 官方提供的软件包，很多都有 @vue 这个前缀。安装完成以后，执行 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"vue -h"}]},{"type":"text","value":" ，如果能正常的看到命令的帮助信息，就说明成功地安装了 Vue CLI。"}]},{"type":"element","tag":"h3","props":{"id":"使用-vue-cli-创建项目"},"children":[{"type":"text","value":"使用 Vue CLI 创建项目"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在全局范围安装了 @vue/cli 这个软件包以后，可以使用 vue 这个命令，我们可以使用它创建一个 Vue 项目。"}]},{"type":"element","tag":"code","props":{"code":"cd ~/desktop\nvue create nid-vue\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cd ~/desktop\nvue create nid-vue\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"进入到想要保存项目的地方以后，执行 vue create 命令创建一个名字是 nid-vue 的 Vue 项目，这个命令会问一些问题。"}]},{"type":"element","tag":"code","props":{"code":"? Please pick a preset:\n  Default ([Vue 3] babel, eslint)\n  Default ([Vue 2] babel, eslint)\n❯ Manually select features\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"? Please pick a preset:\n  Default ([Vue 3] babel, eslint)\n  Default ([Vue 2] babel, eslint)\n❯ Manually select features\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"首先可以选择一个项目的预设（preset），选择 Default Vue 3 会创建一个默认的使用 3.x 版本的 Vue 项目。我们可以选择 Manually select features（手动选择功能），用键盘的上下箭头按键可以选择，按下回车确定选择。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/frontend/image(6).png"},"children":[]},{"type":"text","value":"\n如果选择手动选择功能，会让我们继续选择项目需要的一些功能，按空格按键可以切换选择功能，比如可以将 TypeScript、Router、Vuex 这几个功能选上，按回车继续下一步。"}]},{"type":"element","tag":"code","props":{"code":"? Choose a version of Vue.js that you want to start the project with\n❯ 3.x\n  2.x\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"? Choose a version of Vue.js that you want to start the project with\n❯ 3.x\n  2.x\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"这一步是选择要使用的 Vue 版本，默认选择 3.x。"}]},{"type":"element","tag":"code","props":{"code":"? Use class-style component syntax? (y/N)\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"? Use class-style component syntax? (y/N)\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"是否选择类风格的组件，默认是 N，表示不选择。"}]},{"type":"element","tag":"code","props":{"code":"? Use Babel alongside TypeScript? (Y/n)\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"? Use Babel alongside TypeScript? (Y/n)\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"是否要使用 Babel，可以选择使用 Babel。"}]},{"type":"element","tag":"code","props":{"code":"? Use history mode for router? (Y/n)\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"? Use history mode for router? (Y/n)\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"是否使用历史模式的路由器，选择使用这种模式的路由器。"}]},{"type":"element","tag":"code","props":{"code":"? Pick a linter / formatter config:\n❯ ESLint with error prevention only\n  ESLint + Airbnb config\n  ESLint + Standard config\n  ESLint + Prettier\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"? Pick a linter / formatter config:\n❯ ESLint with error prevention only\n  ESLint + Airbnb config\n  ESLint + Standard config\n  ESLint + Prettier\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"选择检查器与格式化器的配置，可以选择默认的 ESLint with error prevention only。"}]},{"type":"element","tag":"code","props":{"code":"? Pick additional lint features:\n❯◉ Lint on save\n ◯ Lint and fix on commit\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"? Pick additional lint features:\n❯◉ Lint on save\n ◯ Lint and fix on commit\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"选择额外的检查功能，可以选中 Lint on save，然后按回车继续。"}]},{"type":"element","tag":"code","props":{"code":"? Save this as a preset for future projects? (y/N)\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"? Save this as a preset for future projects? (y/N)\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"是否保存为预设，这样以后使用 vue create 命令创建 Vue 项目的时候，可以直接选择使用这个预设，输入 N ，按下回车，表示不保存为预设。"}]},{"type":"element","tag":"code","props":{"code":"🎉  Successfully created project nid-vue.\n👉  Get started with the following commands:\n\n $ cd nid-vue\n $ yarn serve\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"🎉  Successfully created project nid-vue.\n👉  Get started with the following commands:\n\n $ cd nid-vue\n $ yarn serve\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Vue CLI 会根据我们做出的选择将 Vue 项目准备好，成功以后会提示 Successfully created project ..."}]},{"type":"element","tag":"h3","props":{"id":"启动本地开发服务"},"children":[{"type":"text","value":"启动本地开发服务"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 Vue CLI 成功创建了一个 Vue 项目以后，可以使用 VSCode 编辑器打开这个项目。然后可以在终端启动这个 Vue 项目的开发服务，这样就可以在浏览器通过一个本地的地址访问到我们正在开发的 Vue 项目，在编辑器修改项目，保存项目文件以后，在浏览器上显示的应用会实时更新，显示修改之后的项目。"}]},{"type":"element","tag":"code","props":{"code":"cd ~/desktop/nid-vue\nyarn serve\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cd ~/desktop/nid-vue\nyarn serve\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端，进入到项目所在目录以后，执行 yarn serve，这会启动 Vue 项目的本地开发服务，给我们的项目创建一个 Web 服务器，通过一个特定的地址可以直接访问正在开发的 Vue 应用。serve 这个命令是在项目的 package.json 文件里的 scripts 下面定义的。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/frontend/image(7).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Vue 项目开发服务启动后的提示"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"开发服务启动以后会给出访问的地址，在本地电脑上可以通过 localhost 访问，注意访问地址里会带着一个特定的端口号，默认是 8080。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/frontend/image(8).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 Chrome 浏览器访问 Vue 项目的本地开发服务的地址，会显示 Vue 项目默认的一个欢迎界面。"}]}]},"body":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"前端web应用开发环境"},"children":[{"type":"text","value":"前端（Web）应用开发环境"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Vue.js 是在宁皓独立开发者训练营中选择使用的前端应用框架，我们会基于 Vue.js 开发应用的前端，也就是可以通过浏览器使用的应用。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在本地准备好基于 Vue.js 框架的应用开发环境，可以先准备一个调试应用需要的 Chrome 浏览器，在系统里装好 Node.js，再安装一下 Vue.js 框架提供的命令行工具，用这个命令行工具创建一个 Vue.js 应用项目，启动项目的开发服务，再用 VSCode 打开这个项目，就可以开启本次训练营的前端应用开发之旅了。"}]},{"type":"element","tag":"h2","props":{"id":"chrome"},"children":[{"type":"text","value":"Chrome"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在训练营中，我们会使用 Chrome 浏览器测试基于 Vue.js 开发的前端应用。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/frontend/image.png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"安装-chrome"},"children":[{"type":"text","value":"安装 Chrome"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Chrome 是 Google 公司开发的产品，在国内无法正常访问 Chrome 浏览器的官方网站，我们可以在百度搜索 Chrome 关键词，通过第三方网站下载这款浏览器。"}]},{"type":"element","tag":"h3","props":{"id":"开发者工具"},"children":[{"type":"text","value":"开发者工具"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Chrome 浏览器的开发者工具是前端工程师必备的工具之一，在训练营中，我们会在开发者工具里检查界面元素，在控制台查看输出的调试数据，在应用标签里查看本地存储等等。打开这个工具可以通过菜单 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"视图"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"开发者"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"开发者工具"}]},{"type":"text","value":"。或者使用快捷键 alt + command + I（macOS）、ctrl + shift + I（Windows）。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/frontend/image(1).png"},"children":[]}]},{"type":"element","tag":"h4","props":{"id":"界面语言"},"children":[{"type":"text","value":"界面语言"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在开发者工具 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"设置"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"偏好设置"}]},{"type":"text","value":" 里，打开 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"语言"}]},{"type":"text","value":" 这个下拉菜单，在这里可以选择开发者工具的界面使用的语言。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/frontend/image(2).png"},"children":[]}]},{"type":"element","tag":"h4","props":{"id":"元素"},"children":[{"type":"text","value":"元素"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在开发者工具的元素选项卡可以检查界面元素，比如定位元素在界面上的显示，应用在元素上的样式等等。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/frontend/image(3).png"},"children":[]}]},{"type":"element","tag":"h4","props":{"id":"控制台"},"children":[{"type":"text","value":"控制台"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"开发调试前端应用时，我们经常会使用 console.log() 在控制台上输出一些数据，在开发者工具的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"控制台"}]},{"type":"text","value":" 标签里，可以查看前端应用输出到控制台上的数据。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/frontend/image(4).png"},"children":[]}]},{"type":"element","tag":"h4","props":{"id":"应用"},"children":[{"type":"text","value":"应用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"前端应用会在浏览器的本地存储里存储一些数据，在开发者工具 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"应用"}]},{"type":"text","value":" 标签的下面，可以在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"存储"}]},{"type":"text","value":" 的下面，查看应用的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"本地存储空间"}]},{"type":"text","value":" 里的数据。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/frontend/image(5).png"},"children":[]}]},{"type":"element","tag":"h2","props":{"id":"nodejs"},"children":[{"type":"text","value":"Node.js"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在训练营中开发 Vue.js 应用时，应用的开发服务与创建应用零部件需要的命令行工具都需要用到 Node.js。在准备服务端应用开发环境的时候，我们已经在系统里准备好了 Node.js。"}]},{"type":"element","tag":"h2","props":{"id":"yarn"},"children":[{"type":"text","value":"Yarn"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"yarn 与 npm 一样，都可以作为 Node.js 项目的包管理工具。在开发 Node.js 服务端项目或者前端项目都会用到包管理工具，一般来说二选其一即可，在 NIDC（宁皓独立开发者训练营） 中，开发服务端项目的时候会使用 npm 作为包管理工具，开发基于 Vue.js 框架的前端应用时，会使用 yarn 作为项目的包管理工具。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://yarnpkg.com/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://yarnpkg.com/"}]}]},{"type":"element","tag":"h3","props":{"id":"安装-yarn"},"children":[{"type":"text","value":"安装 Yarn"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"以前需要用 npm 安装 yarn，现在 yarn 这个包管理工具包含在版本 >=16.10 的 Node.js 里，在终端执行一下："}]},{"type":"element","tag":"code","props":{"code":"corepack enable\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"corepack enable\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果使用的 Node.js 版本 <16.10，可以在全局范围安装一下 corepack："}]},{"type":"element","tag":"code","props":{"code":"npm install corepack --global\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"npm install corepack --global\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"完成以后，应该就可以使用 yarn 这个包管理工具了。"}]},{"type":"element","tag":"code","props":{"code":"yarn --help\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"yarn --help\n"}]}]}]},{"type":"element","tag":"h2","props":{"id":"vuejs"},"children":[{"type":"text","value":"Vue.js"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 NIDC（宁皓独立开发者训练营）中，我们会使用 Vue 提供的命令行工具，比如去创建全部的 Vue 项目，启动本地开发服务，编译生成在生产环境上使用的应用等等。"}]},{"type":"element","tag":"h3","props":{"id":"vue-cli-命令行工具"},"children":[{"type":"text","value":"Vue CLI 命令行工具"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在全局范围安装一下 Vue CLI 这个包，这样就可以在任何地方使用 vue 命令了。"}]},{"type":"element","tag":"code","props":{"code":"npm install @vue/cli --global\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"npm install @vue/cli --global\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端，用 npm 在全局范围安装一个叫 @vue/cli 的东西，这个 @vue/cli 是软件包的名字，Vue 官方提供的软件包，很多都有 @vue 这个前缀。安装完成以后，执行 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"vue -h"}]},{"type":"text","value":" ，如果能正常的看到命令的帮助信息，就说明成功地安装了 Vue CLI。"}]},{"type":"element","tag":"h3","props":{"id":"使用-vue-cli-创建项目"},"children":[{"type":"text","value":"使用 Vue CLI 创建项目"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在全局范围安装了 @vue/cli 这个软件包以后，可以使用 vue 这个命令，我们可以使用它创建一个 Vue 项目。"}]},{"type":"element","tag":"code","props":{"code":"cd ~/desktop\nvue create nid-vue\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cd ~/desktop\nvue create nid-vue\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"进入到想要保存项目的地方以后，执行 vue create 命令创建一个名字是 nid-vue 的 Vue 项目，这个命令会问一些问题。"}]},{"type":"element","tag":"code","props":{"code":"? Please pick a preset:\n  Default ([Vue 3] babel, eslint)\n  Default ([Vue 2] babel, eslint)\n❯ Manually select features\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"? Please pick a preset:\n  Default ([Vue 3] babel, eslint)\n  Default ([Vue 2] babel, eslint)\n❯ Manually select features\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"首先可以选择一个项目的预设（preset），选择 Default Vue 3 会创建一个默认的使用 3.x 版本的 Vue 项目。我们可以选择 Manually select features（手动选择功能），用键盘的上下箭头按键可以选择，按下回车确定选择。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/frontend/image(6).png"},"children":[]},{"type":"text","value":"\n如果选择手动选择功能，会让我们继续选择项目需要的一些功能，按空格按键可以切换选择功能，比如可以将 TypeScript、Router、Vuex 这几个功能选上，按回车继续下一步。"}]},{"type":"element","tag":"code","props":{"code":"? Choose a version of Vue.js that you want to start the project with\n❯ 3.x\n  2.x\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"? Choose a version of Vue.js that you want to start the project with\n❯ 3.x\n  2.x\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"这一步是选择要使用的 Vue 版本，默认选择 3.x。"}]},{"type":"element","tag":"code","props":{"code":"? Use class-style component syntax? (y/N)\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"? Use class-style component syntax? (y/N)\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"是否选择类风格的组件，默认是 N，表示不选择。"}]},{"type":"element","tag":"code","props":{"code":"? Use Babel alongside TypeScript? (Y/n)\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"? Use Babel alongside TypeScript? (Y/n)\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"是否要使用 Babel，可以选择使用 Babel。"}]},{"type":"element","tag":"code","props":{"code":"? Use history mode for router? (Y/n)\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"? Use history mode for router? (Y/n)\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"是否使用历史模式的路由器，选择使用这种模式的路由器。"}]},{"type":"element","tag":"code","props":{"code":"? Pick a linter / formatter config:\n❯ ESLint with error prevention only\n  ESLint + Airbnb config\n  ESLint + Standard config\n  ESLint + Prettier\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"? Pick a linter / formatter config:\n❯ ESLint with error prevention only\n  ESLint + Airbnb config\n  ESLint + Standard config\n  ESLint + Prettier\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"选择检查器与格式化器的配置，可以选择默认的 ESLint with error prevention only。"}]},{"type":"element","tag":"code","props":{"code":"? Pick additional lint features:\n❯◉ Lint on save\n ◯ Lint and fix on commit\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"? Pick additional lint features:\n❯◉ Lint on save\n ◯ Lint and fix on commit\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"选择额外的检查功能，可以选中 Lint on save，然后按回车继续。"}]},{"type":"element","tag":"code","props":{"code":"? Save this as a preset for future projects? (y/N)\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"? Save this as a preset for future projects? (y/N)\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"是否保存为预设，这样以后使用 vue create 命令创建 Vue 项目的时候，可以直接选择使用这个预设，输入 N ，按下回车，表示不保存为预设。"}]},{"type":"element","tag":"code","props":{"code":"🎉  Successfully created project nid-vue.\n👉  Get started with the following commands:\n\n $ cd nid-vue\n $ yarn serve\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"🎉  Successfully created project nid-vue.\n👉  Get started with the following commands:\n\n $ cd nid-vue\n $ yarn serve\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Vue CLI 会根据我们做出的选择将 Vue 项目准备好，成功以后会提示 Successfully created project ..."}]},{"type":"element","tag":"h3","props":{"id":"启动本地开发服务"},"children":[{"type":"text","value":"启动本地开发服务"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 Vue CLI 成功创建了一个 Vue 项目以后，可以使用 VSCode 编辑器打开这个项目。然后可以在终端启动这个 Vue 项目的开发服务，这样就可以在浏览器通过一个本地的地址访问到我们正在开发的 Vue 项目，在编辑器修改项目，保存项目文件以后，在浏览器上显示的应用会实时更新，显示修改之后的项目。"}]},{"type":"element","tag":"code","props":{"code":"cd ~/desktop/nid-vue\nyarn serve\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cd ~/desktop/nid-vue\nyarn serve\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端，进入到项目所在目录以后，执行 yarn serve，这会启动 Vue 项目的本地开发服务，给我们的项目创建一个 Web 服务器，通过一个特定的地址可以直接访问正在开发的 Vue 应用。serve 这个命令是在项目的 package.json 文件里的 scripts 下面定义的。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/frontend/image(7).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Vue 项目开发服务启动后的提示"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"开发服务启动以后会给出访问的地址，在本地电脑上可以通过 localhost 访问，注意访问地址里会带着一个特定的端口号，默认是 8080。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/frontend/image(8).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 Chrome 浏览器访问 Vue 项目的本地开发服务的地址，会显示 Vue 项目默认的一个欢迎界面。"}]}],"toc":{"title":"","searchDepth":2,"depth":2,"links":[{"id":"chrome","depth":2,"text":"Chrome","children":[{"id":"安装-chrome","depth":3,"text":"安装 Chrome"},{"id":"开发者工具","depth":3,"text":"开发者工具"}]},{"id":"nodejs","depth":2,"text":"Node.js"},{"id":"yarn","depth":2,"text":"Yarn","children":[{"id":"安装-yarn","depth":3,"text":"安装 Yarn"}]},{"id":"vuejs","depth":2,"text":"Vue.js","children":[{"id":"vue-cli-命令行工具","depth":3,"text":"Vue CLI 命令行工具"},{"id":"使用-vue-cli-创建项目","depth":3,"text":"使用 Vue CLI 创建项目"},{"id":"启动本地开发服务","depth":3,"text":"启动本地开发服务"}]}]}},"_type":"markdown","_id":"content:docs:2.env:2.frontend.md","_source":"content","_file":"docs/2.env/2.frontend.md","_extension":"md"},{"_path":"/docs/env/mobile","_draft":false,"_partial":false,"_empty":false,"title":"移动端开发环境","description":"Flutter 是在宁皓独立开发者训练营中要使用的客户端应用框架，在训练营中我们会基于 Flutter 框架构建移动端应用。在本地电脑安装好 Flutter SDK，准备好 iOS 与 Android 应用的开发环境，就可以开启本次训练营的移动应用开发之旅了。","excerpt":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"移动端iosandroid应用开发环境"},"children":[{"type":"text","value":"移动端（iOS、Android）应用开发环境"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Flutter 是在宁皓独立开发者训练营中要使用的客户端应用框架，在训练营中我们会基于 Flutter 框架构建移动端应用。在本地电脑安装好 Flutter SDK，准备好 iOS 与 Android 应用的开发环境，就可以开启本次训练营的移动应用开发之旅了。"}]},{"type":"element","tag":"h2","props":{"id":"flutter"},"children":[{"type":"text","value":"Flutter"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 Flutter 的官方网站下载合适的 Flutter SDK。下载下来的东西通常就是一个 .zip 格式的压缩包，把解压之后得到的 flutter 目录放在系统里的某个地方，再配置一下系统的 PATH 环境变量， 在任何地方能都执行 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"flutter"}]},{"type":"text","value":" 这个命令，就算是安装配置好了 Flutter。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"下载的时候要选择适合在自己电脑操作系统上运行的版本，比如 Windows，macOS 或者 Linux 版本的 Flutter。下载以后得到的压缩包文件可能像这样："},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"flutter_macos_3.0.0-stable.zip"}]},{"type":"text","value":"，文件名里的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"macos"}]},{"type":"text","value":" 指的是操作系统，3*.0.0* 是 Flutter SDK 的版本号，"},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"stable"}]},{"type":"text","value":" 表示这是一个稳定版本的 Flutter，除了稳定版，还有测试版（beta）与开发版（dev）。"}]},{"type":"element","tag":"blockquote","props":{},"children":[{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Software Development Kit 简称为 SDK，指的就是软件开发工具包。"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://flutter.dev/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://flutter.dev"}]}]},{"type":"element","tag":"h3","props":{"id":"构建跨平台客户端应用"},"children":[{"type":"text","value":"构建跨平台客户端应用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Flutter 是个跨平台的应用框架，基于 Flutter 框架可以构建 iOS、Android、Web、Windows、macOS 与 Linux 平台上的应用。在宁皓独立开发者训练营中，我们专注于使用 Flutter 构建移动端应用，也就是可以在 iOS 或 Android 平台上运行的应用。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果你想构建 iOS 平台应用，需要一台使用 macOS 系统的 Mac 电脑（iMac、MacBook Pro ... ）。在 macOS 系统里，我们可以使用 Flutter 构建 macOS、iOS 与 Android 应用。在 Windows 系统里，我们无法使用 Flutter 构建 macOS 与 iOS 平台的应用，只能构建 Windows 或 Android 平台的应用。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果想在真实的 iOS 设备上测试开发的 iOS 平台的应用，除了需要一台 iPhone 手机，还需要付年费加入苹果的开发者计划。"}]},{"type":"element","tag":"h3","props":{"id":"macos安装-flutter-sdk"},"children":[{"type":"text","value":"macOS：安装 Flutter SDK"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"到 Flutter 的官方网站下载适合在 macOS 系统里使用 Flutter SDK，可以选择 Intel 或 Apple Silicon 类型的 Flutter SDK，在 macOS 系统的关于本机那里可以查看电脑用的是 Intel 还是 Apple Silicon。如果是 Intel，就下载 flutter_macos_3.x.x-stable.zip，如果是 Apple Silicon 就下载 flutter_macos_arm64_3.x.x-stable.zip。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"下载地址"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://docs.flutter.dev/get-started/install/macos","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://docs.flutter.dev/get-started/install/macos"}]}]},{"type":"element","tag":"h4","props":{"id":"apple-silicon"},"children":[{"type":"text","value":"Apple Silicon"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果在 Apple Silicon 架构的 Mac 电脑上安装 Flutter，需要安装 Rosetta translation environment，在终端执行："}]},{"type":"element","tag":"code","props":{"code":"`sudo softwareupdate --install-rosetta --agree-to-license`\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"`sudo softwareupdate --install-rosetta --agree-to-license`\n"}]}]}]},{"type":"element","tag":"h4","props":{"id":"安装-flutter-sdk"},"children":[{"type":"text","value":"安装 Flutter SDK"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"下载下来的是一个压缩包，解压以后把它放在系统的某个目录里面，比如 /Applications，在这个目录里会有很多应用程序。在终端，执行："}]},{"type":"element","tag":"code","props":{"code":"cd /Applications\nunzip ~/Downloads/flutter_macos_*.zip\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cd /Applications\nunzip ~/Downloads/flutter_macos_*.zip\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"进入到系统根目录下的 Applications 目录里面，然后用系统自带的 unzip 解压一下用户主目录下的 Downloads 目录里的 Flutter SDK 压缩包。注意在解压的时候我用了一个 * 号省略了文件名里的一些东西。你也可以具体设置一下这个要解压的压缩包的文件名。如果你用的是 Safari 浏览器下载的 Flutter SDK，完成以后很可能自动帮你解压了压缩包。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"不管怎么样，我们要做的就是下载 Flutter SDK ，把解压之后得到的 flutter 这个目录放到 /Applications 这个目录里面。"}]},{"type":"element","tag":"h4","props":{"id":"配置-path-环境变量"},"children":[{"type":"text","value":"配置 PATH 环境变量"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"为了可以在系统的任何地方直接执行 flutter 命令，我们需要将这个命令所在的目录添加到 PATH 这个环境变量里。"}]},{"type":"element","tag":"code","props":{"code":"code ~/.zprofile\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"code ~/.zprofile\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"用 VSCode 打开当前用户主目录下的 .zprofile 这个配置文件，然后在这个文件里添加一行代码："}]},{"type":"element","tag":"code","props":{"code":"export PATH=$PATH:/Applications/flutter/bin\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"export PATH=$PATH:/Applications/flutter/bin\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"上面这行配置的意思就是把 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"/Applications/flutter/bin"}]},{"type":"text","value":" 这个路径添加到 PATH 这个环境变量里。在终端执行命令的时候，系统会在环境变量目录里搜寻要执行的命令，找到匹配的命令就会执行它。保存一下这个配置文件，然后重启终端。"}]},{"type":"element","tag":"code","props":{"code":"flutter --help\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"flutter --help\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端，测试执行一下 flutter 命令，应该会显示这个命令的帮助信息，这样我们就算是在 macOS 系统上安装配置好了 Flutter SDK。"}]},{"type":"element","tag":"h3","props":{"id":"windows安装-flutter-sdk"},"children":[{"type":"text","value":"windows：安装 Flutter SDK"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 Flutter 官方网站下载最新的适合在 Windows 平台上使用的 Flutter SDK。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"下载地址"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://docs.flutter.dev/get-started/install/windows","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://docs.flutter.dev/get-started/install/windows"}]}]},{"type":"element","tag":"h4","props":{"id":"安装-flutter-sdk-1"},"children":[{"type":"text","value":"安装 Flutter SDK"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"解压下载下来的 Flutter SDK 压缩包，这样会得到一个 flutter 目录，把这个目录放在某个地方，比如 C 盘的根目录下面（ C:\\）。"}]},{"type":"element","tag":"h4","props":{"id":"配置-path-环境变量-1"},"children":[{"type":"text","value":"配置 PATH 环境变量"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"把 flutter 命令所在的目录的路径添加到 PATH 环境变量里，这样就可以在任何地方直接使用 flutter 命令了。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 Windows 系统里推荐大家用 Cmder 作为命令行界面，之前安装它的时候把它放在了 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"C:\\Program Files"}]},{"type":"text","value":" 这个目录里面，打开这个目录下面的 config/user_profile.sh 这个配置文件，然后添加一行代码："}]},{"type":"element","tag":"code","props":{"code":"export PATH=/c/flutter/bin:${PATH}\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"export PATH=/c/flutter/bin:${PATH}\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"上面这行的意思就是让 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"/c/flutter/bin"}]},{"type":"text","value":" 这个目录作为系统的环境变量目录，在终端执行命令的时候，系统会在环境变量目录里寻找要执行的命令。保存一下文件，重启一下终端（Cmder）。"}]},{"type":"element","tag":"code","props":{"code":"flutter --help\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"flutter --help\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开 Cmder 以后，执行上面这行命令，如果能看到一些帮助信息，说明我们已经在 Windows 系统上安装配置好了 Flutter。"}]},{"type":"element","tag":"h3","props":{"id":"配置使用国内镜像"},"children":[{"type":"text","value":"配置使用国内镜像"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"下载升级 Flutter SDK，安装管理项目需要的软件包的时候，默认会从 Flutter 官方提供的地址那里下载。我们也可以配置使用国内提供的镜像服务，这样速度会更快一些。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在系统里设置两个环境变量就可以了，可以编辑一下终端的配置文件，macOS 编辑的是 ~/.zprofile，Windows 用户编辑的是 cmder/config/user_profile.sh，在配置文件里添加下面这两行内容："}]},{"type":"element","tag":"code","props":{"code":"export PUB_HOSTED_URL=https://pub.flutter-io.cn\nexport FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"export PUB_HOSTED_URL=https://pub.flutter-io.cn\nexport FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果你发现镜像不管用了，可以换用上海交通大学提供的镜像地址："}]},{"type":"element","tag":"code","props":{"code":"export PUB_HOSTED_URL=https://mirrors.sjtug.sjtu.edu.cn\nexport FLUTTER_STORAGE_BASE_URL=https://dart-pub.mirrors.sjtug.sjtu.edu.cn\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"export PUB_HOSTED_URL=https://mirrors.sjtug.sjtu.edu.cn\nexport FLUTTER_STORAGE_BASE_URL=https://dart-pub.mirrors.sjtug.sjtu.edu.cn\n"}]}]}]},{"type":"element","tag":"h2","props":{"id":"ios-应用的开发环境"},"children":[{"type":"text","value":"iOS 应用的开发环境"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"安装了 Flutter SDK 以后，如果您用的是 macOS 系统，下面可以准备一下 iOS 应用的开发环境，如果您用的是 Windows 系统，可以跳过这一步，直接去准备 Android 应用的开发环境。"}]},{"type":"element","tag":"h3","props":{"id":"homebrew"},"children":[{"type":"text","value":"Homebrew"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Homebrew 是用在 macOS 系统里的一种软件包管理工具，使用它可以管理系统里的一些软件包，我们经常要用它去安装一些命令行工具。Homebrew 不是必须的工具，但它是一个很好的帮手。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 macOS 系统里安装 Homebrew ，一般就是执行一下 Homebrew 官方网站里提供的一行脚本，在 brew.sh 这网站你可以找到这行要执行的脚本，复制一下要执行的命令，然后打开终端，执行一下。安装完成以后，就可以在终端，使用 brew 这个命令管理系统的软件包了。"}]},{"type":"element","tag":"code","props":{"code":"/bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"/bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"\n"}]}]}]},{"type":"element","tag":"h3","props":{"id":"xcode"},"children":[{"type":"text","value":"Xcode"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Xcode 是开发 iOS 与 macOS 应用时必备的工具。在 App Store 里面，搜索并安装 Xcode，安装过程需要一段时间。完成以后，打开这个软件，可能会提示需要安装一些额外的组件，点击 Install，确定安装这些额外的组件。"}]},{"type":"element","tag":"code","props":{"code":"flutter doctor\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"flutter doctor\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端，执行上面这个命令可以检查 Flutter 的开发环境，先观察结果里出现的 Xcode，如果有下面这样的提示，说明已经准备好了 Xcode："}]},{"type":"element","tag":"code","props":{"code":"[✓] Xcode - develop for iOS and macOS\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"[✓] Xcode - develop for iOS and macOS\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"通常会存在一些问题，比如可能会提示 Xcode 安装的不完整，Cocoapods 版本太低等等。仔细阅读问题的说明，然后根据这些提示解决存在的问题。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"假设提示 Xcode installation is incomplete（Xcode 安装的不完整），可以分别执行下面这两个命令："}]},{"type":"element","tag":"code","props":{"code":"sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer\nsudo xcodebuild -runFirstLaunch\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer\nsudo xcodebuild -runFirstLaunch\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"另外还可能会提示我们 Cocoapods 版本太低了，它是 Swift 语言的包管理工具。要使用新版本的 Cocoapods，可以用 Homebrew 去安装一个，执行："}]},{"type":"element","tag":"code","props":{"code":"brew install cocoapods\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"brew install cocoapods\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"完成以后，再执行一下："}]},{"type":"element","tag":"code","props":{"code":"brew link --overwrite cocoapods\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"brew link --overwrite cocoapods\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"确定系统使用的 Cocoapods 的版本，可以执行："}]},{"type":"element","tag":"code","props":{"code":"pod --version\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"pod --version\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"升级了新版本的 Cocoapods 以后，再次执行 flutter doctor，观察 Xcode 是否已经准备好了，如果没有问题，会在 Xcode 前面出现一个 "},{"type":"element","tag":"span","props":{},"children":[{"type":"text","value":"✓"}]},{"type":"text","value":" 。准备好 Xcode 以后，就可以用 Flutter 开发 iOS 或者 macOS 平台的应用了。"}]},{"type":"element","tag":"h2","props":{"id":"android-平台应用开发环境"},"children":[{"type":"text","value":"Android 平台应用开发环境"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"用 Flutter 可以开发适用于 Android（安卓）平台的应用，也就是可以在安卓手机上安装使用的 App。在 Windows 与 macOS 系统上都可以基于 Flutter 开发 Android 应用，开发这种应用需要用到 Android Studio 提供的一些功能。"}]},{"type":"element","tag":"h3","props":{"id":"android-studio"},"children":[{"type":"text","value":"Android Studio"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"配置 Android Studio 时需要连接 Google 提供的服务，在国内连接这些服务会遇到网络问题，我们需要提前准备好可以访问到 Google 服务的网络代理服务，然后通过配置，让 Android Studio 可以通过这个网络代理访问到 Google 提供的服务。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://developer.android.com/studio","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://developer.android.com/studio"}]}]},{"type":"element","tag":"h4","props":{"id":"安装-android-studio"},"children":[{"type":"text","value":"安装 Android Studio"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"访问 Android 开发者网站下载适用于特定操作系统的 Android Studio，安装后打开它。"}]},{"type":"element","tag":"h4","props":{"id":"配置-android-studio"},"children":[{"type":"text","value":"配置 Android Studio"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"第一次打开 Android Studio 需要做一些配置，这些配置在以后也是可以修改的。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"下载 Android SDK 的时候，在国内可能会遇到网络问题，会提示 Unable to access Android SDK add-on list（无法访问 Android SDK 扩展列表），通过配置使用网络代理可以解决这个问题。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/mobile/image.png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"点击 Setup Proxy 配置代理服务，勾选 Manual proxy configuration（手动代理配置），然后输入代理服务的 Host name（主机名）还有 Port number（端口号）。无法直接通过网络访问 Android SDK 列表时，就需要通过代理服务来访问这个列表，注意这个代理服务需要我们自己想办法准备好。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/mobile/image(1).png"},"children":[]},{"type":"text","value":"\n配置好以后，点击 OK，如果一切正常，能够访问到扩展列表， Android Studio 就会启动配置，一路回车基本就可以完成这些配置了，配置完成以后 Android Studio 会去下载 SDK 还有相关工具。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/mobile/image(2).png"},"children":[]}]},{"type":"element","tag":"h4","props":{"id":"验证-android-工具链"},"children":[{"type":"text","value":"验证 Android 工具链"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"安装配置好 Android Studio 以后，基本上就已经准备好了基于 Flutter 开发 Android 平台应用需要的工具。在终端，执行："}]},{"type":"element","tag":"code","props":{"code":"flutter doctor\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"flutter doctor\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"观察 Android toolchain 出现的问题，很可能会提示 Android licenses not accepted，意思是有些条款还得我们确认同意一下。执行："}]},{"type":"element","tag":"code","props":{"code":"flutter doctor --android-licenses\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"flutter doctor --android-licenses\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这个命令会提示很多次确认同意条款，输入 y 按下回车表示确定。完成以后，再次执行 flutter doctor，准备好 Android 工具链以后，会在 Android toolchain 前面出现一个 "},{"type":"element","tag":"span","props":{},"children":[{"type":"text","value":"✓"}]},{"type":"text","value":" ，像下面这样："}]},{"type":"element","tag":"code","props":{"code":"[✓] Android toolchain - develop for Android devices ...\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"[✓] Android toolchain - develop for Android devices ...\n"}]}]}]},{"type":"element","tag":"h2","props":{"id":"设备模拟器"},"children":[{"type":"text","value":"设备模拟器"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"我们可以在真实的 iOS 与 Android 设备上运行基于 Flutter 开发的应用，也可以在这些平台提供的模拟器上运行调试开发的应用。安装了 Xcode 以后，就可以直接使用 iOS 设备的模拟器了。通过 Android Studio 可以创建一些 Android 模拟器。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端，查看可用的设备模拟器，可以执行："}]},{"type":"element","tag":"code","props":{"code":"flutter emulators\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"flutter emulators\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"正常的话，列出的内容像下面这样："}]},{"type":"element","tag":"code","props":{"code":"2 available emulators:\n\napple_ios_simulator • iOS Simulator   • Apple  • ios\nPixel_XL_API_28     • Pixel XL API 28 • Google • android\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"2 available emulators:\n\napple_ios_simulator • iOS Simulator   • Apple  • ios\nPixel_XL_API_28     • Pixel XL API 28 • Google • android\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"上面提示我的系统里要有两台模拟器，一个是 iOS 平台的模拟器，模拟器 ID 是 apple_ios_simulator，还有一台是 Android 平台的模拟器，模拟器 ID 是 Pixel_XL_API_28。 在开发 Flutter 应用的时候，可以选择在这些模拟器上运行调试应用。"}]},{"type":"element","tag":"h3","props":{"id":"android-模拟器"},"children":[{"type":"text","value":"Android 模拟器"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开 Android Studio，在启动界面上，点击右下角的 Configure，选择 AVD Manager，可以管理 Android 模拟器。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/mobile/image(3).png"},"children":[]}]},{"type":"element","tag":"blockquote","props":{},"children":[{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"AVD：Android Virtual Device 安卓虚拟设备，也就是我们说的模拟器。"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 AVD Manager 这里可以添加或编辑模拟器，新建一个 Android 模拟器，名字可以设置成 flutter。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/mobile/image(4).png"},"children":[]},{"type":"text","value":"\n创建了 Android 模拟器以后 ，在终端，执行一下 flutter emulators，应该就会列出刚才新创建的 Android 模拟器。"}]},{"type":"element","tag":"h2","props":{"id":"web-应用开发环境"},"children":[{"type":"text","value":"Web 应用开发环境"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"用 Flutter 可以开发 Web 应用，也就是可以在浏览器上运行的应用。"}]},{"type":"element","tag":"h3","props":{"id":"准备-chrome-浏览器"},"children":[{"type":"text","value":"准备 Chrome 浏览器"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"安装好 Chrome 浏览器以后，执行 flutter doctor，一直正常的话会在 Chrome 前面显示 "},{"type":"element","tag":"span","props":{},"children":[{"type":"text","value":"✓"}]},{"type":"text","value":" ，这样我们就可以基于 Flutter 开发 Web 应用了。"}]},{"type":"element","tag":"code","props":{"code":"[✓] Chrome - develop for the web\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"[✓] Chrome - develop for the web\n"}]}]}]},{"type":"element","tag":"h2","props":{"id":"代码编辑器"},"children":[{"type":"text","value":"代码编辑器"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"我们会使用 VSCode 编辑器编写 Flutter 应用的代码，这需要给编辑器安装一个 flutter 扩展。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/mobile/image(5).png"},"children":[]},{"type":"text","value":"\n装好 Flutter 扩展以后，打开编辑器的命令面板，搜索 Flutter，你会看到一些相关的命令。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/mobile/image(6).png"},"children":[]}]},{"type":"element","tag":"h2","props":{"id":"创建-flutter-项目"},"children":[{"type":"text","value":"创建 Flutter 项目"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 Flutter SDK 提供的 flutter 命令可以创建一个 Flutter 项目。"}]},{"type":"element","tag":"code","props":{"code":"cd ~/desktop\nflutter create xb2_flutter\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cd ~/desktop\nflutter create xb2_flutter\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这两行命令，会在桌面上创建一个全新的 Flutter 项目，用 VSCode 编辑器打开这个项目。"}]},{"type":"element","tag":"h2","props":{"id":"运行与调试-flutter-应用"},"children":[{"type":"text","value":"运行与调试 Flutter 应用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开项目里的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"src/main.dart"}]},{"type":"text","value":" 这个文件，这是应用的入口文件。"}]},{"type":"element","tag":"h3","props":{"id":"选择设备"},"children":[{"type":"text","value":"选择设备"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"点击编辑器底部右下角出现的设备名称（比如 Chrome），这会打开一个设备列表，可以先选择使用 Chrome，这样运行编辑器的调试以后，会在 Chrome 浏览器上打开正在开发的 Flutter 应用。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/mobile/image(7).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 VSCode 编辑器中选择要使用的设备"}]}]},{"type":"element","tag":"h3","props":{"id":"运行与调试"},"children":[{"type":"text","value":"运行与调试"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开 VSCode 编辑器的调试功能，点击 Run and Debug。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/mobile/image(8).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"VSCode 编辑器的调试功能"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"成功以后会在 Chrome 浏览器上打开正在 Flutter 应用。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/mobile/image(9).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 Chrome 浏览器运行的 Flutter 应用"}]}]},{"type":"element","tag":"h3","props":{"id":"测试编辑"},"children":[{"type":"text","value":"测试编辑"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开 src/main.dart ，修改一下 MyHomePage 小部件的 title 参数的值，改成 "},{"type":"element","tag":"a","props":{"href":"http://ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"ninghao.net"}]},{"type":"text","value":" 。"}]},{"type":"element","tag":"code","props":{"code":"class MyApp extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      ...\n      home: MyHomePage(title: '**ninghao.net**'),\n    );\n  }\n}\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"class MyApp extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      ...\n      home: MyHomePage(title: '**ninghao.net**'),\n    );\n  }\n}\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"保存一下文件，观察一下在 Chrome 浏览器上运行的应用界面，你会发现界面头部显示的标题会变成 "},{"type":"element","tag":"a","props":{"href":"http://ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"ninghao.net"}]},{"type":"text","value":" 。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/mobile/image(10).png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"在模拟器上运行调试应用"},"children":[{"type":"text","value":"在模拟器上运行调试应用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开编辑器的调试功能，停止运行调试。然后重新选择选择一个设备，这次选择一个模拟器设备，然后再运行调试，这样会在选择的这台模拟器上运行正在开发的 Flutter 应用。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"编辑项目文件，保存文件以后可以实时的看到界面的变化。不过有时也需要重启一下调试，可以打开编辑器的调试（Run and Debug），然后点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"重启"}]},{"type":"text","value":" 按钮。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/mobile/image(11).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 iOS 设备模拟器中运行的 Flutter 应用"}]}]}]},"body":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"移动端iosandroid应用开发环境"},"children":[{"type":"text","value":"移动端（iOS、Android）应用开发环境"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Flutter 是在宁皓独立开发者训练营中要使用的客户端应用框架，在训练营中我们会基于 Flutter 框架构建移动端应用。在本地电脑安装好 Flutter SDK，准备好 iOS 与 Android 应用的开发环境，就可以开启本次训练营的移动应用开发之旅了。"}]},{"type":"element","tag":"h2","props":{"id":"flutter"},"children":[{"type":"text","value":"Flutter"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 Flutter 的官方网站下载合适的 Flutter SDK。下载下来的东西通常就是一个 .zip 格式的压缩包，把解压之后得到的 flutter 目录放在系统里的某个地方，再配置一下系统的 PATH 环境变量， 在任何地方能都执行 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"flutter"}]},{"type":"text","value":" 这个命令，就算是安装配置好了 Flutter。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"下载的时候要选择适合在自己电脑操作系统上运行的版本，比如 Windows，macOS 或者 Linux 版本的 Flutter。下载以后得到的压缩包文件可能像这样："},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"flutter_macos_3.0.0-stable.zip"}]},{"type":"text","value":"，文件名里的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"macos"}]},{"type":"text","value":" 指的是操作系统，3*.0.0* 是 Flutter SDK 的版本号，"},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"stable"}]},{"type":"text","value":" 表示这是一个稳定版本的 Flutter，除了稳定版，还有测试版（beta）与开发版（dev）。"}]},{"type":"element","tag":"blockquote","props":{},"children":[{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Software Development Kit 简称为 SDK，指的就是软件开发工具包。"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://flutter.dev/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://flutter.dev"}]}]},{"type":"element","tag":"h3","props":{"id":"构建跨平台客户端应用"},"children":[{"type":"text","value":"构建跨平台客户端应用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Flutter 是个跨平台的应用框架，基于 Flutter 框架可以构建 iOS、Android、Web、Windows、macOS 与 Linux 平台上的应用。在宁皓独立开发者训练营中，我们专注于使用 Flutter 构建移动端应用，也就是可以在 iOS 或 Android 平台上运行的应用。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果你想构建 iOS 平台应用，需要一台使用 macOS 系统的 Mac 电脑（iMac、MacBook Pro ... ）。在 macOS 系统里，我们可以使用 Flutter 构建 macOS、iOS 与 Android 应用。在 Windows 系统里，我们无法使用 Flutter 构建 macOS 与 iOS 平台的应用，只能构建 Windows 或 Android 平台的应用。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果想在真实的 iOS 设备上测试开发的 iOS 平台的应用，除了需要一台 iPhone 手机，还需要付年费加入苹果的开发者计划。"}]},{"type":"element","tag":"h3","props":{"id":"macos安装-flutter-sdk"},"children":[{"type":"text","value":"macOS：安装 Flutter SDK"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"到 Flutter 的官方网站下载适合在 macOS 系统里使用 Flutter SDK，可以选择 Intel 或 Apple Silicon 类型的 Flutter SDK，在 macOS 系统的关于本机那里可以查看电脑用的是 Intel 还是 Apple Silicon。如果是 Intel，就下载 flutter_macos_3.x.x-stable.zip，如果是 Apple Silicon 就下载 flutter_macos_arm64_3.x.x-stable.zip。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"下载地址"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://docs.flutter.dev/get-started/install/macos","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://docs.flutter.dev/get-started/install/macos"}]}]},{"type":"element","tag":"h4","props":{"id":"apple-silicon"},"children":[{"type":"text","value":"Apple Silicon"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果在 Apple Silicon 架构的 Mac 电脑上安装 Flutter，需要安装 Rosetta translation environment，在终端执行："}]},{"type":"element","tag":"code","props":{"code":"`sudo softwareupdate --install-rosetta --agree-to-license`\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"`sudo softwareupdate --install-rosetta --agree-to-license`\n"}]}]}]},{"type":"element","tag":"h4","props":{"id":"安装-flutter-sdk"},"children":[{"type":"text","value":"安装 Flutter SDK"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"下载下来的是一个压缩包，解压以后把它放在系统的某个目录里面，比如 /Applications，在这个目录里会有很多应用程序。在终端，执行："}]},{"type":"element","tag":"code","props":{"code":"cd /Applications\nunzip ~/Downloads/flutter_macos_*.zip\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cd /Applications\nunzip ~/Downloads/flutter_macos_*.zip\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"进入到系统根目录下的 Applications 目录里面，然后用系统自带的 unzip 解压一下用户主目录下的 Downloads 目录里的 Flutter SDK 压缩包。注意在解压的时候我用了一个 * 号省略了文件名里的一些东西。你也可以具体设置一下这个要解压的压缩包的文件名。如果你用的是 Safari 浏览器下载的 Flutter SDK，完成以后很可能自动帮你解压了压缩包。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"不管怎么样，我们要做的就是下载 Flutter SDK ，把解压之后得到的 flutter 这个目录放到 /Applications 这个目录里面。"}]},{"type":"element","tag":"h4","props":{"id":"配置-path-环境变量"},"children":[{"type":"text","value":"配置 PATH 环境变量"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"为了可以在系统的任何地方直接执行 flutter 命令，我们需要将这个命令所在的目录添加到 PATH 这个环境变量里。"}]},{"type":"element","tag":"code","props":{"code":"code ~/.zprofile\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"code ~/.zprofile\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"用 VSCode 打开当前用户主目录下的 .zprofile 这个配置文件，然后在这个文件里添加一行代码："}]},{"type":"element","tag":"code","props":{"code":"export PATH=$PATH:/Applications/flutter/bin\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"export PATH=$PATH:/Applications/flutter/bin\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"上面这行配置的意思就是把 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"/Applications/flutter/bin"}]},{"type":"text","value":" 这个路径添加到 PATH 这个环境变量里。在终端执行命令的时候，系统会在环境变量目录里搜寻要执行的命令，找到匹配的命令就会执行它。保存一下这个配置文件，然后重启终端。"}]},{"type":"element","tag":"code","props":{"code":"flutter --help\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"flutter --help\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端，测试执行一下 flutter 命令，应该会显示这个命令的帮助信息，这样我们就算是在 macOS 系统上安装配置好了 Flutter SDK。"}]},{"type":"element","tag":"h3","props":{"id":"windows安装-flutter-sdk"},"children":[{"type":"text","value":"windows：安装 Flutter SDK"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 Flutter 官方网站下载最新的适合在 Windows 平台上使用的 Flutter SDK。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"下载地址"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://docs.flutter.dev/get-started/install/windows","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://docs.flutter.dev/get-started/install/windows"}]}]},{"type":"element","tag":"h4","props":{"id":"安装-flutter-sdk-1"},"children":[{"type":"text","value":"安装 Flutter SDK"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"解压下载下来的 Flutter SDK 压缩包，这样会得到一个 flutter 目录，把这个目录放在某个地方，比如 C 盘的根目录下面（ C:\\）。"}]},{"type":"element","tag":"h4","props":{"id":"配置-path-环境变量-1"},"children":[{"type":"text","value":"配置 PATH 环境变量"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"把 flutter 命令所在的目录的路径添加到 PATH 环境变量里，这样就可以在任何地方直接使用 flutter 命令了。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 Windows 系统里推荐大家用 Cmder 作为命令行界面，之前安装它的时候把它放在了 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"C:\\Program Files"}]},{"type":"text","value":" 这个目录里面，打开这个目录下面的 config/user_profile.sh 这个配置文件，然后添加一行代码："}]},{"type":"element","tag":"code","props":{"code":"export PATH=/c/flutter/bin:${PATH}\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"export PATH=/c/flutter/bin:${PATH}\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"上面这行的意思就是让 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"/c/flutter/bin"}]},{"type":"text","value":" 这个目录作为系统的环境变量目录，在终端执行命令的时候，系统会在环境变量目录里寻找要执行的命令。保存一下文件，重启一下终端（Cmder）。"}]},{"type":"element","tag":"code","props":{"code":"flutter --help\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"flutter --help\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开 Cmder 以后，执行上面这行命令，如果能看到一些帮助信息，说明我们已经在 Windows 系统上安装配置好了 Flutter。"}]},{"type":"element","tag":"h3","props":{"id":"配置使用国内镜像"},"children":[{"type":"text","value":"配置使用国内镜像"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"下载升级 Flutter SDK，安装管理项目需要的软件包的时候，默认会从 Flutter 官方提供的地址那里下载。我们也可以配置使用国内提供的镜像服务，这样速度会更快一些。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在系统里设置两个环境变量就可以了，可以编辑一下终端的配置文件，macOS 编辑的是 ~/.zprofile，Windows 用户编辑的是 cmder/config/user_profile.sh，在配置文件里添加下面这两行内容："}]},{"type":"element","tag":"code","props":{"code":"export PUB_HOSTED_URL=https://pub.flutter-io.cn\nexport FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"export PUB_HOSTED_URL=https://pub.flutter-io.cn\nexport FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果你发现镜像不管用了，可以换用上海交通大学提供的镜像地址："}]},{"type":"element","tag":"code","props":{"code":"export PUB_HOSTED_URL=https://mirrors.sjtug.sjtu.edu.cn\nexport FLUTTER_STORAGE_BASE_URL=https://dart-pub.mirrors.sjtug.sjtu.edu.cn\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"export PUB_HOSTED_URL=https://mirrors.sjtug.sjtu.edu.cn\nexport FLUTTER_STORAGE_BASE_URL=https://dart-pub.mirrors.sjtug.sjtu.edu.cn\n"}]}]}]},{"type":"element","tag":"h2","props":{"id":"ios-应用的开发环境"},"children":[{"type":"text","value":"iOS 应用的开发环境"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"安装了 Flutter SDK 以后，如果您用的是 macOS 系统，下面可以准备一下 iOS 应用的开发环境，如果您用的是 Windows 系统，可以跳过这一步，直接去准备 Android 应用的开发环境。"}]},{"type":"element","tag":"h3","props":{"id":"homebrew"},"children":[{"type":"text","value":"Homebrew"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Homebrew 是用在 macOS 系统里的一种软件包管理工具，使用它可以管理系统里的一些软件包，我们经常要用它去安装一些命令行工具。Homebrew 不是必须的工具，但它是一个很好的帮手。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 macOS 系统里安装 Homebrew ，一般就是执行一下 Homebrew 官方网站里提供的一行脚本，在 brew.sh 这网站你可以找到这行要执行的脚本，复制一下要执行的命令，然后打开终端，执行一下。安装完成以后，就可以在终端，使用 brew 这个命令管理系统的软件包了。"}]},{"type":"element","tag":"code","props":{"code":"/bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"/bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"\n"}]}]}]},{"type":"element","tag":"h3","props":{"id":"xcode"},"children":[{"type":"text","value":"Xcode"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Xcode 是开发 iOS 与 macOS 应用时必备的工具。在 App Store 里面，搜索并安装 Xcode，安装过程需要一段时间。完成以后，打开这个软件，可能会提示需要安装一些额外的组件，点击 Install，确定安装这些额外的组件。"}]},{"type":"element","tag":"code","props":{"code":"flutter doctor\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"flutter doctor\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端，执行上面这个命令可以检查 Flutter 的开发环境，先观察结果里出现的 Xcode，如果有下面这样的提示，说明已经准备好了 Xcode："}]},{"type":"element","tag":"code","props":{"code":"[✓] Xcode - develop for iOS and macOS\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"[✓] Xcode - develop for iOS and macOS\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"通常会存在一些问题，比如可能会提示 Xcode 安装的不完整，Cocoapods 版本太低等等。仔细阅读问题的说明，然后根据这些提示解决存在的问题。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"假设提示 Xcode installation is incomplete（Xcode 安装的不完整），可以分别执行下面这两个命令："}]},{"type":"element","tag":"code","props":{"code":"sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer\nsudo xcodebuild -runFirstLaunch\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer\nsudo xcodebuild -runFirstLaunch\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"另外还可能会提示我们 Cocoapods 版本太低了，它是 Swift 语言的包管理工具。要使用新版本的 Cocoapods，可以用 Homebrew 去安装一个，执行："}]},{"type":"element","tag":"code","props":{"code":"brew install cocoapods\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"brew install cocoapods\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"完成以后，再执行一下："}]},{"type":"element","tag":"code","props":{"code":"brew link --overwrite cocoapods\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"brew link --overwrite cocoapods\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"确定系统使用的 Cocoapods 的版本，可以执行："}]},{"type":"element","tag":"code","props":{"code":"pod --version\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"pod --version\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"升级了新版本的 Cocoapods 以后，再次执行 flutter doctor，观察 Xcode 是否已经准备好了，如果没有问题，会在 Xcode 前面出现一个 "},{"type":"element","tag":"span","props":{},"children":[{"type":"text","value":"✓"}]},{"type":"text","value":" 。准备好 Xcode 以后，就可以用 Flutter 开发 iOS 或者 macOS 平台的应用了。"}]},{"type":"element","tag":"h2","props":{"id":"android-平台应用开发环境"},"children":[{"type":"text","value":"Android 平台应用开发环境"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"用 Flutter 可以开发适用于 Android（安卓）平台的应用，也就是可以在安卓手机上安装使用的 App。在 Windows 与 macOS 系统上都可以基于 Flutter 开发 Android 应用，开发这种应用需要用到 Android Studio 提供的一些功能。"}]},{"type":"element","tag":"h3","props":{"id":"android-studio"},"children":[{"type":"text","value":"Android Studio"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"配置 Android Studio 时需要连接 Google 提供的服务，在国内连接这些服务会遇到网络问题，我们需要提前准备好可以访问到 Google 服务的网络代理服务，然后通过配置，让 Android Studio 可以通过这个网络代理访问到 Google 提供的服务。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://developer.android.com/studio","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://developer.android.com/studio"}]}]},{"type":"element","tag":"h4","props":{"id":"安装-android-studio"},"children":[{"type":"text","value":"安装 Android Studio"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"访问 Android 开发者网站下载适用于特定操作系统的 Android Studio，安装后打开它。"}]},{"type":"element","tag":"h4","props":{"id":"配置-android-studio"},"children":[{"type":"text","value":"配置 Android Studio"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"第一次打开 Android Studio 需要做一些配置，这些配置在以后也是可以修改的。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"下载 Android SDK 的时候，在国内可能会遇到网络问题，会提示 Unable to access Android SDK add-on list（无法访问 Android SDK 扩展列表），通过配置使用网络代理可以解决这个问题。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/mobile/image.png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"点击 Setup Proxy 配置代理服务，勾选 Manual proxy configuration（手动代理配置），然后输入代理服务的 Host name（主机名）还有 Port number（端口号）。无法直接通过网络访问 Android SDK 列表时，就需要通过代理服务来访问这个列表，注意这个代理服务需要我们自己想办法准备好。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/mobile/image(1).png"},"children":[]},{"type":"text","value":"\n配置好以后，点击 OK，如果一切正常，能够访问到扩展列表， Android Studio 就会启动配置，一路回车基本就可以完成这些配置了，配置完成以后 Android Studio 会去下载 SDK 还有相关工具。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/mobile/image(2).png"},"children":[]}]},{"type":"element","tag":"h4","props":{"id":"验证-android-工具链"},"children":[{"type":"text","value":"验证 Android 工具链"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"安装配置好 Android Studio 以后，基本上就已经准备好了基于 Flutter 开发 Android 平台应用需要的工具。在终端，执行："}]},{"type":"element","tag":"code","props":{"code":"flutter doctor\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"flutter doctor\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"观察 Android toolchain 出现的问题，很可能会提示 Android licenses not accepted，意思是有些条款还得我们确认同意一下。执行："}]},{"type":"element","tag":"code","props":{"code":"flutter doctor --android-licenses\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"flutter doctor --android-licenses\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这个命令会提示很多次确认同意条款，输入 y 按下回车表示确定。完成以后，再次执行 flutter doctor，准备好 Android 工具链以后，会在 Android toolchain 前面出现一个 "},{"type":"element","tag":"span","props":{},"children":[{"type":"text","value":"✓"}]},{"type":"text","value":" ，像下面这样："}]},{"type":"element","tag":"code","props":{"code":"[✓] Android toolchain - develop for Android devices ...\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"[✓] Android toolchain - develop for Android devices ...\n"}]}]}]},{"type":"element","tag":"h2","props":{"id":"设备模拟器"},"children":[{"type":"text","value":"设备模拟器"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"我们可以在真实的 iOS 与 Android 设备上运行基于 Flutter 开发的应用，也可以在这些平台提供的模拟器上运行调试开发的应用。安装了 Xcode 以后，就可以直接使用 iOS 设备的模拟器了。通过 Android Studio 可以创建一些 Android 模拟器。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在终端，查看可用的设备模拟器，可以执行："}]},{"type":"element","tag":"code","props":{"code":"flutter emulators\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"flutter emulators\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"正常的话，列出的内容像下面这样："}]},{"type":"element","tag":"code","props":{"code":"2 available emulators:\n\napple_ios_simulator • iOS Simulator   • Apple  • ios\nPixel_XL_API_28     • Pixel XL API 28 • Google • android\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"2 available emulators:\n\napple_ios_simulator • iOS Simulator   • Apple  • ios\nPixel_XL_API_28     • Pixel XL API 28 • Google • android\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"上面提示我的系统里要有两台模拟器，一个是 iOS 平台的模拟器，模拟器 ID 是 apple_ios_simulator，还有一台是 Android 平台的模拟器，模拟器 ID 是 Pixel_XL_API_28。 在开发 Flutter 应用的时候，可以选择在这些模拟器上运行调试应用。"}]},{"type":"element","tag":"h3","props":{"id":"android-模拟器"},"children":[{"type":"text","value":"Android 模拟器"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开 Android Studio，在启动界面上，点击右下角的 Configure，选择 AVD Manager，可以管理 Android 模拟器。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/mobile/image(3).png"},"children":[]}]},{"type":"element","tag":"blockquote","props":{},"children":[{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"AVD：Android Virtual Device 安卓虚拟设备，也就是我们说的模拟器。"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 AVD Manager 这里可以添加或编辑模拟器，新建一个 Android 模拟器，名字可以设置成 flutter。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/mobile/image(4).png"},"children":[]},{"type":"text","value":"\n创建了 Android 模拟器以后 ，在终端，执行一下 flutter emulators，应该就会列出刚才新创建的 Android 模拟器。"}]},{"type":"element","tag":"h2","props":{"id":"web-应用开发环境"},"children":[{"type":"text","value":"Web 应用开发环境"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"用 Flutter 可以开发 Web 应用，也就是可以在浏览器上运行的应用。"}]},{"type":"element","tag":"h3","props":{"id":"准备-chrome-浏览器"},"children":[{"type":"text","value":"准备 Chrome 浏览器"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"安装好 Chrome 浏览器以后，执行 flutter doctor，一直正常的话会在 Chrome 前面显示 "},{"type":"element","tag":"span","props":{},"children":[{"type":"text","value":"✓"}]},{"type":"text","value":" ，这样我们就可以基于 Flutter 开发 Web 应用了。"}]},{"type":"element","tag":"code","props":{"code":"[✓] Chrome - develop for the web\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"[✓] Chrome - develop for the web\n"}]}]}]},{"type":"element","tag":"h2","props":{"id":"代码编辑器"},"children":[{"type":"text","value":"代码编辑器"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"我们会使用 VSCode 编辑器编写 Flutter 应用的代码，这需要给编辑器安装一个 flutter 扩展。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/mobile/image(5).png"},"children":[]},{"type":"text","value":"\n装好 Flutter 扩展以后，打开编辑器的命令面板，搜索 Flutter，你会看到一些相关的命令。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/mobile/image(6).png"},"children":[]}]},{"type":"element","tag":"h2","props":{"id":"创建-flutter-项目"},"children":[{"type":"text","value":"创建 Flutter 项目"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 Flutter SDK 提供的 flutter 命令可以创建一个 Flutter 项目。"}]},{"type":"element","tag":"code","props":{"code":"cd ~/desktop\nflutter create xb2_flutter\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"cd ~/desktop\nflutter create xb2_flutter\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面这两行命令，会在桌面上创建一个全新的 Flutter 项目，用 VSCode 编辑器打开这个项目。"}]},{"type":"element","tag":"h2","props":{"id":"运行与调试-flutter-应用"},"children":[{"type":"text","value":"运行与调试 Flutter 应用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开项目里的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"src/main.dart"}]},{"type":"text","value":" 这个文件，这是应用的入口文件。"}]},{"type":"element","tag":"h3","props":{"id":"选择设备"},"children":[{"type":"text","value":"选择设备"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"点击编辑器底部右下角出现的设备名称（比如 Chrome），这会打开一个设备列表，可以先选择使用 Chrome，这样运行编辑器的调试以后，会在 Chrome 浏览器上打开正在开发的 Flutter 应用。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/mobile/image(7).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 VSCode 编辑器中选择要使用的设备"}]}]},{"type":"element","tag":"h3","props":{"id":"运行与调试"},"children":[{"type":"text","value":"运行与调试"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开 VSCode 编辑器的调试功能，点击 Run and Debug。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/mobile/image(8).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"VSCode 编辑器的调试功能"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"成功以后会在 Chrome 浏览器上打开正在 Flutter 应用。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/mobile/image(9).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 Chrome 浏览器运行的 Flutter 应用"}]}]},{"type":"element","tag":"h3","props":{"id":"测试编辑"},"children":[{"type":"text","value":"测试编辑"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开 src/main.dart ，修改一下 MyHomePage 小部件的 title 参数的值，改成 "},{"type":"element","tag":"a","props":{"href":"http://ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"ninghao.net"}]},{"type":"text","value":" 。"}]},{"type":"element","tag":"code","props":{"code":"class MyApp extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      ...\n      home: MyHomePage(title: '**ninghao.net**'),\n    );\n  }\n}\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"class MyApp extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      ...\n      home: MyHomePage(title: '**ninghao.net**'),\n    );\n  }\n}\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"保存一下文件，观察一下在 Chrome 浏览器上运行的应用界面，你会发现界面头部显示的标题会变成 "},{"type":"element","tag":"a","props":{"href":"http://ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"ninghao.net"}]},{"type":"text","value":" 。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/mobile/image(10).png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"在模拟器上运行调试应用"},"children":[{"type":"text","value":"在模拟器上运行调试应用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开编辑器的调试功能，停止运行调试。然后重新选择选择一个设备，这次选择一个模拟器设备，然后再运行调试，这样会在选择的这台模拟器上运行正在开发的 Flutter 应用。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"编辑项目文件，保存文件以后可以实时的看到界面的变化。不过有时也需要重启一下调试，可以打开编辑器的调试（Run and Debug），然后点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"重启"}]},{"type":"text","value":" 按钮。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/env/mobile/image(11).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在 iOS 设备模拟器中运行的 Flutter 应用"}]}]}],"toc":{"title":"","searchDepth":2,"depth":2,"links":[{"id":"flutter","depth":2,"text":"Flutter","children":[{"id":"构建跨平台客户端应用","depth":3,"text":"构建跨平台客户端应用"},{"id":"macos安装-flutter-sdk","depth":3,"text":"macOS：安装 Flutter SDK"},{"id":"windows安装-flutter-sdk","depth":3,"text":"windows：安装 Flutter SDK"},{"id":"配置使用国内镜像","depth":3,"text":"配置使用国内镜像"}]},{"id":"ios-应用的开发环境","depth":2,"text":"iOS 应用的开发环境","children":[{"id":"homebrew","depth":3,"text":"Homebrew"},{"id":"xcode","depth":3,"text":"Xcode"}]},{"id":"android-平台应用开发环境","depth":2,"text":"Android 平台应用开发环境","children":[{"id":"android-studio","depth":3,"text":"Android Studio"}]},{"id":"设备模拟器","depth":2,"text":"设备模拟器","children":[{"id":"android-模拟器","depth":3,"text":"Android 模拟器"}]},{"id":"web-应用开发环境","depth":2,"text":"Web 应用开发环境","children":[{"id":"准备-chrome-浏览器","depth":3,"text":"准备 Chrome 浏览器"}]},{"id":"代码编辑器","depth":2,"text":"代码编辑器"},{"id":"创建-flutter-项目","depth":2,"text":"创建 Flutter 项目"},{"id":"运行与调试-flutter-应用","depth":2,"text":"运行与调试 Flutter 应用","children":[{"id":"选择设备","depth":3,"text":"选择设备"},{"id":"运行与调试","depth":3,"text":"运行与调试"},{"id":"测试编辑","depth":3,"text":"测试编辑"},{"id":"在模拟器上运行调试应用","depth":3,"text":"在模拟器上运行调试应用"}]}]}},"_type":"markdown","_id":"content:docs:2.env:3.mobile.md","_source":"content","_file":"docs/2.env/3.mobile.md","_extension":"md"},{"_path":"/docs/3rd","_draft":false,"_partial":false,"_empty":false,"title":"三方服务","description":"在宁皓独立开发者训练营中，我们会用到一些三方服务，比如商户版的支付宝与微信支付，还需要微信公众平台提供的网站应用服务。前期并不需要这些服务，在中后期的训练内容中才会用到，如果你希望在开发的应用中集成这些三方服务，可以提前在这些服务的平台申请开通这些服务。","excerpt":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"三方服务"},"children":[{"type":"text","value":"三方服务"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在宁皓独立开发者训练营中，我们会用到一些三方服务，比如商户版的支付宝与微信支付，还需要微信公众平台提供的网站应用服务。前期并不需要这些服务，在中后期的训练内容中才会用到，如果你希望在开发的应用中集成这些三方服务，可以提前在这些服务的平台申请开通这些服务。"}]}]},"navigation":false,"body":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"三方服务"},"children":[{"type":"text","value":"三方服务"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在宁皓独立开发者训练营中，我们会用到一些三方服务，比如商户版的支付宝与微信支付，还需要微信公众平台提供的网站应用服务。前期并不需要这些服务，在中后期的训练内容中才会用到，如果你希望在开发的应用中集成这些三方服务，可以提前在这些服务的平台申请开通这些服务。"}]}],"toc":{"title":"","searchDepth":2,"depth":2,"links":[]}},"_type":"markdown","_id":"content:docs:3.3rd:0.index.md","_source":"content","_file":"docs/3.3rd/0.index.md","_extension":"md"},{"_path":"/docs/3rd/prepare","_draft":false,"_partial":false,"_empty":false,"title":"公司、服务器、域名","description":"申请商户版支付宝与微信支付时需要公司资质、对公账户与备过案的域名。","excerpt":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"公司服务器域名"},"children":[{"type":"text","value":"公司、服务器、域名"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"申请商户版支付宝与微信支付时需要公司资质、对公账户与备过案的域名。"}]},{"type":"element","tag":"h2","props":{"id":"注册公司"},"children":[{"type":"text","value":"注册公司"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在开发的应用中需要用到商户版的支付宝与微信支付，申请开通这些服务时可能会要求我们提供公司资质与对公账户，具体的要求需要仔细阅读它们的相关文档，或者直接咨询人工客服。如果需要提供公司资质，我们需要在当地的工商部门申请注册公司。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"申请注册公司之前，要提前准备好公司名称还有营业范围，公司名称要多准备几个，因为新注册的公司不能与已有公司的名称相同或读音相同或相近，所以重名的机率还是挺大的。"}]},{"type":"element","tag":"h3","props":{"id":"费用"},"children":[{"type":"text","value":"费用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"注册公司的成本在几百元左右，包含工本费、手续费与制作公章的费用，具体需要的费用因地而异，总之花不了太多钱。公司需要每月申报，如果请人帮忙记账，费用大概是每月 200 元左右。"}]},{"type":"element","tag":"h2","props":{"id":"对公账户"},"children":[{"type":"text","value":"对公账户"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"对公账户是跟你的公司绑定在一起的银行账户，以后对方可以将款项直接打到你的对公账户里。大部分银行都支持开通这种服务，你需要带着公司手续（比如营业执照、公章、法人印鉴等）到某家银行申请开通对公帐户。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"三方服务在审核资质时，可能会向我们的对公账户打一笔小额且不固定的钱，我们需要在三方服务那里填写确切的金额，以此验证对公账户的真实有效性。这有点像是通过短信验证码来验证用户的身份。"}]},{"type":"element","tag":"h3","props":{"id":"费用-1"},"children":[{"type":"text","value":"费用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"银行每年都会收取对公账户的年费，一年大概几百块钱。"}]},{"type":"element","tag":"h2","props":{"id":"云服务器"},"children":[{"type":"text","value":"云服务器"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"云服务器可以提供计算、存储与带宽能力，在云服务商（阿里云、腾讯云 ... ）那里购买一台云服务器以后，可以在服务器上安装配置各种服务，在宁皓独立开发者训练营中会有专门的训练内容针对如何购买、配置与使用云服务器。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"以下两种情况需要购买云服务器："}]},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"部署应用"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"备案域名"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果我们打算正式发布在训练营中开发的应用，就需要一台云服务器，在上面要搭建生产环境，然后将应用部署到服务器上。又因为在申请商户版支付宝与微信支付时，可能需要提供备过案的域名，现在备案域名一般要在云服务商那里完成，也就是域名需要跟某家云服务商绑定在一起，在没有云服务器的情况下，云服务商也无法提供备案服务。"}]},{"type":"element","tag":"h3","props":{"id":"费用-2"},"children":[{"type":"text","value":"费用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"云服务器的计算能力、内存、存储空间与带宽，决定了云服务器的价格。"}]},{"type":"element","tag":"h3","props":{"id":"推荐配置"},"children":[{"type":"text","value":"推荐配置"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果你无法确定要购买的云服务器的配置，可以先购买一台 1 核 CPU、2G 内存，按量付费的云服务器。"}]},{"type":"element","tag":"h2","props":{"id":"准备域名"},"children":[{"type":"text","value":"准备域名"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"域名就是 “网络地盘的名字”，比如 "},{"type":"element","tag":"a","props":{"href":"https://ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"ninghao.net"}]},{"type":"text","value":"、"},{"type":"element","tag":"a","props":{"href":"https://ninghao.co/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"ninghao.co"}]},{"type":"text","value":"。有了域名以后，可以配置一些子域名，分别指向我们提供的应用服务，这样通过这些子域名就可以使用这些应用服务了。"}]},{"type":"element","tag":"h3","props":{"id":"注册域名"},"children":[{"type":"text","value":"注册域名"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在域名服务商那里可以申请注册域名，一般的云服务商都提供这种服务（阿里云、腾讯云 ... ）。注册域名时要确定域名的后缀（.com、.net、.co ...）支持实名认证，并不是所有的域名后缀都支持实名认证。无法完成域名的实名认证，也就无法完成域名的备案。"}]},{"type":"element","tag":"h3","props":{"id":"所有者"},"children":[{"type":"text","value":"所有者"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在云服务商那里申请注册域名时，需要填写域名的所有者信息，注意这个所有者信息一但确定，以后想要修改会比较麻烦。填写的所有者信息会影响以后对域名做实名认证与备案。也就是如果以后打算以公司名义对域名做实名认证与备案，在申请注册域名时填写的所有者就必须得是这个公司。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"申请商户版支付宝与微信支付时填写的域名信息，会要求域名的所有者、备案信息与申请人相符（同一个公司名称）。"}]},{"type":"element","tag":"h3","props":{"id":"实名认证"},"children":[{"type":"text","value":"实名认证"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"成功注册了域名以后，要在云服务商那里完成对域名的实名认证，备案域名要求域名已经完成了实名认证。"}]},{"type":"element","tag":"h3","props":{"id":"域名备案"},"children":[{"type":"text","value":"域名备案"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果域名要指向国内的服务器，就必须先完成域名备案，完成备案以后会得到一个备案号，备案一般都是在云服务商那里完成。"}]},{"type":"element","tag":"h3","props":{"id":"费用-3"},"children":[{"type":"text","value":"费用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"不同的域名后缀需要的年费略有差别，每年几十块到几百块。"}]}]},"body":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"公司服务器域名"},"children":[{"type":"text","value":"公司、服务器、域名"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"申请商户版支付宝与微信支付时需要公司资质、对公账户与备过案的域名。"}]},{"type":"element","tag":"h2","props":{"id":"注册公司"},"children":[{"type":"text","value":"注册公司"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在开发的应用中需要用到商户版的支付宝与微信支付，申请开通这些服务时可能会要求我们提供公司资质与对公账户，具体的要求需要仔细阅读它们的相关文档，或者直接咨询人工客服。如果需要提供公司资质，我们需要在当地的工商部门申请注册公司。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"申请注册公司之前，要提前准备好公司名称还有营业范围，公司名称要多准备几个，因为新注册的公司不能与已有公司的名称相同或读音相同或相近，所以重名的机率还是挺大的。"}]},{"type":"element","tag":"h3","props":{"id":"费用"},"children":[{"type":"text","value":"费用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"注册公司的成本在几百元左右，包含工本费、手续费与制作公章的费用，具体需要的费用因地而异，总之花不了太多钱。公司需要每月申报，如果请人帮忙记账，费用大概是每月 200 元左右。"}]},{"type":"element","tag":"h2","props":{"id":"对公账户"},"children":[{"type":"text","value":"对公账户"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"对公账户是跟你的公司绑定在一起的银行账户，以后对方可以将款项直接打到你的对公账户里。大部分银行都支持开通这种服务，你需要带着公司手续（比如营业执照、公章、法人印鉴等）到某家银行申请开通对公帐户。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"三方服务在审核资质时，可能会向我们的对公账户打一笔小额且不固定的钱，我们需要在三方服务那里填写确切的金额，以此验证对公账户的真实有效性。这有点像是通过短信验证码来验证用户的身份。"}]},{"type":"element","tag":"h3","props":{"id":"费用-1"},"children":[{"type":"text","value":"费用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"银行每年都会收取对公账户的年费，一年大概几百块钱。"}]},{"type":"element","tag":"h2","props":{"id":"云服务器"},"children":[{"type":"text","value":"云服务器"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"云服务器可以提供计算、存储与带宽能力，在云服务商（阿里云、腾讯云 ... ）那里购买一台云服务器以后，可以在服务器上安装配置各种服务，在宁皓独立开发者训练营中会有专门的训练内容针对如何购买、配置与使用云服务器。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"以下两种情况需要购买云服务器："}]},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"部署应用"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"备案域名"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果我们打算正式发布在训练营中开发的应用，就需要一台云服务器，在上面要搭建生产环境，然后将应用部署到服务器上。又因为在申请商户版支付宝与微信支付时，可能需要提供备过案的域名，现在备案域名一般要在云服务商那里完成，也就是域名需要跟某家云服务商绑定在一起，在没有云服务器的情况下，云服务商也无法提供备案服务。"}]},{"type":"element","tag":"h3","props":{"id":"费用-2"},"children":[{"type":"text","value":"费用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"云服务器的计算能力、内存、存储空间与带宽，决定了云服务器的价格。"}]},{"type":"element","tag":"h3","props":{"id":"推荐配置"},"children":[{"type":"text","value":"推荐配置"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果你无法确定要购买的云服务器的配置，可以先购买一台 1 核 CPU、2G 内存，按量付费的云服务器。"}]},{"type":"element","tag":"h2","props":{"id":"准备域名"},"children":[{"type":"text","value":"准备域名"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"域名就是 “网络地盘的名字”，比如 "},{"type":"element","tag":"a","props":{"href":"https://ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"ninghao.net"}]},{"type":"text","value":"、"},{"type":"element","tag":"a","props":{"href":"https://ninghao.co/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"ninghao.co"}]},{"type":"text","value":"。有了域名以后，可以配置一些子域名，分别指向我们提供的应用服务，这样通过这些子域名就可以使用这些应用服务了。"}]},{"type":"element","tag":"h3","props":{"id":"注册域名"},"children":[{"type":"text","value":"注册域名"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在域名服务商那里可以申请注册域名，一般的云服务商都提供这种服务（阿里云、腾讯云 ... ）。注册域名时要确定域名的后缀（.com、.net、.co ...）支持实名认证，并不是所有的域名后缀都支持实名认证。无法完成域名的实名认证，也就无法完成域名的备案。"}]},{"type":"element","tag":"h3","props":{"id":"所有者"},"children":[{"type":"text","value":"所有者"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在云服务商那里申请注册域名时，需要填写域名的所有者信息，注意这个所有者信息一但确定，以后想要修改会比较麻烦。填写的所有者信息会影响以后对域名做实名认证与备案。也就是如果以后打算以公司名义对域名做实名认证与备案，在申请注册域名时填写的所有者就必须得是这个公司。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"申请商户版支付宝与微信支付时填写的域名信息，会要求域名的所有者、备案信息与申请人相符（同一个公司名称）。"}]},{"type":"element","tag":"h3","props":{"id":"实名认证"},"children":[{"type":"text","value":"实名认证"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"成功注册了域名以后，要在云服务商那里完成对域名的实名认证，备案域名要求域名已经完成了实名认证。"}]},{"type":"element","tag":"h3","props":{"id":"域名备案"},"children":[{"type":"text","value":"域名备案"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果域名要指向国内的服务器，就必须先完成域名备案，完成备案以后会得到一个备案号，备案一般都是在云服务商那里完成。"}]},{"type":"element","tag":"h3","props":{"id":"费用-3"},"children":[{"type":"text","value":"费用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"不同的域名后缀需要的年费略有差别，每年几十块到几百块。"}]}],"toc":{"title":"","searchDepth":2,"depth":2,"links":[{"id":"注册公司","depth":2,"text":"注册公司","children":[{"id":"费用","depth":3,"text":"费用"}]},{"id":"对公账户","depth":2,"text":"对公账户","children":[{"id":"费用-1","depth":3,"text":"费用"}]},{"id":"云服务器","depth":2,"text":"云服务器","children":[{"id":"费用-2","depth":3,"text":"费用"},{"id":"推荐配置","depth":3,"text":"推荐配置"}]},{"id":"准备域名","depth":2,"text":"准备域名","children":[{"id":"注册域名","depth":3,"text":"注册域名"},{"id":"所有者","depth":3,"text":"所有者"},{"id":"实名认证","depth":3,"text":"实名认证"},{"id":"域名备案","depth":3,"text":"域名备案"},{"id":"费用-3","depth":3,"text":"费用"}]}]}},"_type":"markdown","_id":"content:docs:3.3rd:1.prepare.md","_source":"content","_file":"docs/3.3rd/1.prepare.md","_extension":"md"},{"_path":"/docs/3rd/alipay","_draft":false,"_partial":false,"_empty":false,"title":"支付宝","description":"支付宝的账号分为个人账号与企业账号，作为应用开发者，我们需要使用的是支付宝企业账号。在宁皓独立开发者训练营中，我们会训练如何在自己开发的应用里集成支付宝提供的支付功能，最终会完成一个按年订阅与单个资产购买的功能。","excerpt":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"支付宝"},"children":[{"type":"text","value":"支付宝"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"支付宝的账号分为个人账号与企业账号，作为应用开发者，我们需要使用的是支付宝企业账号。在宁皓独立开发者训练营中，我们会训练如何在自己开发的应用里集成支付宝提供的支付功能，最终会完成一个按年订阅与单个资产购买的功能。"}]},{"type":"element","tag":"h2","props":{"id":"在服务端应用里集成支付宝支付功能"},"children":[{"type":"text","value":"在服务端应用里集成支付宝支付功能"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"要在我们自己开发的服务端应用里集成支付宝的支付功能，要确保在支付宝开放平台创建的应用的状态为已上线，应用的能力列表里包含电脑网站支付与手机网站支付，且状态均为已生效，除此以外还需要准备好下列信息："}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"支付宝应用的 AppID（示例：2021003128658593），这是创建了支付宝应用以后得到的。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"支付宝网关地址（"},{"type":"element","tag":"a","props":{"href":"https://openapi.alipay.com/gateway.do%EF%BC%89%EF%BC%8C%E8%BF%99%E6%98%AF%E4%B8%80%E4%B8%AA%E5%9B%BA%E5%AE%9A%E7%9A%84%E5%9C%B0%E5%9D%80%E3%80%82","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://openapi.alipay.com/gateway.do），这是一个固定的地址。"}]}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"支付宝公钥（MIIBIjANBgkqhkiG9w0BAQEFA...），这是成功配置了支付宝应用的加签管理后得到的。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"应用密钥（MIIEowIBAAKCAQEAqFJt...），这是之前使用支付宝开放平台开发助手生成密钥时得到的。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"异步通知接口（示例："},{"type":"element","tag":"a","props":{"href":"https://sandbox.ninghao.net/payments/alipay/notify%EF%BC%89%EF%BC%8C%E8%BF%99%E6%98%AF%E6%88%91%E4%BB%AC%E8%A6%81%E5%9C%A8%E8%AE%AD%E7%BB%83%E8%90%A5%E4%B8%AD%E8%87%AA%E5%B7%B1%E5%BC%80%E5%8F%91%E7%9A%84%E3%80%82","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://sandbox.ninghao.net/payments/alipay/notify），这是我们要在训练营中自己开发的。"}]}]}]},{"type":"element","tag":"h2","props":{"id":"支付宝企业账号"},"children":[{"type":"text","value":"支付宝企业账号"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用公司相关资质，在支付宝网站申请开通一个支付宝企业账号并完成实名认证。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"有了支付宝企业账户，就可以获得支付宝提供的各种支付能力了，比如在我们的应用里集成支付功能，用户通过应用支付的金额，会直接出现在支付宝企业账户里。这种企业账号跟个人账号的使用并没有太多区别，同样都可以在支付宝客户端登录使用，日常消费也可以使用企业账户里的金额。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image.png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"支付宝开放平台，账户中心里的主账号管理"}]}]},{"type":"element","tag":"h2","props":{"id":"支付宝应用"},"children":[{"type":"text","value":"支付宝应用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在训练营中集成支付宝支付时，需要提前准备好支付宝应用的 ID（AppID）、支付宝公钥等相关信息，这些东西都来自我们创建的支付宝应用。在支付宝开放平台，可以创建支付宝应用。"}]},{"type":"element","tag":"h3","props":{"id":"创建支付宝应用"},"children":[{"type":"text","value":"创建支付宝应用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"登录到支付宝开放平台，在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"控制台"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"我的应用"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"网页&移动应用"}]},{"type":"text","value":" 下面，点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"创建应用"}]},{"type":"text","value":"，创建一个支付宝应用。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(1).png"},"children":[]},{"type":"text","value":"\n创建应用时需要填写应用的名称，上传应用图标，应用类型可以选择 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"网页应用"}]},{"type":"text","value":"。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(2).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在支付宝开放平台创建应用"}]}]},{"type":"element","tag":"h3","props":{"id":"在商家中心绑定应用-id"},"children":[{"type":"text","value":"在商家中心绑定应用 ID"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"创建了支付宝应用以后，需要在支付的商家中心那里绑定一下新创建的这个应用。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"复制一下新创建的应用 ID，然后打开支付宝"},{"type":"element","tag":"a","props":{"href":"https://mrchportalweb.alipay.com/accountmanage/bind/appIdBindList","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"商家中心"}]},{"type":"text","value":"，点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"添加绑定"}]},{"type":"text","value":"，在 AppID 那里填写之前在开放平台创建的应用 ID，点击 下一步完成绑定。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(3).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"支付宝商家中心添加绑定"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(4).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"完成绑定后，会显示在已经绑定应用的下面"}]}]},{"type":"element","tag":"h3","props":{"id":"添加应用的能力"},"children":[{"type":"text","value":"添加应用的能力"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在商家中心完成绑定应用以后，回到开放平台，打开新创建的应用，然后在能力列表的下面，点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"添加能力"}]},{"type":"text","value":"。在弹出的窗口中选择需要的能力，在本地训练营中我们暂时只会用到 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"电脑网站支付（alipay.trade.page.pay）"}]},{"type":"text","value":" 与 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"手机网站支付（alipay.trade.wap.pay）"}]},{"type":"text","value":" 这两项能力。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"有些能力需要签约以后才能生效，应用审核通过之后，可以继续完成能力的签约。提交审核之前，必须先完成应用的开发设置。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(5).png"},"children":[]}]},{"type":"element","tag":"h4","props":{"id":"电脑网站支付能力的状态"},"children":[{"type":"text","value":"电脑网站支付能力的状态"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在我创建的支付宝应用中，电脑网站支付能力的状态为 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"未生效"}]},{"type":"text","value":"，点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"签约"}]},{"type":"text","value":" 时提示已经做过相关产品的签约。经测试，虽然显示能力的状态为未生效，但是我的服务端应用依然可以正常使用支付宝的电脑网站支付功能。"}]},{"type":"element","tag":"h3","props":{"id":"应用的开发设置"},"children":[{"type":"text","value":"应用的开发设置"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在支付宝应用的开发信息下面，需要配置一下应用的相关信息。"}]},{"type":"element","tag":"h4","props":{"id":"接口加签方式密钥证书"},"children":[{"type":"text","value":"接口加签方式（密钥/证书)"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"我们自己开发的服务端应用与支付宝应用相互沟通时，可以通过密钥或证书的方式来确定对方的身份。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在开发信息的下面，配置一下接口加签方式（密钥/证书)，加签模式可以选择 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"公钥"}]},{"type":"text","value":"。然后在 填写公钥字符 这里，输入我们生成的钥匙对里的公钥，注意这里需要一种特殊格式的公钥，内容为一行，可以下载使用"},{"type":"element","tag":"a","props":{"href":"https://opendocs.alipay.com/apis/02kipk","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"支付宝开放平台开发助手"}]},{"type":"text","value":"生成需要的密钥。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(6).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"使用支付宝开放平台开发助手生成密钥（钥匙对）"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"将生成的应用私钥与公钥放在一个安全的地方，开发助手默认会将手成的密钥保存在两个文本文件里。我们需要复制一下应用公钥里的内容，然后点击支付宝应用开发信息下面的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"接口加签方式（密钥/证书)"}]},{"type":"text","value":" 后面的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"设置"}]},{"type":"text","value":"。将复制的应用公钥粘贴到 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"填写公钥字符"}]},{"type":"text","value":" 这里。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(7).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在加签管理界面填写应用公钥字符"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"完成配置以后，会显示之前设置的应用公钥，还会出现 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"支付宝公钥"}]},{"type":"text","value":"。在我们自己开发的服务端应用里，验证异步通知支付结果的时候，会用到这个支付宝公钥。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(8).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"完成加签配置时会显示支付宝公钥"}]}]},{"type":"element","tag":"h4","props":{"id":"应用网关"},"children":[{"type":"text","value":"应用网关"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"应用网关指的是我们的服务端应用提供的一个接口的地址，支付宝会将一些通知结果发送到这个接口。在训练营里我们会练习如何定义这个接口。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(9).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在支付宝开放平台配置应用网关"}]}]},{"type":"element","tag":"h4","props":{"id":"授权回调地址"},"children":[{"type":"text","value":"授权回调地址"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"用户使用支付宝在我们的应用里完成支付以后，支付宝会将用户重定向到一个网址，在支付宝应用的开发设置里可以配置一下这个具体的网址，或者只提供一个允许的回调地址的域名。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(10).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在支付宝开放平台配置授权回调地址"}]}]},{"type":"element","tag":"h3","props":{"id":"提交审核"},"children":[{"type":"text","value":"提交审核"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"完成支付宝应用的开发配置以后，可以提交审核应用，一般当天就可以完成审核。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(11).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"审核中的支付宝应用"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(12).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"支付宝应用审核通过以后会提示已上线"}]}]},{"type":"element","tag":"h2","props":{"id":"支付宝开放平台开发助手"},"children":[{"type":"text","value":"支付宝开放平台开发助手"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"a","props":{"href":"https://opendocs.alipay.com/apis/02kipk","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"支付宝开放平台开发助手"}]},{"type":"text","value":"提供了生成应用密钥、验证签名等功能，可以辅助我们开发调试支付宝应用。"}]},{"type":"element","tag":"h3","props":{"id":"解决-macos-无法安装的问题"},"children":[{"type":"text","value":"解决 macOS 无法安装的问题"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 macOS 系统里安装支付宝开放平台开发助手时，有可能会提示无法打开，解决这个问题，我们可以打开系统的安全性与隐私，在窗口底部会显示已阻止使用“支付宝开放平台助手...” ，点击右侧的 仍要打开，应该就可以安装支付宝开放平台开发助手了。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(13).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"macOS 系统的安全性与隐私"}]}]}]},"body":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"支付宝"},"children":[{"type":"text","value":"支付宝"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"支付宝的账号分为个人账号与企业账号，作为应用开发者，我们需要使用的是支付宝企业账号。在宁皓独立开发者训练营中，我们会训练如何在自己开发的应用里集成支付宝提供的支付功能，最终会完成一个按年订阅与单个资产购买的功能。"}]},{"type":"element","tag":"h2","props":{"id":"在服务端应用里集成支付宝支付功能"},"children":[{"type":"text","value":"在服务端应用里集成支付宝支付功能"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"要在我们自己开发的服务端应用里集成支付宝的支付功能，要确保在支付宝开放平台创建的应用的状态为已上线，应用的能力列表里包含电脑网站支付与手机网站支付，且状态均为已生效，除此以外还需要准备好下列信息："}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"支付宝应用的 AppID（示例：2021003128658593），这是创建了支付宝应用以后得到的。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"支付宝网关地址（"},{"type":"element","tag":"a","props":{"href":"https://openapi.alipay.com/gateway.do%EF%BC%89%EF%BC%8C%E8%BF%99%E6%98%AF%E4%B8%80%E4%B8%AA%E5%9B%BA%E5%AE%9A%E7%9A%84%E5%9C%B0%E5%9D%80%E3%80%82","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://openapi.alipay.com/gateway.do），这是一个固定的地址。"}]}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"支付宝公钥（MIIBIjANBgkqhkiG9w0BAQEFA...），这是成功配置了支付宝应用的加签管理后得到的。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"应用密钥（MIIEowIBAAKCAQEAqFJt...），这是之前使用支付宝开放平台开发助手生成密钥时得到的。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"异步通知接口（示例："},{"type":"element","tag":"a","props":{"href":"https://sandbox.ninghao.net/payments/alipay/notify%EF%BC%89%EF%BC%8C%E8%BF%99%E6%98%AF%E6%88%91%E4%BB%AC%E8%A6%81%E5%9C%A8%E8%AE%AD%E7%BB%83%E8%90%A5%E4%B8%AD%E8%87%AA%E5%B7%B1%E5%BC%80%E5%8F%91%E7%9A%84%E3%80%82","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://sandbox.ninghao.net/payments/alipay/notify），这是我们要在训练营中自己开发的。"}]}]}]},{"type":"element","tag":"h2","props":{"id":"支付宝企业账号"},"children":[{"type":"text","value":"支付宝企业账号"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用公司相关资质，在支付宝网站申请开通一个支付宝企业账号并完成实名认证。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"有了支付宝企业账户，就可以获得支付宝提供的各种支付能力了，比如在我们的应用里集成支付功能，用户通过应用支付的金额，会直接出现在支付宝企业账户里。这种企业账号跟个人账号的使用并没有太多区别，同样都可以在支付宝客户端登录使用，日常消费也可以使用企业账户里的金额。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image.png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"支付宝开放平台，账户中心里的主账号管理"}]}]},{"type":"element","tag":"h2","props":{"id":"支付宝应用"},"children":[{"type":"text","value":"支付宝应用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在训练营中集成支付宝支付时，需要提前准备好支付宝应用的 ID（AppID）、支付宝公钥等相关信息，这些东西都来自我们创建的支付宝应用。在支付宝开放平台，可以创建支付宝应用。"}]},{"type":"element","tag":"h3","props":{"id":"创建支付宝应用"},"children":[{"type":"text","value":"创建支付宝应用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"登录到支付宝开放平台，在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"控制台"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"我的应用"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"网页&移动应用"}]},{"type":"text","value":" 下面，点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"创建应用"}]},{"type":"text","value":"，创建一个支付宝应用。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(1).png"},"children":[]},{"type":"text","value":"\n创建应用时需要填写应用的名称，上传应用图标，应用类型可以选择 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"网页应用"}]},{"type":"text","value":"。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(2).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在支付宝开放平台创建应用"}]}]},{"type":"element","tag":"h3","props":{"id":"在商家中心绑定应用-id"},"children":[{"type":"text","value":"在商家中心绑定应用 ID"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"创建了支付宝应用以后，需要在支付的商家中心那里绑定一下新创建的这个应用。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"复制一下新创建的应用 ID，然后打开支付宝"},{"type":"element","tag":"a","props":{"href":"https://mrchportalweb.alipay.com/accountmanage/bind/appIdBindList","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"商家中心"}]},{"type":"text","value":"，点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"添加绑定"}]},{"type":"text","value":"，在 AppID 那里填写之前在开放平台创建的应用 ID，点击 下一步完成绑定。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(3).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"支付宝商家中心添加绑定"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(4).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"完成绑定后，会显示在已经绑定应用的下面"}]}]},{"type":"element","tag":"h3","props":{"id":"添加应用的能力"},"children":[{"type":"text","value":"添加应用的能力"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在商家中心完成绑定应用以后，回到开放平台，打开新创建的应用，然后在能力列表的下面，点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"添加能力"}]},{"type":"text","value":"。在弹出的窗口中选择需要的能力，在本地训练营中我们暂时只会用到 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"电脑网站支付（alipay.trade.page.pay）"}]},{"type":"text","value":" 与 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"手机网站支付（alipay.trade.wap.pay）"}]},{"type":"text","value":" 这两项能力。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"有些能力需要签约以后才能生效，应用审核通过之后，可以继续完成能力的签约。提交审核之前，必须先完成应用的开发设置。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(5).png"},"children":[]}]},{"type":"element","tag":"h4","props":{"id":"电脑网站支付能力的状态"},"children":[{"type":"text","value":"电脑网站支付能力的状态"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在我创建的支付宝应用中，电脑网站支付能力的状态为 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"未生效"}]},{"type":"text","value":"，点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"签约"}]},{"type":"text","value":" 时提示已经做过相关产品的签约。经测试，虽然显示能力的状态为未生效，但是我的服务端应用依然可以正常使用支付宝的电脑网站支付功能。"}]},{"type":"element","tag":"h3","props":{"id":"应用的开发设置"},"children":[{"type":"text","value":"应用的开发设置"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在支付宝应用的开发信息下面，需要配置一下应用的相关信息。"}]},{"type":"element","tag":"h4","props":{"id":"接口加签方式密钥证书"},"children":[{"type":"text","value":"接口加签方式（密钥/证书)"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"我们自己开发的服务端应用与支付宝应用相互沟通时，可以通过密钥或证书的方式来确定对方的身份。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在开发信息的下面，配置一下接口加签方式（密钥/证书)，加签模式可以选择 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"公钥"}]},{"type":"text","value":"。然后在 填写公钥字符 这里，输入我们生成的钥匙对里的公钥，注意这里需要一种特殊格式的公钥，内容为一行，可以下载使用"},{"type":"element","tag":"a","props":{"href":"https://opendocs.alipay.com/apis/02kipk","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"支付宝开放平台开发助手"}]},{"type":"text","value":"生成需要的密钥。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(6).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"使用支付宝开放平台开发助手生成密钥（钥匙对）"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"将生成的应用私钥与公钥放在一个安全的地方，开发助手默认会将手成的密钥保存在两个文本文件里。我们需要复制一下应用公钥里的内容，然后点击支付宝应用开发信息下面的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"接口加签方式（密钥/证书)"}]},{"type":"text","value":" 后面的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"设置"}]},{"type":"text","value":"。将复制的应用公钥粘贴到 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"填写公钥字符"}]},{"type":"text","value":" 这里。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(7).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在加签管理界面填写应用公钥字符"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"完成配置以后，会显示之前设置的应用公钥，还会出现 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"支付宝公钥"}]},{"type":"text","value":"。在我们自己开发的服务端应用里，验证异步通知支付结果的时候，会用到这个支付宝公钥。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(8).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"完成加签配置时会显示支付宝公钥"}]}]},{"type":"element","tag":"h4","props":{"id":"应用网关"},"children":[{"type":"text","value":"应用网关"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"应用网关指的是我们的服务端应用提供的一个接口的地址，支付宝会将一些通知结果发送到这个接口。在训练营里我们会练习如何定义这个接口。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(9).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在支付宝开放平台配置应用网关"}]}]},{"type":"element","tag":"h4","props":{"id":"授权回调地址"},"children":[{"type":"text","value":"授权回调地址"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"用户使用支付宝在我们的应用里完成支付以后，支付宝会将用户重定向到一个网址，在支付宝应用的开发设置里可以配置一下这个具体的网址，或者只提供一个允许的回调地址的域名。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(10).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在支付宝开放平台配置授权回调地址"}]}]},{"type":"element","tag":"h3","props":{"id":"提交审核"},"children":[{"type":"text","value":"提交审核"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"完成支付宝应用的开发配置以后，可以提交审核应用，一般当天就可以完成审核。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(11).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"审核中的支付宝应用"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(12).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"支付宝应用审核通过以后会提示已上线"}]}]},{"type":"element","tag":"h2","props":{"id":"支付宝开放平台开发助手"},"children":[{"type":"text","value":"支付宝开放平台开发助手"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"a","props":{"href":"https://opendocs.alipay.com/apis/02kipk","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"支付宝开放平台开发助手"}]},{"type":"text","value":"提供了生成应用密钥、验证签名等功能，可以辅助我们开发调试支付宝应用。"}]},{"type":"element","tag":"h3","props":{"id":"解决-macos-无法安装的问题"},"children":[{"type":"text","value":"解决 macOS 无法安装的问题"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 macOS 系统里安装支付宝开放平台开发助手时，有可能会提示无法打开，解决这个问题，我们可以打开系统的安全性与隐私，在窗口底部会显示已阻止使用“支付宝开放平台助手...” ，点击右侧的 仍要打开，应该就可以安装支付宝开放平台开发助手了。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/alipay/image(13).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"macOS 系统的安全性与隐私"}]}]}],"toc":{"title":"","searchDepth":2,"depth":2,"links":[{"id":"在服务端应用里集成支付宝支付功能","depth":2,"text":"在服务端应用里集成支付宝支付功能"},{"id":"支付宝企业账号","depth":2,"text":"支付宝企业账号"},{"id":"支付宝应用","depth":2,"text":"支付宝应用","children":[{"id":"创建支付宝应用","depth":3,"text":"创建支付宝应用"},{"id":"在商家中心绑定应用-id","depth":3,"text":"在商家中心绑定应用 ID"},{"id":"添加应用的能力","depth":3,"text":"添加应用的能力"},{"id":"应用的开发设置","depth":3,"text":"应用的开发设置"},{"id":"提交审核","depth":3,"text":"提交审核"}]},{"id":"支付宝开放平台开发助手","depth":2,"text":"支付宝开放平台开发助手","children":[{"id":"解决-macos-无法安装的问题","depth":3,"text":"解决 macOS 无法安装的问题"}]}]}},"_type":"markdown","_id":"content:docs:3.3rd:2.alipay.md","_source":"content","_file":"docs/3.3rd/2.alipay.md","_extension":"md"},{"_path":"/docs/3rd/wxpay","_draft":false,"_partial":false,"_empty":false,"title":"微信支付","description":"在宁皓独立开发者训练营，我们会集成商户版微信支付提供的支付功能，完成付费订阅与数字资产销售的功能。","excerpt":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"微信支付"},"children":[{"type":"text","value":"微信支付"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在宁皓独立开发者训练营，我们会集成商户版微信支付提供的支付功能，完成付费订阅与数字资产销售的功能。"}]},{"type":"element","tag":"h2","props":{"id":"在服务端应用里集成微信支付功能"},"children":[{"type":"text","value":"在服务端应用里集成微信支付功能"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"要在我们自己开发的服务端应用里集成商户版微信支付，需要申请开通商户版微信支付，还需要给它关联一个应用，比如公众平台服务号，或者小程序。按说也应该可以关联在微信开放平台注册的网站应用，不过在实际操作中，无法关联网站应用。在训练营中选择使用的是公众平台服务号+微信支付。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在开发过程中需要准备以下信息："}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"应用 ID（示例：wx58264149db20f28e），这是与微信支付关联的 AppID，比如公众平台服务号的 AppID。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"微信支付商户号（示例：1428508902），这是注册商户版微信支付以后获得的商户号。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"微信支付密钥（3f47ebf92...），在商户版微信支付账户设置- API 安全里设置，训练营中用的是 APIv2 密钥。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"异步通知接口（示例："},{"type":"element","tag":"a","props":{"href":"https://sandbox.ninghao.net/payments/wxpay/notify%EF%BC%89%EF%BC%8C%E8%BF%99%E6%98%AF%E6%88%91%E4%BB%AC%E8%A6%81%E5%9C%A8%E8%AE%AD%E7%BB%83%E8%90%A5%E4%B8%AD%E8%87%AA%E5%B7%B1%E5%BC%80%E5%8F%91%E7%9A%84%E3%80%82","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://sandbox.ninghao.net/payments/wxpay/notify），这是我们要在训练营中自己开发的。"}]}]}]},{"type":"element","tag":"h2","props":{"id":"微信公众平台服务号"},"children":[{"type":"text","value":"微信公众平台服务号"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在微信公众平台使用企业身份注册一个服务号并完成实名认证，每年需要 300 元的认证费用。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://mp.weixin.qq.com/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://mp.weixin.qq.com"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"登录到公众平台，在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"设置与开发"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"基本配置"}]},{"type":"text","value":" 里会显示公众号的 AppID。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image.png"},"children":[]}]},{"type":"element","tag":"h2","props":{"id":"微信支付商户平台"},"children":[{"type":"text","value":"微信支付商户平台"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在我们的应用里集成了微信支付并完成相关支付功能以后，用户通过微信支付的金额会出现在微信支付商户平台这里，我们可以将金额提到对公司的对公账户里，或者直接转至个人银行账户（需要额外的手续费）。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"登录到微信支付商户平台，点击成为商户，注册一个微信支付商户号，通过审核以后开通需要的支付产品，然后再关联应用 ID。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://pay.weixin.qq.com/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://pay.weixin.qq.com"}]}]},{"type":"element","tag":"h3","props":{"id":"注册微信支付商户号"},"children":[{"type":"text","value":"注册微信支付商户号"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在微信支付商户平台注册微信支付商户号时，提前准备好下列信息："}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"营业执照照片"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"法人身份证正面与反面照片"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"对公账户相关信息（银行名称、开户行、账号...）"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(1).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"点击页面上的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"注册微信支付商户号"}]},{"type":"text","value":"，会提示你使用管理员的微信客户端扫描二维码。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(2).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"管理员确认操作以后，点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"申请注册"}]},{"type":"text","value":"，下一步需要填写商户相关资料，首先要选择主体身份，类型应该是企业，然后上传准备好的营业执照的照片。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(3).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"继续填写法人相关信息，证件类型可以选择身份证，再分别上传身份证的正面与反面照片。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(4).png"},"children":[]},{"type":"text","value":"\n然后填写经营与行业相关信息，比如商户简称、客服电话等。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(5).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"确认商户相关资料以后，需要等待审核。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(6).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"提示 7 到 15 天完成审核，不过我的审核在当天上午就完成了。通过以后会收到微信通知，然后需要登录微信支付商户平台完成签约。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(7).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"签约成功以后，会提示成为微信支付商家。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(8).png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"开通支付产品"},"children":[{"type":"text","value":"开通支付产品"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"登录到微信支付商户平台，在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"产品中心"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"我的产品"}]},{"type":"text","value":" 页面，选择要开通的支付产品。在宁皓独立开发者训练营中，需要用的是 Native 支付，这种支付方式需要在应用界面生成微信支付二维码，用户可以使用微信客户端扫描页面上的二维码完成支付。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(9).png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"关联应用"},"children":[{"type":"text","value":"关联应用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在微信支付商户平台，"},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"产品中心"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"AppID 账号管理"}]},{"type":"text","value":" 页面，可以关联应用，比如关联一下公众平台服务号。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(10).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在关联状态那里会提示 待授权，这时需要登录公众平台，打开 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"微信支付"}]},{"type":"text","value":" 页面，在待关联商户号这里会显示要关联的微信支付商户。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(11).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在公众号平台，微信支付页面显示的待关联商户号"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"确认以后，会在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"已关联商户号"}]},{"type":"text","value":" 的下面显示被关联的微信支付商户。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(12).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在公众号平台，微信支付页面显示的已关联商户号"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在公众号那里确定关联微信支付商户以后，回到微信支付商户平台，"},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"产品中心"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"AppID 账号管理"}]},{"type":"text","value":" 页面，应用的关联状态会变成 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"已关联"}]},{"type":"text","value":"。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(13).png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"设置接口密钥"},"children":[{"type":"text","value":"设置接口密钥"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在微信支付商户平台，"},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"账户中心"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"API 安全"}]},{"type":"text","value":" 页面可以设置接口密钥。在宁皓独立开发者训练营，我们用的是 v2 微信支付接口，所以在这里要设置一下 APIv2 密钥。密钥的内容是由字母数字组成的 32 位随机字符，使用 openssl 命令生成随机字符串，在终端执行："}]},{"type":"element","tag":"code","props":{"code":"openssl rand -hex 16\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"openssl rand -hex 16\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令会得到一串长度是 32 位的字符串，示例：cac2241ac9c61a8062a66e178d058247，然后可以将其作为微信支付的 APIv2 密钥。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(14).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"设置密钥需要完成安全验证，要输入操作密码（提前自行设置）与手机验证码。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(15).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"完成以后会在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"设置 APIV2 密钥"}]},{"type":"text","value":" 的右侧显示 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"已设置"}]},{"type":"text","value":"。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(16).png"},"children":[]}]}]},"body":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"微信支付"},"children":[{"type":"text","value":"微信支付"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在宁皓独立开发者训练营，我们会集成商户版微信支付提供的支付功能，完成付费订阅与数字资产销售的功能。"}]},{"type":"element","tag":"h2","props":{"id":"在服务端应用里集成微信支付功能"},"children":[{"type":"text","value":"在服务端应用里集成微信支付功能"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"要在我们自己开发的服务端应用里集成商户版微信支付，需要申请开通商户版微信支付，还需要给它关联一个应用，比如公众平台服务号，或者小程序。按说也应该可以关联在微信开放平台注册的网站应用，不过在实际操作中，无法关联网站应用。在训练营中选择使用的是公众平台服务号+微信支付。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在开发过程中需要准备以下信息："}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"应用 ID（示例：wx58264149db20f28e），这是与微信支付关联的 AppID，比如公众平台服务号的 AppID。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"微信支付商户号（示例：1428508902），这是注册商户版微信支付以后获得的商户号。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"微信支付密钥（3f47ebf92...），在商户版微信支付账户设置- API 安全里设置，训练营中用的是 APIv2 密钥。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"异步通知接口（示例："},{"type":"element","tag":"a","props":{"href":"https://sandbox.ninghao.net/payments/wxpay/notify%EF%BC%89%EF%BC%8C%E8%BF%99%E6%98%AF%E6%88%91%E4%BB%AC%E8%A6%81%E5%9C%A8%E8%AE%AD%E7%BB%83%E8%90%A5%E4%B8%AD%E8%87%AA%E5%B7%B1%E5%BC%80%E5%8F%91%E7%9A%84%E3%80%82","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://sandbox.ninghao.net/payments/wxpay/notify），这是我们要在训练营中自己开发的。"}]}]}]},{"type":"element","tag":"h2","props":{"id":"微信公众平台服务号"},"children":[{"type":"text","value":"微信公众平台服务号"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在微信公众平台使用企业身份注册一个服务号并完成实名认证，每年需要 300 元的认证费用。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://mp.weixin.qq.com/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://mp.weixin.qq.com"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"登录到公众平台，在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"设置与开发"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"基本配置"}]},{"type":"text","value":" 里会显示公众号的 AppID。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image.png"},"children":[]}]},{"type":"element","tag":"h2","props":{"id":"微信支付商户平台"},"children":[{"type":"text","value":"微信支付商户平台"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在我们的应用里集成了微信支付并完成相关支付功能以后，用户通过微信支付的金额会出现在微信支付商户平台这里，我们可以将金额提到对公司的对公账户里，或者直接转至个人银行账户（需要额外的手续费）。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"登录到微信支付商户平台，点击成为商户，注册一个微信支付商户号，通过审核以后开通需要的支付产品，然后再关联应用 ID。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://pay.weixin.qq.com/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://pay.weixin.qq.com"}]}]},{"type":"element","tag":"h3","props":{"id":"注册微信支付商户号"},"children":[{"type":"text","value":"注册微信支付商户号"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在微信支付商户平台注册微信支付商户号时，提前准备好下列信息："}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"营业执照照片"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"法人身份证正面与反面照片"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"对公账户相关信息（银行名称、开户行、账号...）"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(1).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"点击页面上的 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"注册微信支付商户号"}]},{"type":"text","value":"，会提示你使用管理员的微信客户端扫描二维码。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(2).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"管理员确认操作以后，点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"申请注册"}]},{"type":"text","value":"，下一步需要填写商户相关资料，首先要选择主体身份，类型应该是企业，然后上传准备好的营业执照的照片。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(3).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"继续填写法人相关信息，证件类型可以选择身份证，再分别上传身份证的正面与反面照片。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(4).png"},"children":[]},{"type":"text","value":"\n然后填写经营与行业相关信息，比如商户简称、客服电话等。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(5).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"确认商户相关资料以后，需要等待审核。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(6).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"提示 7 到 15 天完成审核，不过我的审核在当天上午就完成了。通过以后会收到微信通知，然后需要登录微信支付商户平台完成签约。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(7).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"签约成功以后，会提示成为微信支付商家。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(8).png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"开通支付产品"},"children":[{"type":"text","value":"开通支付产品"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"登录到微信支付商户平台，在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"产品中心"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"我的产品"}]},{"type":"text","value":" 页面，选择要开通的支付产品。在宁皓独立开发者训练营中，需要用的是 Native 支付，这种支付方式需要在应用界面生成微信支付二维码，用户可以使用微信客户端扫描页面上的二维码完成支付。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(9).png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"关联应用"},"children":[{"type":"text","value":"关联应用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在微信支付商户平台，"},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"产品中心"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"AppID 账号管理"}]},{"type":"text","value":" 页面，可以关联应用，比如关联一下公众平台服务号。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(10).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在关联状态那里会提示 待授权，这时需要登录公众平台，打开 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"微信支付"}]},{"type":"text","value":" 页面，在待关联商户号这里会显示要关联的微信支付商户。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(11).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在公众号平台，微信支付页面显示的待关联商户号"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"确认以后，会在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"已关联商户号"}]},{"type":"text","value":" 的下面显示被关联的微信支付商户。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(12).png"},"children":[]},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"在公众号平台，微信支付页面显示的已关联商户号"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在公众号那里确定关联微信支付商户以后，回到微信支付商户平台，"},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"产品中心"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"AppID 账号管理"}]},{"type":"text","value":" 页面，应用的关联状态会变成 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"已关联"}]},{"type":"text","value":"。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(13).png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"设置接口密钥"},"children":[{"type":"text","value":"设置接口密钥"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在微信支付商户平台，"},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"账户中心"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"API 安全"}]},{"type":"text","value":" 页面可以设置接口密钥。在宁皓独立开发者训练营，我们用的是 v2 微信支付接口，所以在这里要设置一下 APIv2 密钥。密钥的内容是由字母数字组成的 32 位随机字符，使用 openssl 命令生成随机字符串，在终端执行："}]},{"type":"element","tag":"code","props":{"code":"openssl rand -hex 16\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"openssl rand -hex 16\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行上面的命令会得到一串长度是 32 位的字符串，示例：cac2241ac9c61a8062a66e178d058247，然后可以将其作为微信支付的 APIv2 密钥。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(14).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"设置密钥需要完成安全验证，要输入操作密码（提前自行设置）与手机验证码。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(15).png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"完成以后会在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"设置 APIV2 密钥"}]},{"type":"text","value":" 的右侧显示 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"已设置"}]},{"type":"text","value":"。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/wxpay/image(16).png"},"children":[]}]}],"toc":{"title":"","searchDepth":2,"depth":2,"links":[{"id":"在服务端应用里集成微信支付功能","depth":2,"text":"在服务端应用里集成微信支付功能"},{"id":"微信公众平台服务号","depth":2,"text":"微信公众平台服务号"},{"id":"微信支付商户平台","depth":2,"text":"微信支付商户平台","children":[{"id":"注册微信支付商户号","depth":3,"text":"注册微信支付商户号"},{"id":"开通支付产品","depth":3,"text":"开通支付产品"},{"id":"关联应用","depth":3,"text":"关联应用"},{"id":"设置接口密钥","depth":3,"text":"设置接口密钥"}]}]}},"_type":"markdown","_id":"content:docs:3.3rd:3.wxpay.md","_source":"content","_file":"docs/3.3rd/3.wxpay.md","_extension":"md"},{"_path":"/docs/3rd/weixin","_draft":false,"_partial":false,"_empty":false,"title":"微信开放平台","description":"在宁皓独立开发者训练营中，会在要开发的应用里实现一个微信登录功能，这需要先在微信开放平台注册账户，完成实名认证后再去创建一个开放平台的网站应用。","excerpt":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"微信开放平台"},"children":[{"type":"text","value":"微信开放平台"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在宁皓独立开发者训练营中，会在要开发的应用里实现一个微信登录功能，这需要先在微信开放平台注册账户，完成实名认证后再去创建一个开放平台的网站应用。"}]},{"type":"element","tag":"h2","props":{"id":"微信开放平台-1"},"children":[{"type":"text","value":"微信开放平台"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"登录微信开放平台，注册一个帐号。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://open.weixin.qq.com/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://open.weixin.qq.com"}]}]},{"type":"element","tag":"h2","props":{"id":"开发者资质认证"},"children":[{"type":"text","value":"开发者资质认证"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"登录到微信开放平台，在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"帐号中心"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"开发者资质认证"}]},{"type":"text","value":" 页面，申请完成认证，认证需要缴纳 ￥ 300 元的费用。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/weixin/image.png"},"children":[]}]},{"type":"element","tag":"h2","props":{"id":"绑定公众号"},"children":[{"type":"text","value":"绑定公众号"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"管理中心"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"公众帐号"}]},{"type":"text","value":" 页面，绑定我们之前注册并认证过的微信公众帐号。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/weixin/image(1).png"},"children":[]}]},{"type":"element","tag":"h2","props":{"id":"创建网站应用"},"children":[{"type":"text","value":"创建网站应用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"管理中心"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"网站应用"}]},{"type":"text","value":" 页面，在这里创建一个网站应用。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/weixin/image(2).png"},"children":[]},{"type":"text","value":"\n审核通过以后，打开这个网站应用，这里会显示这个网站应用的 AppID，可以重置 AppSecret（应用密钥），我们要实现的微信功能暂时用不到这些东西。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/weixin/image(3).png"},"children":[]}]},{"type":"element","tag":"h2","props":{"id":"设置授权回调域"},"children":[{"type":"text","value":"设置授权回调域"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开网站应用页面，在开发信息的下面设置一下 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"授权回调域"}]},{"type":"text","value":"，在使用微信登录时，用户确定登录后，微信提供的登录页面会重定向到一个指定的地址，这个地址允许的域名需要在授权回调域这里设置一下。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/weixin/image(4).png"},"children":[]}]}]},"body":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"微信开放平台"},"children":[{"type":"text","value":"微信开放平台"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在宁皓独立开发者训练营中，会在要开发的应用里实现一个微信登录功能，这需要先在微信开放平台注册账户，完成实名认证后再去创建一个开放平台的网站应用。"}]},{"type":"element","tag":"h2","props":{"id":"微信开放平台-1"},"children":[{"type":"text","value":"微信开放平台"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"登录微信开放平台，注册一个帐号。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://open.weixin.qq.com/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://open.weixin.qq.com"}]}]},{"type":"element","tag":"h2","props":{"id":"开发者资质认证"},"children":[{"type":"text","value":"开发者资质认证"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"登录到微信开放平台，在 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"帐号中心"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"开发者资质认证"}]},{"type":"text","value":" 页面，申请完成认证，认证需要缴纳 ￥ 300 元的费用。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/weixin/image.png"},"children":[]}]},{"type":"element","tag":"h2","props":{"id":"绑定公众号"},"children":[{"type":"text","value":"绑定公众号"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"管理中心"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"公众帐号"}]},{"type":"text","value":" 页面，绑定我们之前注册并认证过的微信公众帐号。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/weixin/image(1).png"},"children":[]}]},{"type":"element","tag":"h2","props":{"id":"创建网站应用"},"children":[{"type":"text","value":"创建网站应用"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"管理中心"}]},{"type":"text","value":" - "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"网站应用"}]},{"type":"text","value":" 页面，在这里创建一个网站应用。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/weixin/image(2).png"},"children":[]},{"type":"text","value":"\n审核通过以后，打开这个网站应用，这里会显示这个网站应用的 AppID，可以重置 AppSecret（应用密钥），我们要实现的微信功能暂时用不到这些东西。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/weixin/image(3).png"},"children":[]}]},{"type":"element","tag":"h2","props":{"id":"设置授权回调域"},"children":[{"type":"text","value":"设置授权回调域"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开网站应用页面，在开发信息的下面设置一下 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"授权回调域"}]},{"type":"text","value":"，在使用微信登录时，用户确定登录后，微信提供的登录页面会重定向到一个指定的地址，这个地址允许的域名需要在授权回调域这里设置一下。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/weixin/image(4).png"},"children":[]}]}],"toc":{"title":"","searchDepth":2,"depth":2,"links":[{"id":"微信开放平台-1","depth":2,"text":"微信开放平台"},{"id":"开发者资质认证","depth":2,"text":"开发者资质认证"},{"id":"绑定公众号","depth":2,"text":"绑定公众号"},{"id":"创建网站应用","depth":2,"text":"创建网站应用"},{"id":"设置授权回调域","depth":2,"text":"设置授权回调域"}]}},"_type":"markdown","_id":"content:docs:3.3rd:4.weixin.md","_source":"content","_file":"docs/3.3rd/4.weixin.md","_extension":"md"},{"_path":"/docs/3rd/lets-encrypt","_draft":false,"_partial":false,"_empty":false,"title":"Let’s Encrypt","description":"Let’s Encrypt 是一家免费颂发 SSL 证书的机构，我们在宁皓独立开发者训练营中开发的应用，最终需要部署到真正的服务器上，在配置 Web 服务使用 HTTPS 协议的时候，需要用到正规机构颂发的 SSL 证书。","excerpt":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"lets-encrypt"},"children":[{"type":"text","value":"Let’s Encrypt"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Let’s Encrypt 是一家免费颂发 SSL 证书的机构，我们在宁皓独立开发者训练营中开发的应用，最终需要部署到真正的服务器上，在配置 Web 服务使用 HTTPS 协议的时候，需要用到正规机构颂发的 SSL 证书。"}]},{"type":"element","tag":"h2","props":{"id":"流程"},"children":[{"type":"text","value":"流程"}]},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"配置域名指向我们的云务器的 IP 地址。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"在云服务器上安装 Nginx。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"配置 Nginx，创建 Web 服务器。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"重载 Nginx 服务，让新的配置生效。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"在云服务器上，安装申请 Let’s Encrypt 证书时需要使用的 certbot 工具。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"执行 certbot 命令申请 Let’s Encrypt 证书。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"重载 Nginx 服务，让新的配置生效。"}]}]},{"type":"element","tag":"h2","props":{"id":"添加域名-dns-记录"},"children":[{"type":"text","value":"添加域名 DNS 记录"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开域名服务商提供的域名管理界面，我们需要配置域名的 DNS 记录，记录类型选择 A，这种记录对应的值应该是一个 IP 地址，也就应该就是我们的云服务器的 IP 地址，主机记录可以根据自己的需求设置。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"为了演示，我在自己的 "},{"type":"element","tag":"a","props":{"href":"http://ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"ninghao.net"}]},{"type":"text","value":" 这个域名里配置了一条记录："}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"记录类型：A"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"主机记录：nid-ssl-demo"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"记录值：42.120.40.68"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"记录生效以后，"},{"type":"element","tag":"a","props":{"href":"http://nid-ssl-demo.ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"nid-ssl-demo.ninghao.net"}]},{"type":"text","value":" 就会指向我的一台云服务器，地址是 42.120.40.68。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/lets-encrypt/image.png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/lets-encrypt/image(1).png"},"children":[]},{"type":"text","value":"\n要确定配置的 DNS 记录已经生效了，可以在终端，使用 ping 这个命令："}]},{"type":"element","tag":"code","props":{"code":"ping nid-ssl-demo.ninghao.net\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"ping nid-ssl-demo.ninghao.net\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"注意观察返回的结果，在域名的后面会出现一个 IP 地址，这个 IP  地址如果是我们之前配置的 DNS  记录里的那个记录值，就说明 DNS 记录已经生效了。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"PING nid-ssl-demo.ninghao.net (42.120.40.68)\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"PING nid-ssl-demo.ninghao.net (42.120.40.68)\n"}]}]}]},{"type":"element","tag":"h2","props":{"id":"nginx"},"children":[{"type":"text","value":"NGINX"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在宁皓独立开发者训练营中，部署服务端应用、Web前端应用、配置 SSH 通道，这些都会用到使用 NGINX 创建的各种 Web 服务器。"}]},{"type":"element","tag":"h3","props":{"id":"安装与启动-nginx"},"children":[{"type":"text","value":"安装与启动 NGINX"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"先要使用 SSH 登录到远程的云服务器，然后通过执行一些命令来安装与启动 NGINX 服务。"}]},{"type":"element","tag":"h4","props":{"id":"在-ubuntu-系统里安装-nginx"},"children":[{"type":"text","value":"在 Ubuntu 系统里安装 NGINX"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"远程登录到服务器以后，使用 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"apt"}]},{"type":"text","value":" 直接安装一下 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"nginx"}]},{"type":"text","value":"："}]},{"type":"element","tag":"code","props":{"code":"sudo apt install nginx -y\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"sudo apt install nginx -y\n"}]}]}]},{"type":"element","tag":"h4","props":{"id":"设置开机自启动-nginx"},"children":[{"type":"text","value":"设置开机自启动 NGINX"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"让 NGINX 服务可以开机自启动，执行："}]},{"type":"element","tag":"code","props":{"code":"sudo systemctl enable nginx\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"sudo systemctl enable nginx\n"}]}]}]},{"type":"element","tag":"h4","props":{"id":"启动-nginx-服务"},"children":[{"type":"text","value":"启动 NGINX 服务"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"启动 NGINX 服务，执行："}]},{"type":"element","tag":"code","props":{"code":"sudo systemctl start nginx\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"sudo systemctl start nginx\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"启动以后，查看一下 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"nginx"}]},{"type":"text","value":" 这个服务的当前的状态，执行："}]},{"type":"element","tag":"code","props":{"code":"sudo systemctl status nginx\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"sudo systemctl status nginx\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果发现 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Active: active (running)"}]},{"type":"text","value":" ，表示这个服务正在运行。"}]},{"type":"element","tag":"h4","props":{"id":"测试-web-服务"},"children":[{"type":"text","value":"测试 Web 服务"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Nginx 服务启动之后，它就可以提供 Web 服务了。打开在本地电脑上的浏览器，访问服务器的 IP 地址，或者指向这个 IP  地址的域名，你会看到一个 Nginx 的欢迎页面。注意在地址栏上输入域名或者 IP 地址的时候，要使用 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"http://"}]},{"type":"text","value":" ，不要使用 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"https://"}]},{"type":"text","value":" 。比如："},{"type":"element","tag":"a","props":{"href":"http://xb2-node-api.ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"http://nid-ssl-demo.ninghao.net"}]}]},{"type":"text","value":" 。"}]},{"type":"element","tag":"h3","props":{"id":"创建-nginx-服务器"},"children":[{"type":"text","value":"创建 NGINX 服务器"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"通过 NGINX 的配置文件，可以在一台云服务器上同时创建多个 NGINX 服务器，每个服务器都有自己的一套配置，比如设置服务器监听的端口号，绑定自己的域名，配置服务器的 SSL 证书等等。我们先看一下如何创建一个 NGINX 的 Web  服务器，访问某个域名时，可以得到云服务器某个目录里的资源（各种文件）。"}]},{"type":"element","tag":"h4","props":{"id":"准备资源目录"},"children":[{"type":"text","value":"准备资源目录"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"先在云服务器的某个目录里面新建一个目录，这个目录一会儿会作为 NGINX 的 Web 服务器的主目录。"}]},{"type":"element","tag":"code","props":{"code":"mkdir -p /mnt/nid-ssl-demo\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"mkdir -p /mnt/nid-ssl-demo\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 /mnt 的下面，新建一个 nid-ssl-demo 目录，一会以儿配置好 NGINX 服务器以后，就可以通过指定的网址访问到这个目录里的资源了。"}]},{"type":"element","tag":"h4","props":{"id":"准备资源"},"children":[{"type":"text","value":"准备资源"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 Web 服务的资源目录要里，新建一个 HTML 文档。"}]},{"type":"element","tag":"code","props":{"code":"vi /mnt/nid-ssl-demo/index.html\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"vi /mnt/nid-ssl-demo/index.html\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"文件内容如下："}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"/mnt/nid-ssl-demo/index.html"}]}]},{"type":"element","tag":"code","props":{"code":"<html>\n  <head>\n    <meta charset=\"UTF-8\">\n   </head>\n  <body>\n    <h1 style=\"font-weight:200\">\n      宁皓独立开发者训练营 🏕 \n    </h1>\n  </body>\n</html>\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"<html>\n  <head>\n    <meta charset=\"UTF-8\">\n   </head>\n  <body>\n    <h1 style=\"font-weight:200\">\n      宁皓独立开发者训练营 🏕 \n    </h1>\n  </body>\n</html>\n"}]}]}]},{"type":"element","tag":"h4","props":{"id":"创建-nginx-服务器-1"},"children":[{"type":"text","value":"创建 NGINX 服务器"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"通过 NGINX 的配置文件，可以创建需要的各种服务器，比如 Web 服务器，反向代理服务器等等。在 NGINX 的配置文件里，用一个 server 区块就可以定义一个服务器，在这个 server 配置区块里，可以设置服务器监听的端口号，绑定的域名，使用的 SSL 证书等等。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"创建配置文件"}]}]},{"type":"element","tag":"code","props":{"code":"vi /etc/nginx/conf.d/nid-ssl-demo.ninghao.net.conf\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"vi /etc/nginx/conf.d/nid-ssl-demo.ninghao.net.conf\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"文件内容如下："}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"/etc/nginx/conf.d/nid-ssl-demo.ninghao.net.conf"}]}]},{"type":"element","tag":"code","props":{"code":"server {\n  listen 80;\n  server_name nid-ssl-demo.ninghao.net;\n  \n  location / {\n    root /mnt/nid-ssl-demo;\n    index index.html;\n  }\n}\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"server {\n  listen 80;\n  server_name nid-ssl-demo.ninghao.net;\n  \n  location / {\n    root /mnt/nid-ssl-demo;\n    index index.html;\n  }\n}\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"上面这段 NGINX 配置，用 server 区块定义了一个 Web 服务器，监听的端口号是 80，这是 HTTP 协议默认使用的端口号。server_name 指令设置的是绑定在这台服务器上的域名。然后用了一个 location 区块，在里面用 root 指令定义了根目录（资源目录），index 指令设置的是可以默认打开的一些东西，比如 index.html。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"重载 NGINX"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"添加了新的配置以后需要重载 NGINX 才能生效，执行："}]},{"type":"element","tag":"code","props":{"code":"sudo systemctl reload nginx\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"sudo systemctl reload nginx\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"测试"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"访问 "},{"type":"element","tag":"a","props":{"href":"http://nid-ssl-demo.ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"nid-ssl-demo.ninghao.net"}]},{"type":"text","value":" 的时候，默认就会获取到云服务器 /mnt/nid-ssl-demo 目录下的 index.html 这个文件。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/lets-encrypt/image(2).png"},"children":[]},{"type":"text","value":"\n注意在地址栏里的地址的左边，会显示 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"不安全"}]},{"type":"text","value":" ，这是因为当前使用的是 HTTP 协议访问的服务器，如果让这台 Web 服务器支持使用 HTTPS 协议访问，需要先从证书机构那里申请 SSL 证书，然后再配置 NGINX 服务器使用这个 SSL 证书。"}]},{"type":"element","tag":"h2","props":{"id":"certbot"},"children":[{"type":"text","value":"Certbot"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"申请 Let’s Encrypt 颁发的 SSL 证书，可以使用一个叫 Certbot 的命令行工具。Certbot 为不同的平台提供了各自的安装方法，下面我们会在 Ubuntu 系统里安装一下这个工具。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://certbot.eff.org/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://certbot.eff.org"}]}]},{"type":"element","tag":"h3","props":{"id":"在-ubuntu-系统里安装-certbot"},"children":[{"type":"text","value":"在 Ubuntu 系统里安装 Certbot"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"登录到使用 Ubuntu 系统的云服务器。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"安装 snapd"}]}]},{"type":"element","tag":"code","props":{"code":"sudo apt install snapd\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"sudo apt install snapd\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"安装 certbot"}]}]},{"type":"element","tag":"code","props":{"code":"sudo snap install --classic certbot\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"sudo snap install --classic certbot\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"设置可在全局执行 certbot 命令"}]}]},{"type":"element","tag":"code","props":{"code":"sudo ln -s /snap/bing/certbot /usr/bin/certbot\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"sudo ln -s /snap/bing/certbot /usr/bin/certbot\n"}]}]}]},{"type":"element","tag":"h3","props":{"id":"用-certbot-申请-lets-encrypt-颂发的-ssl-证书"},"children":[{"type":"text","value":"用 Certbot 申请 Let’s Encrypt 颂发的 SSL 证书"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"首先要确定已经配置好了域名的 DNS  记录，指向我们的云服务器，并在云服务器上安装配置好了 NGINX 服务器。"}]},{"type":"element","tag":"h4","props":{"id":"用-certbot--申请-ssl-证书"},"children":[{"type":"text","value":"用 certbot ** 申请 SSL 证书"}]},{"type":"element","tag":"code","props":{"code":"sudo certbot --nginx\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"sudo certbot --nginx\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"上面这个命令会申请签发 SSL  证书，并且会自动下载安装好申请的 SSL 证书，也就是它会修改我们网站的 NGINX 配置文件，在里面添加使用 SSL 证书需要的配置。"}]},{"type":"element","tag":"h4","props":{"id":"选择要申请-ssl-证书的域名"},"children":[{"type":"text","value":"选择要申请 SSL 证书的域名"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 Certbot 命令申请 SSL 证书时，会根据 NGINX 的服务器来判断可以申请 SSL 证书的域名。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行 certbot 命令时会提示我们选择要申请 SSL 证书的域名，输入列出的指定的序号，然后按回车确认。"}]},{"type":"element","tag":"code","props":{"code":"Saving debug log to /var/log/letsencrypt/letsencrypt.log\nPlugins selected: Authenticator nginx, Installer nginx\nStarting new HTTPS connection (1): acme-v02.api.letsencrypt.org\n\nWhich names would you like to activate HTTPS for?\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n1: nid-ssl-demo.ninghao.net\n...\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nSelect the appropriate numbers separated by commas and/or spaces, or leave input\nblank to select all options shown (Enter 'c' to cancel): \n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"Saving debug log to /var/log/letsencrypt/letsencrypt.log\nPlugins selected: Authenticator nginx, Installer nginx\nStarting new HTTPS connection (1): acme-v02.api.letsencrypt.org\n\nWhich names would you like to activate HTTPS for?\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n1: nid-ssl-demo.ninghao.net\n...\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nSelect the appropriate numbers separated by commas and/or spaces, or leave input\nblank to select all options shown (Enter 'c' to cancel): \n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"选择域名序号并按下回车以后，会提示是否需要将 HTTP 的访问重定向到 HTTPS。1 表示不重定向，2 表示重定向，如果选择要重定向 HTTP 的访问，certbot 会修改服务器对应的 NGINX 配置，将服务器的 HTTP 访问重定向到 HTTPS。"}]},{"type":"element","tag":"code","props":{"code":"Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n1: No redirect - Make no further changes to the webserver configuration.\n2: Redirect - Make all requests redirect to secure HTTPS access...\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nSelect the appropriate number [1-2] then [enter] (press 'c' to cancel): 2\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n1: No redirect - Make no further changes to the webserver configuration.\n2: Redirect - Make all requests redirect to secure HTTPS access...\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nSelect the appropriate number [1-2] then [enter] (press 'c' to cancel): 2\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"证书颂发成功就提示 Congratulations，申请的证书文件默认会放在 /etc/letsencrypt/live 目录对应的域名目录下面，比如 "},{"type":"element","tag":"a","props":{"href":"http://nid-ssl-demo.ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"nid-ssl-demo.ninghao.net"}]},{"type":"text","value":"，在这个目录里会有两个文件：fullchain.pem 与 privkey.pem。"}]},{"type":"element","tag":"code","props":{"code":"Congratulations! You have successfully enabled https://nid-ssl-demo.ninghao.net\n...\nIMPORTANT NOTES:\n - Congratulations! Your certificate and chain have been saved at:\n   /etc/letsencrypt/live/nid-ssl-demo.ninghao.net/fullchain.pem\n   Your key file has been saved at:\n   /etc/letsencrypt/live/nid-ssl-demo.ninghao.net/privkey.pem\n   Your cert will expire on 2022-08-13. To obtain a new or tweaked\n   version of this certificate in the future, simply run certbot again\n   with the \"certonly\" option. To non-interactively renew *all* of\n   your certificates, run \"certbot renew\"\n - ...\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"Congratulations! You have successfully enabled https://nid-ssl-demo.ninghao.net\n...\nIMPORTANT NOTES:\n - Congratulations! Your certificate and chain have been saved at:\n   /etc/letsencrypt/live/nid-ssl-demo.ninghao.net/fullchain.pem\n   Your key file has been saved at:\n   /etc/letsencrypt/live/nid-ssl-demo.ninghao.net/privkey.pem\n   Your cert will expire on 2022-08-13. To obtain a new or tweaked\n   version of this certificate in the future, simply run certbot again\n   with the \"certonly\" option. To non-interactively renew *all* of\n   your certificates, run \"certbot renew\"\n - ...\n"}]}]}]},{"type":"element","tag":"h4","props":{"id":"观察网站的-nginx-配置文件"},"children":[{"type":"text","value":"观察网站的 NGINX 配置文件"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"观察一下申请了 SSL 证书的域名相关的 NGINX 配置文件，你会发现一些结尾有 # managed by Certbot 注释的配置，这些东西是 certbot 工具加上去的。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"/etc/nginx/conf.d/nid-ssl-demo.ninghao.net.conf"}]}]},{"type":"element","tag":"code","props":{"code":"server {\n  listen 443 ssl; # managed by Certbot\n  server_name nid-ssl-demo.ninghao.net;\n\n  ssl_certificate /etc/letsencrypt/live/nid-ssl-demo.ninghao.net/fullchain.pem; # managed by Certbot\n  ssl_certificate_key /etc/letsencrypt/live/nid-ssl-demo.ninghao.net/privkey.pem; # managed by Certbot\n  include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot\n  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot\n\n  location / {\n    root /mnt/nid-ssl-demo;\n    index index.html;\n  }\n}\n\nserver {\n  listen 80;\n  server_name nid-ssl-demo.ninghao.net;\n\n  if ($host = nid-ssl-demo.ninghao.net) {\n    return 301 https://$host$request_uri;\n  } # managed by Certbot\n\n  return 404; # managed by Certbot\n}\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"server {\n  listen 443 ssl; # managed by Certbot\n  server_name nid-ssl-demo.ninghao.net;\n\n  ssl_certificate /etc/letsencrypt/live/nid-ssl-demo.ninghao.net/fullchain.pem; # managed by Certbot\n  ssl_certificate_key /etc/letsencrypt/live/nid-ssl-demo.ninghao.net/privkey.pem; # managed by Certbot\n  include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot\n  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot\n\n  location / {\n    root /mnt/nid-ssl-demo;\n    index index.html;\n  }\n}\n\nserver {\n  listen 80;\n  server_name nid-ssl-demo.ninghao.net;\n\n  if ($host = nid-ssl-demo.ninghao.net) {\n    return 301 https://$host$request_uri;\n  } # managed by Certbot\n\n  return 404; # managed by Certbot\n}\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"你会发现在这个 NGINX 的配置文件里有两个 server 区块，一个监听的端口是 443，一个监听的是 80 端口，这个监听 80 端口的服务器里面做了重定向的配置，服务器接收到的访问会被重定向到 "},{"type":"element","tag":"a","props":{"href":"https://%24host%24request_uri/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://$host$request_uri"}]},{"type":"text","value":" 。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在监听 443 端口的服务器里面，会多了一些配置信息，这些配置信息是由 certbot 加上去的，主要就是指定 SSL 证书文件的位置，还有一些相关的配置。"}]},{"type":"element","tag":"h4","props":{"id":"用-https-协议访问主机名"},"children":[{"type":"text","value":"用 HTTPS 协议访问主机名"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在浏览器测试一下，使用 HTTPS 协议访问申请并配置了 SSL 证书的主机名。观察地址栏里的地址左侧，会出现一个小锁着图标，说明当前是通过 HTTPS 协议访问的服务。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/lets-encrypt/image(3).png"},"children":[]},{"type":"text","value":"\n点开小锁头图标，然后点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"证书有效"}]},{"type":"text","value":"，会显示 SSL 证书相关信息。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/lets-encrypt/image(4).png"},"children":[]},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/lets-encrypt/image(5).png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"注意事项"},"children":[{"type":"text","value":"注意事项"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"用 Certbot 申请的 Let’s Encrypt 证书，有效期是 3 个月，也就是 3 个月以后你需要重新执行 certbot 命令为指定的域名申请新的 SSL 证书。"}]}]},"body":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"lets-encrypt"},"children":[{"type":"text","value":"Let’s Encrypt"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Let’s Encrypt 是一家免费颂发 SSL 证书的机构，我们在宁皓独立开发者训练营中开发的应用，最终需要部署到真正的服务器上，在配置 Web 服务使用 HTTPS 协议的时候，需要用到正规机构颂发的 SSL 证书。"}]},{"type":"element","tag":"h2","props":{"id":"流程"},"children":[{"type":"text","value":"流程"}]},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"配置域名指向我们的云务器的 IP 地址。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"在云服务器上安装 Nginx。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"配置 Nginx，创建 Web 服务器。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"重载 Nginx 服务，让新的配置生效。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"在云服务器上，安装申请 Let’s Encrypt 证书时需要使用的 certbot 工具。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"执行 certbot 命令申请 Let’s Encrypt 证书。"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"重载 Nginx 服务，让新的配置生效。"}]}]},{"type":"element","tag":"h2","props":{"id":"添加域名-dns-记录"},"children":[{"type":"text","value":"添加域名 DNS 记录"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开域名服务商提供的域名管理界面，我们需要配置域名的 DNS 记录，记录类型选择 A，这种记录对应的值应该是一个 IP 地址，也就应该就是我们的云服务器的 IP 地址，主机记录可以根据自己的需求设置。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"为了演示，我在自己的 "},{"type":"element","tag":"a","props":{"href":"http://ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"ninghao.net"}]},{"type":"text","value":" 这个域名里配置了一条记录："}]},{"type":"element","tag":"ul","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"记录类型：A"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"主机记录：nid-ssl-demo"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"记录值：42.120.40.68"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"记录生效以后，"},{"type":"element","tag":"a","props":{"href":"http://nid-ssl-demo.ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"nid-ssl-demo.ninghao.net"}]},{"type":"text","value":" 就会指向我的一台云服务器，地址是 42.120.40.68。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/lets-encrypt/image.png"},"children":[]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/lets-encrypt/image(1).png"},"children":[]},{"type":"text","value":"\n要确定配置的 DNS 记录已经生效了，可以在终端，使用 ping 这个命令："}]},{"type":"element","tag":"code","props":{"code":"ping nid-ssl-demo.ninghao.net\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"ping nid-ssl-demo.ninghao.net\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"注意观察返回的结果，在域名的后面会出现一个 IP 地址，这个 IP  地址如果是我们之前配置的 DNS  记录里的那个记录值，就说明 DNS 记录已经生效了。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"PING nid-ssl-demo.ninghao.net (42.120.40.68)\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"PING nid-ssl-demo.ninghao.net (42.120.40.68)\n"}]}]}]},{"type":"element","tag":"h2","props":{"id":"nginx"},"children":[{"type":"text","value":"NGINX"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在宁皓独立开发者训练营中，部署服务端应用、Web前端应用、配置 SSH 通道，这些都会用到使用 NGINX 创建的各种 Web 服务器。"}]},{"type":"element","tag":"h3","props":{"id":"安装与启动-nginx"},"children":[{"type":"text","value":"安装与启动 NGINX"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"先要使用 SSH 登录到远程的云服务器，然后通过执行一些命令来安装与启动 NGINX 服务。"}]},{"type":"element","tag":"h4","props":{"id":"在-ubuntu-系统里安装-nginx"},"children":[{"type":"text","value":"在 Ubuntu 系统里安装 NGINX"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"远程登录到服务器以后，使用 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"apt"}]},{"type":"text","value":" 直接安装一下 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"nginx"}]},{"type":"text","value":"："}]},{"type":"element","tag":"code","props":{"code":"sudo apt install nginx -y\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"sudo apt install nginx -y\n"}]}]}]},{"type":"element","tag":"h4","props":{"id":"设置开机自启动-nginx"},"children":[{"type":"text","value":"设置开机自启动 NGINX"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"让 NGINX 服务可以开机自启动，执行："}]},{"type":"element","tag":"code","props":{"code":"sudo systemctl enable nginx\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"sudo systemctl enable nginx\n"}]}]}]},{"type":"element","tag":"h4","props":{"id":"启动-nginx-服务"},"children":[{"type":"text","value":"启动 NGINX 服务"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"启动 NGINX 服务，执行："}]},{"type":"element","tag":"code","props":{"code":"sudo systemctl start nginx\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"sudo systemctl start nginx\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"启动以后，查看一下 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"nginx"}]},{"type":"text","value":" 这个服务的当前的状态，执行："}]},{"type":"element","tag":"code","props":{"code":"sudo systemctl status nginx\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"sudo systemctl status nginx\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果发现 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"Active: active (running)"}]},{"type":"text","value":" ，表示这个服务正在运行。"}]},{"type":"element","tag":"h4","props":{"id":"测试-web-服务"},"children":[{"type":"text","value":"测试 Web 服务"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"Nginx 服务启动之后，它就可以提供 Web 服务了。打开在本地电脑上的浏览器，访问服务器的 IP 地址，或者指向这个 IP  地址的域名，你会看到一个 Nginx 的欢迎页面。注意在地址栏上输入域名或者 IP 地址的时候，要使用 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"http://"}]},{"type":"text","value":" ，不要使用 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"https://"}]},{"type":"text","value":" 。比如："},{"type":"element","tag":"a","props":{"href":"http://xb2-node-api.ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"http://nid-ssl-demo.ninghao.net"}]}]},{"type":"text","value":" 。"}]},{"type":"element","tag":"h3","props":{"id":"创建-nginx-服务器"},"children":[{"type":"text","value":"创建 NGINX 服务器"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"通过 NGINX 的配置文件，可以在一台云服务器上同时创建多个 NGINX 服务器，每个服务器都有自己的一套配置，比如设置服务器监听的端口号，绑定自己的域名，配置服务器的 SSL 证书等等。我们先看一下如何创建一个 NGINX 的 Web  服务器，访问某个域名时，可以得到云服务器某个目录里的资源（各种文件）。"}]},{"type":"element","tag":"h4","props":{"id":"准备资源目录"},"children":[{"type":"text","value":"准备资源目录"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"先在云服务器的某个目录里面新建一个目录，这个目录一会儿会作为 NGINX 的 Web 服务器的主目录。"}]},{"type":"element","tag":"code","props":{"code":"mkdir -p /mnt/nid-ssl-demo\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"mkdir -p /mnt/nid-ssl-demo\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 /mnt 的下面，新建一个 nid-ssl-demo 目录，一会以儿配置好 NGINX 服务器以后，就可以通过指定的网址访问到这个目录里的资源了。"}]},{"type":"element","tag":"h4","props":{"id":"准备资源"},"children":[{"type":"text","value":"准备资源"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在 Web 服务的资源目录要里，新建一个 HTML 文档。"}]},{"type":"element","tag":"code","props":{"code":"vi /mnt/nid-ssl-demo/index.html\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"vi /mnt/nid-ssl-demo/index.html\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"文件内容如下："}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"/mnt/nid-ssl-demo/index.html"}]}]},{"type":"element","tag":"code","props":{"code":"<html>\n  <head>\n    <meta charset=\"UTF-8\">\n   </head>\n  <body>\n    <h1 style=\"font-weight:200\">\n      宁皓独立开发者训练营 🏕 \n    </h1>\n  </body>\n</html>\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"<html>\n  <head>\n    <meta charset=\"UTF-8\">\n   </head>\n  <body>\n    <h1 style=\"font-weight:200\">\n      宁皓独立开发者训练营 🏕 \n    </h1>\n  </body>\n</html>\n"}]}]}]},{"type":"element","tag":"h4","props":{"id":"创建-nginx-服务器-1"},"children":[{"type":"text","value":"创建 NGINX 服务器"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"通过 NGINX 的配置文件，可以创建需要的各种服务器，比如 Web 服务器，反向代理服务器等等。在 NGINX 的配置文件里，用一个 server 区块就可以定义一个服务器，在这个 server 配置区块里，可以设置服务器监听的端口号，绑定的域名，使用的 SSL 证书等等。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"创建配置文件"}]}]},{"type":"element","tag":"code","props":{"code":"vi /etc/nginx/conf.d/nid-ssl-demo.ninghao.net.conf\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"vi /etc/nginx/conf.d/nid-ssl-demo.ninghao.net.conf\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"文件内容如下："}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"/etc/nginx/conf.d/nid-ssl-demo.ninghao.net.conf"}]}]},{"type":"element","tag":"code","props":{"code":"server {\n  listen 80;\n  server_name nid-ssl-demo.ninghao.net;\n  \n  location / {\n    root /mnt/nid-ssl-demo;\n    index index.html;\n  }\n}\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"server {\n  listen 80;\n  server_name nid-ssl-demo.ninghao.net;\n  \n  location / {\n    root /mnt/nid-ssl-demo;\n    index index.html;\n  }\n}\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"上面这段 NGINX 配置，用 server 区块定义了一个 Web 服务器，监听的端口号是 80，这是 HTTP 协议默认使用的端口号。server_name 指令设置的是绑定在这台服务器上的域名。然后用了一个 location 区块，在里面用 root 指令定义了根目录（资源目录），index 指令设置的是可以默认打开的一些东西，比如 index.html。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"重载 NGINX"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"添加了新的配置以后需要重载 NGINX 才能生效，执行："}]},{"type":"element","tag":"code","props":{"code":"sudo systemctl reload nginx\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"sudo systemctl reload nginx\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"测试"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"访问 "},{"type":"element","tag":"a","props":{"href":"http://nid-ssl-demo.ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"nid-ssl-demo.ninghao.net"}]},{"type":"text","value":" 的时候，默认就会获取到云服务器 /mnt/nid-ssl-demo 目录下的 index.html 这个文件。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/lets-encrypt/image(2).png"},"children":[]},{"type":"text","value":"\n注意在地址栏里的地址的左边，会显示 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"不安全"}]},{"type":"text","value":" ，这是因为当前使用的是 HTTP 协议访问的服务器，如果让这台 Web 服务器支持使用 HTTPS 协议访问，需要先从证书机构那里申请 SSL 证书，然后再配置 NGINX 服务器使用这个 SSL 证书。"}]},{"type":"element","tag":"h2","props":{"id":"certbot"},"children":[{"type":"text","value":"Certbot"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"申请 Let’s Encrypt 颁发的 SSL 证书，可以使用一个叫 Certbot 的命令行工具。Certbot 为不同的平台提供了各自的安装方法，下面我们会在 Ubuntu 系统里安装一下这个工具。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"官方网站"}]},{"type":"text","value":"："},{"type":"element","tag":"a","props":{"href":"https://certbot.eff.org/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://certbot.eff.org"}]}]},{"type":"element","tag":"h3","props":{"id":"在-ubuntu-系统里安装-certbot"},"children":[{"type":"text","value":"在 Ubuntu 系统里安装 Certbot"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"登录到使用 Ubuntu 系统的云服务器。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"安装 snapd"}]}]},{"type":"element","tag":"code","props":{"code":"sudo apt install snapd\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"sudo apt install snapd\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"安装 certbot"}]}]},{"type":"element","tag":"code","props":{"code":"sudo snap install --classic certbot\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"sudo snap install --classic certbot\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"设置可在全局执行 certbot 命令"}]}]},{"type":"element","tag":"code","props":{"code":"sudo ln -s /snap/bing/certbot /usr/bin/certbot\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"sudo ln -s /snap/bing/certbot /usr/bin/certbot\n"}]}]}]},{"type":"element","tag":"h3","props":{"id":"用-certbot-申请-lets-encrypt-颂发的-ssl-证书"},"children":[{"type":"text","value":"用 Certbot 申请 Let’s Encrypt 颂发的 SSL 证书"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"首先要确定已经配置好了域名的 DNS  记录，指向我们的云服务器，并在云服务器上安装配置好了 NGINX 服务器。"}]},{"type":"element","tag":"h4","props":{"id":"用-certbot--申请-ssl-证书"},"children":[{"type":"text","value":"用 certbot ** 申请 SSL 证书"}]},{"type":"element","tag":"code","props":{"code":"sudo certbot --nginx\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"sudo certbot --nginx\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"上面这个命令会申请签发 SSL  证书，并且会自动下载安装好申请的 SSL 证书，也就是它会修改我们网站的 NGINX 配置文件，在里面添加使用 SSL 证书需要的配置。"}]},{"type":"element","tag":"h4","props":{"id":"选择要申请-ssl-证书的域名"},"children":[{"type":"text","value":"选择要申请 SSL 证书的域名"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 Certbot 命令申请 SSL 证书时，会根据 NGINX 的服务器来判断可以申请 SSL 证书的域名。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"执行 certbot 命令时会提示我们选择要申请 SSL 证书的域名，输入列出的指定的序号，然后按回车确认。"}]},{"type":"element","tag":"code","props":{"code":"Saving debug log to /var/log/letsencrypt/letsencrypt.log\nPlugins selected: Authenticator nginx, Installer nginx\nStarting new HTTPS connection (1): acme-v02.api.letsencrypt.org\n\nWhich names would you like to activate HTTPS for?\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n1: nid-ssl-demo.ninghao.net\n...\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nSelect the appropriate numbers separated by commas and/or spaces, or leave input\nblank to select all options shown (Enter 'c' to cancel): \n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"Saving debug log to /var/log/letsencrypt/letsencrypt.log\nPlugins selected: Authenticator nginx, Installer nginx\nStarting new HTTPS connection (1): acme-v02.api.letsencrypt.org\n\nWhich names would you like to activate HTTPS for?\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n1: nid-ssl-demo.ninghao.net\n...\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nSelect the appropriate numbers separated by commas and/or spaces, or leave input\nblank to select all options shown (Enter 'c' to cancel): \n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"选择域名序号并按下回车以后，会提示是否需要将 HTTP 的访问重定向到 HTTPS。1 表示不重定向，2 表示重定向，如果选择要重定向 HTTP 的访问，certbot 会修改服务器对应的 NGINX 配置，将服务器的 HTTP 访问重定向到 HTTPS。"}]},{"type":"element","tag":"code","props":{"code":"Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n1: No redirect - Make no further changes to the webserver configuration.\n2: Redirect - Make all requests redirect to secure HTTPS access...\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nSelect the appropriate number [1-2] then [enter] (press 'c' to cancel): 2\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n1: No redirect - Make no further changes to the webserver configuration.\n2: Redirect - Make all requests redirect to secure HTTPS access...\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\nSelect the appropriate number [1-2] then [enter] (press 'c' to cancel): 2\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"证书颂发成功就提示 Congratulations，申请的证书文件默认会放在 /etc/letsencrypt/live 目录对应的域名目录下面，比如 "},{"type":"element","tag":"a","props":{"href":"http://nid-ssl-demo.ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"nid-ssl-demo.ninghao.net"}]},{"type":"text","value":"，在这个目录里会有两个文件：fullchain.pem 与 privkey.pem。"}]},{"type":"element","tag":"code","props":{"code":"Congratulations! You have successfully enabled https://nid-ssl-demo.ninghao.net\n...\nIMPORTANT NOTES:\n - Congratulations! Your certificate and chain have been saved at:\n   /etc/letsencrypt/live/nid-ssl-demo.ninghao.net/fullchain.pem\n   Your key file has been saved at:\n   /etc/letsencrypt/live/nid-ssl-demo.ninghao.net/privkey.pem\n   Your cert will expire on 2022-08-13. To obtain a new or tweaked\n   version of this certificate in the future, simply run certbot again\n   with the \"certonly\" option. To non-interactively renew *all* of\n   your certificates, run \"certbot renew\"\n - ...\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"Congratulations! You have successfully enabled https://nid-ssl-demo.ninghao.net\n...\nIMPORTANT NOTES:\n - Congratulations! Your certificate and chain have been saved at:\n   /etc/letsencrypt/live/nid-ssl-demo.ninghao.net/fullchain.pem\n   Your key file has been saved at:\n   /etc/letsencrypt/live/nid-ssl-demo.ninghao.net/privkey.pem\n   Your cert will expire on 2022-08-13. To obtain a new or tweaked\n   version of this certificate in the future, simply run certbot again\n   with the \"certonly\" option. To non-interactively renew *all* of\n   your certificates, run \"certbot renew\"\n - ...\n"}]}]}]},{"type":"element","tag":"h4","props":{"id":"观察网站的-nginx-配置文件"},"children":[{"type":"text","value":"观察网站的 NGINX 配置文件"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"观察一下申请了 SSL 证书的域名相关的 NGINX 配置文件，你会发现一些结尾有 # managed by Certbot 注释的配置，这些东西是 certbot 工具加上去的。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"/etc/nginx/conf.d/nid-ssl-demo.ninghao.net.conf"}]}]},{"type":"element","tag":"code","props":{"code":"server {\n  listen 443 ssl; # managed by Certbot\n  server_name nid-ssl-demo.ninghao.net;\n\n  ssl_certificate /etc/letsencrypt/live/nid-ssl-demo.ninghao.net/fullchain.pem; # managed by Certbot\n  ssl_certificate_key /etc/letsencrypt/live/nid-ssl-demo.ninghao.net/privkey.pem; # managed by Certbot\n  include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot\n  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot\n\n  location / {\n    root /mnt/nid-ssl-demo;\n    index index.html;\n  }\n}\n\nserver {\n  listen 80;\n  server_name nid-ssl-demo.ninghao.net;\n\n  if ($host = nid-ssl-demo.ninghao.net) {\n    return 301 https://$host$request_uri;\n  } # managed by Certbot\n\n  return 404; # managed by Certbot\n}\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"server {\n  listen 443 ssl; # managed by Certbot\n  server_name nid-ssl-demo.ninghao.net;\n\n  ssl_certificate /etc/letsencrypt/live/nid-ssl-demo.ninghao.net/fullchain.pem; # managed by Certbot\n  ssl_certificate_key /etc/letsencrypt/live/nid-ssl-demo.ninghao.net/privkey.pem; # managed by Certbot\n  include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot\n  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot\n\n  location / {\n    root /mnt/nid-ssl-demo;\n    index index.html;\n  }\n}\n\nserver {\n  listen 80;\n  server_name nid-ssl-demo.ninghao.net;\n\n  if ($host = nid-ssl-demo.ninghao.net) {\n    return 301 https://$host$request_uri;\n  } # managed by Certbot\n\n  return 404; # managed by Certbot\n}\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"你会发现在这个 NGINX 的配置文件里有两个 server 区块，一个监听的端口是 443，一个监听的是 80 端口，这个监听 80 端口的服务器里面做了重定向的配置，服务器接收到的访问会被重定向到 "},{"type":"element","tag":"a","props":{"href":"https://%24host%24request_uri/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"https://$host$request_uri"}]},{"type":"text","value":" 。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在监听 443 端口的服务器里面，会多了一些配置信息，这些配置信息是由 certbot 加上去的，主要就是指定 SSL 证书文件的位置，还有一些相关的配置。"}]},{"type":"element","tag":"h4","props":{"id":"用-https-协议访问主机名"},"children":[{"type":"text","value":"用 HTTPS 协议访问主机名"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在浏览器测试一下，使用 HTTPS 协议访问申请并配置了 SSL 证书的主机名。观察地址栏里的地址左侧，会出现一个小锁着图标，说明当前是通过 HTTPS 协议访问的服务。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/lets-encrypt/image(3).png"},"children":[]},{"type":"text","value":"\n点开小锁头图标，然后点击 "},{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"证书有效"}]},{"type":"text","value":"，会显示 SSL 证书相关信息。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/lets-encrypt/image(4).png"},"children":[]},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/lets-encrypt/image(5).png"},"children":[]}]},{"type":"element","tag":"h3","props":{"id":"注意事项"},"children":[{"type":"text","value":"注意事项"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"用 Certbot 申请的 Let’s Encrypt 证书，有效期是 3 个月，也就是 3 个月以后你需要重新执行 certbot 命令为指定的域名申请新的 SSL 证书。"}]}],"toc":{"title":"","searchDepth":2,"depth":2,"links":[{"id":"流程","depth":2,"text":"流程"},{"id":"添加域名-dns-记录","depth":2,"text":"添加域名 DNS 记录"},{"id":"nginx","depth":2,"text":"NGINX","children":[{"id":"安装与启动-nginx","depth":3,"text":"安装与启动 NGINX"},{"id":"创建-nginx-服务器","depth":3,"text":"创建 NGINX 服务器"}]},{"id":"certbot","depth":2,"text":"Certbot","children":[{"id":"在-ubuntu-系统里安装-certbot","depth":3,"text":"在 Ubuntu 系统里安装 Certbot"},{"id":"用-certbot-申请-lets-encrypt-颂发的-ssl-证书","depth":3,"text":"用 Certbot 申请 Let’s Encrypt 颂发的 SSL 证书"},{"id":"注意事项","depth":3,"text":"注意事项"}]}]}},"_type":"markdown","_id":"content:docs:3.3rd:5.lets-encrypt.md","_source":"content","_file":"docs/3.3rd/5.lets-encrypt.md","_extension":"md"},{"_path":"/docs/3rd/ssh-tunnel","_draft":false,"_partial":false,"_empty":false,"title":"通过互联网访问本地服务","description":"使用 NGINX 配合 SSH 通道，可以把来自互联网的访问转发到在本地电脑上运行的服务来处理。","excerpt":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"通过互联网访问本地服务"},"children":[{"type":"text","value":"通过互联网访问本地服务"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 NGINX 配合 SSH 通道，可以把来自互联网的访问转发到在本地电脑上运行的服务来处理。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在宁皓独立开发者训练营中调试微信支付与支付宝支付时，服务端应用需要处理这些支付服务发送过来的支付结果。为了能让在本地电脑上运行的服务端应用收到这些支付结果，就必须得保证能够直接通过互联网访问在本地电脑上运行的应用，可以使用 NGINX 与 SSH 通道的方式实现这个需求。"}]},{"type":"element","tag":"h2","props":{"id":"流程"},"children":[{"type":"text","value":"流程"}]},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"准备一台能通过互联网访问到的云服务器"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"配置域名 DNS 记录，指向云服务器的 IP 地址"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"在云服务器上安装配置 NGINX 反向代理服务器"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"在本地电脑，使用 SSH 在云服务器与本地电脑之间打个通道"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"先配置好一台能在互联网上被访问到的反向代理服务器（Web 服务器），服务器会将请求转发至本地服务器的某个特定端口，然后用 SSH 将这个服务器上的特定端口（比如 7689）与本地电脑上的某个特定端口（比如 3000）连通，这个本地电脑上的特定端口就是运行本地服务端应用时使用的端口，这样当有人在互联网访问这个反向代理服务器时，实际提供服务的是在我们本地电脑上运行的服务端应用。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/ssh-tunnel/image.png"},"children":[]}]},{"type":"element","tag":"h2","props":{"id":"添加域名-dns-记录"},"children":[{"type":"text","value":"添加域名 DNS 记录"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在域名服务商提供的域名管理界面，添加一条 A 类型的 DNS 记录，记录值是我们的云服务器的 IP 地址，主机名可以随意设置。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"比如我为 "},{"type":"element","tag":"a","props":{"href":"http://ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"ninghao.net"}]},{"type":"text","value":" 这个域名添加了一条 DNS 记录，让 "},{"type":"element","tag":"a","props":{"href":"http://sandbox.ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"sandbox.ninghao.net"}]},{"type":"text","value":" 指向我的一台云服务器，准备好 NGINX 服务器与 SSH 通道以后，就可以在互联网通过这个域名直接访问在我本地电脑上运行的应用了。"}]},{"type":"element","tag":"h2","props":{"id":"创建反向代理服务器"},"children":[{"type":"text","value":"创建反向代理服务器"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 SSH 远程登录云服务器以后，需要配置一个 NGINX 的反向代理服务器。"}]},{"type":"element","tag":"code","props":{"code":"vi /etc/nginx/conf.d/sandbox.ninghao.net.conf\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"vi /etc/nginx/conf.d/sandbox.ninghao.net.conf\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"内容如下："}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"/etc/nginx/conf.d/sandbox.ninghao.net.conf"}]}]},{"type":"element","tag":"code","props":{"code":"server {\n  listen                      80;\n  server_name                 sandbox.ninghao.net;\n  client_max_body_size        200m;\n\n  location / {\n    proxy_set_header          X-Real-IP $remote_addr;\n    proxy_set_header          X-Real-Port $remote_port;\n    proxy_set_header          X-Forwarded-For $proxy_add_x_forwarded_for;\n    proxy_set_header          Host $http_host;\n    proxy_set_header          X-Forwarded-Proto $scheme;\n    proxy_http_version 1.1;\n    proxy_set_header          Upgrade $http_upgrade;\n    proxy_set_header          Connection \"upgrade\";\n    proxy_redirect            off;\n    expires                   off;\n    sendfile                  off;\n    proxy_pass                http://127.0.0.1:7689;\n  }\n}\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"server {\n  listen                      80;\n  server_name                 sandbox.ninghao.net;\n  client_max_body_size        200m;\n\n  location / {\n    proxy_set_header          X-Real-IP $remote_addr;\n    proxy_set_header          X-Real-Port $remote_port;\n    proxy_set_header          X-Forwarded-For $proxy_add_x_forwarded_for;\n    proxy_set_header          Host $http_host;\n    proxy_set_header          X-Forwarded-Proto $scheme;\n    proxy_http_version 1.1;\n    proxy_set_header          Upgrade $http_upgrade;\n    proxy_set_header          Connection \"upgrade\";\n    proxy_redirect            off;\n    expires                   off;\n    sendfile                  off;\n    proxy_pass                http://127.0.0.1:7689;\n  }\n}\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"上面这个 NGINX 配置，创建了一台反向代理服务器，监听的端口是 80，绑定的域名是 "},{"type":"element","tag":"a","props":{"href":"http://sandbox.ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"sandbox.ninghao.net"}]},{"type":"text","value":"，在用一个 location 区块，配置一下代理服务相关的东西，注意这里的 proxy_pass 指令的值被设置成了 "},{"type":"element","tag":"a","props":{"href":"http://127.0.0.1:7689%EF%BC%8C%E4%B9%9F%E5%B0%B1%E6%98%AF%E5%BD%93%E6%9C%89%E4%BA%BA%E8%AE%BF%E9%97%AE/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"http://127.0.0.1:7689，意思是将访问转发到本地"}]},{"type":"text","value":"服务器的 7689 这个端口。"},{"type":"element","tag":"a","props":{"href":"http://127.0.0.1:7689%EF%BC%8C%E4%B9%9F%E5%B0%B1%E6%98%AF%E5%BD%93%E6%9C%89%E4%BA%BA%E8%AE%BF%E9%97%AE/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"也就是当有人访问"}]},{"type":"text","value":" "},{"type":"element","tag":"a","props":{"href":"http://sandbox.ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"http://sandbox.ninghao.net"}]},{"type":"text","value":" 的时候，这个反向代理服务器会将请求转给本地服务器的 7689 这个端口。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果你希望通过 HTTPS 访问这个服务器，可以使用 certbot，为 "},{"type":"element","tag":"a","props":{"href":"http://sandbox.ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"sandbox.ninghao.net"}]},{"type":"text","value":" 这个域名申请一个 SSL 证书。完成以后，这个 NGINX 服务器的配置文件看起来会像下面这样："}]},{"type":"element","tag":"code","props":{"code":"server {\n  listen                      443 ssl http2;\n  server_name                 sandbox.ninghao.net;\n  ...\n\n  ssl_certificate /etc/letsencrypt/live/sandbox.ninghao.net/fullchain.pem; # managed by Certbot\n  ssl_certificate_key /etc/letsencrypt/live/sandbox.ninghao.net/privkey.pem; # managed by Certbot\n  include /etc/letsencrypt/options-ssl-nginx.conf;\n  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;\n\n  location / {\n    ...\n    proxy_pass                http://127.0.0.1:7689;\n  }\n}\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"server {\n  listen                      443 ssl http2;\n  server_name                 sandbox.ninghao.net;\n  ...\n\n  ssl_certificate /etc/letsencrypt/live/sandbox.ninghao.net/fullchain.pem; # managed by Certbot\n  ssl_certificate_key /etc/letsencrypt/live/sandbox.ninghao.net/privkey.pem; # managed by Certbot\n  include /etc/letsencrypt/options-ssl-nginx.conf;\n  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;\n\n  location / {\n    ...\n    proxy_pass                http://127.0.0.1:7689;\n  }\n}\n"}]}]}]},{"type":"element","tag":"h2","props":{"id":"ssh-通道"},"children":[{"type":"text","value":"SSH 通道"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"回到本地电脑上，确定在本地电脑上的服务端应用已经运行了，这个应用通过 3000 这个端口提供服务。现在我们要使用 SSH，在本地电脑与云服务器之间打个通道。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"ssh -vnNT -R 7689:localhost:3000 root@42.120.40.68\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"ssh -vnNT -R 7689:localhost:3000 root@42.120.40.68\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开本地电脑的终端，执行 ssh 命令，要加上一些参数，比如 vnNT 这四个参数，还需要一个 R 参数，具体这些参数表示的意思可以执行 man ssh 命令，查看 ssh 命令的使用说明。7689 指的是远程云服务器上的一个端口，还记得上面我们配置的那台 NGINX 反向代理服务器吗？它会将访问转发到 7689 这个端口。localhost:3000 是我在本地电脑上运行的服务端应用的地址。root 是远程登录服务器时使用的用户名，42.120.40.68 是我的云服务器的 IP 地址。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"一切正常会出现下面这样的文字："}]},{"type":"element","tag":"code","props":{"code":"remote forward success for: listen 7689, connect localhost:3000\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"remote forward success for: listen 7689, connect localhost:3000\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"现在如果有人通过互联网访问 "},{"type":"element","tag":"a","props":{"href":"http://sandbox.ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"sandbox.ninghao.net"}]},{"type":"text","value":"，服务器会将请求转发到 7689 这个端口，又因为我们使用 SSH 在本地电脑与云服务器之间打了一个通道，这个通道的服务器这一边，设置使用的端口号是 7689，本地电脑这边的地址是 localhost:3000，这样访问 sandbox.ninghao.net，实际提供服务的就是在我们本地电脑上运行的应用。"}]}]},"body":{"type":"root","children":[{"type":"element","tag":"h1","props":{"id":"通过互联网访问本地服务"},"children":[{"type":"text","value":"通过互联网访问本地服务"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 NGINX 配合 SSH 通道，可以把来自互联网的访问转发到在本地电脑上运行的服务来处理。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在宁皓独立开发者训练营中调试微信支付与支付宝支付时，服务端应用需要处理这些支付服务发送过来的支付结果。为了能让在本地电脑上运行的服务端应用收到这些支付结果，就必须得保证能够直接通过互联网访问在本地电脑上运行的应用，可以使用 NGINX 与 SSH 通道的方式实现这个需求。"}]},{"type":"element","tag":"h2","props":{"id":"流程"},"children":[{"type":"text","value":"流程"}]},{"type":"element","tag":"ol","props":{},"children":[{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"准备一台能通过互联网访问到的云服务器"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"配置域名 DNS 记录，指向云服务器的 IP 地址"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"在云服务器上安装配置 NGINX 反向代理服务器"}]},{"type":"element","tag":"li","props":{},"children":[{"type":"text","value":"在本地电脑，使用 SSH 在云服务器与本地电脑之间打个通道"}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"先配置好一台能在互联网上被访问到的反向代理服务器（Web 服务器），服务器会将请求转发至本地服务器的某个特定端口，然后用 SSH 将这个服务器上的特定端口（比如 7689）与本地电脑上的某个特定端口（比如 3000）连通，这个本地电脑上的特定端口就是运行本地服务端应用时使用的端口，这样当有人在互联网访问这个反向代理服务器时，实际提供服务的是在我们本地电脑上运行的服务端应用。\n"},{"type":"element","tag":"img","props":{"alt":"","src":"/images/docs/3rd/ssh-tunnel/image.png"},"children":[]}]},{"type":"element","tag":"h2","props":{"id":"添加域名-dns-记录"},"children":[{"type":"text","value":"添加域名 DNS 记录"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"在域名服务商提供的域名管理界面，添加一条 A 类型的 DNS 记录，记录值是我们的云服务器的 IP 地址，主机名可以随意设置。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"比如我为 "},{"type":"element","tag":"a","props":{"href":"http://ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"ninghao.net"}]},{"type":"text","value":" 这个域名添加了一条 DNS 记录，让 "},{"type":"element","tag":"a","props":{"href":"http://sandbox.ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"sandbox.ninghao.net"}]},{"type":"text","value":" 指向我的一台云服务器，准备好 NGINX 服务器与 SSH 通道以后，就可以在互联网通过这个域名直接访问在我本地电脑上运行的应用了。"}]},{"type":"element","tag":"h2","props":{"id":"创建反向代理服务器"},"children":[{"type":"text","value":"创建反向代理服务器"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"使用 SSH 远程登录云服务器以后，需要配置一个 NGINX 的反向代理服务器。"}]},{"type":"element","tag":"code","props":{"code":"vi /etc/nginx/conf.d/sandbox.ninghao.net.conf\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"vi /etc/nginx/conf.d/sandbox.ninghao.net.conf\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"内容如下："}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"em","props":{},"children":[{"type":"text","value":"/etc/nginx/conf.d/sandbox.ninghao.net.conf"}]}]},{"type":"element","tag":"code","props":{"code":"server {\n  listen                      80;\n  server_name                 sandbox.ninghao.net;\n  client_max_body_size        200m;\n\n  location / {\n    proxy_set_header          X-Real-IP $remote_addr;\n    proxy_set_header          X-Real-Port $remote_port;\n    proxy_set_header          X-Forwarded-For $proxy_add_x_forwarded_for;\n    proxy_set_header          Host $http_host;\n    proxy_set_header          X-Forwarded-Proto $scheme;\n    proxy_http_version 1.1;\n    proxy_set_header          Upgrade $http_upgrade;\n    proxy_set_header          Connection \"upgrade\";\n    proxy_redirect            off;\n    expires                   off;\n    sendfile                  off;\n    proxy_pass                http://127.0.0.1:7689;\n  }\n}\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"server {\n  listen                      80;\n  server_name                 sandbox.ninghao.net;\n  client_max_body_size        200m;\n\n  location / {\n    proxy_set_header          X-Real-IP $remote_addr;\n    proxy_set_header          X-Real-Port $remote_port;\n    proxy_set_header          X-Forwarded-For $proxy_add_x_forwarded_for;\n    proxy_set_header          Host $http_host;\n    proxy_set_header          X-Forwarded-Proto $scheme;\n    proxy_http_version 1.1;\n    proxy_set_header          Upgrade $http_upgrade;\n    proxy_set_header          Connection \"upgrade\";\n    proxy_redirect            off;\n    expires                   off;\n    sendfile                  off;\n    proxy_pass                http://127.0.0.1:7689;\n  }\n}\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"上面这个 NGINX 配置，创建了一台反向代理服务器，监听的端口是 80，绑定的域名是 "},{"type":"element","tag":"a","props":{"href":"http://sandbox.ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"sandbox.ninghao.net"}]},{"type":"text","value":"，在用一个 location 区块，配置一下代理服务相关的东西，注意这里的 proxy_pass 指令的值被设置成了 "},{"type":"element","tag":"a","props":{"href":"http://127.0.0.1:7689%EF%BC%8C%E4%B9%9F%E5%B0%B1%E6%98%AF%E5%BD%93%E6%9C%89%E4%BA%BA%E8%AE%BF%E9%97%AE/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"http://127.0.0.1:7689，意思是将访问转发到本地"}]},{"type":"text","value":"服务器的 7689 这个端口。"},{"type":"element","tag":"a","props":{"href":"http://127.0.0.1:7689%EF%BC%8C%E4%B9%9F%E5%B0%B1%E6%98%AF%E5%BD%93%E6%9C%89%E4%BA%BA%E8%AE%BF%E9%97%AE/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"也就是当有人访问"}]},{"type":"text","value":" "},{"type":"element","tag":"a","props":{"href":"http://sandbox.ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"http://sandbox.ninghao.net"}]},{"type":"text","value":" 的时候，这个反向代理服务器会将请求转给本地服务器的 7689 这个端口。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"如果你希望通过 HTTPS 访问这个服务器，可以使用 certbot，为 "},{"type":"element","tag":"a","props":{"href":"http://sandbox.ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"sandbox.ninghao.net"}]},{"type":"text","value":" 这个域名申请一个 SSL 证书。完成以后，这个 NGINX 服务器的配置文件看起来会像下面这样："}]},{"type":"element","tag":"code","props":{"code":"server {\n  listen                      443 ssl http2;\n  server_name                 sandbox.ninghao.net;\n  ...\n\n  ssl_certificate /etc/letsencrypt/live/sandbox.ninghao.net/fullchain.pem; # managed by Certbot\n  ssl_certificate_key /etc/letsencrypt/live/sandbox.ninghao.net/privkey.pem; # managed by Certbot\n  include /etc/letsencrypt/options-ssl-nginx.conf;\n  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;\n\n  location / {\n    ...\n    proxy_pass                http://127.0.0.1:7689;\n  }\n}\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"server {\n  listen                      443 ssl http2;\n  server_name                 sandbox.ninghao.net;\n  ...\n\n  ssl_certificate /etc/letsencrypt/live/sandbox.ninghao.net/fullchain.pem; # managed by Certbot\n  ssl_certificate_key /etc/letsencrypt/live/sandbox.ninghao.net/privkey.pem; # managed by Certbot\n  include /etc/letsencrypt/options-ssl-nginx.conf;\n  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;\n\n  location / {\n    ...\n    proxy_pass                http://127.0.0.1:7689;\n  }\n}\n"}]}]}]},{"type":"element","tag":"h2","props":{"id":"ssh-通道"},"children":[{"type":"text","value":"SSH 通道"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"回到本地电脑上，确定在本地电脑上的服务端应用已经运行了，这个应用通过 3000 这个端口提供服务。现在我们要使用 SSH，在本地电脑与云服务器之间打个通道。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"element","tag":"strong","props":{},"children":[{"type":"text","value":"示例"}]}]},{"type":"element","tag":"code","props":{"code":"ssh -vnNT -R 7689:localhost:3000 root@42.120.40.68\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"ssh -vnNT -R 7689:localhost:3000 root@42.120.40.68\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"打开本地电脑的终端，执行 ssh 命令，要加上一些参数，比如 vnNT 这四个参数，还需要一个 R 参数，具体这些参数表示的意思可以执行 man ssh 命令，查看 ssh 命令的使用说明。7689 指的是远程云服务器上的一个端口，还记得上面我们配置的那台 NGINX 反向代理服务器吗？它会将访问转发到 7689 这个端口。localhost:3000 是我在本地电脑上运行的服务端应用的地址。root 是远程登录服务器时使用的用户名，42.120.40.68 是我的云服务器的 IP 地址。"}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"一切正常会出现下面这样的文字："}]},{"type":"element","tag":"code","props":{"code":"remote forward success for: listen 7689, connect localhost:3000\n"},"children":[{"type":"element","tag":"pre","props":{},"children":[{"type":"element","tag":"code","props":{"__ignoreMap":""},"children":[{"type":"text","value":"remote forward success for: listen 7689, connect localhost:3000\n"}]}]}]},{"type":"element","tag":"p","props":{},"children":[{"type":"text","value":"现在如果有人通过互联网访问 "},{"type":"element","tag":"a","props":{"href":"http://sandbox.ninghao.net/","rel":["nofollow","noopener","noreferrer"],"target":"_blank"},"children":[{"type":"text","value":"sandbox.ninghao.net"}]},{"type":"text","value":"，服务器会将请求转发到 7689 这个端口，又因为我们使用 SSH 在本地电脑与云服务器之间打了一个通道，这个通道的服务器这一边，设置使用的端口号是 7689，本地电脑这边的地址是 localhost:3000，这样访问 sandbox.ninghao.net，实际提供服务的就是在我们本地电脑上运行的应用。"}]}],"toc":{"title":"","searchDepth":2,"depth":2,"links":[{"id":"流程","depth":2,"text":"流程"},{"id":"添加域名-dns-记录","depth":2,"text":"添加域名 DNS 记录"},{"id":"创建反向代理服务器","depth":2,"text":"创建反向代理服务器"},{"id":"ssh-通道","depth":2,"text":"SSH 通道"}]}},"_type":"markdown","_id":"content:docs:3.3rd:6.ssh-tunnel.md","_source":"content","_file":"docs/3.3rd/6.ssh-tunnel.md","_extension":"md"}]