资产捆绑

本指南旨在帮助您理解 Frappe Framework 中静态资源打包的工作原理。

Frappe 自带一个富管理界面,可通过 /app 访问,这是一个使用现代 JavaScript 语法编写的单页应用(SPA),其样式使用 SASS(.scss)文件编写。这些文件无法被浏览器直接理解,因此在发送到浏览器解析和执行之前,需要先进行编译。

Frappe 自带一个资源打包器,可以编译客户端资源,例如:

  • .js (使用 importexport 的现代语法)
  • .ts (TypeScript 文件)
  • .vue (Vue 单文件组件)
  • .css (使用 PostCSS 处理的 CSS)
  • .scss (SASS 文件)
  • .sass (使用缩进语法的 SASS 文件)
  • .styl (Stylus 文件)
  • .less (Less 文件)

这些文件根据类型被编译为 .js.css 格式,然后发送到浏览器。

构建资源

要使用资源打包器编译资源,您需要在 frappe-bench 文件夹下运行以下命令:

$ bench build

您也可以通过指定 --apps 选项来为特定应用运行该命令。

# build only frappe assets
$ bench build --apps frappe

# build only frappe and erpnext assets
$ bench build --apps frappe,erpnext

监听模式

当您使用打包文件时,每次修改源文件后都需要重新运行构建命令。资源打包器提供了监听模式,它会监听文件系统的变化,并在文件发生更改时自动重新构建。

运行以下命令将启动一个长时间运行的进程,它会监听您的文件并在文件变化时重新构建。每次重新构建时,它都会记录一行包含“Compiled changes…”文本的日志。

$ bench watch
Watching for changes...
1:17:28 PM: Compiled changes...

您也可以通过指定 --apps 选项来为特定应用运行该命令。

# watch only erpnext assets
$ bench watch --apps erpnext

从版本 14 开始,如果在监听模式下资源被重新构建,Desk 将自动重新加载。此行为可以通过设置 LIVE_RELOAD 环境变量,或更改 common_site_config.json 中的 live_reload 值来切换。

打包文件

打包文件是资源打包器进行编译的资源入口点。例如,如果您的应用 public 文件夹中有一个名为 main.bundle.js 的文件,它将被打包器自动拾取并编译到 /assets/[app]/dist/js/main.bundle.[hash].js。根据输出内容计算出的唯一哈希值也会附加到文件名中,这对于浏览器中的缓存失效非常有用。

类似地,如果 public 文件夹中有一个名为 style.bundle.scss 的文件,它将被编译到 /assets/[app]/dist/css/style.bundle.css。请注意,扩展名从 .scss 变为了 .css,因为浏览器可以理解 CSS 文件,但不能理解 SASS 文件。打包文件可以存在于 public 文件夹中的任何嵌套级别,但它们始终会根据类型编译到 dist/jsdist/css 目录中。这意味着如果存在一个位于 public/main.bundle.js 的文件和另一个位于 public/src/main.bundle.js 的文件,后者的编译输出将覆盖前者。打包器也会对此类冲突打印警告。

更多打包输入和输出的示例:

输入 输出
[app]/public/main.bundle.js /assets/dist/[app]/js/main.bundle.[hash].js
[app]/public/src/main.bundle.js /assets/dist/[app]/js/main.bundle.[hash].js
[app]/public/src/utils/utils.bundle.js /assets/dist/[app]/js/utils.bundle.[hash].js
[app]/public/main.bundle.ts /assets/dist/[app]/js/main.bundle.[hash].js
[app]/public/main.bundle.css /assets/dist/[app]/css/main.bundle.[hash].css
[app]/public/styles/main.bundle.css /assets/dist/[app]/css/main.bundle.[hash].css
[app]/public/main.bundle.scss /assets/dist/[app]/css/main.bundle.[hash].css
[app]/public/main.bundle.sass /assets/dist/[app]/css/main.bundle.[hash].css
[app]/public/main.bundle.styl /assets/dist/[app]/css/main.bundle.[hash].css
[app]/public/main.bundle.less /assets/dist/[app]/css/main.bundle.[hash].css

从 npm 导入库

如果您熟悉现代 Web 开发,您可能需要从 npm 安装第三方库并在项目中使用。

假设您想在应用中使用 dayjs 库来处理日期和时间。首先,您需要在应用文件夹的根目录下运行以下命令,通过 yarn 安装它。

$ cd frappe-bench/apps/myapp
$ yarn add dayjs

现在,您可以像这样在源文件中导入它:

myapp/public/main.bundle.js

import * as dayjs from 'dayjs';

console.log(dayjs())

在 HTML 中包含打包资源

当打包文件被编译时,输出文件包含一个唯一的哈希值。因此,您不能硬编码文件的路径,因为下次您修改该文件时,哈希值会发生变化。Frappe 提供了一些辅助方法来解决这个问题。

在自定义 HTML 文件中包含资源

Jinja 方法 include_scriptinclude_style 将分别输出包含 .js.css 文件 HTML 标记的正确文件路径。

index.html



My App

 {{ include_style('style.bundle.css') }}

 {{ include_script('main.bundle.js') }}

index.html(渲染后)



My App

 <script type="text/javascript" src="/assets/myapp/dist/js/main.bundle.BYJXV4LB.js"></script>

在 app.html 中包含资源

如果您想从您的应用中包含打包资源到 /app 中,您可以使用
app_include_jsapp_include_css 将它们加载到 app.html 中。

[app]/hooks.py

app_include_js = ['main.bundle.js']
app_include_css = ['style.bundle.css']

获取打包资源路径

如果由于某些原因您只需要打包资源的路径,您可以使用
bundled_asset Jinja 方法来生成它。

Jinja

{{ bundled_asset('main.bundle.js') }}

渲染结果

/assets/myapp/dist/js/main.bundle.BYJXV4LB.js

Python API

这些 API 在 Python 中也可用。您可以从 jinja_globals.py 中导入它们。

from frappe.utils.jinja_globals import bundled_asset, include_script, include_style

bundled_asset('main.bundle.js')

/app 中延迟包含打包资源

如果您想在管理界面(/app)中延迟加载打包资源,您可以使用
frappe.require 方法。

frappe.require('main.bundle.js').then(() => {
 // main.bundle.js is now loaded
})

当您想根据某些条件加载代码时,这种方法非常有用。
首次页面加载不会受到影响,并且对性能更有利。

生产模式

当您将应用部署到生产环境时,您可以在生产模式下构建您的资源。在这种模式下,打包器会压缩您的打包最终输出,
从而减小文件大小。

要在生产模式下构建您的资源,请运行以下命令:

$ bench build --production