简介

Vite 是一款现代前端构建工具,它提供了极速的开发环境,并可将你的代码打包以用于生产环境。在使用 Laravel 构建应用时,通常可借助 Vite 将应用的 CSS 与 JavaScript 文件打包成可投入生产环境的资源。

Laravel 通过提供官方插件与 Blade 指令,可与 Vite 实现无缝集成,用于在开发和生产环境中加载资源。

安装与配置

以下文档将说明如何手动安装并配置 Laravel Vite 插件。不过,Laravel 的 入门套件已包含所有这些脚手架,是快速上手 Laravel 与 Vite 的最快方式。

安装 Node

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

node -v
npm -v

你可以通过Node 官方网站提供的简易图形化安装程序,轻松安装最新版本的 Node 和 NPM。如果你正在使用 Laravel Sail,也可以通过 Sail 调用 Node 和 NPM:

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

安装 Vite 和 Laravel 插件

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

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。

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 入口文件的情况下运行效果最佳:


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

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

此时,你应通过 JavaScript 引入 CSS。通常,这一操作会在应用的 resources/js/app.js 文件中完成:


import './bootstrap';
import '../css/app.css';

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

使用安全开发服务器

如果你的本地开发 Web 服务器通过 HTTPS 提供应用服务,可能会遇到无法连接到 Vite 开发服务器的问题。

如果你使用的是 Laravel Herd 并对站点进行了安全配置,或者你使用的是 Laravel Valet 且已对应用执行了 安全命令,Laravel Vite 插件会自动检测并使用生成的 TLS 证书。

如果你使用的主机名与应用目录名称不一致来保护站点,你可以在应用的 vite.config.js 文件中手动指定主机名:


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

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

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


// ...
import fs from 'fs';

const host = 'my-app.test';

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

如果你无法为系统生成受信任的证书,可以安装并配置 @vitejs/plugin-basic-ssl 插件。在使用不受信任的证书时,运行 npm run dev 命令时,你需要在浏览器中通过控制台里的“Local”链接,接受 Vite 开发服务器的证书警告。

在 WSL2 上通过 Sail 运行开发服务器

在 Windows 子系统 Linux 2(WSL2)环境下,通过 Laravel Sail 运行 Vite 开发服务器时,你需要在 vite.config.js 文件中添加以下配置,以确保浏览器能够与开发服务器进行通信:


// ...

export default defineConfig({
    // ...
    server: {
        hmr: {
            host: 'localhost',
        },
    },
});

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

加载脚本与样式

在配置好 Vite 入口文件后,你可以在应用根模板的 <head> 标签中添加 @vite() Blade 指令来引用这些入口文件:

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

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

如果你通过 JavaScript 引入 CSS,则只需包含 JavaScript 入口文件:

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

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

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

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

<!doctype html>
<head>
    {{-- 给定的构建路径相对于公共路径。 --}}

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

内联资源

在某些情况下,可能需要包含资源的原始内容,而非链接到资源的版本化 URL。例如,当向 PDF 生成器传递 HTML 内容时,可能需要将资源内容直接嵌入到页面中。你可以使用 Vite 门面(facade)提供的 content 方法输出 Vite 资源的内容:

@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 命令会对应用的资源进行版本化处理和打包,使其准备好部署到生产环境:

# 运行 Vite 开发服务器...
npm run dev

# 为生产环境构建并版本化资源...
npm run build

如果你在 WSL2 环境下通过 Sail 运行开发服务器,可能需要一些额外的配置选项。

使用 JavaScript

别名

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

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

你可以在 vite.config.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 插件:

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

随后,你可以将该插件加入到你的 vite.config.js 配置文件中。当在 Laravel 中使用 Vue 插件时,你还需要设置以下几个附加选项:

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: {
                    // 当在单文件组件中引用资源时,Vue 插件会重写资源 URL,
                    // 使其指向 Laravel Web 服务器。将其设置为 `null` 则允许
                    // Laravel 插件改为将资源 URL 重写,使其指向 Vite 服务器。
                    base: null,

                    // Vue 插件会解析绝对 URL,并将其视为磁盘上文件的绝对路径。
                    // 将其设置为 `false` 则会保留绝对 URL 不做更改,从而可以
                    // 按预期引用 public 目录中的资源。
                    includeAbsolute: false,
                },
            },
        }),
    ],
});

Laravel 的 入门套件已包含正确的 Laravel、Vue 和 Vite 配置。这些入门套件是快速上手 Laravel、Vue 和 Vite 的最佳方式。

React

如果你想使用 React 框架构建前端,那么你还需要安装 @vitejs/plugin-react 插件:

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

随后,你可以将该插件加入到你的 vite.config.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 指令。

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

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

Laravel 的 入门套件已包含正确的 Laravel、React 和 Vite 配置。这些入门套件是快速上手 Laravel、React 和 Vite 的最佳方式。

Svelte

如果你想使用 Svelte 框架构建前端,那么你还需要安装 @sveltejs/vite-plugin-svelte 插件:

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

随后,你可以将该插件加入到你的 vite.config.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(),
  ],
});

Laravel 的 入门套件已包含正确的 Laravel、Svelte 和 Vite 配置。这些入门套件是快速上手 Laravel、Svelte 和 Vite 的最佳方式。

Inertia

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

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)
  },
});

如果你在使用 Vite 的代码拆分功能与 Inertia 时,我们建议配置资源预取。

Laravel 的 入门套件已包含正确的 Laravel、Inertia 和 Vite 配置。这些入门套件是快速上手 Laravel、Inertia 和 Vite 的最佳方式。

URL 处理

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

在引用相对资源路径时,应记住这些路径是相对于引用它们的文件而言的。任何通过相对路径引用的资源都将由 Vite 重写、版本化并打包。

请参考以下项目结构:

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

下面的示例展示了 Vite 将如何处理相对 URL 和绝对 URL:

<!-- 此资源不由 Vite 处理,且不会被包含在构建中 -->
<img src="/taylor.png">

<!-- 此资源将被 Vite 重写、版本化并打包 -->
<img src="../../images/abigail.png">

处理样式表

Laravel 的 入门套件已包含正确的 Tailwind 和 Vite 配置。或者,如果你想在不使用我们的入门套件的情况下使用 Tailwind 和 Laravel,请查看 Tailwind 针对 Laravel 的安装指南。

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

composer run dev

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

处理字体

Laravel Vite 插件可以为你的应用程序提供经过优化的自托管字体。当配置好字体后,该插件会解析所请求的字体文件,将其作为 Vite 资源输出,生成字体 CSS,并写入字体清单,该清单可由 Blade 的 @fonts 指令使用。

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

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 以及本地字体的提供程序辅助函数:

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 选项可以指向单个字体文件、目录或通配符模式。插件会自动发现受支持的字体文件,并根据文件名推断其字重和样式:

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

如果你需要完全控制可用的变体,可以使用 variants 选项显式定义它们:

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 作为开发依赖项进行安装:

npm install --save-dev fontaine

如果未安装 fontaine,或者无法读取字体文件,Laravel 将跳过该字体的优化备用方案,继续使用通过 fallbacks 选项配置的任何字体。

本地字体会从上述的 src 或 variants 选项中解析,而不使用 weights、styles 和 subsets。

使用 Blade 与路由

使用 Vite 处理静态资源

在 JavaScript 或 CSS 中引用资源时,Vite 会自动对其进行处理和版本管理。此外,在构建基于 Blade 的应用时,Vite 还可以处理和版本管理仅在 Blade 模板中引用的静态资源。

然而,要实现这一点,你需要通过在插件的 assets 选项中指定这些资源,让 Vite 感知到它们。该选项适用于你希望直接使用 Vite::asset 引用的静态文件。如果你希望 Laravel 生成字体 CSS 和预加载链接,请使用 fonts 选项。

例如,如果你想处理并版本管理存储在 resources/images 中的所有图片,以及存储在 resources/fonts 中的所有字体,你应该在 Vite 配置中添加以下内容:

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

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

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

在 Laravel Vite 插件 3 版本之前,静态资源必须使用 import.meta.glob 在应用的入口文件中导入。assets 选项是因 Vite 8 的变化而引入的。

保存时刷新

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

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/**

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

如果这些默认路径不符合你的需求,你可以指定自定义的监视路径列表:

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 定义:

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 中使用的别名。通常,“宏(macros)”应定义在 服务提供者的 boot 方法中:

/**
 * 初始化任何应用服务。
 */
public function boot(): void
{
    Vite::macro('image', fn (string $asset) => $this->asset("resources/images/{$asset}"));
}

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

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

资源预取

当使用 Vite 的代码分割功能构建 SPA 时,所需的资源会在每次页面导航时被获取。这种行为可能会导致 UI 渲染延迟。如果这对你所选择的前端框架造成了问题,Laravel 提供了在初始页面加载时主动预取应用的 JavaScript 和 CSS 资源的能力。

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

<?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 个并发下载。你可以根据应用程序的需求调整并发数,若希望应用程序一次性下载所有资源,也可指定不限制并发数:

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

默认情况下,预取会在页面加载事件触发时开始。如果你想自定义预取开始的时间,可以指定一个Vite会监听的事件:

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

根据上述代码,当你在window对象上手动触发vite:prefetch事件时,预取就会开始。例如,你可以让预取在页面加载3秒后开始:

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

自定义基础 URL

如果你的Vite编译后的资源部署在与应用程序不同的域名上,例如通过CDN部署,必须在应用程序的.env文件中指定ASSET_URL环境变量:

ASSET_URL=https://cdn.example.com

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

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

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

环境变量

你可以在应用程序的.env文件中为环境变量添加VITE_前缀,将其注入到你的 JavaScript 代码中:

VITE_SENTRY_DSN_PUBLIC=http://example.com

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

import.meta.env.VITE_SENTRY_DSN_PUBLIC

在测试中禁用 Vite

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

如果你希望在测试期间模拟Vite,可以调用withoutVite方法,该方法适用于任何继承自LaravelTestCase类的测试:

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

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

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

        // ...
    }
}

如果你希望在所有测试中禁用Vite,可以在基础TestCase类的setUp方法中调用withoutVite方法:


<?php

namespace Tests;

use Illuminate\Foundation\Testing\TestCase as BaseTestCase;

abstract class TestCase extends BaseTestCase
{
    protected function setUp(): void
    {
        parent::setUp();

        $this->withoutVite();
    }
}

服务器端渲染(SSR)

Laravel Vite插件让使用Vite搭建服务器端渲染变得十分简单。首先,在resources/js/ssr.js处创建SSR入口文件,并通过向Laravel插件传递配置项来指定该入口:

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构建:


"scripts": {
     "dev": "vite",
     "build": "vite build"
     "build": "vite build && vite build --ssr"
}

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

npm run build
node bootstrap/ssr/ssr.js

如果你使用配合Inertia的SSR,你可以改用inertia:start-ssr Artisan命令来启动SSR服务器:

php artisan inertia:start-ssr

Laravel的入门套件已内置了正确的Laravel、Inertia SSR和Vite配置。这些入门套件是快速上手Laravel、Inertia SSR和Vite的最佳途径。

脚本与样式标签属性

内容安全策略(CSP) Nonce

如果你想在脚本和样式标签上添加nonce属性,作为你的内容安全策略的一部分,你可以在自定义中间件中使用useCspNonce方法生成或指定一个nonce:

<?php

namespace App\Http\Middleware;

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

class AddContentSecurityPolicyHeaders
{
    /**
     * 处理传入请求。
     *
     * @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会自动在所有生成的脚本和样式标签中添加nonce属性。

如果你需要在其他地方指定nonce,包括Laravel入门套件中包含的Ziggy @route指令,你可以使用cspNonce方法获取它:

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

如果你已有现成的nonce,想让Laravel使用,可以将该nonce传递给useCspNonce方法:

Vite::useCspNonce($nonce);

子资源完整性(SRI)

如果Vite清单中包含资源的integrity哈希,Laravel会自动在生成的任何脚本和样式标签上添加integrity属性,以此强制实施子资源完整性。默认情况下,Vite不会在清单中包含integrity哈希,但你可以通过安装vite-plugin-manifest-sri NPM插件来启用该功能:

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

然后,你可以在vite.config.js文件中启用该插件:


import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import manifestSRI from 'vite-plugin-manifest-sri';

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

如有需要,你还可以自定义存放完整性哈希的清单键名:

use Illuminate\Support\Facades\Vite;

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

如果你想完全禁用此自动检测,可以向useIntegrityKey方法传递false:

Vite::useIntegrityKey(false);

任意属性

如果你需要在脚本和样式标签上添加额外的属性,比如data-turbo-track属性,可以通过useScriptTagAttributes和useStyleTagAttributes方法进行指定。通常,这些方法应在服务提供者中调用:

use Illuminate\Support\Facades\Vite;

Vite::useScriptTagAttributes([
    'data-turbo-track' => 'reload', // 为属性指定一个值……
    'async' => true, // 指定一个无值属性……
    'integrity' => false, // 排除原本会被包含的属性……
]);

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

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

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,
]);

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

高级自定义

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

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

    {{
        Vite::useHotFile(storage_path('vite.hot')) // 自定义“hot”文件……
            ->useBuildDirectory('bundle') // 自定义构建目录……
            ->useManifestFilename('assets.json') // 自定义 manifest 文件名……
            ->withEntryPoints(['resources/js/app.js']) // 指定入口点……
            ->createAssetPathsUsing(function (string $path, ?bool $secure) { // 自定义构建资源的后端路径生成……
                return "https://cdn.example.com/{$path}";
            })
    }}
</head>

在 vite.config.js 文件中,你需要指定相同的配置:

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

export default defineConfig({
    plugins: [
        laravel({
            hotFile: 'storage/vite.hot', // 自定义“hot”文件……
            buildDirectory: 'bundle', // 自定义构建目录……
            input: ['resources/js/app.js'], // 指定入口点……
        }),
    ],
    build: {
      manifest: 'assets.json', // 自定义 manifest 文件名……
    },
});

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

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

  • ::1
  • 127.0.0.1
  • localhost
  • *.test
  • *.localhost
  • 项目 .env 中的 APP_URL

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

APP_URL=https://my-app.laravel

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


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

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

也可包含正则表达式模式,若想为给定顶级域(如 *.laravel)允许所有源,这会很有用:


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

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

修正开发服务器 URL

Vite 生态中的某些插件默认以正斜杠开头的 URL 始终指向 Vite 开发服务器。但受 Laravel 集成特性影响,实际情况并非如此。

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

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

vite-imagetools 插件预期输出的 URL 会被 Vite 拦截,随后插件可处理所有以 /@imagetools 开头的 URL。若你使用的插件有此预期,需手动修正 URL。可在 vite.config.js 文件中使用 transformOnServe 选项实现。

在该示例中,我们会为生成代码中所有 /@imagetools 的出现位置添加开发服务器 URL 作为前缀:

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:

1- <img src="/@imagetools/f0b2f404b13f052c604e632f2fb60381bf61a520">2+ <img src="http://[::1]:5173/@imagetools/f0b2f404b13f052c604e632f2fb60381bf61a520">
- <img src="/@imagetools/f0b2f404b13f052c604e632f2fb60381bf61a520">
+ <img src="http://[::1]:5173/@imagetools/f0b2f404b13f052c604e632f2fb60381bf61a520">