在过去十多年里,我们一直在打包CSS和JavaScript文件。这些构建工具使我们能够在利用CSS和JS新特性的同时,继续支持旧版浏览器。它们还有助于优化客户端网络性能,将内容压缩到尽可能小,并将文件合并成一个大包以减少网络握手。在这个过程中,我们经历了大量构建工具的迭代:从 Grunt (2012) 到 Gulp (2013),再到 Webpack (2014)、Parcel (2017),以及 esbuild (2020) 和 Vite (2020)。 然而,随着现代浏览器技术的发展,对这些构建工具的需求已大大减少。
现代CSS原生支持了许多当初需要构建工具才能实现的功能。例如用于组织代码的CSS嵌套、变量,以及用于特性检测的@supports。
JavaScript ES6 / ES2015是一个巨大的进步,此后这门语言一直在稳步发展。它现在通过import/export关键字提供了原生的模块支持。
与此同时,HTTP/2的性能改进使得可以在同一连接上进行并行请求,从而消除了HTTP/1.x协议的限制。
这些构建过程非常复杂,特别是对于Django初学者而言。相关的工具和最佳实践更新迅速。需要学习的内容很多,并且你需要了解如何在Django项目中使用它们。你可以建立一个工作流,将构建结果存储在静态文件夹中,但Django核心本身并不支持构建流水线,因此这很大程度上需要从众多第三方包中进行选择,并将它们集成到项目中。 这种复杂性带来的好处已不再那么明显,尤其是对初学者来说。构建工具仍然有其优势,但你完全可以在不使用或学习任何构建流程的情况下,创造出专业的结果。
无构建JavaScript教程
为了展示现代浏览器的能力,让我们用一些更新的JavaScript来扩展Django的投票教程。我们将使用现代JS模块,并且不需要构建系统。 为了让我们的应用需要JavaScript,我们为投票功能增加一个新需求:允许用户添加自己的选项,而不仅仅是投票给现有选项。我们更新表单,在选择代码下方添加一个新选项:
|
|
现在,如果现有选项不合适,我们的用户可以为投票添加自己的选项。我们可以更新投票视图来处理这个新选项,并进行更多验证:
- 如果没有选择投票选项,我们就处理添加新选项
- 如果没有选择投票选项也没有新的
choice_text,则显示错误 - 如果两者都选择了,也显示错误。
随着逻辑变得越来越复杂,如果我们有一些JavaScript来处理会更好。我们可以编写一个脚本来为我们处理部分表单验证。
|
|
注意上面代码中我们如何使用export default。这意味着form_validation.js是一个JavaScript模块。当我们创建main.js文件时,可以用import语句导入它:
|
|
最后,我们使用Django常用的静态模板标签将脚本添加到details.html文件的底部。注意type="module",这是告诉浏览器我们将使用import/export语句所必需的。
|
|
就是这样!我们获得了现代JavaScript的模块化优势,而无需任何构建过程。浏览器会为我们处理模块加载。并且得益于HTTP/2的并行请求,即使有很多模块,也不会造成性能损失。
生产环境部署
为了部署,我们只需要Django对收集静态文件到一个位置的支持,以及其对文件名添加哈希值的支持。在生产环境中,使用ManifestStaticFilesStorage存储后端是个好主意。它会通过将文件内容的MD5哈希值附加到文件名上来存储所处理的文件名。这允许你设置很远的未来缓存过期时间,这对性能有好处,同时还能确保新版本的文件能够送达用户的浏览器。
此外,这个后端也能够更新import语句中对form_validation.js的引用,使其使用新的带版本号的文件名。
未来的工作
ManifestStaticFilesStorage可以工作,但其很多实现细节带来了不便。对开发者来说,它可以更容易使用。
- 对带哈希文件的
import/export支持不够健壮。 - CSS中引用文件的注释可能导致
collectstatic时出错。 - 无法处理CSS/JS中的循环依赖。
- 当文件缺失时,
collectstatic过程中的错误信息不总是清晰。 StaticFilesStorage和ManifestStaticFilesStorage的实现差异可能导致生产环境中出现开发环境没有的错误(例如关于前导斜杠的#26329号问题)。- 配置常见选项意味着需要子类化存储后端,而我们本可以使用现有的
OPTIONS字典。 - 如果使用并行化,收集静态文件可以更快(拉取请求:#19935 使用线程池并行化collectstatic)。
我们在Django on the Med 🏖️冲刺活动中讨论了这些可能的改进,我希望我们能取得进展。
我构建了django-manifeststaticfiles-enhanced来尝试修复所有这些问题。核心工作是基于Ned Batchelder的JsLex(Django以前用过)为CSS和JS切换到词法分析器。通过与Claude Code合作处理语法覆盖的繁重工作,它被扩展以支持现代JS和CSS。
它还改用拓扑排序来查找依赖关系,而之前我们使用的是更暴力的方法——反复处理直到看不到变化为止,这导致了更多的工作量,尤其是在使用网络存储的后端上。这也意味着我们无法处理循环依赖。
为了验证其有效性,我在50多个项目上运行了性能基准测试,它经过了问题测试,并且性能相当(通常是更好的)。平均而言,它大约快30%。
虽然这些改进会受欢迎,但你现在就可以在Django项目中尝试无构建的JavaScript和CSS!现代浏览器使得无需复杂工具就能创建出色的前端体验成为可能。