在 VS Code 中尝试

教程的问题

2022 年 3 月 8 日,作者 Burke Holland,@burkeholland

写一篇优秀的教程并非易事。我应该知道这一点——我写过很多教程,但并非每一篇都取得了巨大成功。

事实证明,写一篇优秀的教程不在于你写了什么,而在于开发者是否无需阅读每一个字就能成功。在本文中,我们将探讨开发容器如何减少用户可能遇到的错误,以及 Laravel PHP 项目如何在他们自己的教程中优雅地实现这一点,并取得了显著效果。

没人阅读

我们自己关于如何在 Visual Studio Code 中使用开发容器的教程长期以来完成率较低,大约只有 4 - 6%。

dev containers learn module screenshot

为了弄清楚人们在哪一步放弃,我们进行了用户研究,并观察了人们尝试完成我们教程的过程。那真是……令人痛苦。

人们为什么无法完成教程的原因立即显而易见:没人阅读它。人们直接跳过了说明,径直进入了操作步骤。不可避免地,他们会因为犯了本来可以通过阅读说明避免的错误而卡住。

宾夕法尼亚州立大学教授 John M. Carroll 在他的重要著作《纽伦堡漏斗——为实用计算机技能设计极简主义教学》中谈到了这一点。他写道:“[学习者]忙于学习,无暇充分利用教学材料。这就是理解的悖论。”

我对此深有同感,你可能也是。当我浏览教程时,我的眼睛在扫视代码块,因为我试图通过实践来学习。我简直忙于学习,没时间阅读说明。

人们不会读你的教程。或者至少不会像你希望的那么多。你所能做的最好的事情就是尽可能地消除学习过程中读者可能出错的地方。一种方法是使用预配置的容器环境,完全移除任何环境设置步骤。

容器化开发环境

任何教程的很大一部分通常都用于列出一长串的前置条件和环境设置。我清楚地记得尝试学习 Ruby on Rails 时,大部分时间都花在尝试在 Windows 上正确安装 Ruby 上——奇怪“gem”到底是什么,为什么它们都莫名其妙地丢失了。

容器化开发环境的理念是,你在 Docker 容器内部进行开发。这使得拥有一个完全可移植、完全配置好的开发环境成为可能,你可以随意启动或关闭它。然后,你可以将这个环境作为一组配置文件交给其他人。

但是如何容器内部开发呢?容器不像有 UI 可以直接启动 VS Code 那样。

VS Code 的 开发容器 扩展正是如此。它包含配置 Docker 容器作为开发环境的机制,也允许你从 VS Code 连接到该环境。它通过在容器内安装一个小型服务器组件来实现这一点,你的本地 VS Code 与该组件通信。然后你就可以像在本地一样进行开发,但 VS Code 连接的是容器环境而不是你的本地环境。

The Dev Containers extension screenshot from extension gallery

为了创建容器化开发环境,通常你需要了解一些关于 Docker 的知识。很多人确实了解,但很多人不了解(你看不到我,但我的手正举着),所以这个扩展试图尽可能地抽象化容器设置过程。我设置了一个新的 Python 容器。一个向导会引导你选择基础镜像和 Python 版本。然后它会通过一个选择列表让你有机会向镜像添加额外的软件。在这种情况下,我添加了 Azure CLI、Dotnet CLI 和 PowerShell……

Adding a dev container configuration to a Python project

这个过程会在项目中添加一个 .devcontainer 文件夹,其中包含必要的 Dockerfile。它还会添加一个 devcontainer.json 文件,这是定义开发容器各个方面的标准,例如应该安装哪些扩展,容器构建后应该运行哪些设置命令等。由于你对环境及其设置拥有完全控制权,你可以自动化几乎所有事情——包括依赖项安装、库版本等。

通过这种方式,你可以字面上地将一个完整、即开即用的环境交给某人,无需额外的设置步骤,也不会引发关于 Ruby gems 的存在危机。

有些人已经在采用基于开发容器的方法,帮助他们的用户快速启动并运行那些原本非常复杂的环境。一个很好的例子是用于 PHP 的 Laravel 框架。

Laravel 解决方案

Laravel 是一个用于 PHP 的开源 MVC 框架。它是全面的,因为它还包括对象关系映射 (ORM)、直接数据库访问、包管理系统等功能。Laravel 功能强大。为了体验它,入门时你至少需要一个数据库。通常这要求用户不仅安装 PHP,还要安装数据库——通常是 MySQL。当用户只是想试用一下你的框架时,这要求就太高了。

Laravel 使用容器化开发环境和名为 Sail 的工具解决了这个问题。要从零开始使用 Laravel、MySQL Server 和 Redis Cache,你只需运行一个命令……

    curl -s "https://laravel.build/example-app?with=mysql,redis" | bash

这会创建一个包含 docker-compose 文件的新项目。该文件设置了三个容器——一个应用容器、一个 MySQL 容器和一个 Redis 容器。你无需了解关于容器或这三个服务中的任何一个。Sail 为你抽象了所有这些。然后你执行 Sail 命令来启动环境……

    ./vendor/bin/sail up

示例应用就这样运行了。无需安装 PHP。无需安装 Laravel。无需依赖项解析步骤。只有即刻的成功。

An example Laravel application running in the browser on localhost

我指定我们的项目有一个 MySQL Server 和一个 Redis Cache,所以当项目启动时,我们实际上获得了三个容器。我们可以使用 VS Code 的 Docker 扩展看到这些。

The Docker extension in VS Code

这些容器相互联网,这样我们可以从应用容器中调用 MySQL 或 Redis 缓存容器。

如果你连接一个交互式终端到 sail-8.1/app 容器,你会在 /var/www/html 文件夹中看到你的项目。Docker 将你机器上的项目“挂载”到容器中,因此你在开发过程中所做的任何更改都会在你刷新时反映在应用中。

The file structure of the Laravel project in a container

添加开发容器

开发容器 扩展的支持也已添加。要为此项目添加正确的开发容器配置,你可以搭建相同的项目并添加 &devcontainer 标志。

    curl -s "https://laravel.build/example-app?with=mysql,redis&devcontainer" | bash

请注意,如果你想将开发容器添加到现有的 Sail/Laravel 项目,你可以运行 php artisan sail:install --devcontainer 命令来实现。

这会创建相同的项目配置,但会包含一个 .devcontainer 文件夹。VS Code 将自动检测该文件夹,并提示你在容器中重新打开项目,从而跳过必需的 sail up 步骤。

A notification in VS Code saying "Reopen in container"

VS Code 连接到容器,因此你是在容器环境内部进行开发,而不是在本地环境。你会知道这一点,因为 VS Code 左下角的远程指示器会告诉你……

The remote indicator in VS Code showing connection to a container

在容器内部开发与在外部开发相比,有一些明显的优势。

开发上下文与应用上下文一致

连接到容器后,你进行开发的上下文与应用运行的上下文相同。因此,你的终端变成了容器的终端……

The VS Code terminal connected to the running container instance

开发容器扩展还为你提供了更全面的视图,例如哪些端口被转发——以防你忘记你的应用运行在哪里。

The port forwarding view in VS Code showing port 80 forwarded

Laravel 应用会自动启动,并且应用日志会被导流到容器日志中。由于你可能想查看应用中正在发生什么,开发容器扩展在 VS Code 中提供了一个新视图,你可以在其中查看所有正在运行的容器,并连接到流式容器日志。

The Laravel application container logs in VS Code

自动化开发环境设置

最好的开发者体验应该包括编辑器的定制。这包括编辑器本身的设置,以及任何需要添加到开箱即用体验中的扩展或其他支持。

对于 VS Code 和 Laravel,扩展在 devcontainer.json 中被建议,但被注释掉了,以便它们不会自动安装。这允许用户从一组已经确定的扩展中选择,而不必自己去寻找正确配置编辑器的方法。

    ...
    "extensions": [
        // "mikestead.dotenv",
        // "amiralizadeh9480.laravel-extra-intellisense",
        // "ryannaddy.laravel-artisan",
        // "onecentlin.laravel5-snippets",
        // "onecentlin.laravel-blade"
    ],

少读多做

人们不阅读。这也没关系。Laravel 的教程不一定比其他教程短,但重要的是,如果你直接跳到代码并运行命令,它就能工作。开发容器使这成为可能。现在,要是我们也能为我们自己的使用 Docker 容器作为 Visual Studio Code 开发环境教程创建一个开发容器就好了……

编程愉快!

Burke Holland (@burkeholland)