編譯資源 (Mix)
簡介
Laravel Mix 是一個由 Laracasts 作者 Jeffrey Way 開發的套件,該套件使用了各種常見的 CSS 與 JavaScript 預處理器,可在 Laravel 專案上使用 Fluent API 來定義 Webpack 建置步驟。
換句話說,使用 Mix 就可以輕鬆地編譯並 Minify 專案的 CSS 與 JavaScript 檔案。只要簡單地串接幾個方法,就可以流暢的定義素材管道。例如:
1mix.js('resources/js/app.js', 'public/js')2 .postCss('resources/css/app.css', 'public/css');
1mix.js('resources/js/app.js', 'public/js')2 .postCss('resources/css/app.css', 'public/css');
若你搞不懂怎麼用 Webpack 與編輯素材、或是覺得很複雜的話,你一定會喜歡 Laravel Mix 的。不過,不一定要使用 Laravel Mix 也能開發你的網站。你可以自由決定要使用哪個素材管道工具,甚至也可以不使用任何工具。
若想使用 Laravel 與 Tailwind CSS 來製作網站且想要有個起始點的話,請參考看看我們的專案入門套件。
安裝與設定
安裝 Node
在執行 Mix 前,請先確保你的電腦上已安裝 Node.js 與 NPM:
1node -v2npm -v
1node -v2npm -v
可以從 [Node 官方網站]中取得圖形界面安裝程式來輕鬆地安裝最新版的 Node 與 NPM。或者,如果你用的是 Laravel Sail,可以像這樣在 Sail 上叫用 Node 與 NPM:
1./sail node -v2./sail npm -v
1./sail node -v2./sail npm -v
安裝 Laravel Mix
剩下的步驟就是安裝 Laravel Mix 了。在新安裝的 Laravel 專案中,可以在專案根目錄上看到一個 package.json
檔案。使用預設的 package.json
檔就可以直接開始使用 Laravel Mix 了。可以把這個檔案像成跟 composer.json
檔一樣,只不過 package.json
定義的是不是 PHP 的相依性套件而是 Node 的相依性套件。可以像這樣安裝 package.json
中參照的相依性套件:
1npm install
1npm install
執行 Mix
Mix 是基於 Webpack 的一個設定層。因此要執行 Mix 任務,只需要執行 Laravel 中預設 package.json
檔案內提供的其中一個 NPM 指令碼即可。執行 dev
或 production
指令碼後,專案中的所有 CSS 與 JavaScript 素材都會被編譯並放在專案的 public
目錄內:
1// 執行所有 Mix 任務...2npm run dev34// 執行所有 Mix 任務,並 Minify 輸出...5npm run prod
1// 執行所有 Mix 任務...2npm run dev34// 執行所有 Mix 任務,並 Minify 輸出...5npm run prod
監控素材的更改
npm run watch
指令會在終端機內持續執行,並監控所有相關的 CSS 與 JavaScript 檔案有沒有被修改。若偵測到有檔案被更改過,Webpack 會自動重新編譯素材:
1npm run watch
1npm run watch
對於一些特定的本機開發環境,Webpack 肯跟沒辦法偵測到檔案修改。若在你的系統上有這個狀況,請考慮使用 watch-poll
指令:
1npm run watch-poll
1npm run watch-poll
處理樣式表
你的專案中的 webpack.mix.js
就是用來編譯所有素材的入口。可以把這個檔案想成是對 [Webpack] 設定的包裝。可以將多個 Mix 任務串連在一起,以定義要怎麼編譯素材。
Tailwind CSS
Tailwind CSS 是一套現代的、Utility-First的框架,可在只接觸 HTML 的情況下製作出讓人驚艷的網站。我們來看看要怎麼在 Laravel 專案中搭配 Laravel Mix 使用 Tailwind CSS。首先,我們先使用 NPM 安裝 Tailwind,並產生 Tailwind 的設定檔:
1npm install23npm install -D tailwindcss45npx tailwindcss init
1npm install23npm install -D tailwindcss45npx tailwindcss init
init
指令會產生一個 tailwind.config.js
檔案。在該檔案的 content
中,可用來定義專案中所有的 HTML 樣板、JavaScript 元件、以及其他包含 Tailwind Class 名稱的原始檔。在 content
中定義這些檔案的路徑後,Tailwind 才能在正式 CSS 組建中移除這些檔案未使用的 CSS Class:
1content: [2 './storage/framework/views/*.php',3 './resources/**/*.blade.php',4 './resources/**/*.js',5 './resources/**/*.vue',6],
1content: [2 './storage/framework/views/*.php',3 './resources/**/*.blade.php',4 './resources/**/*.js',5 './resources/**/*.vue',6],
接著,我們在專案的 resources/css/app.css
檔案中加上 Tailwind 的各個「Layer」:
1@tailwind base;2@tailwind components;3@tailwind utilities;
1@tailwind base;2@tailwind components;3@tailwind utilities;
設定好 Tailwind 的 Layer 後,就可以來修改專案的 webpack.mix.js
檔以編譯由 Tailwind 驅動的 CSS:
1mix.js('resources/js/app.js', 'public/js')2 .postCss('resources/css/app.css', 'public/css', [3 require('tailwindcss'),4 ]);
1mix.js('resources/js/app.js', 'public/js')2 .postCss('resources/css/app.css', 'public/css', [3 require('tailwindcss'),4 ]);
最後,應在主要的 Layout 樣板中參考這個樣式表。大多數的專案都把主 Layout 保存在 resources/views/layouts/app.blade.php
中。此外,也請確保該樣板中有沒有加上回應式 Viewport 的 meta
標籤:
1<head>2 <meta charset="UTF-8" />3 <meta name="viewport" content="width=device-width, initial-scale=1.0" />4 <link href="/css/app.css" rel="stylesheet">5</head>
1<head>2 <meta charset="UTF-8" />3 <meta name="viewport" content="width=device-width, initial-scale=1.0" />4 <link href="/css/app.css" rel="stylesheet">5</head>
PostCSS
PostCSS 是一個可用來變換 CSS 的強力工具。Laravel Mix 中已內建支援。預設情況下,Mix 使用了很流行的 Autoprefixer 外掛來自動套用任何需要的 CSS3 Vendor Prefix。不過,也可以自行增加專案所需要的其他外掛。
首先,請通過 NPM 安裝所需的外掛,然後把這些外掛放在呼叫 Mix postCSS
方法時提供的陣列裡面。postCss
方法接受的第一個引數是 CSS 檔的路徑,而第二個引數則是編譯好的檔案要放置的目錄:
1mix.postCss('resources/css/app.css', 'public/css', [2 require('postcss-custom-properties')3]);
1mix.postCss('resources/css/app.css', 'public/css', [2 require('postcss-custom-properties')3]);
或者,執行 postCss
時也可以不提供任何外掛,來做簡單的 CSS 編譯與 Minify:
1mix.postCss('resources/css/app.css', 'public/css');
1mix.postCss('resources/css/app.css', 'public/css');
Sass
sass
方法可用來把 Sass 編譯瀏覽器看得懂的 CSS。sass
方法接受 Sass 檔的路徑作為其第一個引數,以及編譯好的檔案要放置的目錄作為其第二個引數:
1mix.sass('resources/sass/app.scss', 'public/css');
1mix.sass('resources/sass/app.scss', 'public/css');
可以呼叫多次 sass
方法來將多個 Sass 檔編譯到各個對應的 CSS 檔上,並設定產生 CSS 的輸出目錄:
1mix.sass('resources/sass/app.sass', 'public/css')2 .sass('resources/sass/admin.sass', 'public/css/admin');
1mix.sass('resources/sass/app.sass', 'public/css')2 .sass('resources/sass/admin.sass', 'public/css/admin');
URL 的處理
由於 Laravel Mix 是基於 Webpack 製作的,因此我們最好也要瞭解一些 Webpack 的概念。在做 CSS 編譯時,Webpack 會複寫並最佳化樣式表中的 url()
呼叫。雖然一聽到這個狀況,感覺可能有點奇怪,但其實這時很實用的功能。我們先假設我們像編譯一個 Sass 檔,裡面包含了相對網址的圖片:
1.example {2 background: url('../images/example.png');3}
1.example {2 background: url('../images/example.png');3}
傳入任何相對路徑給 url()
時,就不會套用 URL 複寫。舉例來說,url('/images/thing.png')
或 url('http://example.com/images/thing.png')
這兩種寫法將不會被修改。
預設情況下,Laravel Mix 與 Webpack 會找到 example.png
這個檔案,並將其複製到 public/images
資料夾中,然後在產生的樣式表中複寫掉 url()
。因此,編譯過的 CSS 會是這樣:
1.example {2 background: url(/images/example.png?d41d8cd98f00b204e9800998ecf8427e);3}
1.example {2 background: url(/images/example.png?d41d8cd98f00b204e9800998ecf8427e);3}
雖然這個功能可能很實用,但你可能已經依照你的需求設定好資料夾結構了。這時,我們可以像這樣禁用 url()
複寫:
1mix.sass('resources/sass/app.scss', 'public/css').options({2 processCssUrls: false3});
1mix.sass('resources/sass/app.scss', 'public/css').options({2 processCssUrls: false3});
在 webpack.mix.js
中這樣加上之後,Mix 就不會再尋找 url()
並幫你把素材複製到 public 目錄下了。換句話說,編譯好的 CSS 會跟你原本寫的一樣:
1.example {2 background: url("../images/thing.png");3}
1.example {2 background: url("../images/thing.png");3}
Source Map
雖然預設情況下沒有啟用 Source Map,但可以在 webpack.mix.js
檔案中呼叫 mix.sourceMaps()
方法來啟用。雖然這樣一來,編譯 / 效能的成本也會增加,但有了 Source Map,就算使用經過編譯的素材,也能為瀏覽器開發人員工具提供額外的偵錯資訊:
1mix.js('resources/js/app.js', 'public/js')2 .sourceMaps();
1mix.js('resources/js/app.js', 'public/js')2 .sourceMaps();
Source Map 的格式
Webpack 提供了多種 Source Map 格式。預設情況下,Mix 的 Source Map 格式設為 eval-source-map
,使用這種格式時建置時間會比較快。若想更改 Source Map 的格式,可使用 sourceMaps
方法:
1let productionSourceMaps = false;23mix.js('resources/js/app.js', 'public/js')4 .sourceMaps(productionSourceMaps, 'source-map');
1let productionSourceMaps = false;23mix.js('resources/js/app.js', 'public/js')4 .sourceMaps(productionSourceMaps, 'source-map');
處理 JavaScript
Mix 提供了多種可協助你處理 JavaScript 檔案的功能,如編譯現代的 ECMAScript、打包模組、Minify、合併多個純 JavaScript 檔案⋯⋯等。更好的是,這些功能全部都可流暢地互相配合使用,完全不需額外設定:
1mix.js('resources/js/app.js', 'public/js');
1mix.js('resources/js/app.js', 'public/js');
只要這一行程式碼,就可以使用:
- 最新的 ECMAScript 語法
- 模組 (Module)
- 為正式環境 Minify 原始碼
Vue
使用 vue
方法時,Mix 會自動安裝支援 Vue 單檔案元件所需的 Babel 外掛。不需要其他進一步的設定:
1mix.js('resources/js/app.js', 'public/js')2 .vue();
1mix.js('resources/js/app.js', 'public/js')2 .vue();
編譯好 JavaScript 後,就可以在專案中參照這個檔案:
1<head>2 <!-- ... -->34 <script src="/js/app.js"></script>5</head>
1<head>2 <!-- ... -->34 <script src="/js/app.js"></script>5</head>
React
Mix 會自動安裝支援 React 所需的 Babel 外掛。要開始使用 React,請呼叫 react
方法:
1mix.js('resources/js/app.jsx', 'public/js')2 .react();
1mix.js('resources/js/app.jsx', 'public/js')2 .react();
Mix 會在幕後下載並包含適當的 babel-preset-react
Babel 外掛。編譯好 JavaScript 後,就可以像這樣在專案內參照該檔案:
1<head>2 <!-- ... -->34 <script src="/js/app.js"></script>5</head>
1<head>2 <!-- ... -->34 <script src="/js/app.js"></script>5</head>
拆分出第三方函式庫
將所有用在專案內的 JavaScript 跟一些第三方函式庫 (如 React 或 Vue) 一起打包可能會有個缺點,就是我們很難做長期的快取。舉例來說,只要更新專案內的一部分程式碼,就算沒更改第三方函式庫,瀏覽器還是必須重新下載整個第三方函式庫。
若常常更改專案的 JavaScript,應考慮將第三方函式庫拆分成獨立的檔案。這樣一來,更改專案的程式碼就不會影響 vendor.js
這個大檔案的快取。通過 Mix 的 extract
方法就可輕鬆實現:
1mix.js('resources/js/app.js', 'public/js')2 .extract(['vue'])
1mix.js('resources/js/app.js', 'public/js')2 .extract(['vue'])
extract
方法接收一組包含要拆分為獨立 vendor.js
檔案的函式庫或模組陣列。使用上述範例中的這個程式碼片段,Mix 會產生下列檔案:
-
public/js/manifest.js
: Webpack Manifest Runtime -
public/js/vendor.js
: 第三方函式庫 -
public/js/app.js
: 專案程式碼
為了避免產生 JavaScript 錯誤,請確保使用正確的順序載入這些檔案:
1<script src="/js/manifest.js"></script>2<script src="/js/vendor.js"></script>3<script src="/js/app.js"></script>
1<script src="/js/manifest.js"></script>2<script src="/js/vendor.js"></script>3<script src="/js/app.js"></script>
自訂 Webpack 設定
有時候,我們可能會需要手動修改底層的 Webpack 設定。舉例來說,我們可能會想參照一個特別的 Loader或外掛。
Mix 提供了一個實用的 webpackConfig
方法,能讓我們合併部分的 Webpack 設定。這樣做的好處是我們就不需要複製並維護一個完整的 webpack.config.js
。webpackConfig
方法接受一個物件,該物件中應包含我們要套用的 Webpack 設定。
1mix.webpackConfig({2 resolve: {3 modules: [4 path.resolve(__dirname, 'vendor/laravel/spark/resources/assets/js')5 ]6 }7});
1mix.webpackConfig({2 resolve: {3 modules: [4 path.resolve(__dirname, 'vendor/laravel/spark/resources/assets/js')5 ]6 }7});
版本化 / 避免快取
許多的開發人員都會在編譯過的素材檔名後方加上時戳或一些不重複的字串來讓瀏覽器不要載入舊版的程式碼,強制載入新的素材。使用 version
方法,就可以讓 Mix 自動處理這個部分。
version
方法會在所有編譯的檔案名稱後方加上一段不重複的雜湊,讓我們能更方便地避免檔案被快取:
1mix.js('resources/js/app.js', 'public/js')2 .version();
1mix.js('resources/js/app.js', 'public/js')2 .version();
產生好版本化的檔案後,因為我們不知道實際的檔案名稱,所以可使用 Laravel 的 mix
全域函式來在 View 載入有加上適當雜湊的素材。mix
韓式會自動判斷有雜湊的檔案目前的名稱:
1<script src="{{ mix('/js/app.js') }}"></script>
1<script src="{{ mix('/js/app.js') }}"></script>
由於在開發的時候通常不需要使用版本化的檔案,因此我們可以設定讓 Mix 只在 npm run prod
時才做版本化:
1mix.js('resources/js/app.js', 'public/js');23if (mix.inProduction()) {4 mix.version();5}
1mix.js('resources/js/app.js', 'public/js');23if (mix.inProduction()) {4 mix.version();5}
自訂 Mix 的基礎 URL
若要將 Mix 編譯的素材部署到與你的網站不同的 CDN 上,則需要修改 mix
函式產生的基礎 URL。只需要在專案的 config/app.php
設定檔中加上 mix_url
設定即可:
1'mix_url' => env('MIX_ASSET_URL', null)
1'mix_url' => env('MIX_ASSET_URL', null)
設定好 Mix URL 後,mix
函式在產生素材網址的時候,就會在前方加上剛才設定的 URL:
1https://cdn.example.com/js/app.js?id=1964becbdd96414518cd
1https://cdn.example.com/js/app.js?id=1964becbdd96414518cd
Browsersync 重新整理
BrowserSync 可以自動偵測檔案更改,並在不需手動重新整理的情況下將這些修改插入到瀏覽器內。可以呼叫 mix.browserSync()
方法來啟用 BrowserSync 支援:
1mix.browserSync('laravel.test');
1mix.browserSync('laravel.test');
可以傳入 JavaScript 物件給 browserSync
方法來指定 BrowserSync選項:
1mix.browserSync({2 proxy: 'laravel.test'3});
1mix.browserSync({2 proxy: 'laravel.test'3});
接著,使用 npm run watch
指令來開啟 Webpack 的開發伺服器。之後,當我們修改了監聽的 JavaScript 或 PHP 檔案時,瀏覽器就會即時重新整理頁面,並反映出所做的更改。
環境變數
只要在 .env
檔案中定義的環境變數名稱前方加上 MIX_
,就可以將這些環境變數插入到 webpack.mix.js
檔案中:
1MIX_SENTRY_DSN_PUBLIC=http://example.com
1MIX_SENTRY_DSN_PUBLIC=http://example.com
在 .env
檔案中定義好環境變數後,就可以使用 process.enb
物件來存取這個變數。不過,若在任務執行時更改環境變數,則可能需要重新啟動該任務才能反映出更改過的值:
1process.env.MIX_SENTRY_DSN_PUBLIC
1process.env.MIX_SENTRY_DSN_PUBLIC
通知
當可以顯示通知時,Mix 會自動在編譯的時候顯示系統通知,能讓你即時了解到是否有編譯成功。不過,有的情況下我們可能會想禁用通知。這種情況包含在正式環境伺服器上執行 Mix 時。可以使用 disableNotifications
方法來禁用通知:
1mix.disableNotifications();
1mix.disableNotifications();