Skip to content

资源打包 (Vite)

介绍

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

Laravel 通过提供官方插件和 Blade 指令来无缝集成 Vite,以加载开发和生产环境的资源。

lightbulb

你在使用 Laravel Mix 吗?Vite 已经在新的 Laravel 安装中取代了 Laravel Mix。有关 Mix 的文档,请访问 Laravel Mix 网站。如果你想切换到 Vite,请参阅我们的 迁移指南

选择 Vite 和 Laravel Mix

在过渡到 Vite 之前,新的 Laravel 应用使用 Mix,它由 webpack 提供支持,用于打包资源。Vite 专注于在构建丰富的 JavaScript 应用时提供更快和更高效的体验。如果你正在开发单页应用 (SPA),包括使用 Inertia 等工具开发的应用,Vite 将是完美的选择。

Vite 也适用于传统的服务器端渲染应用,带有 JavaScript "点缀",包括使用 Livewire 的应用。然而,它缺乏 Laravel Mix 支持的一些功能,例如将未在 JavaScript 应用中直接引用的任意资源复制到构建中。

回迁到 Mix

你是否已经开始使用我们的 Vite 脚手架创建了一个新的 Laravel 应用,但需要回迁到 Laravel Mix 和 webpack?没问题。请查阅我们的 从 Vite 迁移到 Mix 的官方指南

安装与设置

lightbulb

以下文档讨论了如何手动安装和配置 Laravel Vite 插件。然而,Laravel 的 starter kits 已经包含了所有这些脚手架,是开始使用 Laravel 和 Vite 的最快方式。

安装 Node

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

sh
node -v
npm -v

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

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

安装 Vite 和 Laravel 插件

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

sh
npm install

配置 Vite

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

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", 
      "resources/js/app.js",
    ]),
  ],
});

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

js
import "./bootstrap";
import "../css/app.css"; 

Laravel 插件还支持多个入口点和高级配置选项,例如 SSR 入口点

使用安全的开发服务器

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

如果你使用 Laravel Valet 进行本地开发,并对应用运行了 secure 命令,你可以配置 Vite 开发服务器自动使用 Valet 生成的 TLS 证书:

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

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

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

js
// ...
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 插件。使用不受信任的证书时,你需要在浏览器中接受 Vite 开发服务器的证书警告,方法是运行 npm run dev 命令时,按照控制台中的 "Local" 链接进行操作。

加载脚本和样式

配置好 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>
    {{-- 给定的构建路径是相对于公共路径的。 --}}

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

运行 Vite

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

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

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

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

处理 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 插件:

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

          // Vue 插件将解析绝对 URL 并将其视为磁盘上的绝对路径。
          // 将其设置为 `false` 将使绝对 URL 保持不变,
          // 以便它们可以按预期引用公共目录中的资源。
          includeAbsolute: false,
        },
      },
    }),
  ],
});
lightbulb

Laravel 的 starter kits 已经包含了正确的 Laravel、Vue 和 Vite 配置。查看 Laravel Breeze 以最快的方式开始使用 Laravel、Vue 和 Vite。

React

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

sh
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 指令之前调用。

lightbulb

Laravel 的 starter kits 已经包含了正确的 Laravel、React 和 Vite 配置。查看 Laravel Breeze 以最快的方式开始使用 Laravel、React 和 Vite。

Inertia

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

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 }) {
    return createApp({ render: () => h(App, props) })
      .use(plugin)
      .mount(el);
  },
});
lightbulb

Laravel 的 starter kits 已经包含了正确的 Laravel、Inertia 和 Vite 配置。查看 Laravel Breeze 以最快的方式开始使用 Laravel、Inertia 和 Vite。

URL 处理

使用 Vite 并在应用的 HTML、CSS 或 JS 中引用资源时,有几个注意事项。首先,如果你使用绝对路径引用资源,Vite 不会将该资源包含在构建中;因此,你应该确保该资源在公共目录中可用。

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

考虑以下项目结构:

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

以下示例演示了 Vite 如何处理相对和绝对 URL:

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

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

处理样式表

你可以在 Vite 文档 中了解更多关于 Vite 的 CSS 支持。如果你使用 PostCSS 插件,例如 Tailwind,可以在项目根目录中创建一个 postcss.config.js 文件,Vite 将自动应用它:

js
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};

处理 Blade 和路由

使用 Vite 处理静态资源

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

然而,为了实现这一点,你需要通过将静态资源导入应用的入口点来使 Vite 知道你的资源。例如,如果你想处理和版本化存储在 resources/images 中的所有图像和存储在 resources/fonts 中的所有字体,你应该在应用的 resources/js/app.js 入口点中添加以下内容:

js
import.meta.glob(["../images/**", "../fonts/**"]);

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

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

保存时刷新

当应用使用传统的服务器端渲染与 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/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
/**
 * 启动任何应用服务。
 *
 * @return void
 */
public function boot()
{
    Vite::macro('image', fn ($asset) => $this->asset("resources/images/{$asset}"));
}

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

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

自定义基础 URL

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

ASSET_URL=https://cdn.example.com

配置资源 URL 后,所有重写的资源 URL 将以配置的值为前缀:

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

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

环境变量

你可以通过在应用的 .env 文件中为它们加上 VITE_ 前缀来将环境变量注入到 JavaScript 中:

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
use Tests\TestCase;

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

        // ...
    }
}

如果你想为所有测试禁用 Vite,可以在基类 TestCasesetUp 方法中调用 withoutVite 方法:

php
<?php

namespace Tests;

use Illuminate\Foundation\Testing\TestCase as BaseTestCase;

abstract class TestCase extends BaseTestCase
{
    use CreatesApplication;

    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"
     "build": "vite build && vite build --ssr"
}

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

sh
npm run build
node bootstrap/ssr/ssr.mjs
lightbulb

Laravel 的 starter kits 已经包含了正确的 Laravel、Inertia SSR 和 Vite 配置。查看 Laravel Breeze 以最快的方式开始使用 Laravel、Inertia SSR 和 Vite。

脚本和样式标签属性

内容安全策略 (CSP) Nonce

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

php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Vite;

class AddContentSecurityPolicyHeaders
{
    /**
     * 处理传入请求。
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        Vite::useCspNonce();

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

调用 useCspNonce 方法后,Laravel 将自动在所有生成的脚本和样式标签上包含 nonce 属性。

如果你需要在其他地方指定 nonce,包括 Laravel 的 starter kits 中包含的 Ziggy @route 指令,可以使用 cspNonce 方法检索它:

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

如果你已经有一个 nonce 并希望指示 Laravel 使用它,可以将 nonce 传递给 useCspNonce 方法:

php
Vite::useCspNonce($nonce);

子资源完整性 (SRI)

如果你的 Vite 清单包含资源的 integrity 哈希,Laravel 将自动在它生成的任何脚本和样式标签上添加 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"; 

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

如果需要,还可以自定义清单中找到完整性哈希的键:

php
use Illuminate\Support\Facades\Vite;

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

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

php
Vite::useIntegrityKey(false);

任意属性

如果你需要在脚本和样式标签上包含其他属性,例如 data-turbo-track 属性,可以通过 useScriptTagAttributesuseStyleTagAttributes 方法指定它们。通常,这些方法应该从 服务提供者 中调用:

php
use Illuminate\Support\Facades\Vite;

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

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

如果你需要有条件地添加属性,可以传递一个回调,该回调将接收资源源路径、其 URL、其清单块和整个清单:

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

在 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') // 自定义清单文件名...
            ->withEntryPoints(['resources/js/app.js']) // 指定入口点...
    }}
</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", // 自定义清单文件名...
  },
});

修正开发服务器 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 选项来实现。

在这个特定示例中,我们将在生成的代码中将所有 /@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"/>
<img src="http://[::1]:5173/@imagetools/f0b2f404b13f052c604e632f2fb60381bf61a520"/>