认识Phoenix:Elixir上类似Rails的现代Web应用框架

本文概述

Phoenix框架已经迅速发展并迅速普及, 提供了诸如Ruby on Rails之类的框架的生产力, 同时也是现有最快的框架之一。打破了必须牺牲性能才能提高生产率的说法。

那么凤凰到底是什么呢?

Phoenix是使用Elixir编程语言构建的Web框架。基于Erlang VM构建的Elixir用于构建低延迟, 容错的分布式系统, 这些系统已成为现代Web应用程序越来越重要的品质。你可以从此博客文章或其官方指南中了解有关Elixir的更多信息。

如果你是Ruby on Rails的开发人员, 则一定要对Phoenix感兴趣, 因为它可以带来性能上的提高。其他框架的开发人员也可以跟随以了解Phoenix如何进行Web开发。

在Elixir上认识Phoenix:现代Web应用程序的类似Rails的框架

在本文中, 我们将学习Phoenix中的一些内容, 如果你来自Ruby on Rails领域, 则应该记住这些内容。

与Ruby不同, Elixir是一种函数式编程语言, 这可能是你可能要面对的最大区别。但是, 这两个平台之间存在一些关键差异, 因此学习Phoenix的任何人都应该意识到这些差异, 以便最大程度地提高生产力。

命名约定更简单。

这虽然很小, 但是如果你来自Ruby on Rails, 则很容易弄乱。

在Phoenix中, 惯例是将所有内容都写为单数形式。因此, 你将拥有一个” UserController”, 而不是像在Ruby on Rails中那样的” UsersController”。除在命名数据库表时使用约定以复数形式命名表外, 这适用于所有地方。

在学习了何时何地在Ruby on Rails中使用复数形式之后, 这似乎没什么大不了的, 但是起初, 当我学习使用Ruby on Rails时, 这有点令人困惑, 并且我相信它已经混淆了很多其他也一样。凤凰城给你带来了少一件事的担心。

路由更易于管理。

Phoenix和Ruby on Rails在路由方面非常相似。关键区别在于你如何控制请求的处理方式。

在Ruby on Rails(和其他Rack应用程序)中, 这是通过中间件完成的, 而在Phoenix中, 这是通过所谓的”插件”完成的。

插头是你用来处理连接的工具。

例如, 中间件Rails :: Rack :: Logger在Rails中记录一个请求, 在Phoenix中可以使用Plug.Logger插件来完成。基本上, 你在Rack中间件中可以执行的任何操作都可以使用插件完成。

以下是Phoenix中路由器的示例:

defmodule HelloWorld.Router do
  use HelloWorld.Web, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", HelloWorld do
    pipe_through :browser

    get "/", HelloController, :index
  end

  scope "/api", HelloWorld do
    pipe_through :api
  end
end

首先, 让我们看一下管道。

这些是请求将通过的插头组。将其视为中间件堆栈。例如, 可以使用它们来验证请求是否需要HTML, 获取会话并确保请求是安全的。这一切都发生在到达控制器之前。

由于你可以指定多个管道, 因此可以选择特定路线所需的插头。例如, 在路由器中, 我们有用于页面(HTML)和API(JSON)请求的不同管道。

对不同类型的请求使用完全相同的管道并不总是很有意义。凤凰城给了我们这种灵活性。

视图和模板是两件事。

Phoenix中的视图与Ruby on Rails中的视图不同。

Phoenix中的视图负责渲染模板, 并提供使原始数据更易于模板使用的功能。 Phoenix中的视图最类似于Ruby on Rails中的帮助器, 另外还渲染了模板。

让我们写一个显示” Hello, World!”的示例。在浏览器中。

Ruby on Rails:

app / controllers / hello_controller.rb:

class HelloController < ApplicationController
  def index
  end
end

app / views / hello / index.html.erb:

<h1>Hello, World!</h1>

凤凰:

网站/控制器/hello_controller.ex:

defmodule HelloWorld.HelloController do
  use HelloWorld.Web, :controller

  def index(conn, _params) do
    render conn, "index.html"
  end
end

网站/视图/hello_view.ex:

defmodule HelloWorld.HelloView do
  use HelloWorld.Web, :view
end

web / templates / hello / index.html.eex:

<h1>Hello, World!</h1>

这些示例都显示” Hello, World!”在浏览器中, 但有一些主要区别。

首先, 与Ruby on Rails不同, 你必须明确声明要在Phoenix中呈现的模板。

接下来, 我们必须在Phoenix中包含一个视图, 该视图位于控制器和模板之间。

现在, 你可能想知道为什么我们需要一个视图, 如果它是空的?这里的关键是, 在底层, Phoenix将模板编译成一个大致等于以下代码的函数:

defmodule HelloWorld.HelloView do
  use HelloWorld.Web, :view

  def render("index.html", _assigns) do
    raw("<h1>Hello, World!</h1>")
  end
end

你可以删除模板, 并使用新的渲染功能, 你将获得相同的结果。当你只想返回文本甚至JSON时, 这很有用。

模型就是这样:数据模型。

在Phoenix中, 模型主要处理数据验证, 其模式, 与其他模型的关系以及显示方式。

首先, 在模型中指定架构可能听起来很奇怪, 但是它使你可以轻松创建”虚拟”字段, 这些字段不会持久化到数据库中。让我们看一个例子:

defmodule HelloPhoenix.User do
  use HelloPhoenix.Web, :model

  schema "users" do
    field :name, :string
    field :email, :string
    field :password, :string, virtual: true
    field :password_hash, :string
  end
end

在这里, 我们在”用户”表中定义了四个字段:名称, 电子邮件, 密码和password_hash。

除了设置为”虚拟”的”密码”字段外, 这里没有什么有趣的地方。

这使我们可以设置和获取该字段, 而无需将更改保存到数据库。这很有用, 因为我们有逻辑将该密码转换为哈希, 然后将其保存在” password_hash”字段中, 然后将其保存到数据库中。

你仍然需要创建一个迁移。需要模型中的模式, 因为字段不会像Ruby on Rails一样自动加载到模型中。

Phoenix和Ruby on Rails之间的区别在于该模型不处理对数据库的持久性。这由称为” Repo”的模块处理, 该模块配置有数据库信息, 如下例所示:

config :my_app, Repo, adapter: Ecto.Adapters.Postgres, database: "ecto_simple", username: "postgres", password: "postgres", hostname: "localhost"

此代码包含在特定于环境的配置文件中, 例如config / dev.exs或config / test.exs。这样, 我们便可以使用Repo来执行数据库操作, 例如创建和更新。

Repo.insert(%User{name: "John Smith", example: "[email protected]"}) do
  {:ok, user} -> # Insertion was successful
  {:error, changeset} -> # Insertion failed
end

这是Phoenix中控制器中的常见示例。

我们为用户提供名称和电子邮件, 然后回购尝试在数据库中创建新记录。然后, 我们可以选择处理示例中所示的成功或失败尝试。

为了更好地理解此代码, 你需要了解Elixir中的模式匹配。该函数返回的值是一个元组。该函数返回两个值的元组, 即状态, 然后是模型或变更集。变更集是一种跟踪变更和验证模型的方法(变更集将在下一节中讨论)。

第一个元组, 从上到下, 与尝试将User插入数据库的函数返回的元组的模式匹配, 将运行其定义的函数。

我们可以为状态设置一个变量, 而不是原子(这基本上就是Ruby中的符号), 但是然后我们将成功和失败的尝试都匹配, 并且在两种情况下都使用相同的函数。指定我们要匹配的原子, 我们专门为该状态定义一个函数。

Ruby on Rails通过ActiveRecord将持久性功能内置到模型中。这给模型增加了更多的责任, 因此有时会使模型的测试更加复杂。在Phoenix中, 这已经以一种有意义的方式分离, 并防止了使用持久性逻辑膨胀每个模型的情况。

变更集允许明确的验证和转换规则。

在Ruby on Rails中, 验证和转换数据可能是难以发现错误的来源。这是因为当通过” before_create”和验证之类的回调转换数据时, 这并不是立即显而易见的。

在Phoenix中, 你可以使用变更集显式地进行这些验证和转换。这是我在Phoenix中最喜欢的功能之一。

让我们通过在以前的模型中添加一个变更集来看看它:

defmodule HelloPhoenix.User do
  use HelloPhoenix.Web, :model

  schema "users" do
    field :name, :string
    field :email, :string
    field :password, :string, virtual: true
    field :password_hash, :string
  end
  
  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:name, :email, :password])
    |> validate_required([:email, :password])
  end
end

在这里, 变更集有两件事。

首先, 它调用” cast”函数, 该函数是允许字段的白名单, 类似于Ruby on Rails中的” strong_parameters”, 然后验证是否包括” email”和” password”字段, 从而使” name”字段可选的。这样, 用户只能修改你允许的字段。

这种方法的好处是, 我们不仅限于一个变更集。我们可以创建多个变更集。通常有一个用于注册的变更集和一个用于更新用户的变更集。也许我们只想在注册时要求输入密码字段, 而在更新用户时则不需要。

将这种方法与Ruby on Rails中通常执行的方法进行比较, 你必须指定验证仅应在”创建”上运行。有时, 一旦拥有复杂的模型, Rails就会很难弄清代码在做什么。

导入功能简单明了, 但很灵活。

名为” Ecto”的库的许多功能都已导入到模型中。模型通常在顶部附近有这条线:

use HelloPhoenix.Web, :model

” HelloPhoenix.Web”模块位于” web / web.ex”中。在模块中, 应该有一个称为”模型”的函数, 如下所示:

def model do
  quote do
    use Ecto.Schema

    import Ecto
    import Ecto.Changeset
    import Ecto.Query
  end
end

在这里, 你可以看到我们正在从Ecto导入哪些模块。你可以在此处删除或添加任何其他模块, 它们将被导入所有模型。

还有类似的功能, 例如”视图”和”控制器”, 它们分别为视图和控制器提供相同的目的。

quote和use关键字似乎令人困惑。对于此示例, 你可以将引号视为在正在调用该函数的模块的上下文中直接运行该代码。因此, 这等同于在模块中的引号内编写代码。

use关键字还允许你在调用代码的上下文中运行代码。它本质上需要指定的模块, 然后在调用它的上下文中在运行它的模块上调用__using__宏。你可以在官方指南中阅读有关报价和使用的更多信息。

这确实可以帮助你了解某些功能在框架中的位置, 并有助于减轻框架在做很多”魔术”的感觉。

并发是核心。

在Phoenix中, 并发是免费提供的, 因为它是Elixir的主要功能。你将获得一个可以生成多个进程并在多个内核上运行的应用程序, 而无需担心线程安全性和可靠性。

你可以在Elixir中简单地产生一个新过程:

spawn fn -> 1 + 2 end

生成之后和结束之前的所有内容都将在新进程中运行。

实际上, Phoenix中的每个请求都是在自己的过程中处理的。 Elixir利用Erlang VM的功能”免费”带来可靠, 高效的并发。

这也使Phoenix成为运行使用WebSockets的服务的绝佳选择, 因为WebSockets需要维护客户端和服务器之间的开放连接(这意味着你需要构建应用程序, 以便它可以处理数千个并发连接)。

这些要求会给基于Ruby on Rails的项目增加很多复杂性, 但是Phoenix可以通过Elixir免费满足这些要求。

如果要在Phoenix应用程序中使用WebSockets, 则需要使用Channels。它与Ruby on Rails中的ActionCable等效, 但设置起来较为简单, 因为你不需要运行单独的服务器。

Phoenix使构建现代Web应用程序变得轻而易举。

虽然我们主要关注差异, 但Phoenix确实与Ruby on Rails有一些共同点。

Phoenix大致遵循与Ruby on Rails相同的MVC模式, 因此, 既然你知道主要的区别, 那么弄清楚什么代码在哪里就不难了。 Phoenix还具有与Ruby on Rails类似的生成器, 用于创建模型, 控制器, 迁移等。

学习了Elixir之后, 一旦对Phoenix感到更满意, 你将慢慢达到Ruby on Rails的生产力水平。

几次我觉得效率不高是因为我遇到了一个由Ruby中的gem解决的问题, 而我找不到类似的Elixir库。幸运的是, 不断增长的Elixir社区正在逐步填补这些空白。

Phoenix中的差异帮助解决了管理复杂的Ruby on Rails项目带来的许多麻烦。尽管它们不能解决所有问题, 但确实可以帮助你朝正确的方向发展。因为你希望生产率不再是一个有效的借口, 所以选择一个缓慢的框架, Phoenix让你兼而有之。

微信公众号
手机浏览(小程序)
0
分享到:
没有账号? 忘记密码?