AssetMapper:簡單、現代的 CSS & JS 管理
AssetMapper 組件讓您撰寫現代 JavaScript 和 CSS,而無需使用打包工具的複雜性。瀏覽器已經支援許多現代 JavaScript 功能,例如 `import
` 陳述式和 ES6 類別。而 HTTP/2 協定意味著,合併您的資源以減少 HTTP 連線已不再迫切。此組件是一個輕量層,可協助直接將您的檔案提供給瀏覽器。
此組件有兩個主要功能
- 對應 & 版本控制資源:`assets/` 內的所有檔案都公開可用並已進行版本控制。您可以使用 `
{{ asset('images/product.jpg') }}` 在 Twig 範本中參考檔案 `
assets/images/product.jpg
`。最終 URL 將包含版本雜湊,例如 `/assets/images/product-3c16d92m.jpg
`。 - Importmaps:原生瀏覽器功能,讓您更容易使用 JavaScript `
import
` 陳述式(例如 `import { Modal } from 'bootstrap'
`),而無需建置系統。所有瀏覽器都支援此功能(感謝 shim),並且是 HTML 標準的一部分。
安裝
若要安裝 AssetMapper 組件,請執行
1
$ composer require symfony/asset-mapper symfony/asset symfony/twig-pack
除了 `symfony/asset-mapper
` 之外,這也確保您擁有 Asset 組件和 Twig。
如果您正在使用 Symfony Flex,您就完成了!recipe 剛剛新增了許多檔案
- `
assets/app.js
` 您的主要 JavaScript 檔案; - `
assets/styles/app.css
` 您的主要 CSS 檔案; - `
config/packages/asset_mapper.yaml
` 您在其中定義資源「路徑」的位置; - `
importmap.php
` 您的 importmap 組態檔。
它也更新了 `templates/base.html.twig
` 檔案
1 2 3
{% block javascripts %}
+ {% block importmap %}{{ importmap('app') }}{% endblock %}
{% endblock %}
如果您沒有使用 Flex,您需要手動建立和更新這些檔案。請參閱 最新的 asset-mapper recipe 以取得這些檔案的確切內容。
對應和參考資源
AssetMapper 組件透過定義您想要公開公開的資源目錄/路徑來運作。然後,這些資源會進行版本控制,並且易於參考。由於 `asset_mapper.yaml
` 檔案,您的應用程式從一個已對應的路徑開始:`assets/
` 目錄。
如果您建立 `assets/images/duck.png
` 檔案,您可以使用以下程式碼在範本中參考它
1
<img src="{{ asset('images/duck.png') }}">
路徑 - `images/duck.png
` - 相對於您對應的目錄(`assets/
`)。這稱為資源的邏輯路徑。
如果您查看頁面中的 HTML,URL 看起來會像這樣:`/assets/images/duck-3c16d92m.png
`。如果您變更檔案,URL 的版本部分也會自動變更。
在開發環境與生產環境中提供資源
在 `dev
` 環境中,URL `/assets/images/duck-3c16d92m.png
` 由您的 Symfony 應用程式處理並傳回。
對於 `prod
` 環境,在部署之前,您應該執行
1
$ php bin/console asset-map:compile
這會將所有檔案從您對應的目錄實際複製到 `public/assets/
`,以便它們由您的 Web 伺服器直接提供。如需更多詳細資訊,請參閱 部署。
警告
如果您在開發機器上執行 `asset-map:compile
` 命令,您在重新載入頁面時將看不到對資源所做的任何變更。若要解決此問題,請刪除 `public/assets/
` 目錄的內容。這將允許您的 Symfony 應用程式再次動態提供這些資源。
提示
如果您需要將編譯後的資源複製到不同的位置(例如,將它們上傳到 S3),請建立實作 `Symfony
` 的服務,並將其服務 ID(或別名)設定為 `asset_mapper.local_public_assets_filesystem
`(以取代內建服務)。
除錯:查看所有已對應的資源
若要查看應用程式中所有已對應的資源,請執行
1
$ php bin/console debug:asset-map
這會顯示所有已對應的路徑以及每個路徑內的資源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
AssetMapper Paths
------------------
--------- ------------------
Path Namespace prefix
--------- ------------------
assets
Mapped Assets
-------------
------------------ ----------------------------------------------------
Logical Path Filesystem Path
------------------ ----------------------------------------------------
app.js assets/app.js
styles/app.css assets/styles/app.css
images/duck.png assets/images/duck.png
「邏輯路徑」是在參考資源時要使用的路徑,例如從範本。
`debug:asset-map
` 命令提供數個選項來篩選結果
1 2 3 4 5 6 7 8 9 10 11 12 13
# provide an asset name or dir to only show results that match it
$ php bin/console debug:asset-map bootstrap.js
$ php bin/console debug:asset-map style/
# provide an extension to only show that file type
$ php bin/console debug:asset-map --ext=css
# you can also only show assets in vendor/ dir or exclude any results from it
$ php bin/console debug:asset-map --vendor
$ php bin/console debug:asset-map --no-vendor
# you can also combine all filters (e.g. find bold web fonts in your own asset dirs)
$ php bin/console debug:asset-map bold --no-vendor --ext=woff2
7.2
篩選 `debug:asset-map
` 結果的選項已在 Symfony 7.2 中引入。
Importmaps & 撰寫 JavaScript
所有現代瀏覽器都支援 JavaScript import 陳述式和現代 ES6 功能,例如類別。因此,此程式碼「開箱即用」
1 2 3 4 5
// assets/app.js
import Duck from './duck.js';
const duck = new Duck('Waddles');
duck.quack();
1 2 3 4 5 6 7 8 9
// assets/duck.js
export default class {
constructor(name) {
this.name = name;
}
quack() {
console.log(`${this.name} says: Quack!`);
}
}
由於 `{{ importmap('app') }}
` Twig 函數呼叫(您將在本節中學習),瀏覽器會載入並執行 `assets/app.js
` 檔案。
提示
匯入相對檔案時,請務必包含 `.js
` 檔案名稱副檔名。與 Node.js 不同,瀏覽器環境中需要此副檔名。
匯入第三方 JavaScript 套件
假設您想要使用 npm 套件,例如 bootstrap。從技術上來說,這可以透過匯入其完整 URL 來完成,例如從 CDN
1
import { Alert } from 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/+esm';
但是,糟了!需要包含該 URL 很麻煩!相反地,我們可以透過 `importmap:require
` 命令將此套件新增到我們的「importmap」。此命令可用於新增任何 npm 套件
1
$ php bin/console importmap:require bootstrap
這會將 `bootstrap
` 套件新增到您的 `importmap.php
` 檔案
1 2 3 4 5 6 7 8 9 10
// importmap.php
return [
'app' => [
'path' => './assets/app.js',
'entrypoint' => true,
],
'bootstrap' => [
'version' => '5.3.0',
],
];
注意
有時,套件(例如 `bootstrap
`)會具有一個或多個依賴項,例如 `@popperjs/core
`。`importmap:require
` 命令將同時新增主要套件和其依賴項。如果套件包含主要的 CSS 檔案,也會新增該檔案(請參閱 處理第三方 CSS)。
注意
如果您收到 404 錯誤,則 JavaScript 套件可能存在某些問題,導致無法由 `jsDelivr
` CDN 提供服務。例如,套件可能在其 package.json 組態檔中遺失屬性,例如 `main
` 或 `module
`。請嘗試聯絡套件維護者,要求他們修正這些問題。
現在您可以像平常一樣匯入 `bootstrap
` 套件
1 2
import { Alert } from 'bootstrap';
// ...
`importmap.php
` 中的所有套件都會下載到 `assets/vendor/
` 目錄中,該目錄應由 git 忽略(Flex recipe 會將其新增到 `.gitignore
` 給您)。如果遺失某些檔案,您需要執行以下命令才能在其他電腦上下載檔案
1
$ php bin/console importmap:install
您可以執行以下命令,將您的第三方套件更新到其目前版本
1 2 3 4 5 6 7 8
# lists outdated packages and shows their latest versions
$ php bin/console importmap:outdated
# updates all the outdated packages
$ php bin/console importmap:update
# you can also run the commands only for the given list of packages
$ php bin/console importmap:update bootstrap lodash
$ php bin/console importmap:outdated bootstrap lodash
importmap 如何運作?
此 `importmap.php
` 檔案如何讓您匯入 `bootstrap
`?這要歸功於 `base.html.twig
` 中的 `{{ importmap() }}
` Twig 函數,它會輸出 importmap
1 2 3 4 5 6 7
<script type="importmap">{
"imports": {
"app": "/assets/app-4e986c1a.js",
"/assets/duck.js": "/assets/duck-1b7a64b3.js",
"bootstrap": "/assets/vendor/bootstrap/bootstrap.index-f093544d.js"
}
}</script>
Import map 是原生瀏覽器功能。當您從 JavaScript 匯入 `bootstrap
` 時,瀏覽器會查看 `importmap
`,並發現它應該從關聯的路徑擷取套件。
但是 `/assets/duck.js
` 匯入項目從何而來?它並不存在於 `importmap.php
` 中。問得好!
上面的 `assets/app.js
` 檔案匯入 `./duck.js
`。當您使用相對路徑匯入檔案時,您的瀏覽器會尋找相對於匯入檔案的檔案。因此,它會尋找 `/assets/duck.js
`。該 URL 會是正確的,除了 `duck.js
` 檔案已進行版本控制。幸運的是,AssetMapper 組件會看到匯入,並新增從 `/assets/duck.js
` 到正確、已版本控制的檔案名稱的對應。結果:匯入 `./duck.js
` 就能正常運作!
`importmap()
` 函數也會輸出 ES module shim,以便 較舊的瀏覽器 能夠理解 importmap(請參閱 polyfill 組態)。
「app」進入點 & 預先載入
「進入點」是瀏覽器載入的主要 JavaScript 檔案,您的應用程式預設會從一個進入點開始
1 2 3 4 5 6 7 8
// importmap.php
return [
'app' => [
'path' => './assets/app.js',
'entrypoint' => true,
],
// ...
];
除了 importmap 之外,`base.html.twig
` 中的 `{{ importmap('app') }}
` 還會輸出其他一些項目,包括
1
<script type="module">import 'app';</script>
此行指示瀏覽器載入 `app
` importmap 項目,這會導致執行 `assets/app.js
` 中的程式碼。
`importmap()
` 函數也會輸出一組「預先載入」
1 2
<link rel="modulepreload" href="/assets/app-4e986c1a.js">
<link rel="modulepreload" href="/assets/duck-1b7a64b3.js">
這是一種效能最佳化,您可以在下方的 效能:新增預先載入中深入瞭解。
從第三方套件匯入特定檔案
有時您需要從套件匯入特定檔案。例如,假設您正在整合 highlight.js,並且只想匯入核心和特定語言
1 2 3 4 5
import hljs from 'highlight.js/lib/core';
import javascript from 'highlight.js/lib/languages/javascript';
hljs.registerLanguage('javascript', javascript);
hljs.highlightAll();
在這種情況下,將 `highlight.js
` 套件新增到您的 `importmap.php
` 檔案將無法運作:無論您匯入什麼 - 例如 `highlight.js/lib/core
` - 都需要與 `importmap.php
` 檔案中的項目完全相符。
請改用 `importmap:require
` 並將您需要的確切路徑傳遞給它。這也示範了如何一次要求多個套件
1
$ php bin/console importmap:require highlight.js/lib/core highlight.js/lib/languages/javascript
全域變數,例如 jQuery
您可能習慣依賴全域變數 - 例如 jQuery 的 `$
` 變數
1 2 3 4 5
// assets/app.js
import 'jquery';
// app.js or any other file
$('.something').hide(); // WILL NOT WORK!
但在模組環境中(例如使用 AssetMapper),當您匯入程式庫(例如 `jquery
`)時,它不會建立全域變數。相反地,您應該在每個需要它的檔案中匯入它並將其設定為變數
1 2
import $ from 'jquery';
$('.something').hide();
您甚至可以從內嵌 script 標籤執行此操作
1 2 3 4
<script type="module">
import $ from 'jquery';
$('.something').hide();
</script>
如果您確實需要將某些項目變成全域變數,您可以從 `app.js
` 內部手動執行此操作
1 2 3
import $ from 'jquery';
// things on "window" become global variables
window.$ = $;
處理 CSS
CSS 可以透過從 JavaScript 檔案匯入來新增到您的頁面。預設的 `assets/app.js
` 已經匯入 `assets/styles/app.css
`
1 2 3 4
// assets/app.js
import '../styles/app.css';
// ...
當您在 `base.html.twig
` 中呼叫 `importmap('app')
` 時,AssetMapper 會剖析 `assets/app.js
`(以及它匯入的任何 JavaScript 檔案),尋找 CSS 檔案的 `import
` 陳述式。最終的 CSS 檔案集合會以 `link
` 標籤的形式,依照匯入的順序轉譯到頁面上。
注意
匯入 CSS 檔案不是 JavaScript 模組原生支援的功能。AssetMapper 透過為每個 CSS 檔案新增特殊的 importmap 項目來使其運作。這些特殊項目是有效的,但不會執行任何動作。AssetMapper 會為每個 CSS 檔案新增 `<link>
` 標籤,但當 JavaScript 執行 `import
` 陳述式時,不會發生任何額外的事情。
處理第三方 CSS
有時,JavaScript 套件會包含一個或多個 CSS 檔案。例如,`bootstrap
` 套件具有 dist/css/bootstrap.min.css 檔案。
您可以像 JavaScript 檔案一樣要求 CSS 檔案
1
$ php bin/console importmap:require bootstrap/dist/css/bootstrap.min.css
若要在頁面上包含它,請從 JavaScript 檔案匯入它
1 2 3 4
// assets/app.js
import 'bootstrap/dist/css/bootstrap.min.css';
// ...
提示
某些套件(例如 `bootstrap
`)會宣傳它們包含 CSS 檔案。在這些情況下,當您 `importmap:require bootstrap
` 時,為了方便起見,CSS 檔案也會新增到 `importmap.php
`。如果某些套件未在其 package.json 組態檔的 `style
` 屬性中宣傳其 CSS 檔案,請嘗試聯絡套件維護者,要求他們新增該屬性。
CSS 檔案內的路徑
從 CSS 內部,您可以使用一般的 CSS `url()
` 函數和目標檔案的相對路徑來參考其他檔案
1 2 3 4 5
/* assets/styles/app.css */
.quack {
/* file lives at assets/images/duck.png */
background-image: url('../images/duck.png');
}
最終 `app.css
` 檔案中的路徑將自動包含 `duck.png
` 的已版本控制 URL
1 2 3 4
/* public/assets/styles/app-3c16d92m.css */
.quack {
background-image: url('../images/duck-3c16d92m.png');
}
使用 Tailwind CSS
若要搭配 AssetMapper 組件使用 Tailwind CSS 框架,請查看 symfonycasts/tailwind-bundle。
使用 Sass
若要搭配 AssetMapper 組件使用 Sass,請查看 symfonycasts/sass-bundle。
從 JavaScript 檔案延遲匯入 CSS
如果您有一些想要延遲載入的 CSS,您可以透過一般的「動態」匯入語法來完成
1 2 3 4
// assets/any-file.js
import('./lazy.css');
// ...
在這種情況下,`lazy.css
` 將會非同步下載,然後新增到頁面。如果您使用動態匯入來延遲載入 JavaScript 檔案,而該檔案匯入 CSS 檔案(使用非動態 `import
` 語法),則該 CSS 檔案也會非同步下載。
問題與除錯
您可能會遇到一些常見的錯誤和問題。
遺失 importmap 項目
最常見的錯誤之一來自您的瀏覽器主控台,看起來會像這樣
Failed to resolve module specifier " bootstrap". Relative references must start with either "/", "./", or "../".
或
The specifier "bootstrap" was a bare specifier, but was not remapped to anything. Relative module specifiers must start with "./", "../" or "/".
這表示,在您的 JavaScript 中的某個地方,您正在匯入第三方套件 - 例如 `import 'bootstrap'
`。瀏覽器嘗試在您的 `importmap
` 檔案中尋找此套件,但它不在那裡。
修正方法幾乎總是將其新增到您的 `importmap
`
1
$ php bin/console importmap:require bootstrap
注意
某些瀏覽器(例如 Firefox)會顯示此「匯入」程式碼所在的位置,而其他瀏覽器(例如 Chrome)目前則不會。
JavaScript、CSS 或圖片檔案 404 找不到
有時,您正在匯入的 JavaScript 檔案(例如 `import './duck.js'
`),或您正在參考的 CSS/圖片檔案將找不到,您將在瀏覽器主控台中看到 404 錯誤。您也會注意到,404 URL 的檔案名稱中遺失了版本雜湊(例如,404 到 `/assets/duck.js
`,而不是像 `/assets/duck-1b7a64b3.js
` 的路徑)。
這通常是因為路徑錯誤。如果您直接在 Twig 範本中參考檔案
1
<img src="{{ asset('images/duck.png') }}">
那麼您傳遞給 `asset()
` 的路徑應該是檔案的「邏輯路徑」。使用 `debug:asset-map
` 命令來查看應用程式中所有有效的邏輯路徑。
更可能的是,您正在從 CSS 檔案(例如 `@import url('other.css')
`)或 JavaScript 檔案匯入失敗的資源
1 2
// assets/controllers/farm-controller.js
import '../farm/chicken.js';
執行此操作時,路徑應相對於匯入它的檔案(並且,在 JavaScript 檔案中,應以 `./
` 或 `../
` 開頭)。在這種情況下,`../farm/chicken.js
` 將指向 `assets/farm/chicken.js
`。若要查看應用程式中所有無效匯入的清單,請執行
1 2
$ php bin/console cache:clear
$ php bin/console debug:asset-map
任何無效的匯入都會在螢幕頂端顯示為警告(請確保您已安裝 symfony/monolog-bundle
)
1 2
WARNING [asset_mapper] Unable to find asset "../images/ducks.png" referenced in "assets/styles/app.css".
WARNING [asset_mapper] Unable to find asset "./ducks.js" imported from "assets/app.js".
註解程式碼中遺失資源警告
AssetMapper 組件會在您的 JavaScript 檔案中尋找 import
行,以便它可以自動將它們新增到您的 importmap。這是透過正則表達式完成的,效果很好,但並非完美。如果您註解掉一個 import,它仍然會被找到並新增到您的 importmap 中。這不會造成任何損害,但可能會令人感到意外。
如果找不到匯入的路徑,當該資源正在建置時,您會看到警告日誌,您可以忽略它。
使用 AssetMapper 組件部署
當您準備部署時,請執行此命令「編譯」您的資源
1
$ php bin/console asset-map:compile
這會將所有版本化的資源檔案寫入到 public/assets/
目錄中,以及一些 JSON 檔案(manifest.json
、importmap.json
等),以便 importmap
可以極速呈現。
最佳化效能
為了讓您的 AssetMapper 驅動的網站飛快運行,您需要做一些事情。如果您想走捷徑,可以使用像 Cloudflare 這樣的服務,它會自動為您完成大部分這些事情
- 使用 HTTP/2:您的網頁伺服器應執行 HTTP/2 或 HTTP/3,以便瀏覽器可以並行下載資源。HTTP/2 在 Caddy 中自動啟用,並且可以在 Nginx 和 Apache 中啟用。或者,透過像 Cloudflare 這樣的服務代理您的網站,它會自動為您啟用 HTTP/2。
- 壓縮您的資源:您的網頁伺服器應在將資源(JavaScript、CSS、圖片)傳送到瀏覽器之前進行壓縮(例如使用 gzip)。這在 Caddy 中自動啟用,並且可以在 Nginx 和 Apache 中啟用。在 Cloudflare 中,資源預設會被壓縮。
- 設定長期快取過期時間:您的網頁伺服器應在您的資源上設定長期
Cache-Control
HTTP 標頭。由於 AssetMapper 組件在每個資源的檔名中都包含版本雜湊值,因此您可以安全地將max-age
設定為非常長的時間(例如 1 年)。這在任何網頁伺服器中都不是自動的,但可以輕鬆啟用。
完成這些操作後,您可以使用像 Lighthouse 這樣的工具來檢查您網站的效能。
效能:理解預先載入
Lighthouse 可能會報告的一個問題是
避免鏈接關鍵請求
為了理解這個問題,想像一下這個理論上的設置
assets/app.js
匯入./duck.js
assets/duck.js
匯入bootstrap
在沒有預先載入的情況下,當瀏覽器下載頁面時,會發生以下情況
- 瀏覽器下載
assets/app.js
; - 然後它看到
./duck.js
匯入並下載assets/duck.js
; - 然後它看到
bootstrap
匯入並下載assets/bootstrap.js
。
瀏覽器不會並行下載所有 3 個檔案,而是被迫在發現它們時逐一下載。這會損害效能。
AssetMapper 通過輸出「preload」link
標籤來避免這個問題。邏輯如下
A) 當您在範本中呼叫 importmap('app')
時,AssetMapper 組件會查看 assets/app.js
檔案,並找到它匯入的所有 JavaScript 檔案,或那些檔案匯入的檔案等等。
B) 然後它為每個檔案輸出一個 link
標籤,並帶有一個 rel="preload"
屬性。這告訴瀏覽器立即開始下載這些檔案,即使它尚未看到它們的 import
語句。
此外,如果您的應用程式中可以使用 WebLink 組件,Symfony 將在回應中新增一個 Link
標頭以預先載入 CSS 檔案。
常見問題
AssetMapper 組件會合併資源嗎?
不是!但那是因為這不再是必要的了!
過去,合併資源以減少發出的 HTTP 請求數量是很常見的。由於像 HTTP/2 這樣的網頁伺服器的進步,將您的資源分開並讓瀏覽器並行下載它們通常不是問題。事實上,透過保持它們分開,當您更新一個資源時,瀏覽器可以繼續使用所有其他資源的快取版本。
請參閱 最佳化 以取得更多詳細資訊。
AssetMapper 組件會最小化資源嗎?
不是!在大多數情況下,這完全沒問題。網頁伺服器在發送資源之前執行的網頁資源壓縮通常就足夠了。但是,如果您認為您可以從最小化資源中受益(除了稍後壓縮它們之外),您可以使用 SensioLabs Minify Bundle。
這個 bundle 與 AssetMapper 無縫整合,並在運行 asset-map:compile
命令時自動最小化所有網頁資源(如在生產環境中提供資源章節中所述)。
請參閱 最佳化 以取得更多詳細資訊。
AssetMapper 組件是否已準備好用於生產環境?效能好嗎?
是的!非常!AssetMapper 組件利用了瀏覽器技術(如 importmaps 和原生 import
支援)和網頁伺服器(如 HTTP/2,它允許並行下載資源)的進步。請參閱關於最小化和合併的其他問題以及 最佳化 以取得更多詳細資訊。
https://ux.symfony.com 網站運行在 AssetMapper 組件上,並具有 99% 的 Google Lighthouse 分數。
AssetMapper 組件是否適用於所有瀏覽器?
是的!像 importmaps 和 import
語句這樣的功能在所有現代瀏覽器中都受到支援,但 AssetMapper 組件附帶一個 ES module shim 以支援舊瀏覽器中的 importmap
。因此,它在任何地方都有效(請參閱下面的註解)。
在您自己的程式碼中,如果您依賴現代 ES6 JavaScript 功能,例如 class 語法,這在除了最舊的瀏覽器之外的所有瀏覽器中都受到支援。如果您確實需要支援非常舊的瀏覽器,您應該使用像 Encore 而不是 AssetMapper 組件這樣的工具。
注意
import 語句 無法被 polyfill 或 shimmed 以在每個瀏覽器上工作。但是,只有最舊的瀏覽器不支援它 - 基本上是 IE 11(微軟不再支援它,並且全球使用率低於 0.4%)。
AssetMapper 組件已 shimmed importmap
功能以在所有瀏覽器中工作。但是,shim 無法與「動態」匯入一起使用
1 2 3 4 5 6 7
// this works
import { add } from './math.js';
// this will not work in the oldest browsers
import('./math.js').then(({ add }) => {
// ...
});
如果您想使用動態匯入並且需要支援某些較舊的瀏覽器 (https://caniuse.dev.org.tw/import-maps),您可以使用 shim 中的 importShim()
函數:https://www.npmjs.com/package/es-module-shims#user-content-polyfill-edge-case-dynamic-import
我可以搭配 Sass 或 Tailwind 使用嗎?
當然!請參閱 使用 Tailwind CSS 或 使用 Sass。
我可以搭配 TypeScript 使用嗎?
當然!請參閱 使用 TypeScript。
我可以搭配 JSX 或 Vue 使用嗎?
可能不會。如果您正在用 React、Svelte 或其他前端框架編寫應用程式,您最好直接使用它們的工具。
JSX 可以直接編譯為原生 JavaScript 檔案,但如果您使用大量的 JSX,您可能需要使用像 Encore 這樣的工具。有關將其與 AssetMapper 組件一起使用的更多詳細資訊,請參閱 UX React 文件。
Vue 檔案可以用原生 JavaScript 撰寫,這些檔案將與 AssetMapper 組件一起工作。但是您不能使用組件撰寫單一檔案組件(即 .vue
檔案),因為這些組件必須在建置系統中使用。有關將其與 AssetMapper 組件一起使用的更多詳細資訊,請參閱 UX Vue.js 文件。
我可以檢查程式碼風格並格式化我的程式碼嗎?
AssetMapper 沒有這個功能,但您可以在您的專案中安裝 kocal/biome-js-bundle 來檢查和格式化您的前端資源。它比像 Prettier 這樣的替代方案快得多,並且不需要任何配置來處理您的 JavaScript、TypeScript 和 CSS 檔案。
使用 TypeScript
要將 TypeScript 與 AssetMapper 組件一起使用,請查看 sensiolabs/typescript-bundle。
第三方套件 & 自訂資源路徑
所有具有 Resources/public/
或 public/
目錄的 bundle 都將自動將該目錄新增為「資源路徑」,使用命名空間:bundles/<BundleName>
。例如,如果您正在使用 BabdevPagerfantaBundle 並且您運行 debug:asset-map
命令,您將看到一個資源,其邏輯路徑是 bundles/babdevpagerfanta/css/pagerfanta.css
。
這表示您可以使用 asset()
函數在範本中呈現這些資源
1
<link rel="stylesheet" href="{{ asset('bundles/babdevpagerfanta/css/pagerfanta.css') }}">
實際上,這個路徑 - bundles/babdevpagerfanta/css/pagerfanta.css
- 已經在沒有 AssetMapper 組件的應用程式中工作,因為 assets:install
命令將資源從 bundle 複製到 public/bundles/
。但是,當啟用 AssetMapper 組件時,pagerfanta.css
檔案將自動版本化!它將輸出類似這樣的內容
1
<link rel="stylesheet" href="/assets/bundles/babdevpagerfanta/css/pagerfanta-ea64fc9c.css">
覆寫第三方資源
如果您想覆寫第三方資源,您可以透過在您的 assets/
目錄中建立一個同名的檔案來做到這一點。例如,如果您想覆寫 pagerfanta.css
檔案,請在 assets/bundles/babdevpagerfanta/css/pagerfanta.css
建立一個檔案。這個檔案將被用來代替原始檔案。
注意
如果一個 bundle 呈現它們自己的資源,但它們使用非預設的 資源套件,那麼將不會使用 AssetMapper 組件。例如,EasyAdminBundle 就會發生這種情況。
匯入 assets/
目錄之外的資源
您可以匯入位於您的資源路徑(即 assets/
目錄)之外的資源。例如
1 2 3 4
/* assets/styles/app.css */
/* you can reach above assets/ */
@import url('../../vendor/babdev/pagerfanta-bundle/Resources/public/css/pagerfanta.css');
但是,如果您收到像這樣的錯誤
「app」importmap 條目包含路徑「vendor/some/package/assets/foo.js」,但它似乎不在您的任何資源路徑中。
這表示您指向的是一個有效的檔案,但該檔案不在您的任何資源路徑中。您可以透過將路徑新增到您的 asset_mapper.yaml
檔案來修正此問題
1 2 3 4 5 6
# config/packages/asset_mapper.yaml
framework:
asset_mapper:
paths:
- assets/
- vendor/some/package/assets
然後再次嘗試該命令。
組態選項
您可以透過運行來查看每個可用的配置選項和一些資訊
1
$ php bin/console config:dump framework asset_mapper
下面描述了一些更重要的選項。
framework.asset_mapper.paths
此配置保存將掃描資源的所有目錄。這可以是一個簡單的列表
1 2 3 4 5
framework:
asset_mapper:
paths:
- assets/
- vendor/some/package/assets
或者您可以為每個路徑指定一個「命名空間」,該命名空間將在資源地圖中使用
1 2 3 4 5
framework:
asset_mapper:
paths:
assets/: ''
vendor/some/package/assets/: 'some-package'
在這種情況下,vendor/some/package/assets/
目錄中所有檔案的「邏輯路徑」將以 some-package
為前綴 - 例如 some-package/foo.js
。
framework.asset_mapper.excluded_patterns
這是將從資源地圖中排除的 glob 模式列表
1 2 3 4
framework:
asset_mapper:
excluded_patterns:
- '*/*.scss'
您可以使用 debug:asset-map
命令來仔細檢查您期望的檔案是否包含在資源地圖中。
framework.asset_mapper.exclude_dotfiles
是否從資源映射器中排除任何以 .
開頭的檔案。如果您想避免洩露敏感檔案,例如資源映射器發布的檔案中的 .env
或 .gitignore
,這會很有用。
1 2 3
framework:
asset_mapper:
exclude_dotfiles: true
預設情況下啟用此選項。
framework.asset_mapper.importmap_polyfill
配置舊瀏覽器的 polyfill。預設情況下,ES module shim 是透過 CDN 載入的(即此設定的預設值為 es-module-shims
)
1 2 3 4 5 6 7 8 9
framework:
asset_mapper:
# set this option to false to disable the shim entirely
# (your website/web app won't work in old browsers)
importmap_polyfill: false
# you can also use a custom polyfill by adding it to your importmap.php file
# and setting this option to the key of that file in the importmap.php file
# importmap_polyfill: 'custom_polyfill'
提示
您可以告訴 AssetMapper 在本地載入 ES module shim,方法是使用以下命令,而無需更改您的配置
1
$ php bin/console importmap:require es-module-shims
framework.asset_mapper.importmap_script_attributes
這是將新增到 {{ importmap() }}
Twig 函數呈現的 <script>
標籤的屬性列表
1 2 3 4
framework:
asset_mapper:
importmap_script_attributes:
crossorigin: 'anonymous'
頁面專屬的 CSS & JavaScript
有時您可能會選擇僅在某些頁面上包含 CSS 或 JavaScript 檔案。對於 JavaScript,一種簡單的方法是使用 動態匯入 載入檔案
1 2 3 4 5 6 7
const someCondition = '...';
if (someCondition) {
import('./some-file.js');
// or use async/await
// const something = await import('./some-file.js');
}
另一種選擇是建立一個單獨的 入口點。例如,建立一個 checkout.js
檔案,其中包含您需要的任何 JavaScript 和 CSS
1 2 3 4
// assets/checkout.js
import './checkout.css';
// ...
接下來,將其新增到 importmap.php
並將其標記為入口點
1 2 3 4 5 6 7 8 9
// importmap.php
return [
// the 'app' entrypoint ...
'checkout' => [
'path' => './assets/checkout.js',
'entrypoint' => true,
],
];
最後,在需要此 JavaScript 的頁面上,呼叫 importmap()
並傳遞 app
和 checkout
1 2 3 4 5 6 7 8 9 10
{# templates/products/checkout.html.twig #}
{#
Override an "importmap" block from base.html.twig.
If you don't have that block, add it around the {{ importmap('app') }} call.
#}
{% block importmap %}
{# do NOT call parent() #}
{{ importmap(['app', 'checkout']) }}
{% endblock %}
透過同時傳遞 app
和 checkout
,importmap()
函數將輸出 importmap
,並新增一個載入 app.js
檔案和 checkout.js
檔案的 <script type="module">
標籤。重要的是不要在 importmap
區塊中呼叫 parent()
。每個頁面只能有一個 importmap,因此 importmap()
必須精確呼叫一次。
如果由於某種原因,您只想執行僅 checkout.js
而不執行 app.js
,請僅將 checkout
傳遞給 importmap()
。
使用內容安全策略 (CSP)
如果您正在使用 內容安全策略 (CSP) 來防止跨站腳本攻擊,則 importmap()
函數呈現的內聯 <script>
標籤可能會違反該策略,並且不會被瀏覽器執行。
為了允許這些腳本在不禁用 CSP 提供的安全性的情況下運行,您可以為每個請求生成一個安全的隨機字串(稱為 nonce),並將其包含在 CSP 標頭和 <script>
標籤上的 nonce
屬性中。importmap()
函數接受一個可選的第二個參數,可用於將屬性傳遞給呈現的 <script>
標籤。您可以使用 NelmioSecurityBundle 來生成 nonce 並將其包含在 CSP 標頭中,然後將相同的 nonce 傳遞給 Twig 函數
1 2
{# the csp_nonce() function is defined by the NelmioSecurityBundle #}
{{ importmap('app', {'nonce': csp_nonce('script')}) }}
內容安全策略與 CSS 檔案
如果您的 importmap 包含 CSS 檔案,AssetMapper 會使用一個技巧,透過將 data:application/javascript
新增到呈現的 importmap 中來載入這些檔案(請參閱 處理 CSS)。
這可能會導致瀏覽器報告 CSP 違規並阻止載入 CSS 檔案。為了防止這種情況,您可以將 strict-dynamic 新增到您的內容安全策略的 script-src
指令中,以告知瀏覽器允許 importmap 載入其他資源。
注意
當使用 strict-dynamic
時,瀏覽器將忽略 script-src
中的任何其他來源,例如 'self'
或 'unsafe-inline'
,因此任何其他 <script>
標籤也需要透過 nonce 來信任。
開發環境中 AssetMapper 組件的快取系統
當在偵錯模式下開發您的應用程式時,AssetMapper 組件將計算每個資源檔案的內容並快取它。每當該檔案更改時,組件將自動重新計算內容。
系統還考慮了「依賴關係」:如果 app.css
包含 @import url('other.css')
,那麼每當 other.css
更改時,app.css
檔案內容也將被重新計算。這是因為 other.css
的版本雜湊值將會更改...這將導致 app.css
的最終內容發生更改,因為它在內部包含了最終的 other.css
檔名。
大多數情況下,這個系統都能正常運作。但是,如果您有一個檔案在您預期時沒有被重新計算,您可以運行
1
$ php bin/console cache:clear
這將強制 AssetMapper 組件重新計算所有檔案的內容。
對您的依賴套件執行安全性稽核
與 npm
類似,AssetMapper 組件捆綁了一個命令,用於檢查您的應用程式的依賴項中的安全漏洞
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
$ php bin/console importmap:audit
-------- --------------------------------------------- --------- ------- ---------- -----------------------------------------------------
Severity Title Package Version Patched in More info
-------- --------------------------------------------- --------- ------- ---------- -----------------------------------------------------
Medium jQuery Cross Site Scripting vulnerability jquery 3.3.1 3.5.0 https://api.github.com/advisories/GHSA-257q-pV89-V3xv
High Prototype Pollution in JSON5 via Parse Method json5 1.0.0 1.0.2 https://api.github.com/advisories/GHSA-9c47-m6qq-7p4h
Medium semver vulnerable to RegExp Denial of Service semver 4.3.0 5.7.2 https://api.github.com/advisories/GHSA-c2qf-rxjj-qqgw
Critical Prototype Pollution in minimist minimist 1.1.3 1.2.6 https://api.github.com/advisories/GHSA-xvch-5gv4-984h
Medium ESLint dependencies are vulnerable minimist 1.1.3 1.2.2 https://api.github.com/advisories/GHSA-7fhm-mqm4-2wp7
Medium Bootstrap Vulnerable to Cross-Site Scripting bootstrap 4.1.3 4.3.1 https://api.github.com/advisories/GHSA-9v3M-8fp8-mi99
-------- --------------------------------------------- --------- ------- ---------- -----------------------------------------------------
7 packages found: 7 audited / 0 skipped
6 vulnerabilities found: 1 Critical / 1 High / 4 Medium
如果未發現漏洞,該命令將返回 0
退出代碼,否則返回 1
退出代碼。這表示您可以無縫地將此命令整合到您的 CI 中,以便在發現任何新漏洞時收到警告。
提示
該命令採用 --format
選項,以在 txt
和 json
之間選擇輸出格式。