elixir-phoenix
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePhoenix Project Setup
Phoenix项目搭建
Standard patterns for Phoenix projects using Bun, Tailwind v4, devenv, and PostgreSQL.
使用Bun、Tailwind v4、devenv和PostgreSQL的Phoenix项目标准配置方案。
1. Create the Phoenix Project
1. 创建Phoenix项目
bash
undefinedbash
undefinedStandard project
标准项目
mix phx.new my_app --database postgres
mix phx.new my_app --database postgres
Umbrella app
Umbrella应用
mix phx.new my_app --umbrella --database postgres
After generation, replace the default esbuild config with bun.mix phx.new my_app --umbrella --database postgres
项目生成后,将默认的esbuild配置替换为Bun。2. Replace esbuild with Bun
2. 用Bun替换esbuild
In deps, replace with:
mix.exs{:esbuild, ...}elixir
{:bun, "~> 1.4", runtime: Mix.env() == :dev},
{:tailwind, "~> 0.2", runtime: Mix.env() == :dev},In , replace the block with:
config/config.exsconfig :esbuildelixir
config :bun,
version: "1.3.4",
my_app: [
args: ~w(build assets/js/app.js --outdir=priv/static/assets --external /fonts/* --external /images/*),
cd: Path.expand("../", __DIR__)
]
config :tailwind,
version: "4.1.11",
my_app: [
args: ~w(--input=assets/css/app.css --output=priv/static/assets/app.css),
cd: Path.expand("../", __DIR__)
]For umbrella apps, use for .
Path.expand("../apps/my_app", __DIR__)cd:In , update watchers:
config/dev.exselixir
watchers: [
tailwind: {Tailwind, :install_and_run, [:my_app, ~w(--watch)]},
bun: {Bun, :install_and_run, [:my_app, ~w(--sourcemap=inline --watch)]}
]Update aliases:
mix.exselixir
"assets.deploy": [
"phx.digest.clean",
"tailwind my_app --minify",
"bun my_app --minify",
"phx.digest"
]在的依赖中,将替换为:
mix.exs{:esbuild, ...}elixir
{:bun, "~> 1.4", runtime: Mix.env() == :dev},
{:tailwind, "~> 0.2", runtime: Mix.env() == :dev},在中,将代码块替换为:
config/config.exsconfig :esbuildelixir
config :bun,
version: "1.3.4",
my_app: [
args: ~w(build assets/js/app.js --outdir=priv/static/assets --external /fonts/* --external /images/*),
cd: Path.expand("../", __DIR__)
]
config :tailwind,
version: "4.1.11",
my_app: [
args: ~w(--input=assets/css/app.css --output=priv/static/assets/app.css),
cd: Path.expand("../", __DIR__)
]对于umbrella应用,使用。
cd:Path.expand("../apps/my_app", __DIR__)在中,更新监控器:
config/dev.exselixir
watchers: [
tailwind: {Tailwind, :install_and_run, [:my_app, ~w(--watch)]},
bun: {Bun, :install_and_run, [:my_app, ~w(--sourcemap=inline --watch)]}
]更新中的别名:
mix.exselixir
"assets.deploy": [
"phx.digest.clean",
"tailwind my_app --minify",
"bun my_app --minify",
"phx.digest"
]3. Set NODE_PATH for Phoenix Dependency Resolution
3. 设置NODE_PATH以解析Phoenix依赖
Set to the project root's directory so Bun resolves Node-style
packages from Phoenix's directory. Imports like
and will resolve to the Elixir dependency packages without
needing .
NODE_PATHdeps/deps/import {Socket} from "phoenix"import "phoenix_html"node_modules/In , this is already configured using an absolute path:
devenv.nixnix
env.NODE_PATH = "${config.git.root}/deps";For non-devenv setups, export it in your shell or build scripts:
bash
export NODE_PATH="$(pwd)/deps"No or symlinks needed — Phoenix ships JS alongside its Elixir source in
.
npm installdeps/<package>/priv/static/将设置为项目根目录的文件夹,这样Bun就能从Phoenix的目录解析Node风格的包。像和这样的导入会直接解析到Elixir依赖包,无需。
NODE_PATHdeps/deps/import {Socket} from "phoenix"import "phoenix_html"node_modules/在中,已经通过绝对路径配置了这一变量:
devenv.nixnix
env.NODE_PATH = "${config.git.root}/deps";对于非devenv环境,在shell或构建脚本中导出该变量:
bash
export NODE_PATH="$(pwd)/deps"无需执行或创建符号链接——Phoenix会将JS文件与Elixir源码一起放在目录中。
npm installdeps/<package>/priv/static/5. Configure runtime.exs for Binary Paths
5. 配置runtime.exs以设置二进制文件路径
In , read env vars so devenv-provided binaries are used instead of
downloading copies. Both packages require explicit configuration:
config/runtime.exselixir
if System.get_env("MIX_BUN_PATH") do
config :bun, path: System.get_env("MIX_BUN_PATH")
end
if System.get_env("MIX_TAILWIND_PATH") do
config :tailwind, path: System.get_env("MIX_TAILWIND_PATH")
endWhen is set, the hex packages skip downloading and use the provided binary directly.
devenv sets these env vars via to point at the Nix store paths (see step 4).
pathlib.getExe在中,读取环境变量,从而使用devenv提供的二进制文件,而非下载副本。这两个包都需要显式配置:
config/runtime.exselixir
if System.get_env("MIX_BUN_PATH") do
config :bun, path: System.get_env("MIX_BUN_PATH")
end
if System.get_env("MIX_TAILWIND_PATH") do
config :tailwind, path: System.get_env("MIX_TAILWIND_PATH")
end当设置了时,hex包会跳过下载步骤,直接使用提供的二进制文件。devenv通过设置这些环境变量,指向Nix存储路径(见步骤4)。
pathlib.getExe6. Set Up devenv
6. 设置devenv
See references/devenv-template.md for the full and templates.
devenv.yamldevenv.nixKey points:
- resolves Nix store paths for
lib.getExeandMIX_BUN_PATHMIX_TAILWIND_PATH - PostgreSQL runs via Unix socket only () — no port conflicts
listen_addresses = "" - uses
DATABASE_URLparameter pointing to devenv's state directory?socket= - is set so
PGHOSTand Ecto both find the socket automaticallypsql
完整的和模板请参考references/devenv-template.md。
devenv.yamldevenv.nix核心要点:
- 解析
lib.getExe和MIX_BUN_PATH对应的Nix存储路径MIX_TAILWIND_PATH - PostgreSQL仅通过Unix套接字运行()——避免端口冲突
listen_addresses = "" - 使用
DATABASE_URL参数指向devenv的状态目录?socket= - 设置以便
PGHOST和Ecto都能自动找到套接字psql
7. Configure Ecto for URL-based Connection
7. 配置Ecto以支持基于URL的连接
In , support both Unix socket (devenv) and TCP (manual setup):
config/dev.exselixir
db_config =
[
username: System.get_env("POSTGRES_USER", "my_app_dev"),
password: System.get_env("POSTGRES_PASSWORD", "my_app_dev"),
database: System.get_env("POSTGRES_DB", "my_app_dev"),
show_sensitive_data_on_connection_error: true,
pool_size: 10
]
|> then(fn config ->
case System.get_env("PGHOST") do
nil ->
config ++ [hostname: System.get_env("POSTGRES_HOST", "localhost"),
port: String.to_integer(System.get_env("POSTGRES_PORT", "5432"))]
pghost when is_binary(pghost) ->
if String.starts_with?(pghost, "/") do
config ++ [socket_dir: pghost]
else
config ++ [hostname: pghost,
port: String.to_integer(System.get_env("POSTGRES_PORT", "5432"))]
end
end
end)
config :my_app, MyApp.Repo, db_configThis auto-detects whether is a Unix socket path or a hostname.
PGHOST在中,同时支持Unix套接字(devenv环境)和TCP(手动配置)两种方式:
config/dev.exselixir
db_config =
[
username: System.get_env("POSTGRES_USER", "my_app_dev"),
password: System.get_env("POSTGRES_PASSWORD", "my_app_dev"),
database: System.get_env("POSTGRES_DB", "my_app_dev"),
show_sensitive_data_on_connection_error: true,
pool_size: 10
]
|> then(fn config ->
case System.get_env("PGHOST") do
nil ->
config ++ [hostname: System.get_env("POSTGRES_HOST", "localhost"),
port: String.to_integer(System.get_env("POSTGRES_PORT", "5432"))]
pghost when is_binary(pghost) ->
if String.starts_with?(pghost, "/") do
config ++ [socket_dir: pghost]
else
config ++ [hostname: pghost,
port: String.to_integer(System.get_env("POSTGRES_PORT", "5432"))]
end
end
end)
config :my_app, MyApp.Repo, db_config该配置会自动检测是Unix套接字路径还是主机名。
PGHOST8. Production Database via URL
8. 通过URL配置生产环境数据库
In for prod:
config/runtime.exselixir
if config_env() == :prod do
database_url =
System.get_env("DATABASE_URL") ||
raise "DATABASE_URL is not set"
config :my_app, MyApp.Repo,
url: database_url,
pool_size: String.to_integer(System.get_env("POOL_SIZE", "10"))
end在生产环境的中:
config/runtime.exselixir
if config_env() == :prod do
database_url =
System.get_env("DATABASE_URL") ||
raise "DATABASE_URL is not set"
config :my_app, MyApp.Repo,
url: database_url,
pool_size: String.to_integer(System.get_env("POOL_SIZE", "10"))
end9. End-to-End Tests
9. 端到端测试
E2e tests live in alongside the standard folder. They are
expensive and must never run automatically — only on explicit request.
e2e_test/test/Add the alias in :
test.e2emix.exselixir
defp aliases do
[
# ... existing aliases ...
"test.e2e": ["run --no-halt", "cmd mix test e2e_test/"]
]
endConfigure as an extra test path in project config so Mix
finds the helpers, but exclude it from the default run:
e2e_test/mix.exsmix testelixir
def project do
[
# ...
test_paths: ["test"],
elixirc_paths: elixirc_paths(Mix.env()),
]
endRun e2e tests explicitly:
bash
mix test.e2eNever addto CI pipelines, pre-commit hooks, or any automated alias that runs as part of a normal build or test cycle.test.e2e
端到端测试位于目录,与标准的文件夹同级。这类测试资源消耗较大,绝不能自动运行——仅在明确请求时执行。
e2e_test/test/在中添加别名:
mix.exstest.e2eelixir
defp aliases do
[
# ... 现有别名 ...
"test.e2e": ["run --no-halt", "cmd mix test e2e_test/"]
]
end在的项目配置中,将设置为额外的测试路径,以便Mix能找到辅助函数,但将其排除在默认的运行范围之外:
mix.exse2e_test/mix testelixir
def project do
[
# ...
test_paths: ["test"],
elixirc_paths: elixirc_paths(Mix.env()),
]
end显式运行端到端测试:
bash
mix test.e2e切勿将添加到CI流水线、预提交钩子或任何作为正常构建或测试周期一部分自动运行的别名中。test.e2e
Quick Reference
快速参考
| Tool | Hex Package | Env Var | devenv Source |
|---|---|---|---|
| Bun | | | |
| Tailwind | | | |
| PostgreSQL | | | |
| 工具 | Hex包 | 环境变量 | devenv来源 |
|---|---|---|---|
| Bun | | | |
| Tailwind | | | |
| PostgreSQL | | | |