Phoenix LiveView 1.2 发布了。升级动作不大:从 1.1 过来,主要是把 mix.exs 里的依赖改成 {:phoenix_live_view, "~> 1.2.0"},再重新拉依赖。
真正有意思的是另一件事:LiveView 现在允许你在 HEEx 模板里就近写 CSS。组件在哪里,样式就放在哪里。听起来像现代前端框架的常规操作,但 LiveView 1.2 没有顺手把“样式隔离”默认打包送上。
这不是漏做。更像一次有意的刹车。
LiveView 1.2 到底加了什么
主角是 Colocated CSS。
LiveView 1.1 已经有 Colocated Hooks 和 Colocated JavaScript。1.2 把这条路延伸到 CSS:你可以在 HEEx 里写类似 <style :type={MyApp.ColocatedCSS}>...</style> 的样式块。
编译时,LiveView 会把这些 CSS 抽出来,放到 _build 目录下的 phoenix-colocated,再交给现有 CSS 管线处理。比如 Tailwind、Esbuild。
这点很关键。它不是另起一套样式系统,也不是绕过你原来的前端构建链。
| 变化 | 具体做法 | 对开发者的影响 |
|---|---|---|
| 升级 | phoenix_live_view 改到 ~> 1.2.0 | 迁移入口很轻 |
| Colocated CSS | 在 HEEx 内就近写 <style> | 组件样式更容易跟着组件走 |
| 编译处理 | CSS 抽取到 _build/phoenix-colocated | 继续接 Tailwind、Esbuild 等管线 |
| 样式隔离 | 1.2 不默认启用 scoping | 不能当成天然 scoped CSS 用 |
还有一些小改进,偏工程细节。
HEEx 里的 script / style 可以通过 Phoenix.LiveView.HTMLFormatter.TagFormatter 接入自定义格式化工具。push_event 发送 Phoenix.LiveView.JS structs 时可以自动编码。HEEx 调试注解支持按模块配置。测试警告也能按类别配置。
这些都实用。但这版最该看懂的,还是 Colocated CSS 的边界。
关键限制:样式靠近了,作用域没有默认隔离
就近写 CSS 的好处很直接。
一个按钮组件、一个卡片组件、一个表单组件,样式可以和模板放在一起。维护时少跳文件。删组件时也更容易删干净。
问题也同样直接。
如果你在组件里写 p { font-weight: bold; },没有作用域边界,它可能影响页面上别的 p。代码看上去分开了,CSS 还在全局游荡。
LiveView 1.2 给出的方向是 CSS @scope。
大致思路是:框架可以给模板根节点加边界标记,比如 phx-r;再给使用 scoped colocated CSS 的根元素加类似 phx-css-* 的唯一属性。这样 CSS 可以限定在某个组件范围内生效,并在边界处停止。
这个设计对 slot 场景尤其重要。
调用方传进来的内容,不应该被组件内部样式误伤。边界标记就是在告诉浏览器:谁是组件内部,谁是外部传入。
但官方没有默认 ship scoping。
原因不复杂:截至 2026 年 6 月,@scope 的浏览器支持仍不充分。LiveView 1.2 提供的是 @behaviour,让开发者自定义 scoping 策略。官方文档里有基于 @scope 的实现,适合早期采用者,但它不是默认安全带。
所以这次不能误读成“LiveView 已经给 CSS 做好了完整隔离”。
准确说法是:Colocated CSS 已经来了,默认样式隔离还没来。
该怎么用:小项目可试,组件库要慢一点
我更认可 LiveView 这次的克制。
很多框架在这种节点会选择把体验包装完整。API 漂亮,发布稿好看,示例也顺滑。浏览器兼容、编译复杂度、边界条件,留给用户在生产环境里补课。
LiveView 1.2 没这么干。
它做了底层编译调整,把 HEEx 编译拆成 tokenization 和 parsing,支撑 colocated CSS / JS 这类宏组件,也减少编译复杂度。但到了样式隔离这里,它没有替还不稳的浏览器标准背书。
“知止而后有定。”框架设计最难的,有时不是继续加功能,而是知道自己现在不能承诺什么。
代价也很清楚。克制不会让问题消失,只会把选择权交回团队。
| 使用场景 | 我会怎么选 | 主要风险 |
|---|---|---|
| 小型 LiveView 项目 | 可以试用 Colocated CSS | 注意避免写过宽选择器 |
| 中后台页面 | 适合逐步引入 | 团队要约定命名和边界 |
| 复杂组件库 | 不建议立刻全面迁移 | scoped 策略没定前,污染成本高 |
| 想用样式隔离 | 先评估自定义 behaviour | @scope 兼容性不能假装不存在 |
对普通 LiveView 开发者,我的建议很简单:可以升级,可以试 Colocated CSS,但别把它当 CSS Modules 或 Vue scoped CSS 的等价物。
对维护组件库的团队,动作要更慢。先定三件事:哪些组件允许 colocated CSS,哪些选择器禁止使用,是否采用文档里的早期 scoping 方案。
尤其别在复杂组件库里直接铺开全局迁移。
因为真正贵的不是写样式。是半年后没人知道某条 CSS 到底会影响哪里。
接下来最该观察的也不是“更多语法糖”。而是两个变量:浏览器对 @scope 的支持进展,以及 LiveView 社区会不会沉淀出足够稳的 scoping behaviour 实现。
如果这两个条件跟上,Colocated CSS 才会从“更顺手的组织方式”,变成“更可靠的组件边界”。
回到开头那句话:LiveView 1.2 的重点,不是终于能把 CSS 塞进 HEEx。重点是它把样式拉近组件时,没有假装边界问题已经解决。
这次少见地做对了。只是账单还在开发者手里。
