打包素材 (Vite)
簡介
Vite 是現代化的前端建置工具,提供快速的開發環境,並可快速為正式環境打包程式碼。在使用 Laravel 製作程式時,我們通常會用 Vite 來將專案的 CSS 與 JavaScript 檔案打包成可在正式環境使用的資源。
Laravel 與 Vite 進行了無縫整合,並提供了官方的外掛程式以及 Blade 指示詞來讓在開發環境與正式環境上載入資源。
已經在使用 Laravel Mix 了嗎?在新安裝的 Laravel 中,Vite 已經取代了 Laravel Mix。若要瀏覽 Mix 的說明文件,請瀏覽 Laravel Mix 網站。若要切換至 Vite,請檢視我們的 Vite 遷移指南。
Choosing Between Vite and Laravel Mix
在改用 Vite 前,新的 Laravel 專案使用的都是 Mix。Mix 在打包資源時使用的是 webpack。Vite 則重點在為大量使用 JavaScript 的專案提供更快速更有生產力的環境。若要開發的專案有使用 SPA (單頁應用程式,Single Page Application),甚至可能還是使用了 Inertia 等工具來開發時,則最適合使用 Vite。
若在開發的專案是一些只使用 JavaScript 來「點綴」的傳統伺服器端轉譯的專案,或是使用 Livewire 時,也可以使用 Vite。不過,有一些功能是只有 Laravel Mix 支援而 Vite 不支援的,例如將一些資源直接複製到建置結果中,並且不直接在 JavaScript 程式中參照這些資源。
Migrating Back to Mix
剛開始建立新 Laravel 專案,但用到了 Vite Scaffolding 而需要遷移回 Laravel Mix 與 Webpack 嗎?沒問題。請參考我們的官方 Vite 至 Mix 遷移指南 (英語)。
安裝與設定
注意 下列文件討論的是如何手動安裝與設定 Laravel Vite 外掛。不過,Laravel 的入門套件中已經包含了所有的 Vite Scaffolding。這些入門套件是要開始使用 Laravel 與 Vite 最快的方法。
安裝 Node
請確定有安裝 Node.js (16 版以上)與 NPM,才能開始執行 Vite 與 Laravel 的外掛:
1node -v2npm -v
1node -v2npm -v
可以從 Node 官方網站中取得圖形界面安裝程式來輕鬆地安裝最新版的 Node 與 NPM。或者,如果你用的是 Laravel Sail,可以像這樣在 Sail 上叫用 Node 與 NPM:
1./vendor/bin/sail node -v2./vendor/bin/sail npm -v
1./vendor/bin/sail node -v2./vendor/bin/sail npm -v
Installing Vite and the Laravel Plugin
在新安裝的 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';34export 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';34export 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';34export 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';34export 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。
Working With a Secure Development Server
若你的本機開發環境使用 HTTPS,則在連線到 Vite 開發伺服器時可能會遇到一些問題。
If you are using Laravel Herd and have secured the site or you are using Laravel Valet and have run the secure command against your application, the Laravel Vite plugin will automatically detect and use the generated TLS certificate for you.
If you secured the site using a host that does not match the application's directory name, you may manually specify the host in your application's vite.config.js
file:
1import { defineConfig } from 'vite';2import laravel from 'laravel-vite-plugin';34export default defineConfig({5 plugins: [6 laravel({7 // ...8 detectTls: 'my-app.test',9 }),10 ],11});
1import { defineConfig } from 'vite';2import laravel from 'laravel-vite-plugin';34export default defineConfig({5 plugins: [6 laravel({7 // ...8 detectTls: 'my-app.test',9 }),10 ],11});
使用其他網頁伺服器時,應產生受信任的憑證,並手動設定 Vite 使用所產生的憑證:
1// ...2import fs from 'fs';34const host = 'my-app.test';56export 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';34const host = 'my-app.test';56export 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 開發伺服器的憑證警告。
Running the Development Server in Sail on WSL2
When running the Vite development server within Laravel Sail on Windows Subsystem for Linux 2 (WSL2), you should add the following configuration to your vite.config.js
file to ensure the browser can communicate with the development server:
1// ...23export default defineConfig({4 // ...5 server: {6 hmr: {7 host: 'localhost',8 },9 },10});
1// ...23export default defineConfig({4 // ...5 server: {6 hmr: {7 host: 'localhost',8 },9 },10});
If your file changes are not being reflected in the browser while the development server is running, you may also need to configure Vite's server.watch.usePolling
option.
Loading Your Scripts and Styles
With your Vite entry points configured, you may now reference them in a @vite()
Blade directive that you add to the <head>
of your application's root template:
1<!doctype html>2<head>3 {{-- ... --}}45 @vite(['resources/css/app.css', 'resources/js/app.js'])6</head>
1<!doctype html>2<head>3 {{-- ... --}}45 @vite(['resources/css/app.css', 'resources/js/app.js'])6</head>
若使用 JavaScript 來匯入 CSS,則只需要加上 JavaScript 的 Entry Point 即可:
1<!doctype html>2<head>3 {{-- ... --}}45 @vite('resources/js/app.js')6</head>
1<!doctype html>2<head>3 {{-- ... --}}45 @vite('resources/js/app.js')6</head>
@vite
指示詞會自動偵測到 Vite 的開發伺服器,並自動注入 Vite 的用戶端,讓我們可以使用 HMR (熱模組取代,Hot Module Replacement)。在建置模式時,該指示詞會自動載入編譯過、加上版本的素材,並包含任何已匯入的 CSS。
若有需要,也可以在呼叫 @vite
指示詞時指定編譯資源的建置路徑:
1<!doctype html>2<head>3 {{-- Given build path is relative to public path. --}}45 @vite('resources/js/app.js', 'vendor/courier/build')6</head>
1<!doctype html>2<head>3 {{-- Given build path is relative to public path. --}}45 @vite('resources/js/app.js', 'vendor/courier/build')6</head>
Inline Assets
Sometimes it may be necessary to include the raw content of assets rather than linking to the versioned URL of the asset. For example, you may need to include asset content directly into your page when passing HTML content to a PDF generator. You may output the content of Vite assets using the content
method provided by the Vite
facade:
1@php2use Illuminate\Support\Facades\Vite;3@endphp45<!doctype html>6<head>7 {{-- ... --}}8910 <script>11 {!! Vite::content('resources/js/app.js') !!}12 </script>13</head>
1@php2use Illuminate\Support\Facades\Vite;3@endphp45<!doctype html>6<head>7 {{-- ... --}}8910 <script>11 {!! Vite::content('resources/js/app.js') !!}12 </script>13</head>
執行 Vite
要執行 Vite 有兩種方法。一種是使用 dev
指令來執行開發伺服器,適合用在本機環境開發時。開發伺服器會自動偵測任何檔案的修改,並自動反應到所有開啟的瀏覽器視窗中。
或者,也可以執行 build
指令。build
指令會為專案的素材加上版本,並打包這些素材,讓我們可以將其部署到正式環境中:
1# Run the Vite development server...2npm run dev34# Build and version the assets for production...5npm run build
1# Run the Vite development server...2npm run dev34# Build and version the assets for production...5npm run build
If you are running the development server in Sail on WSL2, you may need some additional configuration options.
處理 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';34export 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';34export default defineConfig({5 plugins: [6 laravel(['resources/ts/app.tsx']),7 ],8 resolve: {9 alias: {10 '@': '/resources/ts',11 },12 },13});
Vue
If you would like to build your frontend using the Vue framework, then you will also need to install the @vitejs/plugin-vue
plugin:
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';45export default defineConfig({6 plugins: [7 laravel(['resources/js/app.js']),8 vue({9 template: {10 transformAssetUrls: {11 // The Vue plugin will re-write asset URLs, when referenced12 // in Single File Components, to point to the Laravel web13 // server. Setting this to `null` allows the Laravel plugin14 // to instead re-write asset URLs to point to the Vite15 // server instead.16 base: null,1718 // The Vue plugin will parse absolute URLs and treat them19 // as absolute paths to files on disk. Setting this to20 // `false` will leave absolute URLs un-touched so they can21 // reference assets in the public directory as expected.22 includeAbsolute: false,23 },24 },25 }),26 ],27});
1import { defineConfig } from 'vite';2import laravel from 'laravel-vite-plugin';3import vue from '@vitejs/plugin-vue';45export default defineConfig({6 plugins: [7 laravel(['resources/js/app.js']),8 vue({9 template: {10 transformAssetUrls: {11 // The Vue plugin will re-write asset URLs, when referenced12 // in Single File Components, to point to the Laravel web13 // server. Setting this to `null` allows the Laravel plugin14 // to instead re-write asset URLs to point to the Vite15 // server instead.16 base: null,1718 // The Vue plugin will parse absolute URLs and treat them19 // as absolute paths to files on disk. Setting this to20 // `false` will leave absolute URLs un-touched so they can21 // reference assets in the public directory as expected.22 includeAbsolute: false,23 },24 },25 }),26 ],27});
Laravel 的入門套件中已經包含了適當的 Laravel、Vue、與 Vite 設定。請參考看看使用 Laravel Breeze,來用最快的速度開始使用 Laravel、Vue、與 Vite。
React
If you would like to build your frontend using the React framework, then you will also need to install the @vitejs/plugin-react
plugin:
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';45export 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';45export default defineConfig({6 plugins: [7 laravel(['resources/js/app.jsx']),8 react(),9 ],10});
請確認包含 JSX 的檔案都使用 .jsx
或 .tsx
副檔名。若有需要,請記得更新 Entry Point,像上文所提到的。
還需要在現有的 @vite
Blade 指示詞旁一起使用 @viteReactRefresh
指示詞。
1@viteReactRefresh2@vite('resources/js/app.jsx')
1@viteReactRefresh2@vite('resources/js/app.jsx')
@viteReactRefresh
指示詞必須在 @vite
指示詞前呼叫。
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';45createInertiaApp({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';45createInertiaApp({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});
Laravel 的入門套件中已經包含了適當的 Laravel、Inertia、與 Vite 設定。請參考看看使用 Laravel Breeze,來用最快的速度開始使用 Laravel、Inertia、與 Vite。
URL 的處理
使用 Vite 並在專案的 HTML、CSS、JS 等地方參照素材時,還需要考慮到幾點。首先,若要參照的資源使用絕對路徑,則 Vite 將不會在建置的結果中包含該資源;同時,還需要確定該素材是否在 public 目錄下可用。
使用相對路徑參照素材時,請記得,路徑是相對於正在參照該資源的檔案。Vite 會複寫使用相對路徑所參照的素材,並為其加上版本,然後進行打包。
來看一下下面這樣的專案結構:
1public/2 taylor.png3resources/4 js/5 Pages/6 Welcome.vue7 images/8 abigail.png
1public/2 taylor.png3resources/4 js/5 Pages/6 Welcome.vue7 images/8 abigail.png
下列範例展示 Vite 如何處理相對與絕對 URL:
1<!-- This asset is not handled by Vite and will not be included in the build -->2<img src="/taylor.png">34<!-- This asset will be re-written, versioned, and bundled by Vite -->5<img src="../../images/abigail.png">
1<!-- This asset is not handled by Vite and will not be included in the build -->2<img src="/taylor.png">34<!-- This asset will be re-written, versioned, and bundled by Vite -->5<img src="../../images/abigail.png">
處理 CSS
你可以在 Vite 說明文件中瞭解更多有關 Vite 對 CSS 的支援。若使用如 Tailwind 等的 PostCSS 外掛,可在專案根目錄中建立一個 postcss.config.js
檔。Vite 會自動套用該檔案中的設定:
1export default {2 plugins: {3 tailwindcss: {},4 autoprefixer: {},5 },6};
1export default {2 plugins: {3 tailwindcss: {},4 autoprefixer: {},5 },6};
Laravel's starter kits already include the proper Tailwind, PostCSS, and Vite configuration. Or, if you would like to use Tailwind and Laravel without using one of our starter kits, check out Tailwind's installation guide for Laravel.
Working With Blade and Routes
使用 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') }}">
Refreshing on Save
若專案使用 Blade 這樣傳統式的伺服器端轉譯,則 Vite 可以在 View 檔案被修改的時候幫你自動重新整理瀏覽器來改進開發流程。若要開始讓 Vite 自動重新整理,只需要將 refresh
選項設為 true
即可。
1import { defineConfig } from 'vite';2import laravel from 'laravel-vite-plugin';34export default defineConfig({5 plugins: [6 laravel({7 // ...8 refresh: true,9 }),10 ],11});
1import { defineConfig } from 'vite';2import laravel from 'laravel-vite-plugin';34export 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';34export default defineConfig({5 plugins: [6 laravel({7 // ...8 refresh: ['resources/views/**'],9 }),10 ],11});
1import { defineConfig } from 'vite';2import laravel from 'laravel-vite-plugin';34export 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';34export 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';34export 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 */4public function boot(): void5{6 Vite::macro('image', fn (string $asset) => $this->asset("resources/images/{$asset}"));7}
1/**2 * Bootstrap any application services.3 */4public function boot(): void5{6 Vite::macro('image', fn (string $asset) => $this->asset("resources/images/{$asset}"));7}
定義好 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
Disabling Vite in Tests
Laravel 的 Vite 整合會在執行測試時嘗試解析素材,要能解析素材就必須要執行 Vite 開發伺服器或建置素材。
If you would prefer to mock Vite during testing, you may call the withoutVite
method, which is available for any tests that extend Laravel's TestCase
class:
1test('without vite example', function () {2 $this->withoutVite();34 // ...5});
1test('without vite example', function () {2 $this->withoutVite();34 // ...5});
1use Tests\TestCase;23class ExampleTest extends TestCase4{5 public function test_without_vite_example(): void6 {7 $this->withoutVite();89 // ...10 }11}
1use Tests\TestCase;23class ExampleTest extends TestCase4{5 public function test_without_vite_example(): void6 {7 $this->withoutVite();89 // ...10 }11}
若想在所有測試中禁用 Vite,可在基礎 TestCase
類別的 setUp
方法中呼叫 withoutVite
方法:
1<?php23namespace Tests;45use Illuminate\Foundation\Testing\TestCase as BaseTestCase;67abstract class TestCase extends BaseTestCase8{9 protected function setUp(): void10 {11 parent::setUp();1213 $this->withoutVite();14 }15}
1<?php23namespace Tests;45use Illuminate\Foundation\Testing\TestCase as BaseTestCase;67abstract class TestCase extends BaseTestCase8{9 protected function setUp(): void10 {11 parent::setUp();1213 $this->withoutVite();14 }15}
伺服器端轉譯 (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';34export 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';34export 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 build2node bootstrap/ssr/ssr.js
1npm run build2node bootstrap/ssr/ssr.js
If you are using SSR with Inertia, you may instead use the inertia:start-ssr
Artisan command to start the SSR server:
1php artisan inertia:start-ssr
1php artisan inertia:start-ssr
Laravel 的入門套件中已經包含了適當的 Laravel、Inertia SSR、與 Vite 設定。請參考看看使用 Laravel Breeze,來用最快的速度開始使用 Laravel、Inertia SSR、與 Vite。
Script and Style Tag Attributes
內容安全性原則 (CSP) 的 Nonce
若想在 script 與 style 上為內容安全性原則 (CSP, Content Security Policy) 加上 nonce
屬性,可以自定一個 Middleware 來使用 useCspNonce
方法來產生或指定一個 Nonce。
1<?php23namespace App\Http\Middleware;45use Closure;6use Illuminate\Http\Request;7use Illuminate\Support\Facades\Vite;8use Symfony\Component\HttpFoundation\Response;910class AddContentSecurityPolicyHeaders11{12 /**13 * Handle an incoming request.14 *15 * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next16 */17 public function handle(Request $request, Closure $next): Response18 {19 Vite::useCspNonce();2021 return $next($request)->withHeaders([22 'Content-Security-Policy' => "script-src 'nonce-".Vite::cspNonce()."'",23 ]);24 }25}
1<?php23namespace App\Http\Middleware;45use Closure;6use Illuminate\Http\Request;7use Illuminate\Support\Facades\Vite;8use Symfony\Component\HttpFoundation\Response;910class AddContentSecurityPolicyHeaders11{12 /**13 * Handle an incoming request.14 *15 * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next16 */17 public function handle(Request $request, Closure $next): Response18 {19 Vite::useCspNonce();2021 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';45export 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';45export default defineConfig({6 plugins: [7 laravel({8 // ...9 }),10 manifestSRI(),11 ],12});
若有需要,也可以指定 Manifest 中保存 Integrity 雜湊的索引鍵:
1use Illuminate\Support\Facades\Vite;23Vite::useIntegrityKey('custom-integrity-key');
1use Illuminate\Support\Facades\Vite;23Vite::useIntegrityKey('custom-integrity-key');
若有需要完全禁用自動偵測,可傳入 false
給 useIntegrityKey
方法:
1Vite::useIntegrityKey(false);
1Vite::useIntegrityKey(false);
任意屬性
若有需要在 script 與 style 標籤上加入其他的額外屬性,如 data-turbo-track
等,則可使用 useScriptTagAttributes
與 useStyleTagAttributes
方法。一般來說,應該在某個 Service Provider 中叫用該方法:
1use Illuminate\Support\Facades\Vite;23Vite::useScriptTagAttributes([4 'data-turbo-track' => 'reload', // Specify a value for the attribute...5 'async' => true, // Specify an attribute without a value...6 'integrity' => false, // Exclude an attribute that would otherwise be included...7]);89Vite::useStyleTagAttributes([10 'data-turbo-track' => 'reload',11]);
1use Illuminate\Support\Facades\Vite;23Vite::useScriptTagAttributes([4 'data-turbo-track' => 'reload', // Specify a value for the attribute...5 'async' => true, // Specify an attribute without a value...6 'integrity' => false, // Exclude an attribute that would otherwise be included...7]);89Vite::useStyleTagAttributes([10 'data-turbo-track' => 'reload',11]);
若有需要有條件地新增屬性,則可傳入一個回呼。該回呼會收到素材的原始檔路徑、其 URL、該素材的 Manifest Chunk、以及整個 Manifest:
1use Illuminate\Support\Facades\Vite;23Vite::useScriptTagAttributes(fn (string $src, string $url, array|null $chunk, array|null $manifest) => [4 'data-turbo-track' => $src === 'resources/js/app.js' ? 'reload' : false,5]);67Vite::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;23Vite::useScriptTagAttributes(fn (string $src, string $url, array|null $chunk, array|null $manifest) => [4 'data-turbo-track' => $src === 'resources/js/app.js' ? 'reload' : false,5]);67Vite::useStyleTagAttributes(fn (string $src, string $url, array|null $chunk, array|null $manifest) => [8 'data-turbo-track' => $chunk && $chunk['isEntry'] ? 'reload' : false,9]);
在執行 Vite 開發伺服器時,$chunk
與 $manifest
屬性會是 null
。
進階客製化
在新安裝好的 Laravel Vite 外掛中使用到了一些合理的慣例。這些慣例應該適用於大多數專案。不過,有時候我們還是需要自定 Vite 的姓外。若要啟用額外的客製化選項,Laravel 提供了下列方法與選項,可用於替代 @vite
Blade 指示詞:
1<!doctype html>2<head>3 {{-- ... --}}45 {{6 Vite::useHotFile(storage_path('vite.hot')) // Customize the "hot" file...7 ->useBuildDirectory('bundle') // Customize the build directory...8 ->useManifestFilename('assets.json') // Customize the manifest filename...9 ->withEntryPoints(['resources/js/app.js']) // Specify the entry points...10 ->createAssetPathsUsing(function (string $path, ?bool $secure) { // Customize the backend path generation for built assets...11 return "https://cdn.example.com/{$path}";12 })13 }}14</head>
1<!doctype html>2<head>3 {{-- ... --}}45 {{6 Vite::useHotFile(storage_path('vite.hot')) // Customize the "hot" file...7 ->useBuildDirectory('bundle') // Customize the build directory...8 ->useManifestFilename('assets.json') // Customize the manifest filename...9 ->withEntryPoints(['resources/js/app.js']) // Specify the entry points...10 ->createAssetPathsUsing(function (string $path, ?bool $secure) { // Customize the backend path generation for built assets...11 return "https://cdn.example.com/{$path}";12 })13 }}14</head>
在 vite.config.js
中,也可以指定相同的設定:
1import { defineConfig } from 'vite';2import laravel from 'laravel-vite-plugin';34export default defineConfig({5 plugins: [6 laravel({7 hotFile: 'storage/vite.hot', // Customize the "hot" file...8 buildDirectory: 'bundle', // Customize the build directory...9 input: ['resources/js/app.js'], // Specify the entry points...10 }),11 ],12 build: {13 manifest: 'assets.json', // Customize the manifest filename...14 },15});
1import { defineConfig } from 'vite';2import laravel from 'laravel-vite-plugin';34export default defineConfig({5 plugins: [6 laravel({7 hotFile: 'storage/vite.hot', // Customize the "hot" file...8 buildDirectory: 'bundle', // Customize the build directory...9 input: ['resources/js/app.js'], // Specify the entry points...10 }),11 ],12 build: {13 manifest: 'assets.json', // Customize the manifest filename...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。
In this particular example, we will prepend the dev server URL to all occurrences of /@imagetools
within the generated code:
1import { defineConfig } from 'vite';2import laravel from 'laravel-vite-plugin';3import { imagetools } from 'vite-imagetools';45export 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';45export 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">