惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
爱范儿
爱范儿
博客园 - 三生石上(FineUI控件)
Vercel News
Vercel News
M
MIT News - Artificial intelligence
L
LangChain Blog
大猫的无限游戏
大猫的无限游戏
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Microsoft Azure Blog
Microsoft Azure Blog
J
Java Code Geeks
Recent Announcements
Recent Announcements
Stack Overflow Blog
Stack Overflow Blog
人人都是产品经理
人人都是产品经理
IT之家
IT之家
F
Fortinet All Blogs
博客园 - 聂微东
U
Unit 42
Martin Fowler
Martin Fowler
腾讯CDC
博客园_首页
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
量子位
阮一峰的网络日志
阮一峰的网络日志
博客园 - Franky

竹林里有冰的博客

Nuxt SSG 博客的尾斜杠到底怎么加? | 竹林里有冰的博客 小米 Xiaomi Book Pro 14 (Ultra X7) Linux 兼容性实测 | 竹林里有冰的博客 国内(大陆)版小米 FCM 熄屏断连:Rootless 环境下的尝试与可能的解决方案 | 竹林里有冰的博客 我没法访问 dl.google.com —— 记一次 TUN 下的网络 debug | 竹林里有冰的博客 Vercel 的缓存控制,你注意过吗? | 竹林里有冰的博客 小记 —— Caddy 在 Layer 4 上的流量代理实践 | 竹林里有冰的博客 你的域名后缀拖慢你的网站速度了嘛?——再谈 DNS 冷启动 | 竹林里有冰的博客 DNS 冷启动:小型站点的“西西弗斯之石” | 竹林里有冰的博客 HTTP/2 Server Push 已事实性“死亡”,我很怀念它 | 竹林里有冰的博客 Nuxt Content v3 中数组字段的筛选困境与性能优化 | 竹林里有冰的博客 后 OCSP 时代,浏览器如何应对证书吊销新挑战 | 竹林里有冰的博客 初试 Github Action Self-hosted Runner,想说爱你不容易 | 竹林里有冰的博客 DNS 解析延迟毁了我的图床优化 | 竹林里有冰的博客 Vue Markdown 渲染优化实战(下):告别 DOM 操作,拥抱 AST 与函数式渲染 | 竹林里有冰的博客 Vue Markdown 渲染优化实战(上):从暴力刷新、分块更新到 Morphdom 的华丽变身 | 竹林里有冰的博客 node-sass 迁移至 dart-sass 踩坑实录 | 竹林里有冰的博客 前端中的量子力学——一打开 F12 就消失的 Bug | 竹林里有冰的博客 2025 年,如何为 web 页面上展示的视频选择合适的压缩算法? | 竹林里有冰的博客 el-image 和 el-table 怎么就打架了?Stacking Context 是什么? | 竹林里有冰的博客 2025年,前端如何使用 JS 将文本复制到剪切板? | 竹林里有冰的博客 ssh 拯救世界——通过 ssh 隧道在内网服务器执行 APT 更新 | 竹林里有冰的博客 Cudy TR3000 吃鹅(daed)记 | 竹林里有冰的博客 使用 Cloudflare Workers 监控 Fedora Copr 构建状态 | 竹林里有冰的博客 基于 Cloudflare Workers 实现的在线服务状态检测告警系统 | 竹林里有冰的博客 构建部署在 Cloudflare Workers 上的 TG Bot | 竹林里有冰的博客 2024年,Firefox 是唯一还在坚持执行在线的 SSL 证书吊销状态检查的主流浏览器 | 竹林里有冰的博客 小爱课程表适配不完全指北——以 ZJUT 本科正方教务系统为例 | 竹林里有冰的博客 将博客从 waline v2 更新到 waline v3 | 竹林里有冰的博客 给家里云装上 Fedora 41 KDE 后,我是如何配置的 | 竹林里有冰的博客 为 Hexo 添加 follow 认证 | 竹林里有冰的博客
jinja2 中如何优雅地实现换行 | 竹林里有冰的博客
竹林里有冰 · 2023-09-03 · via 竹林里有冰的博客

在使用 python 的 jinja2 模板引擎生成 html 的时候,会遇到 \n 换行符无法被正常换行的问题。我本能的想法就是将 \n 替换成 html 标签 <br />,但失败了,jinja2 有自动转义的功能,直接将标签原模原样地渲染了出来,并没有生效。而为这一段代码块关闭自动转义则会有被 js 注入的风险,因此这也不是上策。

在 jinja2 的官方文档中,提出了使用 filter 的方案。也就是说,filter 将 \n 识别出来,并自动替换成 <br /> 标签,并且使用 Markup 函数将这一段 html 文本标记成安全且无需转义的。见: https://jinja.palletsprojects.com/en/3.1.x/api/#custom-filters

import re
from jinja2 import pass_eval_context
from markupsafe import Markup, escape

@pass_eval_context
def nl2br(eval_ctx, value):
    br = "<br>\n"

    if eval_ctx.autoescape:
        value = escape(value)
        br = Markup(br)

    result = "\n\n".join(
        f"<p>{br.join(p.splitlines())}<\p>"
        for p in re.split(r"(?:\r\n|\r(?!\n)|\n){2,}", value)
    )
    return Markup(result) if autoescape else result

使用这段代码后,我遇到了连续两个换行符被识别成一个换行符的问题,依然不满意。

issue#2628 中,我找到了一个相对优雅的解决方案——使用 css 样式来完成这个任务。

通过设置 white-space: pre-line; 的 css 样式,html 在被渲染时将会不再忽略换行符,浏览器就能够在没有 br 标签标注的情况下实现自动换行。而如果设置为 white-space: pre-wrap; 则多个空格将不会再被合并成一个空格,直接治好了我在入门 html 时的各种不适。

此外,通过 word-break: break-word; 的 css 样式可以实现只有当一个单词一整行都显示不下时,才会拆分换行该单词的效果,可以避免 break-all 拆分所有单词或者 normal 时遇到长单词直接元素溢出的问题。