打包素材 (Vite)

簡介

Vite 是現代化的前端建置工具,提供快速的開發環境,並可快速為正式環境打包程式碼。在使用 Laravel 製作程式時,我們通常會用 Vite 來將專案的 CSS 與 JavaScript 檔案打包成可在正式環境使用的資源。

Laravel 與 Vite 進行了無縫整合,並提供了官方的外掛程式以及 Blade 指示詞來讓在開發環境與正式環境上載入資源。

lightbulb

已經在使用 Laravel Mix 了嗎?在新安裝的 Laravel 中,Vite 已經取代了 Laravel Mix。若要瀏覽 Mix 的說明文件,請瀏覽 Laravel Mix 網站。若要切換至 Vite,請檢視我們的 Vite 遷移指南

該選擇 Vite 還是 Laravel Mix

在改用 Vite 前,新的 Laravel 專案使用的都是 Mix。Mix 在打包資源時使用的是 webpack。Vite 則重點在為大量使用 JavaScript 的專案提供更快速更有生產力的環境。若要開發的專案有使用 SPA (單頁應用程式,Single Page Application),甚至可能還是使用了 Inertia 等工具來開發時,則最適合使用 Vite。

若在開發的專案是一些只使用 JavaScript 來「點綴」的傳統伺服器端轉譯的專案,或是使用 Livewire 時,也可以使用 Vite。不過,有一些功能是只有 Laravel Mix 支援而 Vite 不支援的,例如將一些資源直接複製到建置結果中,並且不直接在 JavaScript 程式中參照這些資源。

遷移回 Mix

剛開始建立新 Laravel 專案,但用到了 Vite Scaffolding 而需要遷移回 Laravel Mix 與 Webpack 嗎?沒問題。請參考我們的官方 Vite 至 Mix 遷移指南 (英語)

安裝與設定

注意 下列文件討論的是如何手動安裝與設定 Laravel Vite 外掛。不過,Laravel 的入門套件中已經包含了所有的 Vite Scaffolding。這些入門套件是要開始使用 Laravel 與 Vite 最快的方法。

安裝 Node

請確定有安裝 Node.js (16 版以上)與 NPM,才能開始執行 Vite 與 Laravel 的外掛:

1node -v
2npm -v
1node -v
2npm -v

可以從 Node 官方網站中取得圖形界面安裝程式來輕鬆地安裝最新版的 Node 與 NPM。或者,如果你用的是 Laravel Sail,可以像這樣在 Sail 上叫用 Node 與 NPM:

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

安裝 Vite 與 Laravel 外掛

在新安裝的 Laravel 中,可以看到專案根目錄下有個 package.json。預設的 package.json 檔案已包含了所有開始使用 Vite 與 Laravel Vite 外掛所需的東西。可以使用 NPM 來安裝專案的前端相依套件:

1npm install
1npm install

設定 Vite

Vite 使用專案根目錄的 vite.config.js 檔案來設定。可以依據需求隨意更改該檔案,也可以依照需求來安裝其他的外掛,如 @vitejs/plugin-vue@vitejs/plugin-react

要使用 Laravel 的 Vite 外掛,需要指定專案的 Entry Point(進入點)。Entry Point 可以是 JavaScript 或 CSS 檔,也可以是使用預處理語言的檔案,如 TypeScript、JSX、TSX、或 Sass。

1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3 
4export default defineConfig({
5 plugins: [
6 laravel([
7 'resources/css/app.css',
8 'resources/js/app.js',
9 ]),
10 ],
11});
1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3 
4export default defineConfig({
5 plugins: [
6 laravel([
7 'resources/css/app.css',
8 'resources/js/app.js',
9 ]),
10 ],
11});

若要製作 SPA,或是使用 Intertia 建置的程式,則在 Vite 中最好不使用 CSS Entry Point:

1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3 
4export default defineConfig({
5 plugins: [
6 laravel([
7 'resources/css/app.css',
8 'resources/js/app.js',
9 ]),
10 ],
11});
1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3 
4export default defineConfig({
5 plugins: [
6 laravel([
7 'resources/css/app.css',
8 'resources/js/app.js',
9 ]),
10 ],
11});

不要使用 CSS 進入點,而應該在 JavaScript 中 Import CSS 檔。一般來說,就是在 resources/js/app.js 檔案內 Import:

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

Laravel 的 Vite 外掛也支援多個 Entry Point,且還有一些進階的設定選項,如 SSR Entry Point

在 HTTPS 的開發伺服器使用 Vite

若你的本機開發環境使用 HTTPS,則在連線到 Vite 開發伺服器時可能會遇到一些問題。

如果你使用 Laravel Valet 來作為本機開發環境,並對專案使用了 secure 指令,那麼可以設定讓 Vite 開發伺服器自動使用 Valet 所產生的 TLS 憑證:

1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3 
4export default defineConfig({
5 plugins: [
6 laravel({
7 // ...
8 valetTls: 'my-app.test',
9 }),
10 ],
11});
1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3 
4export default defineConfig({
5 plugins: [
6 laravel({
7 // ...
8 valetTls: 'my-app.test',
9 }),
10 ],
11});

使用其他網頁伺服器時,應產生受信任的憑證,並手動設定 Vite 使用所產生的憑證:

1// ...
2import fs from 'fs';
3 
4const host = 'my-app.test';
5 
6export default defineConfig({
7 // ...
8 server: {
9 host,
10 hmr: { host },
11 https: {
12 key: fs.readFileSync(`/path/to/${host}.key`),
13 cert: fs.readFileSync(`/path/to/${host}.crt`),
14 },
15 },
16});
1// ...
2import fs from 'fs';
3 
4const host = 'my-app.test';
5 
6export default defineConfig({
7 // ...
8 server: {
9 host,
10 hmr: { host },
11 https: {
12 key: fs.readFileSync(`/path/to/${host}.key`),
13 cert: fs.readFileSync(`/path/to/${host}.crt`),
14 },
15 },
16});

若無法產生系統所信任的憑證,可以安裝並設定 @vitejs/plugin-basic-ssl 外掛。在使用未受信任的憑證時,就需要打開執行 npm run dev 指令時顯示在主控台的「Local」連結,以在瀏覽器中接受 Vite 開發伺服器的憑證警告。

載入 Script 與 CSS

設定好 Vite 的 Entry Point 後,我們接著就只需要在專案根樣板中的 <head> 內使用 @vite() Blade 指示詞來參照這些 Entry Point 即可:

1<!doctype html>
2<head>
3 {{-- ... --}}
4 
5 @vite(['resources/css/app.css', 'resources/js/app.js'])
6</head>
1<!doctype html>
2<head>
3 {{-- ... --}}
4 
5 @vite(['resources/css/app.css', 'resources/js/app.js'])
6</head>

若使用 JavaScript 來匯入 CSS,則只需要加上 JavaScript 的 Entry Point 即可:

1<!doctype html>
2<head>
3 {{-- ... --}}
4 
5 @vite('resources/js/app.js')
6</head>
1<!doctype html>
2<head>
3 {{-- ... --}}
4 
5 @vite('resources/js/app.js')
6</head>

@vite 指示詞會自動偵測到 Vite 的開發伺服器,並自動注入 Vite 的用戶端,讓我們可以使用 HMR (熱模組取代,Hot Module Replacement)。在建置模式時,該指示詞會自動載入編譯過、加上版本的素材,並包含任何已匯入的 CSS。

若有需要,也可以在呼叫 @vite 指示詞時指定編譯資源的建置路徑:

1<!doctype html>
2<head>
3 {{-- 提供相對於 public 路徑的建置路徑。 --}}
4 
5 @vite('resources/js/app.js', 'vendor/courier/build')
6</head>
1<!doctype html>
2<head>
3 {{-- 提供相對於 public 路徑的建置路徑。 --}}
4 
5 @vite('resources/js/app.js', 'vendor/courier/build')
6</head>

執行 Vite

要執行 Vite 有兩種方法。一種是使用 dev 指令來執行開發伺服器,適合用在本機環境開發時。開發伺服器會自動偵測任何檔案的修改,並自動反應到所有開啟的瀏覽器視窗中。

或者,也可以執行 build 指令。build 指令會為專案的素材加上版本,並打包這些素材,讓我們可以將其部署到正式環境中:

1# 執行 Vite 的開發伺服器...
2npm run dev
3 
4# 建置素材並加上版本以在正式環境下使用...
5npm run build
1# 執行 Vite 的開發伺服器...
2npm run dev
3 
4# 建置素材並加上版本以在正式環境下使用...
5npm run build

處理 JavaScript

別名

預設情況下,Laravel 的 Vite 外掛提供了一個常見的別名,來讓你快速開始並方便地匯入專案素材:

1{
2 '@' => '/resources/js'
3}
1{
2 '@' => '/resources/js'
3}

也可以在 vite.config.js 設定檔中加上你自己的設定來複寫這個 '@' 別名:

1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3 
4export default defineConfig({
5 plugins: [
6 laravel(['resources/ts/app.tsx']),
7 ],
8 resolve: {
9 alias: {
10 '@': '/resources/ts',
11 },
12 },
13});
1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3 
4export default defineConfig({
5 plugins: [
6 laravel(['resources/ts/app.tsx']),
7 ],
8 resolve: {
9 alias: {
10 '@': '/resources/ts',
11 },
12 },
13});

Vue

若想使用 Vue 框架來建置前端,則也需要安裝 @vitejs/plugin-vue 外掛:

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

接著,就可以在 vite.config.js 設定檔中加上該外掛。接著,要將 Vue 外掛與 Laravel 搭配使用還需要進行一些步驟:

1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3import vue from '@vitejs/plugin-vue';
4 
5export default defineConfig({
6 plugins: [
7 laravel(['resources/js/app.js']),
8 vue({
9 template: {
10 transformAssetUrls: {
11 // Vue 外掛會在使用 SFC (單檔案元件) 時複寫 (Re-write) 素材 URL
12 // 以指向 Laravel 網頁伺服器。將此設定值改為 `null`,可讓 Laravel
13 // 外掛改將複寫的素材 URL 重新指向 Vite 伺服器。
14 base: null,
15 
16 // Vue 外掛會解析絕對 URL,並將這些 URL 視為磁碟上的檔案路徑。
17 // 將此設定改為 `false`,就會使這些 URL 保持不動,以按照逾期地
18 // 參照到 public 目錄下的素材。
19 includeAbsolute: false,
20 },
21 },
22 }),
23 ],
24});
1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3import vue from '@vitejs/plugin-vue';
4 
5export default defineConfig({
6 plugins: [
7 laravel(['resources/js/app.js']),
8 vue({
9 template: {
10 transformAssetUrls: {
11 // Vue 外掛會在使用 SFC (單檔案元件) 時複寫 (Re-write) 素材 URL
12 // 以指向 Laravel 網頁伺服器。將此設定值改為 `null`,可讓 Laravel
13 // 外掛改將複寫的素材 URL 重新指向 Vite 伺服器。
14 base: null,
15 
16 // Vue 外掛會解析絕對 URL,並將這些 URL 視為磁碟上的檔案路徑。
17 // 將此設定改為 `false`,就會使這些 URL 保持不動,以按照逾期地
18 // 參照到 public 目錄下的素材。
19 includeAbsolute: false,
20 },
21 },
22 }),
23 ],
24});
lightbulb

Laravel 的入門套件中已經包含了適當的 Laravel、Vue、與 Vite 設定。請參考看看使用 Laravel Breeze,來用最快的速度開始使用 Laravel、Vue、與 Vite。

React

若想使用 React 框架來建置前端,則也需要安裝 @vitejs/plugin-react 外掛:

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

可以在 vite.config.js 設定檔中加上該外掛:

1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3import react from '@vitejs/plugin-react';
4 
5export default defineConfig({
6 plugins: [
7 laravel(['resources/js/app.jsx']),
8 react(),
9 ],
10});
1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3import react from '@vitejs/plugin-react';
4 
5export default defineConfig({
6 plugins: [
7 laravel(['resources/js/app.jsx']),
8 react(),
9 ],
10});

請確認包含 JSX 的檔案都使用 .jsx.tsx 副檔名。若有需要,請記得更新 Entry Point,像上文所提到的

還需要在現有的 @vite Blade 指示詞旁一起使用 @viteReactRefresh 指示詞。

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

@viteReactRefresh 指示詞必須在 @vite 指示詞前呼叫。

lightbulb

Laravel 的入門套件中已經包含了適當的 Laravel、React、與 Vite 設定。請參考看看使用 Laravel Breeze,來用最快的速度開始使用 Laravel、React、與 Vite。

Inertia

Laravel 的 Vite 外掛中提供了一個方便的 resolvePageComponent 函式,來協助我們解析 Inertia 的頁面元件。下面是搭配 Vue 3 使用該輔助函式的範例。除了在 Vue 3 上使用外,也可以在如 React 等其他的框架上使用該函式:

1import { createApp, h } from 'vue';
2import { createInertiaApp } from '@inertiajs/vue3';
3import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
4 
5createInertiaApp({
6 resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')),
7 setup({ el, App, props, plugin }) {
8 return createApp({ render: () => h(App, props) })
9 .use(plugin)
10 .mount(el)
11 },
12});
1import { createApp, h } from 'vue';
2import { createInertiaApp } from '@inertiajs/vue3';
3import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
4 
5createInertiaApp({
6 resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob('./Pages/**/*.vue')),
7 setup({ el, App, props, plugin }) {
8 return createApp({ render: () => h(App, props) })
9 .use(plugin)
10 .mount(el)
11 },
12});
lightbulb

Laravel 的入門套件中已經包含了適當的 Laravel、Inertia、與 Vite 設定。請參考看看使用 Laravel Breeze,來用最快的速度開始使用 Laravel、Inertia、與 Vite。

URL 的處理

使用 Vite 並在專案的 HTML、CSS、JS 等地方參照素材時,還需要考慮到幾點。首先,若要參照的資源使用絕對路徑,則 Vite 將不會在建置的結果中包含該資源;同時,還需要確定該素材是否在 public 目錄下可用。

使用相對路徑參照素材時,請記得,路徑是相對於正在參照該資源的檔案。Vite 會複寫使用相對路徑所參照的素材,並為其加上版本,然後進行打包。

來看一下下面這樣的專案結構:

1public/
2 taylor.png
3resources/
4 js/
5 Pages/
6 Welcome.vue
7 images/
8 abigail.png
1public/
2 taylor.png
3resources/
4 js/
5 Pages/
6 Welcome.vue
7 images/
8 abigail.png

下列範例展示 Vite 如何處理相對與絕對 URL:

1<!-- 該素材不會被 Vite 出列,且不會包含在建置結果中 -->
2<img src="/taylor.png">
3 
4<!-- 該素材會被 Vite 複寫,且會加上版本並打包 -->
5<img src="../../images/abigail.png">
1<!-- 該素材不會被 Vite 出列,且不會包含在建置結果中 -->
2<img src="/taylor.png">
3 
4<!-- 該素材會被 Vite 複寫,且會加上版本並打包 -->
5<img src="../../images/abigail.png">

處理 CSS

你可以在 Vite 說明文件中瞭解更多有關 Vite 對 CSS 的支援。若使用如 Tailwind 等的 PostCSS 外掛,可在專案根目錄中建立一個 postcss.config.js 檔。Vite 會自動套用該檔案中的設定:

1module.exports = {
2 plugins: {
3 tailwindcss: {},
4 autoprefixer: {},
5 },
6};
1module.exports = {
2 plugins: {
3 tailwindcss: {},
4 autoprefixer: {},
5 },
6};

處理 Blade 與 Route

使用 Vite 處理靜態素材

在 JavaScript 或 CSS 中參照素材時,Vite 會自動處理這些素材並為其加上版本。此外,在建置基於 Blade 的專案時,也可以使用 Vite 來處理只在 Blade 中被參照的靜態資源,並為這些資源加上版本。

不過,若要達成此目的,我們需要先在專案的 Entry Point(進入點) 匯入這些素材,好讓 Vite 知道有這些素材的存在。舉例來說,若想處理並為所有 resources/images 下的圖片、以及 resources/fonts 下的所有字體加上版本,就需要在專案的 resources/js/app.js Entry Point 內加上下列程式碼:

1import.meta.glob([
2 '../images/**',
3 '../fonts/**',
4]);
1import.meta.glob([
2 '../images/**',
3 '../fonts/**',
4]);

接著在執行 npm run build 時,這些素材就會自動被 Vite 處理。我們接著就可以在 Blade 樣板中使用 Vite::asset 方法來參照這些素材。該方法會回傳給定資源加上版本後的 URL:

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

保存時重新整理

若專案使用 Blade 這樣傳統式的伺服器端轉譯,則 Vite 可以在 View 檔案被修改的時候幫你自動重新整理瀏覽器來改進開發流程。若要開始讓 Vite 自動重新整理,只需要將 refresh 選項設為 true 即可。

1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3 
4export default defineConfig({
5 plugins: [
6 laravel({
7 // ...
8 refresh: true,
9 }),
10 ],
11});
1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3 
4export default defineConfig({
5 plugins: [
6 laravel({
7 // ...
8 refresh: true,
9 }),
10 ],
11});

refresh 選項設為 true 後,在執行 npm run dev 時,一旦在下列目錄中保存檔案,就會觸發瀏覽器進行整頁的重新整理:

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

若使用 Ziggy 來在網頁前端中產生 Route 連結,監看 routes/** 目錄就很實用。

若這些預設的路徑不符合你的需求,也可以自行指定一組要監看的路徑清單:

1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3 
4export default defineConfig({
5 plugins: [
6 laravel({
7 // ...
8 refresh: ['resources/views/**'],
9 }),
10 ],
11});
1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3 
4export default defineConfig({
5 plugins: [
6 laravel({
7 // ...
8 refresh: ['resources/views/**'],
9 }),
10 ],
11});

其實,Laravel 的 Vite 外掛使用了 vite-plugin-full-reload 套件,該套件還提供了一些選項,能微調這個重新整理功能的行為。若有需要自定這類微調,可以加上一個 config 定義:

1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3 
4export default defineConfig({
5 plugins: [
6 laravel({
7 // ...
8 refresh: [{
9 paths: ['path/to/watch/**'],
10 config: { delay: 300 }
11 }],
12 }),
13 ],
14});
1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3 
4export default defineConfig({
5 plugins: [
6 laravel({
7 // ...
8 refresh: [{
9 paths: ['path/to/watch/**'],
10 config: { delay: 300 }
11 }],
12 }),
13 ],
14});

別名

在 JavaScript 專案中,為常用的目錄建立別名是很常見的。不過,我們也可以使用 Illuminate\Support\Facade\Vite 類別的 macro 方法來建立能在 Blade 中使用的別名。一般來說,「Macro(巨集)」應在某個 Service Provider 內定義:

1/**
2 * Bootstrap any application services.
3 *
4 * @return void
5 */
6public function boot()
7{
8 Vite::macro('image', fn ($asset) => $this->asset("resources/images/{$asset}"));
9}
1/**
2 * Bootstrap any application services.
3 *
4 * @return void
5 */
6public function boot()
7{
8 Vite::macro('image', fn ($asset) => $this->asset("resources/images/{$asset}"));
9}

定義好 Macro 後,就可以在樣板中呼叫。舉例來說,我們可以使用 image Macro 定義來參照到位在 resources/images/logo.png 的素材:

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

自定 Base URL

若由 Vite 編譯出來的素材會被部署到與專案不同的網域上,如 CDN 等,則可在專案的 .env 檔案中指定 ASSET_URL 環境變數:

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

設定好素材 URL 後,所有被複寫的素材 URL 的前方都會被加上該設定值:

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

請記得,Vite 不會複寫絕對 URL,所以這些絕對 URL 會被保持原樣。

環境變數

在專案的 .env 檔中,只要為環境變數名稱冠上 VITE_ 前置詞,就可將這些環境變數注入到 JavaScript 中:

1VITE_SENTRY_DSN_PUBLIC=http://example.com
1VITE_SENTRY_DSN_PUBLIC=http://example.com

可以使用 import.meta.env 物件來存取注入的變數:

1import.meta.env.VITE_SENTRY_DSN_PUBLIC
1import.meta.env.VITE_SENTRY_DSN_PUBLIC

在測試時禁用 Vite

Laravel 的 Vite 整合會在執行測試時嘗試解析素材,要能解析素材就必須要執行 Vite 開發伺服器或建置素材。

若想在測試時 Mock (模擬) Vite,可呼叫 withoutVite 方法。該方法可在任何繼承了 Laravel TestCase 的類別中使用:

1use Tests\TestCase;
2 
3class ExampleTest extends TestCase
4{
5 public function test_without_vite_example()
6 {
7 $this->withoutVite();
8 
9 // ...
10 }
11}
1use Tests\TestCase;
2 
3class ExampleTest extends TestCase
4{
5 public function test_without_vite_example()
6 {
7 $this->withoutVite();
8 
9 // ...
10 }
11}

若想在所有測試中禁用 Vite,可在基礎 TestCase 類別的 setUp 方法中呼叫 withoutVite 方法:

1<?php
2 
3namespace Tests;
4 
5use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
6 
7abstract class TestCase extends BaseTestCase
8{
9 use CreatesApplication;
10 
11 protected function setUp(): void
12 {
13 parent::setUp();
14 
15 $this->withoutVite();
16 }
17}
1<?php
2 
3namespace Tests;
4 
5use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
6 
7abstract class TestCase extends BaseTestCase
8{
9 use CreatesApplication;
10 
11 protected function setUp(): void
12 {
13 parent::setUp();
14 
15 $this->withoutVite();
16 }
17}

伺服器端轉譯 (SSR)

使用 Laravel 的 Vite 外掛,就可以無痛在 Vite 上設定伺服器端轉譯 (SSR, Server Side Rendering)。若要開始設定 SSR,請建立一個 resources/js/ssr.js 來作為 SSR 的進入點,並在 Laravel 的 Vite 外掛上傳入一組設定來指定該進入點:

1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3 
4export default defineConfig({
5 plugins: [
6 laravel({
7 input: 'resources/js/app.js',
8 ssr: 'resources/js/ssr.js',
9 }),
10 ],
11});
1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3 
4export default defineConfig({
5 plugins: [
6 laravel({
7 input: 'resources/js/app.js',
8 ssr: 'resources/js/ssr.js',
9 }),
10 ],
11});

為了避免我們忘記要重新建置 SSR 進入點,建議調整專案的 package.json 內的「build」指令來建立 SSR 建置:

1"scripts": {
2 "dev": "vite",
3 "build": "vite build"
4 "build": "vite build && vite build --ssr"
5}
1"scripts": {
2 "dev": "vite",
3 "build": "vite build"
4 "build": "vite build && vite build --ssr"
5}

接著,若要開始建置並執行 SSR 伺服器,可執行下列指令:

1npm run build
2node bootstrap/ssr/ssr.mjs
1npm run build
2node bootstrap/ssr/ssr.mjs
lightbulb

Laravel 的入門套件中已經包含了適當的 Laravel、Inertia SSR、與 Vite 設定。請參考看看使用 Laravel Breeze,來用最快的速度開始使用 Laravel、Inertia SSR、與 Vite。

Script 與 Style 標籤的屬性

內容安全性原則 (CSP) 的 Nonce

若想在 script 與 style 上為內容安全性原則 (CSP, Content Security Policy) 加上 nonce 屬性,可以自定一個 Middleware 來使用 useCspNonce 方法來產生或指定一個 Nonce。

1<?php
2 
3namespace App\Http\Middleware;
4 
5use Closure;
6use Illuminate\Support\Facades\Vite;
7 
8class AddContentSecurityPolicyHeaders
9{
10 /**
11 * Handle an incoming request.
12 *
13 * @param \Illuminate\Http\Request $request
14 * @param \Closure $next
15 * @return mixed
16 */
17 public function handle($request, Closure $next)
18 {
19 Vite::useCspNonce();
20 
21 return $next($request)->withHeaders([
22 'Content-Security-Policy' => "script-src 'nonce-".Vite::cspNonce()."'",
23 ]);
24 }
25}
1<?php
2 
3namespace App\Http\Middleware;
4 
5use Closure;
6use Illuminate\Support\Facades\Vite;
7 
8class AddContentSecurityPolicyHeaders
9{
10 /**
11 * Handle an incoming request.
12 *
13 * @param \Illuminate\Http\Request $request
14 * @param \Closure $next
15 * @return mixed
16 */
17 public function handle($request, Closure $next)
18 {
19 Vite::useCspNonce();
20 
21 return $next($request)->withHeaders([
22 'Content-Security-Policy' => "script-src 'nonce-".Vite::cspNonce()."'",
23 ]);
24 }
25}

呼叫 useCspNonce 方法後,Laravel 就會自動在所有產生的 script 與 style 標籤上包含一個 nonce 屬性。

若有需要在別處指定 Nonce,如在 Laravel 入門套件中所包含的 Ziggy @route 指示詞,則可以使用 cspNonce 方法來取得該 Nonce:

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

若已有 Nonce,且想讓 Laravel 使用該 Nonce,則可傳入該 Nonce 給 useCspNonce 方法:

1Vite::useCspNonce($nonce);
1Vite::useCspNonce($nonce);

子資源完整性 (SRI)

若 Vite Manifest 中有包含資源的 `integrity`(完整性) 雜湊,則 Laravel 會自動在所有 Vite 產生的 script 與 style 標籤上加上 integrity 屬性,已強制確保子資源完整性 (SRI, Subresource Integrity)。預設情況下,Vite 不會在其 Manifest 檔中包含 integrity 雜湊。但只要安裝 vite-plugin-manifest-sri NPM 外掛,就可啟用該功能:

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

可以在 vite.config.js 檔中啟用該外掛:

1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3import manifestSRI from 'vite-plugin-manifest-sri';
4 
5export default defineConfig({
6 plugins: [
7 laravel({
8 // ...
9 }),
10 manifestSRI(),
11 ],
12});
1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3import manifestSRI from 'vite-plugin-manifest-sri';
4 
5export default defineConfig({
6 plugins: [
7 laravel({
8 // ...
9 }),
10 manifestSRI(),
11 ],
12});

若有需要,也可以指定 Manifest 中保存 Integrity 雜湊的索引鍵:

1use Illuminate\Support\Facades\Vite;
2 
3Vite::useIntegrityKey('custom-integrity-key');
1use Illuminate\Support\Facades\Vite;
2 
3Vite::useIntegrityKey('custom-integrity-key');

若有需要完全禁用自動偵測,可傳入 falseuseIntegrityKey 方法:

1Vite::useIntegrityKey(false);
1Vite::useIntegrityKey(false);

任意屬性

若有需要在 script 與 style 標籤上加入其他的額外屬性,如 data-turbo-track 等,則可使用 useScriptTagAttributesuseStyleTagAttributes 方法。一般來說,應該在某個 Service Provider 中叫用該方法:

1use Illuminate\Support\Facades\Vite;
2 
3Vite::useScriptTagAttributes([
4 'data-turbo-track' => 'reload', // 為屬性指定值...
5 'async' => true, // 指定一個沒有值的屬性...
6 'integrity' => false, // 排除掉一個原本會被包含的屬性...
7]);
8 
9Vite::useStyleTagAttributes([
10 'data-turbo-track' => 'reload',
11]);
1use Illuminate\Support\Facades\Vite;
2 
3Vite::useScriptTagAttributes([
4 'data-turbo-track' => 'reload', // 為屬性指定值...
5 'async' => true, // 指定一個沒有值的屬性...
6 'integrity' => false, // 排除掉一個原本會被包含的屬性...
7]);
8 
9Vite::useStyleTagAttributes([
10 'data-turbo-track' => 'reload',
11]);

若有需要有條件地新增屬性,則可傳入一個回呼。該回呼會收到素材的原始檔路徑、其 URL、該素材的 Manifest Chunk、以及整個 Manifest:

1use Illuminate\Support\Facades\Vite;
2 
3Vite::useScriptTagAttributes(fn (string $src, string $url, array|null $chunk, array|null $manifest) => [
4 'data-turbo-track' => $src === 'resources/js/app.js' ? 'reload' : false,
5]);
6 
7Vite::useStyleTagAttributes(fn (string $src, string $url, array|null $chunk, array|null $manifest) => [
8 'data-turbo-track' => $chunk && $chunk['isEntry'] ? 'reload' : false,
9]);
1use Illuminate\Support\Facades\Vite;
2 
3Vite::useScriptTagAttributes(fn (string $src, string $url, array|null $chunk, array|null $manifest) => [
4 'data-turbo-track' => $src === 'resources/js/app.js' ? 'reload' : false,
5]);
6 
7Vite::useStyleTagAttributes(fn (string $src, string $url, array|null $chunk, array|null $manifest) => [
8 'data-turbo-track' => $chunk && $chunk['isEntry'] ? 'reload' : false,
9]);
exclamation

在執行 Vite 開發伺服器時,$chunk$manifest 屬性會是 null

進階客製化

在新安裝好的 Laravel Vite 外掛中使用到了一些合理的慣例。這些慣例應該適用於大多數專案。不過,有時候我們還是需要自定 Vite 的姓外。若要啟用額外的客製化選項,Laravel 提供了下列方法與選項,可用於替代 @vite Blade 指示詞:

1<!doctype html>
2<head>
3 {{-- ... --}}
4 
5 {{
6 Vite::useHotFile(storage_path('vite.hot')) // 自定「Hot」檔...
7 ->useBuildDirectory('bundle') // 自定建置目錄...
8 ->useManifestFilename('assets.json') // 自定 Manifest 檔名...
9 ->withEntryPoints(['resources/js/app.js']) // 指定 Entry Point...
10 }}
11</head>
1<!doctype html>
2<head>
3 {{-- ... --}}
4 
5 {{
6 Vite::useHotFile(storage_path('vite.hot')) // 自定「Hot」檔...
7 ->useBuildDirectory('bundle') // 自定建置目錄...
8 ->useManifestFilename('assets.json') // 自定 Manifest 檔名...
9 ->withEntryPoints(['resources/js/app.js']) // 指定 Entry Point...
10 }}
11</head>

vite.config.js 中,也可以指定相同的設定:

1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3 
4export default defineConfig({
5 plugins: [
6 laravel({
7 hotFile: 'storage/vite.hot', // 自定「Hot」檔名...
8 buildDirectory: 'bundle', // 自定建置目錄...
9 input: ['resources/js/app.js'], // 自定 Entry Point...
10 }),
11 ],
12 build: {
13 manifest: 'assets.json', // 自定 Manifest 檔名...
14 },
15});
1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3 
4export default defineConfig({
5 plugins: [
6 laravel({
7 hotFile: 'storage/vite.hot', // 自定「Hot」檔名...
8 buildDirectory: 'bundle', // 自定建置目錄...
9 input: ['resources/js/app.js'], // 自定 Entry Point...
10 }),
11 ],
12 build: {
13 manifest: 'assets.json', // 自定 Manifest 檔名...
14 },
15});

修正開發伺服器的 URL

在 Vite 生態系統中,有些外掛會假設 URL 以斜線 (/) 開頭的 URL 是指向 Vite 開發伺服器的。不過,由於 Laravel 整合的特性,這個假設在 Laravel 中並不成立。

舉例來說,在使用 Vite 伺服器提供素材時,vite-imagetools 外掛會像下面這樣輸出 URL:

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

vite-imagetools 外掛預期這個輸出 URL 會被 Vite 攔截,讓這個外掛能處理所有以 /@imagetools 開頭的所有 URL。若你使用的外掛有預期這樣的行為,就需要手動修正 URL。可以在 vite.config.js 檔案中修改 transformOnServe 選項來修正 URL。

在這個範例中,我們在產生的程式碼中,為所有的 /@imagetools 網址前方加上開發伺服器的 URL:

1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3import { imagetools } from 'vite-imagetools';
4 
5export default defineConfig({
6 plugins: [
7 laravel({
8 // ...
9 transformOnServe: (code, devServerUrl) => code.replaceAll('/@imagetools', devServerUrl+'/@imagetools'),
10 }),
11 imagetools(),
12 ],
13});
1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3import { imagetools } from 'vite-imagetools';
4 
5export default defineConfig({
6 plugins: [
7 laravel({
8 // ...
9 transformOnServe: (code, devServerUrl) => code.replaceAll('/@imagetools', devServerUrl+'/@imagetools'),
10 }),
11 imagetools(),
12 ],
13});

現在,Vite 伺服器在提供素材時,就會輸出指向 Vite 開發伺服器的 URL:

1- <img src="/@imagetools/f0b2f404b13f052c604e632f2fb60381bf61a520">
2+ <img src="http://[::1]:5173/@imagetools/f0b2f404b13f052c604e632f2fb60381bf61a520">
1- <img src="/@imagetools/f0b2f404b13f052c604e632f2fb60381bf61a520">
2+ <img src="http://[::1]:5173/@imagetools/f0b2f404b13f052c604e632f2fb60381bf61a520">
翻譯進度
100% 已翻譯
更新時間:
2023年2月11日 下午1:03:00 [世界標準時間]
翻譯人員:
  • cornch
幫我們翻譯此頁

留言

尚無留言

“Laravel” is a Trademark of Taylor Otwell.
The source documentation is released under MIT license. See laravel/docs on GitHub for details.
The translated documentations are released under MIT license. See cornch/laravel-docs-l10n on GitHub for details.