Skip to content
全部文档

资源打包(Vite)

简介

Vite 是一款现代前端构建工具,提供极快的开发环境,并将代码打包用于生产。使用 Laravel 构建应用时,通常会使用 Vite 将应用的 CSS 与 JavaScript 文件打包为生产就绪的资源。

Laravel 通过提供官方插件与 Blade 指令来无缝集成 Vite,以便在开发与生产中加载资源。

安装与设置

INFO

以下文档讨论如何手动安装与配置 Laravel Vite 插件。不过,Laravel 的起步套件已包含所有这些脚手架,是开始使用 Laravel 与 Vite 的最快方式。

安装 Node

在运行 Vite 与 Laravel 插件之前,必须确保已安装 Node.js(16+)与 NPM:

shell
node -v
npm -v

你可使用 Node 官方网站 上的简单图形安装程序轻松安装最新版 Node 与 NPM。或者,若使用 Laravel Sail,可通过 Sail 调用 Node 与 NPM:

shell
./vendor/bin/sail node -v
./vendor/bin/sail npm -v

安装 Vite 与 Laravel 插件

在全新安装的 Laravel 中,你会在应用目录结构的根目录找到 package.json 文件。默认的 package.json 文件已包含开始使用 Vite 与 Laravel 插件所需的一切。可通过 NPM 安装应用的前端依赖:

shell
npm install

配置 Vite

Vite 通过项目根目录中的 vite.config.js 文件配置。你可根据需要自由自定义此文件,也可安装应用所需的任何其他插件,例如 @vitejs/plugin-react@sveltejs/vite-plugin-svelte@vitejs/plugin-vue

Laravel Vite 插件要求你指定应用的入口点。这些可以是 JavaScript 或 CSS 文件,并包括 TypeScript、JSX、TSX 与 Sass 等预处理语言。

js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: [
        laravel([
            'resources/css/app.css',
            'resources/js/app.js',
        ]),
    ],
});

若你在构建 SPA(包括使用 Inertia 构建的应用),Vite 在没有 CSS 入口点时效果最佳:

js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: [
        laravel([
            'resources/css/app.css', // [tl! remove]
            'resources/js/app.js',
        ]),
    ],
});

相反,你应通过 JavaScript 导入 CSS。通常这会在应用的 resources/js/app.js 文件中完成:

js
import './bootstrap';
import '../css/app.css'; // [tl! add]

Laravel 插件还支持多个入口点以及 SSR 入口点 等高级配置选项。

使用安全的开发服务器

若本地开发 Web 服务器通过 HTTPS 提供应用,你可能会在连接到 Vite 开发服务器时遇到问题。

若你使用 Laravel Herd 并为站点启用了安全访问,或使用 Laravel Valet 并对应用运行了 secure 命令,Laravel Vite 插件会自动检测并为你使用生成的 TLS 证书。

若你使用的主机与应用目录名不匹配来保护站点,可在应用的 vite.config.js 文件中手动指定主机:

js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: [
        laravel({
            // ...
            detectTls: 'my-app.test', // [tl! add]
        }),
    ],
});

使用其他 Web 服务器时,应生成受信任的证书,并手动配置 Vite 使用生成的证书:

js
// ...
import fs from 'fs'; // [tl! add]

const host = 'my-app.test'; // [tl! add]

export default defineConfig({
    // ...
    server: { // [tl! add]
        host, // [tl! add]
        hmr: { host }, // [tl! add]
        https: { // [tl! add]
            key: fs.readFileSync(`/path/to/${host}.key`), // [tl! add]
            cert: fs.readFileSync(`/path/to/${host}.crt`), // [tl! add]
        }, // [tl! add]
    }, // [tl! add]
});

若无法为系统生成受信任的证书,可安装并配置 @vitejs/plugin-basic-ssl 插件。使用不受信任的证书时,需要在浏览器中接受 Vite 开发服务器的证书警告,方法是在运行 npm run dev 命令时点击控制台中的「Local」链接。

在 WSL2 上的 Sail 中运行开发服务器

在 Windows Subsystem for Linux 2(WSL2)上的 Laravel Sail 中运行 Vite 开发服务器时,应向 vite.config.js 文件添加以下配置,以确保浏览器能与开发服务器通信:

js
// ...

export default defineConfig({
    // ...
    server: { // [tl! add:start]
        hmr: {
            host: 'localhost',
        },
    }, // [tl! add:end]
});

若开发服务器运行时文件更改未反映在浏览器中,你可能还需要配置 Vite 的 server.watch.usePolling 选项

加载脚本与样式

配置好 Vite 入口点后,现在可在应用根模板的 <head> 中添加的 @vite() Blade 指令中引用它们:

blade
<!DOCTYPE html>
<head>
    {{-- ... --}}

    @vite(['resources/css/app.css', 'resources/js/app.js'])
</head>

若通过 JavaScript 导入 CSS,则只需包含 JavaScript 入口点:

blade
<!DOCTYPE html>
<head>
    {{-- ... --}}

    @vite('resources/js/app.js')
</head>

@vite 指令会自动检测 Vite 开发服务器并注入 Vite 客户端以启用热模块替换。在构建模式下,该指令会加载已编译并版本化的资源,包括任何导入的 CSS。

如有需要,调用 @vite 指令时也可指定已编译资源的构建路径:

blade
<!doctype html>
<head>
    {{-- Given build path is relative to public path. --}}

    @vite('resources/js/app.js', 'vendor/courier/build')
</head>

内联资源

有时可能需要包含资源的原始内容,而不是链接到资源的版本化 URL。例如,在向 PDF 生成器传递 HTML 内容时,你可能需要将资源内容直接包含到页面中。可使用 Vite facade 提供的 content 方法输出 Vite 资源的内容:

blade
@use('Illuminate\Support\Facades\Vite')

<!doctype html>
<head>
    {{-- ... --}}

    <style>
        {!! Vite::content('resources/css/app.css') !!}
    </style>
    <script>
        {!! Vite::content('resources/js/app.js') !!}
    </script>
</head>

运行 Vite

有两种方式运行 Vite。可通过 dev 命令运行开发服务器,这在本地开发时很有用。开发服务器会自动检测文件更改,并立即反映在任何打开的浏览器窗口中。

或者,运行 build 命令会对应用的资源进行版本化与打包,使其准备好部署到生产环境:

shell
# Run the Vite development server...
npm run dev

# Build and version the assets for production...
npm run build

若在 WSL2 上的 Sail 中运行开发服务器,可能需要一些额外配置选项。

使用 JavaScript

别名

默认情况下,Laravel 插件提供了一个常用别名,帮助你快速上手并方便地导入应用的资源:

js
{
    '@' => '/resources/js'
}

可通过向 vite.config.js 配置文件添加自己的别名来覆盖 '@' 别名:

js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: [
        laravel(['resources/ts/app.tsx']),
    ],
    resolve: {
        alias: {
            '@': '/resources/ts',
        },
    },
});

Vue

若希望使用 Vue 框架构建前端,还需要安装 @vitejs/plugin-vue 插件:

shell
npm install --save-dev @vitejs/plugin-vue

然后可在 vite.config.js 配置文件中包含该插件。将 Vue 插件与 Laravel 一起使用时,还需要一些额外选项:

js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
    plugins: [
        laravel(['resources/js/app.js']),
        vue({
            template: {
                transformAssetUrls: {
                    // The Vue plugin will re-write asset URLs, when referenced
                    // in Single File Components, to point to the Laravel web
                    // server. Setting this to `null` allows the Laravel plugin
                    // to instead re-write asset URLs to point to the Vite
                    // server instead.
                    base: null,

                    // The Vue plugin will parse absolute URLs and treat them
                    // as absolute paths to files on disk. Setting this to
                    // `false` will leave absolute URLs un-touched so they can
                    // reference assets in the public directory as expected.
                    includeAbsolute: false,
                },
            },
        }),
    ],
});

INFO

Laravel 的起步套件已包含正确的 Laravel、Vue 与 Vite 配置。这些起步套件是开始使用 Laravel、Vue 与 Vite 的最快方式。

React

若希望使用 React 框架构建前端,还需要安装 @vitejs/plugin-react 插件:

shell
npm install --save-dev @vitejs/plugin-react

然后可在 vite.config.js 配置文件中包含该插件:

js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import react from '@vitejs/plugin-react';

export default defineConfig({
    plugins: [
        laravel(['resources/js/app.jsx']),
        react(),
    ],
});

需要确保任何包含 JSX 的文件具有 .jsx.tsx 扩展名,并在需要时记得更新入口点,如上文所示

还需要在现有 @vite 指令旁包含额外的 @viteReactRefresh Blade 指令。

blade
@viteReactRefresh
@vite('resources/js/app.jsx')

@viteReactRefresh 指令必须在 @vite 指令之前调用。

INFO

Laravel 的起步套件已包含正确的 Laravel、React 与 Vite 配置。这些起步套件是开始使用 Laravel、React 与 Vite 的最快方式。

Svelte

若希望使用 Svelte 框架构建前端,还需要安装 @sveltejs/vite-plugin-svelte 插件:

shell
npm install --save-dev @sveltejs/vite-plugin-svelte

然后可在 vite.config.js 配置文件中包含该插件。

js
import { svelte } from '@sveltejs/vite-plugin-svelte';
import laravel from 'laravel-vite-plugin';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    laravel({
      input: ['resources/js/app.ts'],
      ssr: 'resources/js/ssr.ts',
      refresh: true,
    }),
    svelte(),
  ],
});

INFO

Laravel 的起步套件已包含正确的 Laravel、Svelte 与 Vite 配置。这些起步套件是开始使用 Laravel、Svelte 与 Vite 的最快方式。

Inertia

Laravel Vite 插件提供了便捷的 resolvePageComponent 函数,帮助你解析 Inertia 页面组件。以下是该辅助函数与 Vue 3 一起使用的示例;不过,你也可在 React 或 Svelte 等其他框架中使用该函数:

js
import { createApp, h } from 'vue';
import { createInertiaApp } from '@inertiajs/vue3';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';

createInertiaApp({
  resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')),
  setup({ el, App, props, plugin }) {
    createApp({ render: () => h(App, props) })
      .use(plugin)
      .mount(el)
  },
});

若在 Inertia 中使用 Vite 的代码分割功能,我们建议配置资源预取

INFO

Laravel 的起步套件已包含正确的 Laravel、Inertia 与 Vite 配置。这些起步套件是开始使用 Laravel、Inertia 与 Vite 的最快方式。

URL 处理

使用 Vite 并在应用的 HTML、CSS 或 JS 中引用资源时,有几点需要注意。首先,若使用绝对路径引用资源,Vite 不会将该资源包含在构建中;因此,应确保该资源在 public 目录中可用。使用专用 CSS 入口点时应避免使用绝对路径,因为在开发期间,浏览器会尝试从托管 CSS 的 Vite 开发服务器加载这些路径,而不是从 public 目录加载。

引用相对资源路径时,应记住路径相对于引用它们的文件。通过相对路径引用的任何资源都会被 Vite 重写、版本化并打包。

考虑以下项目结构:

text
public/
  taylor.png
resources/
  js/
    Pages/
      Welcome.vue
  images/
    abigail.png

以下示例演示 Vite 将如何处理相对与绝对 URL:

html
<!-- This asset is not handled by Vite and will not be included in the build -->
<img src="/taylor.png">

<!-- This asset will be re-written, versioned, and bundled by Vite -->
<img src="../../images/abigail.png">

使用样式表

INFO

Laravel 的起步套件已包含正确的 Tailwind 与 Vite 配置。或者,若希望在不使用起步套件的情况下使用 Tailwind 与 Laravel,请查看 Tailwind 的 Laravel 安装指南

所有 Laravel 应用已包含 Tailwind 与正确配置的 vite.config.js 文件。因此,你只需启动 Vite 开发服务器,或运行会同时启动 Laravel 与 Vite 开发服务器的 dev Composer 命令:

shell
composer run dev

应用的 CSS 可放在 resources/css/app.css 文件中。

使用字体

Laravel Vite 插件可为应用提供优化的自托管字体。配置字体后,插件会解析请求的字体文件,将它们作为 Vite 资源发出,生成字体 CSS,并写入可由 Blade 的 @fonts 指令使用的字体清单。

要配置字体,从 laravel-vite-plugin/fonts 导入一个或多个提供者辅助函数,并将它们添加到 Laravel 插件的 fonts 选项:

js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import { google } from 'laravel-vite-plugin/fonts';

export default defineConfig({
    plugins: [
        laravel({
            input: 'resources/js/app.js',
            fonts: [
                google('Inter', {
                    alias: 'sans',
                    weights: [400, 500, 600, 700],
                    styles: ['normal', 'italic'],
                    subsets: ['latin'],
                    display: 'swap',
                    preload: [
                        { weight: 400 },
                        { weight: 700 },
                    ],
                    fallbacks: ['system-ui', 'sans-serif'],
                }),
            ],
        }),
    ],
});

在本例中,Inter 字体将通过 sans 别名可用。插件会生成 --font-sans CSS 变量以及应用生成字体栈的 .font-sans 工具类。

字体提供者

Laravel Vite 插件包含 Google Fonts、Bunny Fonts、Fontsource 与本地字体的提供者辅助函数:

js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import { bunny, fontsource, google, local } from 'laravel-vite-plugin/fonts';

export default defineConfig({
    plugins: [
        laravel({
            input: 'resources/js/app.js',
            fonts: [
                google('Inter', { alias: 'sans' }),
                bunny('Figtree', { alias: 'body' }),
                fontsource('JetBrains Mono', { alias: 'mono' }),
                local('Brand Sans', {
                    alias: 'brand',
                    src: 'resources/fonts/brand-sans',
                }),
            ],
        }),
    ],
});

fontsource 提供者从已安装的 Fontsource 包读取字体。默认情况下,包名从字体系列派生,例如 @fontsource/jetbrains-mono。若应用使用不同的包名,可使用 package 选项指定。

本地字体

使用本地字体时,src 选项可指向单个字体文件、目录或 glob 模式。插件会发现受支持的字体文件,并根据文件名推断其字重与样式:

js
local('Brand Sans', {
    alias: 'brand',
    src: 'resources/fonts/brand-sans/*.woff2',
})

若需要对可用变体有完全控制,可使用 variants 选项显式定义它们:

js
local('Brand Sans', {
    alias: 'brand',
    variants: [
        { src: 'resources/fonts/BrandSans-Regular.woff2', weight: 400 },
        { src: 'resources/fonts/BrandSans-Italic.woff2', weight: 400, style: 'italic' },
        { src: ['resources/fonts/BrandSans-Bold.woff2', 'resources/fonts/BrandSans-Bold.ttf'], weight: 700 },
    ],
})

字体选项

根据提供者的不同,字体定义可能接受若干允许你自定义生成字体 CSS 的选项:

  • `alias` 定义 Blade `@fonts` 指令使用的名称,默认为字体系列的 slug。
  • `variable` 定义生成的 CSS 变量,默认为 `--font-{alias}`。
  • `weights` 定义应解析的远程或 Fontsource 字重,默认为 `[400]`。
  • `styles` 定义应解析的远程或 Fontsource 字体样式,默认为 `['normal']`。
  • `subsets` 定义应解析的远程或 Fontsource 字体子集,默认为 `['latin']`。
  • `display` 定义 `font-display` 值,默认为 `swap`。
  • `preload` 控制应预加载哪些 WOFF2 字体变体。此选项可以是 `true`、`false`,或 `{ weight, style }` 选择器数组。
  • `fallbacks` 定义应追加到生成字体栈的额外回退字体。
  • `optimizedFallbacks` 尝试使用可选的 `fontaine` 包生成度量调整后的回退字体面,默认为 `true`。

优化回退需要默认未安装的 fontaine 包。若希望 Laravel 生成度量调整后的回退字体面,应将 fontaine 安装为开发依赖:

shell
npm install --save-dev fontaine

若未安装 fontaine 或无法读取字体文件,Laravel 会跳过该字体的优化回退,并继续使用通过 fallbacks 选项配置的任何字体。

本地字体从上文所述的 srcvariants 选项解析,而不是使用 weightsstylessubsets

使用 Blade 与路由

使用 Vite 处理静态资源

在 JavaScript 或 CSS 中引用资源时,Vite 会自动处理并版本化它们。此外,在构建基于 Blade 的应用时,Vite 还可处理并版本化你仅在 Blade 模板中引用的静态资源。

不过,要实现这一点,需要通过在插件的 assets 选项中指定资源,让 Vite 知道它们。此选项适用于希望用 Vite::asset 直接引用的静态文件。若希望 Laravel 生成字体 CSS 与预加载链接,请改用 fonts 选项

例如,若希望处理并版本化存储在 resources/images 中的所有图片以及存储在 resources/fonts 中的所有字体,应向 Vite 配置添加以下内容:

js
laravel({
    input: 'resources/js/app.js',
    assets: ['resources/images/**', 'resources/fonts/**'],
})

现在运行 npm run build 时,这些资源将由 Vite 处理。然后可在 Blade 模板中使用 Vite::asset 方法引用这些资源,该方法会返回给定资源的版本化 URL:

blade
<img src="{{ Vite::asset('resources/images/logo.png') }}">

INFO

在 Laravel Vite 插件第 3 版之前,静态资源必须使用 import.meta.glob 在应用入口点中导入。由于 Vite 8 的变更引入了 assets 选项。

保存时刷新

当应用使用传统的 Blade 服务端渲染构建时,Vite 可通过在你更改应用中的视图文件时自动刷新浏览器来改善开发工作流。开始时,只需将 refresh 选项指定为 true

js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: [
        laravel({
            // ...
            refresh: true,
        }),
    ],
});

refresh 选项为 true 时,在运行 npm run dev 期间保存以下目录中的文件会触发浏览器执行整页刷新:

  • app/Livewire/**
  • app/View/Components/**
  • lang/**
  • resources/lang/**
  • resources/views/**
  • routes/**

监视 routes/** 目录在你使用 Ziggy 在应用前端生成路由链接时很有用。

若这些默认路径不适合你的需求,可指定自己的监视路径列表:

js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: [
        laravel({
            // ...
            refresh: ['resources/views/**'],
        }),
    ],
});

底层上,Laravel Vite 插件使用 vite-plugin-full-reload 包,该包提供一些高级配置选项以微调此功能的行为。若需要这种程度的自定义,可提供 config 定义:

js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: [
        laravel({
            // ...
            refresh: [{
                paths: ['path/to/watch/**'],
                config: { delay: 300 }
            }],
        }),
    ],
});

别名

在 JavaScript 应用中创建别名指向经常引用的目录很常见。不过,你也可通过在 Illuminate\Support\Facades\Vite 类上使用 macro 方法创建在 Blade 中使用的别名。通常,「宏」应在服务提供者boot 方法中定义:

php
/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Vite::macro('image', fn (string $asset) => $this->asset("resources/images/{$asset}"));
}

定义宏后,可在模板中调用它。例如,我们可使用上面定义的 image 宏来引用位于 resources/images/logo.png 的资源:

blade
<img src="{{ Vite::image('logo.png') }}" alt="Laravel Logo">

资源预取

使用 Vite 的代码分割功能构建 SPA 时,每次页面导航都会获取所需资源。此行为可能导致 UI 渲染延迟。若这对你选择的前端框架是个问题,Laravel 提供了在初始页面加载时预先预取应用 JavaScript 与 CSS 资源的能力。

可通过在服务提供者boot 方法中调用 Vite::prefetch 方法,指示 Laravel 预先预取资源:

php
<?php

namespace App\Providers;

use Illuminate\Support\Facades\Vite;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        // ...
    }

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        Vite::prefetch(concurrency: 3);
    }
}

在上例中,每次页面加载时资源会以最多 3 个并发下载进行预取。你可修改并发数以适应应用需求,或在应用应一次下载所有资源时不指定并发限制:

php
/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Vite::prefetch();
}

默认情况下,预取会在页面 load 事件触发时开始。若希望自定义预取开始的时机,可指定 Vite 将监听的事件:

php
/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Vite::prefetch(event: 'vite:prefetch');
}

根据上面的代码,预取现在会在你手动向 window 对象派发 vite:prefetch 事件时开始。例如,可让预取在页面加载三秒后开始:

html
<script>
    addEventListener('load', () => setTimeout(() => {
        dispatchEvent(new Event('vite:prefetch'))
    }, 3000))
</script>

自定义基础 URL

若 Vite 编译的资源部署到与应用分离的域名(例如通过 CDN),必须在应用的 .env 文件中指定 ASSET_URL 环境变量:

ini
ASSET_URL=https://cdn.example.com

配置资源 URL 后,所有重写后的资源 URL 都会加上配置值的前缀:

text
https://cdn.example.com/build/assets/app.9dce8d17.js

请记住,绝对 URL 不会被 Vite 重写,因此不会加前缀。

环境变量

可通过在应用的 .env 文件中以 VITE_ 为前缀,将环境变量注入到 JavaScript 中:

ini
VITE_SENTRY_DSN_PUBLIC=http://example.com

可通过 import.meta.env 对象访问注入的环境变量:

js
import.meta.env.VITE_SENTRY_DSN_PUBLIC

在测试中禁用 Vite

Laravel 的 Vite 集成会在运行测试时尝试解析资源,这要求你要么运行 Vite 开发服务器,要么构建资源。

若希望在测试期间模拟 Vite,可调用 withoutVite 方法,该方法对任何扩展 Laravel TestCase 类的测试都可用:

php
test('without vite example', function () {
    $this->withoutVite();

    // ...
});
php
use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function test_without_vite_example(): void
    {
        $this->withoutVite();

        // ...
    }
}

若希望为所有测试禁用 Vite,可从基础 TestCase 类的 setUp 方法调用 withoutVite 方法:

php
<?php

namespace Tests;

use Illuminate\Foundation\Testing\TestCase as BaseTestCase;

abstract class TestCase extends BaseTestCase
{
    protected function setUp(): void// [tl! add:start]
    {
        parent::setUp();

        $this->withoutVite();
    }// [tl! add:end]
}

服务端渲染(SSR)

Laravel Vite 插件使使用 Vite 设置服务端渲染变得轻松。开始时,在 resources/js/ssr.js 创建 SSR 入口点,并通过向 Laravel 插件传递配置选项来指定入口点:

js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: [
        laravel({
            input: 'resources/js/app.js',
            ssr: 'resources/js/ssr.js',
        }),
    ],
});

为确保你不会忘记重建 SSR 入口点,我们建议增强应用 package.json 中的「build」脚本以创建 SSR 构建:

json
"scripts": {
     "dev": "vite",
     "build": "vite build" // [tl! remove]
     "build": "vite build && vite build --ssr" // [tl! add]
}

然后,要构建并启动 SSR 服务器,可运行以下命令:

shell
npm run build
node bootstrap/ssr/ssr.js

若使用 Inertia 的 SSR,可改用 inertia:start-ssr Artisan 命令启动 SSR 服务器:

shell
php artisan inertia:start-ssr

INFO

Laravel 的起步套件已包含正确的 Laravel、Inertia SSR 与 Vite 配置。这些起步套件是开始使用 Laravel、Inertia SSR 与 Vite 的最快方式。

Script 与 Style 标签属性

内容安全策略(CSP)Nonce

若希望作为内容安全策略的一部分,在 script 与 style 标签上包含 nonce 属性,可在自定义中间件中使用 useCspNonce 方法生成或指定 nonce:

php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Vite;
use Symfony\Component\HttpFoundation\Response;

class AddContentSecurityPolicyHeaders
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        Vite::useCspNonce();

        return $next($request)->withHeaders([
            'Content-Security-Policy' => "script-src 'nonce-".Vite::cspNonce()."'",
        ]);
    }
}

调用 useCspNonce 方法后,Laravel 会自动在所有生成的 script 与 style 标签上包含 nonce 属性。

若需要在其他地方指定 nonce,包括 Laravel 起步套件附带的 Ziggy @route 指令,可使用 cspNonce 方法检索它:

blade
@routes(nonce: Vite::cspNonce())

若已有希望指示 Laravel 使用的 nonce,可将该 nonce 传给 useCspNonce 方法:

php
Vite::useCspNonce($nonce);

子资源完整性(SRI)

若 Vite 清单包含资源的 integrity 哈希,Laravel 会自动在其生成的任何 script 与 style 标签上添加 integrity 属性,以强制执行子资源完整性。默认情况下,Vite 不会在其清单中包含 integrity 哈希,但你可通过安装 vite-plugin-manifest-sri NPM 插件启用它:

shell
npm install --save-dev vite-plugin-manifest-sri

然后可在 vite.config.js 文件中启用此插件:

js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import manifestSRI from 'vite-plugin-manifest-sri';// [tl! add]

export default defineConfig({
    plugins: [
        laravel({
            // ...
        }),
        manifestSRI(),// [tl! add]
    ],
});

如有需要,也可自定义可找到完整性哈希的清单键:

php
use Illuminate\Support\Facades\Vite;

Vite::useIntegrityKey('custom-integrity-key');

若希望完全禁用此自动检测,可将 false 传给 useIntegrityKey 方法:

php
Vite::useIntegrityKey(false);

任意属性

若需要在 script 与 style 标签上包含额外属性,例如 data-turbo-track 属性,可通过 useScriptTagAttributesuseStyleTagAttributes 方法指定它们。通常,这些方法应从服务提供者中调用:

php
use Illuminate\Support\Facades\Vite;

Vite::useScriptTagAttributes([
    'data-turbo-track' => 'reload', // Specify a value for the attribute...
    'async' => true, // Specify an attribute without a value...
    'integrity' => false, // Exclude an attribute that would otherwise be included...
]);

Vite::useStyleTagAttributes([
    'data-turbo-track' => 'reload',
]);

若需要有条件地添加属性,可传入一个回调,该回调将接收资源源路径、其 URL、其清单 chunk 以及整个清单:

php
use Illuminate\Support\Facades\Vite;

Vite::useScriptTagAttributes(fn (string $src, string $url, array|null $chunk, array|null $manifest) => [
    'data-turbo-track' => $src === 'resources/js/app.js' ? 'reload' : false,
]);

Vite::useStyleTagAttributes(fn (string $src, string $url, array|null $chunk, array|null $manifest) => [
    'data-turbo-track' => $chunk && $chunk['isEntry'] ? 'reload' : false,
]);

WARNING

在 Vite 开发服务器运行时,$chunk$manifest 参数将为 null

高级自定义

开箱即用,Laravel 的 Vite 插件使用适用于大多数应用的合理约定;不过,有时你可能需要自定义 Vite 的行为。为启用额外的自定义选项,我们提供以下方法与选项,可用于替代 @vite Blade 指令:

blade
<!doctype html>
<head>
    {{-- ... --}}

    {{
        Vite::useHotFile(storage_path('vite.hot')) // Customize the "hot" file...
            ->useBuildDirectory('bundle') // Customize the build directory...
            ->useManifestFilename('assets.json') // Customize the manifest filename...
            ->withEntryPoints(['resources/js/app.js']) // Specify the entry points...
            ->createAssetPathsUsing(function (string $path, ?bool $secure) { // Customize the backend path generation for built assets...
                return "https://cdn.example.com/{$path}";
            })
    }}
</head>

然后应在 vite.config.js 文件中指定相同的配置:

js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: [
        laravel({
            hotFile: 'storage/vite.hot', // Customize the "hot" file...
            buildDirectory: 'bundle', // Customize the build directory...
            input: ['resources/js/app.js'], // Specify the entry points...
        }),
    ],
    build: {
      manifest: 'assets.json', // Customize the manifest filename...
    },
});

开发服务器跨源资源共享(CORS)

若在从 Vite 开发服务器获取资源时在浏览器中遇到跨源资源共享(CORS)问题,可能需要授予自定义源访问开发服务器的权限。Vite 与 Laravel 插件结合使用时,无需额外配置即允许以下源:

  • ::1
  • 127.0.0.1
  • localhost
  • *.test
  • *.localhost
  • APP_URL in the project's .env

为项目允许自定义源的最简单方式是确保应用的 APP_URL 环境变量与你在浏览器中访问的源匹配。例如,若你访问 https://my-app.laravel,应更新 .env 以匹配:

ini
APP_URL=https://my-app.laravel

若需要对源有更细粒度的控制,例如支持多个源,应利用 Vite 全面且灵活的内置 CORS 服务器配置。例如,可在项目的 vite.config.js 文件中的 server.cors.origin 配置选项中指定多个源:

js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: [
        laravel({
            input: 'resources/js/app.js',
            refresh: true,
        }),
    ],
    server: {  // [tl! add]
        cors: {  // [tl! add]
            origin: [  // [tl! add]
                'https://backend.laravel',  // [tl! add]
                'http://admin.laravel:8566',  // [tl! add]
            ],  // [tl! add]
        },  // [tl! add]
    },  // [tl! add]
});

也可包含正则表达式模式,这在你希望允许给定顶级域名的所有源(例如 *.laravel)时会很有用:

js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: [
        laravel({
            input: 'resources/js/app.js',
            refresh: true,
        }),
    ],
    server: {  // [tl! add]
        cors: {  // [tl! add]
            origin: [ // [tl! add]
                // Supports: SCHEME://DOMAIN.laravel[:PORT] [tl! add]
                /^https?:\/\/.*\.laravel(:\d+)?$/, //[tl! add]
            ], // [tl! add]
        }, // [tl! add]
    }, // [tl! add]
});

修正开发服务器 URL

Vite 生态中的某些插件假定以正斜杠开头的 URL 始终指向 Vite 开发服务器。不过,由于 Laravel 集成的特性,情况并非如此。

例如,在 Vite 提供资源时,vite-imagetools 插件会输出如下 URL:

html
<img src="/@imagetools/f0b2f404b13f052c604e632f2fb60381bf61a520">

vite-imagetools 插件期望输出的 URL 被 Vite 拦截,然后该插件可处理所有以 /@imagetools 开头的 URL。若你使用期望此行为的插件,需要手动修正 URL。可在 vite.config.js 文件中使用 transformOnServe 选项来完成。

在这个特定示例中,我们将为生成代码中所有 /@imagetools 的出现加上开发服务器 URL 前缀:

js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import { imagetools } from 'vite-imagetools';

export default defineConfig({
    plugins: [
        laravel({
            // ...
            transformOnServe: (code, devServerUrl) => code.replaceAll('/@imagetools', devServerUrl+'/@imagetools'),
        }),
        imagetools(),
    ],
});

现在,在 Vite 提供资源时,它会输出指向 Vite 开发服务器的 URL:

html
- <img src="/@imagetools/f0b2f404b13f052c604e632f2fb60381bf61a520"><!-- [tl! remove] -->
+ <img src="http://[::1]:5173/@imagetools/f0b2f404b13f052c604e632f2fb60381bf61a520"><!-- [tl! add] -->