资源
概述
Filament 生态下的所有包都共享着一个资源管理系统。这让官方插件和第三方插件都可以注册 CSS 和 JavaScript 文件,并使它们可以供 Blade 视图消费使用。
FilamentAsset 门面
FilamentAsset 门面被用作将文件注册进资源系统。这些资源文件可以在文件系统的任何位置,不过当运行 php artisan filament:assets 命令之后会被复制到应用的 /public 目录。将这些资源复制到 /public 目录下,我们可以在 Blade 视图中更有预测性地加载它们,同时确保第三方包也能加载它们的资源而无需操心这些资源放置于何处。
资源始终有一个由你决定的唯一 ID,它作为文件名用在将资源复制到 /public 目录时。该 ID 也用于在 Blade 视图中引用资源。这个 ID 也是唯一的,由于这些资源将被复制到以你的插件命名的目录中,所以,在你为插件注册资源时,你不必担心 ID 会和其他插件产生冲突。
FilamentAsset 门面应该在服务提供者的 boot() 方法中调用。它可以在应用的服务提供者(如 AppServiceProvider),或者在插件的服务提供者中调用。
FilamentAsset 门面有一个主方法 register(),它接收一个资源数组用于注册:
use Filament\Support\Facades\FilamentAsset;
public function boot(): void
{
// ...
FilamentAsset::register([
// ...
]);
// ...
}
为插件注册资源
当为插件注册资源时,你应该传入 Composer 包名作为 register() 方法的第二个参数:
use Filament\Support\Facades\FilamentAsset;
FilamentAsset::register([
// ...
], package: 'danharrin/filament-blog');
现在,插件的所有资源都将被复制到位于 public 目录下的自己的目录中,以避免与其他插件可能产生的同名冲突。
注册 CSS 文件
想要在资源系统中注册 CSS 文件,可以在服务提供者的 boot() 方法中使用 FilamentAsset::register() 方法。你必须传入一个 Css 对象数组,每个对象代表着每个要在资源系统中注册的 CSS 文件。
每个 Css 对象都有一个唯一 ID 和一个到 CSS 文件的路径:
use Filament\Support\Assets\Css;
use Filament\Support\Facades\FilamentAsset;
FilamentAsset::register([
Css::make('custom-stylesheet', __DIR__ . '/../../resources/css/custom.css'),
]);
本例中,我们使用 __DIR__ 生成从当前文件到资源的相对路径。比如,如果你将这段代码添加到 /app/Providers/AppServiceProvider.php,那么 CSS 文件应该位于 /resources/css/custom.css。
当运行 php artisan filament:assets 命令时,CSS 文件将被复制到 /public 目录中。此外,它也被加载到其他使用 Filament 的所有 Blade 视图中。如果你只希望在页面元素需要时,才加载这些 CSS,请查看 CSS 懒加载章节。
在插件中使用 Tailwind CSS
一般而言,注册 CSS 文件通常用在注册自定义的样式表。如果你像使用 Tailwind CSS 处理这些文件,你需要考虑它可能的影响,特别当你是插件开发者时。
Tailwind 编译对每个应用都是唯一的 —— 它们包含类的最小集合,只包含 在应用中实际使用到的类。这意味着,如果你是插件开发者,你或许不应该编译你自己的 Tailwind CSS 文件到插件中。相反,你应该提供原始 CSS 文件,并引导用户自己编译 Tailwind CSS 文件。要完成此操作,它们可能只需要将你的 vendor 目录添加到他们的 tailwind.config.js 文件中:
export default {
content: [
'./resources/**/*.blade.php',
'./vendor/filament/**/*.blade.php',
'./vendor/danharrin/filament-blog/resources/views/**/*.blade.php', // Your plugin's vendor directory
],
// ...
}
这意味着,当编译这些 Tailwind CSS 文件时,就会包含所有插件视图中用到的样式类,同时也包含应用及 Filament 核心中使用的类。
不过,使用该技术,对于在后台面板中使用插件的用户,可能会额外的复杂度。如果他们使用的自定义主题,由于他们最终都要编译自己的 CSS 文件,这倒没什么;但是,如果他们使用面板自带的默认样式,你需要留意你插件视图中使用的样式类。比如,如果你使用了一个默认样式表中没有包含的类,用户也没有自己编译,它就不会包含在最终的 CSS 文件中。这意味着你的插件视图可能不会如你期望的那样展示。这是我推荐在插件中 编译及注册 Tailwind CSS 编译后样式表的情形之一。
CSS 懒加载
默认情况下,所有使用资源系统注册的 CSS 文件,在每个 Filament 页面的 <head> 中加载。这是加载 CSS 文件最简单的方式,不过有时候这些资源太大且不是每个页面都需要。这种情况下,你可以利用与 Filament 捆绑的 Alpine.js 的懒加载资源。前提非常简单,您可以在元素上使用 x-load-css 指令,当该元素加载到页面上时,指定的css文件将加载到页面的 <head> 中。这非常适合需要 CSS 文件的小型 UI 元素和整个页面:
<div
x-data="{}"
x-load-css="[@js(\Filament\Support\Facades\FilamentAsset::getStyleHref('custom-stylesheet'))]"
>
<!-- ... -->
</div>
为防止 CSS 文件被自动加载,可以使用 loadedOnRequest() 方法:
use Filament\Support\Assets\Css;
use Filament\Support\Facades\FilamentAsset;
FilamentAsset::register([
Css::make('custom-stylesheet', __DIR__ . '/../../resources/css/custom.css')->loadedOnRequest(),
]);
如果 CSS 文件被注册到插件,你必须将插件包传入作为 FilamentAsset::getStyleHref() 方法的第二个参数。
<div
x-data="{}"
x-load-css="[@js(\Filament\Support\Facades\FilamentAsset::getStyleHref('custom-stylesheet', package: 'danharrin/filament-blog'))]"
>
<!-- ... -->
</div>
使用 URL 注册 CSS 文件
如果你想使用 URL 注册 CSS 文件,也是可行的。这些资源会像平常那样在每个页面中加载,不过运行 php artisan filament:assets 命令 时,不会被复制到 /public 目录中。
这对于从 CDN 中注册外部样式表或已经直接编译到 /public 目录的样式表很有用:
use Filament\Support\Assets\Css;
use Filament\Support\Facades\FilamentAsset;
FilamentAsset::register([
Css::make('example-external-stylesheet', 'https://example.com/external.css'),
Css::make('example-local-stylesheet', asset('css/local.css')),
]);
注册 CSS 变量
有时,你想在 CSS 文件后台使用动态数据。为此你可以在服务提供者的 boot() 方法中使用 FilamentAsset::registerCssVariables() 方法:
use Filament\Support\Facades\FilamentAsset;
FilamentAsset::registerCssVariables([
'background-image' => asset('images/background.jpg'),
]);
现在,你可以从任何的 CSS 文件中访问这个变量:
background-image: var(--background-image);
注册 JavaScript 文件
要使用资源系统注册 JavaScript 文件,可以在服务提供者的 boot() 方法中调用 FilamentAsset::register() 方法。你必须传入一个 Js 对象数组,每个对象代表着一个要注册到资源系统的 JavaScript 文件。
每个 Js 对象都有一个唯一的 ID 以及一个指向 JavaScript 文件的路径:
use Filament\Support\Assets\Js;
FilamentAsset::register([
Js::make('custom-script', __DIR__ . '/../../resources/js/custom.js'),
]);
本例中,我们使用 __DIR__ 生成从当前文件到资源的相对路径。比如,如果你将这段代码添加到 /app/Providers/AppServiceProvider.php,那么 JavaScript 文件应该位于 /resources/js/custom.js。
当运行 php artisan filament:assets 命令时,这个 JavaScript 文件将被复制到 /public 目录中。此外,它也被加载到其他使用 Filament 的所有 Blade 视图中。如果你只希望在页面元素需要时,才加载这些 JavaScript,请查阅JavaScript懒加载章节。
JavaScript 懒加载
默认情况下,所有使用资源系统注册的 JavaScript 文件,在每个 Filament 页面的底部加载。这是加载 JavaScript 文件最简单的方式,不过有时候这些资源太大而且不是每个页面都需要引入。这种情况下,你可以利用与 Filament 捆绑的 Alpine.js 的懒加载资源。前提非常简单,您可以在元素上使用 x-load-js 指令,当该元素加载到页面上时,指定的 JavaScript 文件将在页面底部加载。这非常适合需要 JavaScript 文件的小型 UI 元素和整个页面:
<div
x-data="{}"
x-load-js="[@js(\Filament\Support\Facades\FilamentAsset::getScriptSrc('custom-script'))]"
>
<!-- ... -->
</div>
为避免 CSS 文件自动加载,你可以对 FilamentAsset::register() 调用进行包装,检查控制台是否需要这些资源。如果控制台对其有请求,可以猜想用户正在发布资源而非真正请求:
use Filament\Support\Assets\Js;
use Filament\Support\Facades\FilamentAsset;
if (app()->runningInConsole()) {
FilamentAsset::register([
Js::make('custom-script', __DIR__ . '/../../resources/js/custom.js'),
]);
}
如果你的 JavaScript 文件要注册到插件,你必须在 FilamentAsset::getScriptSrc() 方法中将插件包作为第二个参数传入:
<div
x-data="{}"
x-load-js="[@js(\Filament\Support\Facades\FilamentAsset::getScriptSrc('custom-script', package: 'danharrin/filament-blog'))]"
>
<!-- ... -->
</div>
异步 Alpine.js 组件
有时候,你想让基于 Alpine.js 的组件加载外部 JavaScript 库。最好的方式是,将编译过的 JavaScript 及 Alpine 组件存到一个单独的文件中,并让我们在组件渲染时加载它。
首先,你应该通过 npm 安装 esbuild,我们将使用它创建一个包含外部库和 Alpine 组件的单个 JavaScript 文件:
npm install esbuild --save-dev
然后,你必须创建一个脚本去编译 JavaScript 和 Alpine 组件。你可以将其放置在任何位置,比如: bin/build.js:
import * as esbuild from 'esbuild'
const isDev = process.argv.includes('--dev')
async function compile(options) {
const context = await esbuild.context(options)
if (isDev) {
await context.watch()
} else {
await context.rebuild()
await context.dispose()
}
}
const defaultOptions = {
define: {
'process.env.NODE_ENV': isDev ? `'development'` : `'production'`,
},
bundle: true,
mainFields: ['module', 'main'],
platform: 'neutral',
sourcemap: isDev ? 'inline' : false,
sourcesContent: isDev,
treeShaking: true,
target: ['es2020'],
minify: !isDev,
plugins: [{
name: 'watchPlugin',
setup: function (build) {
build.onStart(() => {
console.log(`Build started at ${new Date(Date.now()).toLocaleTimeString()}: ${build.initialOptions.outfile}`)
})
build.onEnd((result) => {
if (result.errors.length > 0) {
console.log(`Build failed at ${new Date(Date.now()).toLocaleTimeString()}: ${build.initialOptions.outfile}`, result.errors)
} else {
console.log(`Build finished at ${new Date(Date.now()).toLocaleTimeString()}: ${build.initialOptions.outfile}`)
}
})
}
}],
}
compile({
...defaultOptions,
entryPoints: ['./resources/js/components/test-component.js'],
outfile: './resources/js/dist/components/test-component.js',
})
如你所见,在脚本底部,我们将 resources/js/components/test-component.js 文件编译到 resources/js/dist/components/test-component.js。你可以按照需要修改这些路径。你也可以按照需要编译更多的组件。
现在,创建一个名为 resources/js/components/test-component.js 的新文件:
// Import any external JavaScript libraries from NPM here.
export default function testComponent({
state,
}) {
return {
state,
// You can define any other Alpine.js properties here.
init: function () {
// Initialise the Alpine component here, if you need to.
},
// You can define any other Alpine.js functions here.
}
}
现在,你可以运行如下命令将这个文件编译到 resources/js/dist/components/test-component.js:
node bin/build.js
如果你想要监测这个文件的变化,而不仅仅是编译一词,可以试试以下命令:
node bin/build.js --dev
现在,你需要让 Filament 将这个编译过的 JavaScript 文件发布到 Laravel 应用的 /public 目录,这样浏览器才能访问到。为此,你可以在服务提供者的 boot() 方法中调用 FilamentAsset::register() 方法,并传入一个 AlpineComponent 对象:
use Filament\Support\Assets\AlpineComponent;
use Filament\Support\Facades\FilamentAsset;
FilamentAsset::register([
AlpineComponent::make('test-component', __DIR__ . '/../../resources/js/dist/components/test-component.js'),
]);
当你运行 php artisan filament:assets 时,编译过的文件会被复制到 /public 目录中。
最后,你可以在你的视图中使用 ax-load 属性及 FilamentAsset::getAlpineComponentSrc() 方法,加载这个异步 Alpine 组件:
<div
x-ignore
ax-load
ax-load-src="{{ \Filament\Support\Facades\FilamentAsset::getAlpineComponentSrc('test-component') }}"
x-data="testComponent({
state: $wire.{{ $applyStateBindingModifiers("\$entangle('{$statePath}')") }},
})"
>
<input x-model="state" />
</div>
上面是自定义表单字段的例子。它将 state 作为参数传入到 testComponent() 函数中,该参数与 Livewire 组件属性相关联。你可以传入任何你需要的参数,并在 testComponent() 函数中访问这些参数。如果你不使用自定义表单字段,你可以忽视本例中的 state 参数。
ax-load 属性出自于 异步 Alpine 包,该包的其他特性也可以在此处使用。
注册脚本数据
有时,你希望从后端创建数据给 JavaScript 文件。为此,你可以在服务提供者的 boot() 方法中调用 FilamentAsset::registerScriptData() 方法:
use Filament\Support\Facades\FilamentAsset;
FilamentAsset::registerScriptData([
'user' => [
'name' => auth()->user()?->name,
],
]);
现在,你可以在运行时,使用 window.filamentData 对象从 JavaScript 文件中获取这些数据:
window.filamentData.user.name // 'Dan Harrin'
使用 URL 注册 JavaScript 文件
如果你想使用 URL 注册一个 JavaScript 文件,也是可行的。这些资源会像平常那样在每个页面中加载,不过运行 php artisan filament:assets 命令时,不会被复制到 /public 目录中。
这对于从 CDN 中注册外部及脚本或已经直接编译到 /public 目录的脚本很有用:
use Filament\Support\Assets\Js;
FilamentAsset::register([
Js::make('example-external-script', 'https://example.com/external.js'),
Js::make('example-local-script', asset('js/local.js')),
]);