
# 资源打包

-   [简介](#introduction)
-   [安装与配置](#installation)
    -   [安装 Node](#installing-node)
    -   [安装 Vite 和 Laravel 插件](#installing-vite-and-laravel-plugin)
    -   [配置 Vite](#configuring-vite)
    -   [加载你的脚本和样式](#loading-your-scripts-and-styles)
-   [运行 Vite](#running-vite)
-   [使用 JavaScript](#working-with-scripts)
    -   [别名](#aliases)
    -   [Vue](#vue)
    -   [React](#react)
    -   [Svelte](#svelte)
    -   [Inertia](#inertia)
    -   [URL 处理](#url-processing)
-   [使用样式表](#working-with-stylesheets)
-   [使用 Blade 和路由](#working-with-blade-and-routes)
    -   [使用 Vite 处理静态资源](#blade-processing-static-assets)
    -   [保存时刷新](#blade-refreshing-on-save)
    -   [别名](#blade-aliases)
-   [资源预加载](#asset-prefetching)
-   [自定义基础 URL](#custom-base-urls)
-   [环境变量](#environment-variables)
-   [在测试中禁用 Vite](#disabling-vite-in-tests)
-   [服务端渲染（SSR）](#ssr)
-   [脚本和样式标签属性](#script-and-style-attributes)
    -   [内容安全策略（CSP）Nonce](#content-security-policy-csp-nonce)
    -   [子资源完整性（SRI）](#subresource-integrity-sri)
    -   [任意属性](#arbitrary-attributes)
-   [高级自定义](#advanced-customization)
    -   [开发服务器跨域资源共享（CORS）](#cors)
    -   [修正开发服务器 URL](#correcting-dev-server-urls)

## 简介

[Vite](https://vitejs.dev) 是一个现代化的前端构建工具，它提供了极快的开发环境，并可以将你的代码打包用于生产环境。在使用 Laravel 构建应用程序时，你通常会使用 Vite 将应用程序的 CSS 和 JavaScript 文件打包成适用于生产环境的资源。

Laravel 通过提供官方插件和 Blade 指令来加载你的资源，从而与 Vite 实现无缝集成，无论是在开发环境还是生产环境中。

## 安装与配置

> [!注意]
> 以下文档介绍了如何手动安装和配置 Laravel Vite 插件。不过，Laravel 的 [入门套件](/docs/laravel/13.x/starter-kits) 已经包含了所有这些基础配置内容，是开始使用 Laravel 和 Vite 的最快方式。


### 安装 Node

在运行 Vite 和 Laravel 插件之前，你必须确保已经安装 Node.js（16+）和 NPM：

```shell
node -v
npm -v
```

你可以通过[官方 Node 网站](https://nodejs.org/en/download/)提供的简单图形化安装程序轻松安装最新版本的 Node 和 NPM。或者，如果你正在使用 [Laravel Sail](https://laravel.com/docs/laravel/13.x/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 入口点](#ssr)。

#### 使用安全开发服务器

如果你的本地开发 Web 服务器通过 HTTPS 提供应用程序服务，那么你可能会遇到连接 Vite 开发服务器的问题。

如果你正在使用 [Laravel Herd](https://herd.laravel.com)，并且已经为站点启用了安全设置，或者你正在使用 [Laravel Valet](/docs/laravel/13.x/valet) 并且已经对你的应用程序运行了[安全命令](/docs/laravel/13.x/valet#securing-sites)，那么 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 插件](https://github.com/vitejs/vite-plugin-basic-ssl)。当使用不受信任的证书时，你需要在浏览器中接受 Vite 开发服务器的证书警告。运行 `npm run dev` 命令时，可以通过控制台中的 "Local" 链接访问开发服务器，然后按照提示接受证书。

#### 在 WSL2 中使用 Sail 运行开发服务器

当你在 Windows Subsystem for Linux 2（WSL2）中的 [Laravel Sail](/docs/laravel/13.x/sail) 内运行 Vite 开发服务器时，你应该将以下配置添加到你的 `vite.config.js` 文件中，以确保浏览器可以与开发服务器通信：

```js
// ...

export default defineConfig({
    // ...
    server: { // [tl! add:start]
        hmr: {
            host: 'localhost',
        },
    }, // [tl! add:end]
});
```

如果在开发服务器运行期间，你的文件更改没有反映到浏览器中，你可能还需要配置 Vite 的 [server.watch.usePolling 选项](https://vitejs.dev/config/server-options.html#server-watch)。

### 加载你的脚本和样式

配置好 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 客户端以启用热模块替换（Hot Module Replacement）。在构建模式下，该指令会加载你编译后的并带有版本号的资源，包括任何已导入的 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` 门面提供的 `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
# 运行 Vite 开发服务器...
npm run dev

# 为生产环境构建并生成资源版本...
npm run build
```

如果你正在 WSL2 中通过 [Sail](/docs/laravel/13.x/sail) 运行开发服务器，你可能需要一些[额外的配置](#configuring-hmr-in-sail-on-wsl2)选项。



## 使用 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](https://vuejs.org/) 框架构建前端，那么你还需要安装 `@vitejs/plugin-vue` 插件：

```shell
npm install --save-dev @vitejs/plugin-vue
```

然后，你可以在 `vite.config.js` 配置文件中引入该插件。在 Laravel 中使用 Vue 插件时，还需要一些额外的配置选项：

```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: {
                    // 当在单文件组件中引用资源时，
                    // Vue 插件会重写资源 URL，使其指向 Laravel Web
                    // 服务器。将此设置为 `null` 可以让 Laravel 插件
                    // 改为重写资源 URL，使其指向 Vite
                    // 服务器。
                    base: null,

                    // Vue 插件会解析绝对 URL，并将它们视为磁盘上的
                    // 文件绝对路径。将此设置为
                    // `false` 将保持绝对 URL 不变，这样它们可以
                    // 按预期引用 public 目录中的资源。
                    includeAbsolute: false,
                },
            },
        }),
    ],
});
```

> [!注意]
> Laravel 的[入门套件](/docs/laravel/13.x/starter-kits)已经包含了正确的 Laravel、Vue 和 Vite 配置。这些入门套件是开始使用 Laravel、Vue 和 Vite 的最快方式。


### React

如果你希望使用 [React](https://reactjs.org/) 框架构建前端，那么你还需要安装 `@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` 扩展名，并记得在需要时更新你的入口文件，正如[上面所示](#configuring-vite)。

你还需要在现有的 `@vite` 指令旁边添加额外的 `@viteReactRefresh` Blade 指令。

```blade
@viteReactRefresh
@vite('resources/js/app.jsx')
```

`@viteReactRefresh` 指令必须在 `@vite` 指令之前调用。

> [!注意]
> Laravel 的[入门套件](/docs/laravel/13.x/starter-kits)已经包含了正确的 Laravel、React 和 Vite 配置。这些入门套件是开始使用 Laravel、React 和 Vite 的最快方式。

### Svelte

如果你希望使用 [Svelte](https://svelte.dev/) 框架构建前端，那么你还需要安装 `@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(),
  ],
});
```

> [!注意]
> Laravel 的[入门套件](/docs/laravel/13.x/starter-kits)已经包含了正确的 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 的代码拆分功能，我们建议配置[资源预加载](#asset-prefetching)。

> [!注意]
> Laravel 的[入门套件](/docs/laravel/13.x/starter-kits)已经包含了正确的 Laravel、Inertia 和 Vite 配置。这些入门套件是开始使用 Laravel、Inertia 和 Vite 的最快方式。

### URL 处理

在使用 Vite 并在应用程序的 HTML、CSS 或 JS 中引用资源时，有几个注意事项需要考虑。首先，如果你使用绝对路径引用资源，Vite 不会将该资源包含到构建中；因此，你应该确保该资源存在于你的 public 目录中。当使用[专用 CSS 入口点](#configuring-vite)时，你应该避免使用绝对路径，因为在开发过程中，浏览器会尝试从 Vite 开发服务器加载这些路径（CSS 会托管在那里），而不是从你的 public 目录加载。

当引用相对资源路径时，你应该记住，这些路径是相对于引用它们的文件所在位置的。所有通过相对路径引用的资源都会被 Vite 重新写入、生成版本号并进行打包。



考虑以下项目结构：

```text
public/
  taylor.png
resources/
  js/
    Pages/
      Welcome.vue
  images/
    abigail.png
```

下面的示例展示了 Vite 如何处理相对 URL 和绝对 URL：

```html
<!-- 此资源不会被 Vite 处理，也不会包含在构建结果中 -->
<img src="/taylor.png">

<!-- 此资源会被 Vite 重新写入、生成版本号，并进行打包 -->
<img src="../../images/abigail.png">
```

## 使用样式表

> [!注意]
> Laravel 的[入门套件](/docs/laravel/13.x/starter-kits)已经包含了正确的 Tailwind 和 Vite 配置。或者，如果你希望不使用我们的某个入门套件而使用 Tailwind 和 Laravel，可以查看 [Tailwind 针对 Laravel 的安装指南](https://tailwindcss.com/docs/guides/laravel)。

所有 Laravel 应用程序已经包含 Tailwind 和正确配置的 `vite.config.js` 文件。因此，你只需要启动 Vite 开发服务器，或者运行 `dev` Composer 命令，该命令会同时启动 Laravel 和 Vite 开发服务器：

```shell
composer run dev
```

你的应用程序 CSS 可以放置在 `resources/css/app.css` 文件中。

## 使用 Blade 和路由

### 使用 Vite 处理静态资源

当在 JavaScript 或 CSS 中引用资源时，Vite 会自动处理并为它们生成版本号。此外，在构建基于 Blade 的应用程序时，Vite 还可以处理并为仅在 Blade 模板中引用的静态资源生成版本号。

但是，为了实现这一点，你需要通过在插件的 `assets` 选项中指定资源，让 Vite 知道这些资源的存在。例如，如果你希望处理并为存储在 `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') }}">
```

> [!注意]
> 在 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/**`

如果你使用 [Ziggy](https://github.com/tighten/ziggy) 在应用程序前端生成路由链接，那么监听 `routes/**` 目录会非常有用。

如果这些默认路径不符合你的需求，你可以指定自己的监听路径列表：

```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](https://github.com/ElMassimo/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 应用程序中，为经常引用的目录[创建别名](#aliases)是一种常见做法。但是，你也可以通过 `Illuminate\Support\Facades\Vite` 类上的 `macro` 方法创建可在 Blade 中使用的别名。通常，"宏（macros）" 应该在[服务提供者](/docs/laravel/13.x/providers)的 `boot` 方法中定义：

```php
/**
 * 启动任何应用服务。
 */
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 资源的能力。

你可以通过在[服务提供者](/docs/laravel/13.x/providers)的 `boot` 方法中调用 `Vite::prefetch` 方法，让 Laravel 预先加载你的资源：

```php
<?php

namespace App\Providers;

use Illuminate\Support\Facades\Vite;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * 注册任何应用服务。
     */
    public function register(): void
    {
        // ...
    }

    /**
     * 启动任何应用服务。
     */
    public function boot(): void
    {
        Vite::prefetch(concurrency: 3);
    }
}
```



在上面的示例中，资源会在每次页面加载时以最大 `3` 个并发下载数进行预加载。你可以根据应用程序的需求修改并发数量，或者不指定并发限制，让应用程序一次性下载所有资源：

```php
/**
 * 启动任何应用服务。
 */
public function boot(): void
{
    Vite::prefetch();
}
```

默认情况下，预加载会在[页面 *load* 事件](https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event)触发时开始。如果你希望自定义预加载开始的时机，可以指定一个 Vite 将监听的事件：

```php
/**
 * 启动任何应用服务。
 */
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` 环境变量：

```env
ASSET_URL=https://cdn.example.com
```

配置资源 URL 后，所有被重新写入的资源 URL 都会添加配置的前缀：

```text
https://cdn.example.com/build/assets/app.9dce8d17.js
```

请记住，[绝对 URL 不会被 Vite 重新处理](#url-processing)，因此它们不会添加此前缀。

## 环境变量

你可以通过在应用程序的 `.env` 文件中为环境变量添加 `VITE_` 前缀，将环境变量注入到 JavaScript 中：

```env
VITE_SENTRY_DSN_PUBLIC=http://example.com
```



```js
import.meta.env.VITE_SENTRY_DSN_PUBLIC
```

## 在测试中禁用 Vite

Laravel 的 Vite 集成会在运行测试时尝试解析你的资源，这要求你运行 Vite 开发服务器或构建你的资源。

如果你希望在测试期间模拟 Vite，可以调用 `withoutVite` 方法，该方法适用于所有继承 Laravel `TestCase` 类的测试：

```php tab=Pest
test('without vite example', function () {
    $this->withoutVite();

    // ...
});
```

```php tab=PHPUnit
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 配置服务端渲染变得非常简单。开始使用时，创建一个 SSR 入口文件 `resources/js/ssr.js`，并通过向 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](https://inertiajs.com/server-side-rendering)，那么你可以使用 `inertia:start-ssr` Artisan 命令来启动 SSR 服务器：

```shell
php artisan inertia:start-ssr
```

> [!注意]
> Laravel 的[入门套件](/docs/laravel/13.x/starter-kits)已经包含了正确的 Laravel、Inertia SSR 和 Vite 配置。这些入门套件是开始使用 Laravel、Inertia SSR 和 Vite 的最快方式。

## 脚本和样式标签属性

### 内容安全策略（CSP）Nonce

如果你希望将 [nonce 属性](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce)作为[内容安全策略](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)的一部分添加到脚本和样式标签中，你可以在自定义的[中间件](/docs/laravel/13.x/middleware)中使用 `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
{
    /**
     * 处理传入的请求。
     *
     * @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 [入门套件](/docs/laravel/13.x/starter-kits)中包含的 [Ziggy](https://github.com/tighten/ziggy#using-routes-with-a-content-security-policy)[`@route`](https://github.com/tighten/ziggy#using-routes-with-a-content-security-policy) [指令](https://github.com/tighten/ziggy#using-routes-with-a-content-security-policy)，你可以使用 `cspNonce` 方法获取它：

```blade
@routes(nonce: Vite::cspNonce())
```



如果你已经拥有一个 nonce，并希望指示 Laravel 使用它，可以将 nonce 传递给 `useCspNonce` 方法：

```php
Vite::useCspNonce($nonce);
```

### 子资源完整性（SRI）

如果你的 Vite manifest 包含资源的 `integrity` 哈希值，Laravel 会自动在它生成的任何脚本和样式标签上添加 `integrity` 属性，以强制执行[子资源完整性](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity)。

默认情况下，Vite 不会在其 manifest 中包含 `integrity` 哈希值，但你可以通过安装 [vite-plugin-manifest-sri](https://www.npmjs.com/package/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]
    ],
});
```

如果需要，你还可以自定义用于查找完整性哈希值的 manifest 键：

```php
use Illuminate\Support\Facades\Vite;

Vite::useIntegrityKey('custom-integrity-key');
```

如果你希望完全禁用这种自动检测，可以向 `useIntegrityKey` 方法传递 `false`：

```php
Vite::useIntegrityKey(false);
```

### 任意属性

如果你需要在脚本和样式标签中包含额外属性，例如 [data-turbo-track](https://turbo.hotwired.dev/handbook/drive#reloading-when-assets-change) 属性，你可以通过 `useScriptTagAttributes` 和 `useStyleTagAttributes` 方法指定它们。通常，这些方法应该从[服务提供者](/docs/laravel/13.x/providers)中调用：

```php
use Illuminate\Support\Facades\Vite;

Vite::useScriptTagAttributes([
    'data-turbo-track' => 'reload', // 为属性指定一个值...
    'async' => true, // 指定一个没有值的属性...
    'integrity' => false, // 排除一个本应被包含的属性...
]);

Vite::useStyleTagAttributes([
    'data-turbo-track' => 'reload',
]);
```



如果你需要有条件地添加属性，可以传递一个回调函数，该回调函数会接收资源源路径、资源 URL、资源的 manifest chunk 以及完整的 manifest：

```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,
]);
```

> [!警告]
> 当 Vite 开发服务器正在运行时，`$chunk` 和 `$manifest` 参数将会是 `null`。

## 高级自定义

开箱即用的情况下，Laravel 的 Vite 插件使用了合理的约定，这些约定应该可以满足大多数应用程序的需求；但是，有时你可能需要自定义 Vite 的行为。为了启用更多自定义选项，我们提供了以下方法和选项，它们可以替代 `@vite` Blade 指令使用：

```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` 文件中，你应该指定相同的配置：

```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` 文件：

```env
APP_URL=https://my-app.laravel
```

如果你需要对来源进行更精细的控制，例如支持多个来源，则应该使用 [Vite 内置的全面且灵活的 CORS 服务器配置](https://vite.dev/config/server-options.html#server-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-imagetools` 插件在 Vite 提供资源服务时，会输出如下 URL：

```html
<img src="/@imagetools/f0b2f404b13f052c604e632f2fb60381bf61a520">
```

`vite-imagetools` 插件期望输出的 URL 会被 Vite 拦截，然后插件可以处理所有以 `/@imagetools` 开头的 URL。如果你正在使用依赖这种行为的插件，则需要手动修正这些 URL。你可以在 `vite.config.js` 文件中使用 `transformOnServe` 选项完成此操作。

在这个具体示例中，我们会将开发服务器 URL 添加到生成代码中所有出现的 `/@imagetools` 前：

```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] -->
```

