本指南旨在帮助您理解 Frappe Framework 中静态资源打包的工作原理。
Frappe 自带一个富管理界面,可通过 /app 访问,这是一个使用现代 JavaScript 语法编写的单页应用(SPA),其样式使用 SASS(.scss)文件编写。这些文件无法被浏览器直接理解,因此在发送到浏览器解析和执行之前,需要先进行编译。
Frappe 自带一个资源打包器,可以编译客户端资源,例如:
.js(使用import和export的现代语法).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/js 或 dist/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_script 和 include_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_js 和 app_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