Phoenix框架中如何为app.html.heex/root.html.heex创建控制器实现全局菜单?
在Phoenix中实现全局缓存菜单(支持Live/非Live页面)
针对你的需求,核心是把菜单数据的查询逻辑从模板移到全局层面,并通过缓存避免重复查询,同时兼容普通页面和LiveView页面。下面是具体实现步骤:
1. 引入缓存依赖(避免重复查库)
先用Cachex来缓存菜单数据,这样数据库查询只会在缓存失效时执行一次。在mix.exs的依赖中添加:
defp deps do [ # 其他依赖... {:cachex, "~> 3.6"} ] end
执行mix deps.get安装依赖,然后在lib/my_app/application.ex中启动Cachex服务:
def start(_type, _args) do children = [ # 其他子进程... {Cachex, name: :menu_cache} ] Supervisor.start_link(children, strategy: :one_for_one) end
2. 为非Live页面注入菜单数据(全局Plug)
创建一个Plug来全局处理菜单数据注入,所有普通HTTP请求都会经过这个Plug:
# lib/my_app_web/plugs/put_menu_items.ex defmodule MyAppWeb.Plugs.PutMenuItems do import Plug.Conn def init(opts), do: opts def call(conn, _opts) do items = case Cachex.get(:menu_cache, :items) do {:ok, nil} -> # 缓存为空时查询数据库,并存入缓存(设置1小时过期) items = MyApp.Items.Item.list_items() Cachex.put(:menu_cache, :items, items, ttl: :timer.hours(1)) items {:ok, items} -> # 直接返回缓存数据 items end assign(conn, :menu_items, items) end end
然后在lib/my_app_web/endpoint.ex的:browser管道中加入这个Plug:
pipeline :browser do plug :accepts, ["html"] plug :fetch_session plug :fetch_live_flash plug :put_root_layout, html: {MyAppWeb.Layouts, :root} plug :protect_from_forgery plug :put_secure_browser_headers plug MyAppWeb.Plugs.PutMenuItems # 加入这一行 end
3. 为LiveView页面注入菜单数据(全局钩子)
LiveView有独立的连接生命周期,需要用on_mount钩子全局注入数据:
# 在lib/my_app_web.ex中添加全局LiveView钩子 defmodule MyAppWeb do # 其他代码... def live_view do quote do use Phoenix.LiveView, layout: {MyAppWeb.Layouts, :live} unquote(view_helpers()) # 注册全局on_mount钩子 on_mount MyAppWeb.LiveHelpers.PutMenuItems end end # 实现钩子模块 defmodule LiveHelpers.PutMenuItems do import Phoenix.LiveView def on_mount(:default, _params, _session, socket) do items = case Cachex.get(:menu_cache, :items) do {:ok, nil} -> items = MyApp.Items.Item.list_items() Cachex.put(:menu_cache, :items, items, ttl: :timer.hours(1)) items {:ok, items} -> items end {:cont, assign(socket, :menu_items, items)} end end end
4. 在布局模板中使用菜单数据
修改app.html.heex或root.html.heex,直接使用注入的@menu_items:
<div> <ul> <%= for item <- @menu_items do %> <li><%= item.name %></li> <% end %> </ul> <!-- 其他页面内容 --> </div>
关键说明
- 缓存周期可以根据需求调整(比如
ttl: :timer.minutes(30)),如果需要手动刷新缓存,可以调用Cachex.delete(:menu_cache, :items) - 不管是普通控制器页面还是LiveView页面,都会自动获取到缓存后的菜单数据,数据库查询只会在缓存缺失时执行一次
内容的提问来源于stack exchange,提问作者Jun Dalisay
相关产品推荐
相关产品推荐

