无法在 Phoenix umbrella 应用程序中加载资产

Cannot load assets in Phoenix umbrella app

背景

我有一个旧的保护伞应用程序,我正试图让它重新焕发生机。为此,我决定在保护伞下添加一个 phoenix 项目:

mix phx.new.web hello_test --no-ecto

注意 --no-ecto 部分,这很重要。

设置

在 运行ning 在我的伞 apps 文件夹中说命令后,我通过添加以下内容更改了我伞的 config/config.exs 文件:

config :phoenix, json_library: Jason

解决这个问题后,我转到 apps/hello_test/lib/hello_test/application.ex 并向 start 函数添加了以下内容:

children = [
      # Start the Telemetry supervisor
      HelloTest.Telemetry,
      # Start the Endpoint (http/https)
      HelloTest.Endpoint,
      # Start a worker by calling: HelloTest.Worker.start_link(arg)
      # {HelloTest.Worker, arg}
      {Phoenix.PubSub, name: HelloTest.PubSub}
    ]

然后我 运行 mix assets.deployhello_test apps 文件夹中,然后是 mix phx.server

问题

此时我期待在加载到 localhost:4000 时看到一些漂亮的欢迎页面。 相反,我得到一个没有 css 的页面和一条错误消息:

16:17:44.695 [debug] Processing with HelloTest.PageController.index/2
  Parameters: %{}
  Pipelines: [:browser]

16:17:44.737 [info]  Sent 200 in 51ms

16:17:44.821 [info]  GET /assets/app.js

16:17:44.887 [info]  GET /assets/app.css

16:17:44.887 [debug] ** (Phoenix.Router.NoRouteError) no route found for GET /assets/app.js (HelloTest.Router)
    (hello_test 0.1.0) lib/phoenix/router.ex:406: HelloTest.Router.call/2
    (hello_test 0.1.0) lib/hello_test/endpoint.ex:1: HelloTest.Endpoint.plug_builder_call/2
    (hello_test 0.1.0) lib/plug/debugger.ex:136: HelloTest.Endpoint."call (overridable 3)"/2
    (hello_test 0.1.0) lib/hello_test/endpoint.ex:1: HelloTest.Endpoint.call/2
    (phoenix 1.6.5) lib/phoenix/endpoint/cowboy2_handler.ex:54: Phoenix.Endpoint.Cowboy2Handler.init/4
    (cowboy 2.9.0) c:/Users/User/Workplace/market_manager/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
    (cowboy 2.9.0) c:/Users/User/Workplace/market_manager/deps/cowboy/src/cowboy_stream_h.erl:306: :cowboy_stream_h.execute/3
    (cowboy 2.9.0) c:/Users/User/Workplace/market_manager/deps/cowboy/src/cowboy_stream_h.erl:295: :cowboy_stream_h.request_process/3


16:17:44.897 [debug] ** (Phoenix.Router.NoRouteError) no route found for GET /assets/app.css (HelloTest.Router)
    (hello_test 0.1.0) lib/phoenix/router.ex:406: HelloTest.Router.call/2
    (hello_test 0.1.0) lib/hello_test/endpoint.ex:1: HelloTest.Endpoint.plug_builder_call/2
    (hello_test 0.1.0) lib/plug/debugger.ex:136: HelloTest.Endpoint."call (overridable 3)"/2
    (hello_test 0.1.0) lib/hello_test/endpoint.ex:1: HelloTest.Endpoint.call/2
    (phoenix 1.6.5) lib/phoenix/endpoint/cowboy2_handler.ex:54: Phoenix.Endpoint.Cowboy2Handler.init/4
    (cowboy 2.9.0) c:/Users/User/Workplace/market_manager/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
    (cowboy 2.9.0) c:/Users/User/Workplace/market_manager/deps/cowboy/src/cowboy_stream_h.erl:306: :cowboy_stream_h.execute/3
    (cowboy 2.9.0) c:/Users/User/Workplace/market_manager/deps/cowboy/src/cowboy_stream_h.erl:295: :cowboy_stream_h.request_process/3

公平地说,到目前为止完成的设置并非微不足道,但我通过在旧 github 问题和这个社区中无休止地搜索来管理它。

然而,现在不同了。这就像任何凤凰伞应用程序一样简单,但它仍然无法开箱即用。

我找到的唯一解决方案是针对非常旧的 Phoenix 版本,这些版本仍在使用 NPM。我使用的是最新版本的 phoenix,所以这些解决方案不适用:

λ mix phx.new --version
Phoenix installer v1.6.5

λ elixir -v
Erlang/OTP 24 [erts-12.1.4] [source] [64-bit] [smp:6:6] [ds:6:6:10] [async-threads:1] [jit]

Elixir 1.13.1 (compiled with Erlang/OTP 22)

我怎样才能摆脱这个错误并使我的应用 运行 正确?

回答

所以经过一些研究,我最终创建了一个全新的伞式项目,其中包含一个 child phoenix 应用程序。问题无处可见,一切正常。

mix new koko --umbrella
cd koko/apps
mix phx.new.web hello --no-ecto
cd hello
mix assets.deploy

所以一切都指向我的旧 umbrella 应用程序中的一些问题。因为我想做更多测试,所以我在 hello 之外创建了另一个 phoenix 应用程序。

然后我能够重现这个问题。

在这一点上,我怀疑如果您尝试以下操作,保护伞应用程序会出现问题:

  • 添加多个凤凰子应用
  • 当项目过去已有一个(并被删除)时添加一个新的 phoenix 子应用程序

我不明白为什么会这样,但我不明白这里的含义:我必须用一个新的伞形应用程序重新制作我的项目并重新开始。

我自己 运行 对此感兴趣。默认情况下,在 config/dev.exs 下,端点配置有一个指向默认 esbuild 配置文件的 esbuild 密钥:

config :admin, AdminWeb.Endpoint,
  http: [ip: {127, 0, 0, 12}, port: 4000],
  url: [host: "admin.myapp.test"],
  check_origin: false,
  code_reloader: true,
  debug_errors: true,
  secret_key_base: "secret",
  watchers: [
    # Start the esbuild watcher by calling Esbuild.install_and_run(:default, args)
    esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]}
  ]

该配置文件在 config/config.exs 下定义如下:

config :esbuild,
  version: "0.14.0",
  default: [
    args:
      ~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*),
    cd: Path.expand("../apps/myapp_web/assets", __DIR__),
    env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
  ]

它指向默认 Web 应用程序中的资产路径。为了支持多个网络应用程序,您需要向 config/config.exs

添加第二个配置
config :esbuild,
  version: "0.14.0",
  default: [
    args:
      ~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*),
    cd: Path.expand("../apps/myapp_web/assets", __DIR__),
    env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
  ],
admin: [
    args:
      ~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*),
    cd: Path.expand("../apps/admin_web/assets", __DIR__),
    env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
  ],

更改密钥,在本例中为 admin,以及 /apps/ 目录下的相应路径。然后返回 config/dev.exs 切换您的端点配置以指向您刚刚创建的新 esbuild 配置文件。

config :admin, AdminWeb.Endpoint,
  http: [ip: {127, 0, 0, 12}, port: 4000],
  url: [host: "admin.myapp.test"],
  check_origin: false,
  code_reloader: true,
  debug_errors: true,
  secret_key_base: "secret",
  watchers: [
    # Start the esbuild watcher by calling Esbuild.install_and_run(:default, args)
    esbuild: {Esbuild, :install_and_run, [:admin, ~w(--sourcemap=inline --watch)]}
  ]

根据您的发布配置,您可能还需要为其他环境执行此操作,但如果您将构建资产作为发布的一部分,则无需执行此操作即可。