前頭·見·nee·呃
名詞:台口
- 劇場舞台幕布前的部分。
Proscenium將您的前端和用戶端程式碼視為 Rails 應用程式的一等公民,並假定「預設情況下速度很快」的網路。它是即時、按需且零配置地捆綁和縮小 JavaScript (+ JSX)、TypeScript (+TSX) 和 CSS。
亮點:
rails s
即可!顯然,入門取決於您是將 Proscenium 新增到現有 Rails 應用程式還是建立新應用程式。因此,請選擇以下適當的指南:
將此行加入 Rails 應用程式的 Gemfile 中,然後就可以開始了:
gem 'proscenium'
請注意,Proscenium 專為與 Rails 一起使用而設計。
現在,如果您啟動 Rails 應用程序,您可以打開任何前端程式碼(JS、CSS 等)。例如,可以透過https://localhost:3000/app/assets/stylesheets/application.css
存取app/assets/stylesheets/application.css
assets/stylesheets/application.css 中的文件,該文件將被捆綁、轉換和縮小[在生產中]實時。
Proscenium 相信您的前端程式碼與後端程式碼一樣重要,而不是事後的想法 - 它們應該是您的 Rails 應用程式的一等公民。因此,不必將所有 JS 和 CSS 放入“app/assets”目錄中,然後需要單獨的進程來編譯或捆綁,只需將它們放在應用程式中您想要的任何位置,然後運行 Rails!
例如,如果您的app/views/users/index.html.erb
視圖需要一些 JS,只需在app/views/users/index.js
旁邊建立一個 JS 檔案即可。或者,如果您的整個應用程式使用了一些 CSS,請將其放入app/views/layouts/application.css
中並將其與佈局一起載入。也許你有一些 JS 實用函數,所以將它們放在lib/utils.js
中。
只需將 JS(X) 和 CSS 放在您想要的任何位置,Rails 應用程式就會從您放置它們的位置提供服務。
使用上面的例子...
https://localhost:3000/app/views/users/index.js
app/views/users/index.js
index.jshttps://localhost:3000/app/views/layouts/application.css
app/views/layouts/application.css
application.csslib/utils.js
=> https://localhost:3000/lib/utils.js
app/components/menu_component.jsx
https://localhost:3000/app/components/menu_component.jsx
/menu_component.jsxconfig/properties.css
=> https://localhost:3000/config/properties.css
當您的資源自動側載時,Proscenium 的體驗最佳。
使用 Rails,您通常會使用javascript_include_tag
和stylesheet_link_tag
幫助器以聲明方式載入 JavaScript 和 CSS 資源。
例如,您的頂級「應用程式」CSS 可能位於/app/assets/stylesheets/application.css
的檔案中。同樣,您可能有一些全域 JavaScript 位於/app/javascript/application.js
的檔案中。
您可以手動並以聲明方式將這兩個文件包含在應用程式佈局中,如下所示:
<%# /app/views/layouts/application.html.erb %>
<!DOCTYPE html >
< html >
< head >
< title > Hello World </ title >
<%= stylesheet_link_tag 'application' %> <!-- << Your app CSS -->
</ head >
< body >
<%= yield %>
<%= javascript_include_tag 'application' %> <!-- << Your app JS -->
</ body >
</ html >
現在,您可能有一些僅特定視圖和部分所需的 CSS 和 JavaScript,因此您可以將其載入到視圖(或佈局)中,如下所示:
<%# /app/views/users/index.html.erb %>
<%= stylesheet_link_tag 'users' %>
<%= javascript_include_tag 'users' %>
<%# needed by the `users/_user.html.erb` partial %>
<%= javascript_include_tag '_user' %>
<% render @users %>
主要問題是您必須追蹤所有這些資源,並確保所有需要它們的視圖都加載每個資源,但也要避免在不需要時加載它們。這可能是一個真正的痛苦,尤其是當您有很多視圖時。
當使用 Proscenium 側面載入 JavaScript、Typescript 和 CSS 時,它們會自動包含在您的視圖、部分、佈局和元件旁邊,並且僅在需要時包含。
側面載入的工作原理是尋找與視圖、部分、佈局或元件同名的 JS/TS/CSS 檔案。例如,如果您有位於app/views/users/index.html.erb
的視圖,那麼 Proscenium 將在app/views/users/index.js
、 app/views/users/index.ts
或app/views/users/index.css
。如果找到,它會將其包含在該視圖的 HTML 中。
JavaScript 和 Typescript 也支援 JSX。只需使用.jsx
或.tsx
副檔名而不是.js
或.ts
。
只需建立一個與任何視圖、部分或佈局同名的 JS 和/或 CSS 檔案。
讓我們繼續上面的問題範例,其中我們有以下資產
/app/assets/application.css
/app/assets/application.js
/app/assets/users.css
/app/assets/users.js
/app/assets/user.js
您的應用程式佈局位於/app/views/layouts/application.hml.erb
,需要使用者資源的視圖位於/app/views/users/index.html.erb
,因此將您的資源 JS 和 CSS 與它們一起移動:
/app/views/layouts/application.css
/app/views/layouts/application.js
/app/views/users/index.css
/app/views/users/index.js
/app/views/users/_user.js
(部分)現在,在您的佈局和視圖中,將javascript_include_tag
和stylesheet_link_tag
幫助程式替換為 Proscenium 中的include_asset
幫助程式。像這樣的事情:
<!DOCTYPE html >
< html >
< head >
< title > Hello World </ title >
<%= include_assets # <-- %>
</ head >
< body >
<%= yield %>
</ body >
</ html >
在每個頁面請求中,Proscenium 都會檢查您的任何視圖、佈局和部分是否具有同名的 JS/TS/CSS 文件,然後將它們包含在您放置include_assets
幫助程式的位置。
現在您再也不用記得添加您的資產了。只需將它們與您的視圖、局部和佈局一起創建,Proscenium 就會處理剩下的事情。
預設啟用側面加載,但您可以透過在/config/application.rb
中將config.proscenium.side_load
設為false
來停用它。
還有include_stylesheets
和include_javascripts
幫助程序,可讓您控制 CSS 和 JS 資源包含在 HTML 中的位置。如果您想準確控制資產的包含位置,則應使用這些幫助程式而不是include_assets
。
捆綁檔案意味著將任何匯入的依賴項內聯到檔案本身。這個過程是遞歸的,因此依賴項的依賴項(等等)也會被內聯。
Proscenium 將預設即時捆綁。因此沒有單獨的建置步驟或預編譯。
Proscenium 支援從 NPM、透過 URL、本機應用程式甚至其他 Ruby Gem 匯入 JS、JSX、TS、TSX、CSS 和 SVG。
JavaScript 和 TypeScript 支援靜態 ( import
) 和動態 ( import()
) 導入,並且可用於導入 JS、TS、JSX、TSX、JSON、CSS 和 SVG 檔案。
CSS 支援@import
CSS at 規則。
目前,僅當導入路徑是字串文字或全域模式時才會進行捆綁。其他形式的導入路徑不會捆綁,而是逐字保留在產生的輸出中。這是因為捆綁是編譯時操作,而 Proscenium 不支援所有形式的執行時間路徑解析。
以下是一些範例:
// Analyzable imports (will be bundled)
import "pkg" ;
import ( "pkg" ) ;
import ( `./locale- ${ foo } .json` ) ;
// Non-analyzable imports (will not be bundled)
import ( `pkg/ ${ foo } ` ) ;
解決不可分析匯入的方法是將包含此有問題的程式碼的套件標記為未捆綁,以便它不包含在捆綁包中。然後,您需要確保外部套件的副本在運行時可用於您的捆綁程式碼。
node_modules
)完全支援裸導入(不以./
、 /
、 https://
、 http://
開頭的導入),並將透過位於的package.json
檔案使用您選擇的套件管理器(例如,NPM、Yarn、pnpm )在 Rails 應用程式的根目錄下。
使用您選擇的套件管理器安裝您想要匯入的套件...
npm install react
...然後像導入任何其他包一樣導入它。
import React from "react" ;
當然,您可以使用相對或絕對路徑匯入您自己的程式碼(檔案副檔名是可選的,絕對路徑使用您的 Rails 根目錄作為基礎):
import utils from "/lib/utils" ;
import constants from "./constants" ;
import Header from "/app/components/header" ;
@import "/lib/reset" ;
有時您不想捆綁導入。例如,您希望確保僅載入一個 React 實例。在這種情況下,您可以使用unbundle
前綴
import React from "unbundle:react" ;
這僅適用於任何裸露和本地進口。
您也可以在匯入映射中使用unbundle
前綴,這可確保特定路徑的所有匯入始終未捆綁:
{
"imports" : {
"react" : " unbundle:react "
}
}
然後像平常一樣導入:
import React from "react" ;
[開發中]
開箱即用地支援 JS 和 CSS 的導入映射,並且與所使用的瀏覽器無關。這是因為導入映射是由伺服器上的 Proscenium 解析和解析的,而不是由瀏覽器解析。這更快,並且還允許您在尚未支援匯入地圖的瀏覽器中使用匯入地圖。
如果您不熟悉匯入映射,請將它們視為定義別名的一種方法。
只需建立config/import_map.json
並指定要使用的導入。例如:
{
"imports" : {
"react" : " https://esm.sh/[email protected] " ,
"start" : " /lib/start.js " ,
"common" : " /lib/common.css " ,
"@radix-ui/colors/" : " https://esm.sh/@radix-ui/[email protected]/ "
}
}
使用上面的導入映射,我們可以做...
import { useCallback } from "react" ;
import startHere from "start" ;
import styles from "common" ;
對於CSS...
@import "common" ;
@import "@radix-ui/colors/blue.css" ;
您也可以使用 JavaScript 而不是 JSON 編寫導入映射。因此,不要建立config/import_map.json
,而是建立config/import_map.js
,定義一個匿名函數。此函數接受單一environment
參數。
( env ) => ( {
imports : {
react :
env === "development"
? "https://esm.sh/[email protected]?dev"
: "https://esm.sh/[email protected]" ,
} ,
} ) ;
來源映射可以讓您更輕鬆地偵錯程式碼。它們會對從產生的輸出檔案中的行/列偏移轉換回對應原始輸入檔案中的行/列偏移所需的資訊進行編碼。如果您產生的程式碼與原始程式碼有很大不同(例如您的原始程式碼是 TypeScript 或您啟用了縮小),這非常有用。如果您喜歡在瀏覽器的開發人員工具中查看單個文件而不是一個大的捆綁文件,這也很有用。
JavaScript 和 CSS 支援來源映射輸出。每個文件都附加了來源映射的連結。例如:
//# sourceMappingURL=/app/views/layouts/application.js.map
您的瀏覽器開發工具應該選擇此功能並在需要時自動載入來源對應。
您可以從 JS(X) 匯入 SVG,這將會捆綁 SVG 原始碼。此外,如果從 JSX 或 TSX 匯入,SVG 原始碼將呈現為 JSX/TSX 元件。
適用於
>=0.10.0
您可以在proscenium.env
命名空間下從 JavaScript 和 Typescript 定義和存取任何環境變數。
基於效能和安全性原因,您必須在config/application.rb
檔案中聲明您希望公開的環境變數名稱。
config . proscenium . env_vars = Set [ 'API_KEY' , 'SOME_SECRET_VARIABLE' ]
config . proscenium . env_vars << 'ANOTHER_API_KEY'
這假設已經定義了同名的環境變數。如果沒有,您需要在程式碼中使用 Ruby 的ENV
物件或在 shell 中自行定義它。
這些聲明的環境變數將被替換為常數表達式,允許您像這樣使用它:
console . log ( proscenium . env . RAILS_ENV ) ; // console.log("development")
console . log ( proscenium . env . RAILS_ENV === "development" ) ; // console.log(true)
RAILS_ENV
和NODE_ENV
環境變數將始終自動為您宣告。
除此之外,Proscenium 還提供了一個process.env.NODE_ENV
變量,該變量設定為與proscenium.env.RAILS_ENV
相同的值。提供它是為了支援社區現有的工具,這些工具通常依賴該變數。
環境變數在幫助 Tree Shaking 方面特別強大。
function start ( ) {
console . log ( "start" ) ;
}
function doSomethingDangerous ( ) {
console . log ( "resetDatabase" ) ;
}
proscenium . env . RAILS_ENV === "development" && doSomethingDangerous ( ) ;
start ( ) ;
在開發中,上面的程式碼將轉換為以下程式碼,丟棄定義,並呼叫doSomethingDangerous()
。
function start ( ) {
console . log ( "start" ) ;
}
start ( ) ;
請注意,出於安全原因,URL 匯入中不會取代環境變數。
未定義的環境變數將被替換為undefined
。
console . log ( proscenium . env . UNKNOWN ) ; // console.log((void 0).UNKNOWN)
這意味著依賴於此的程式碼不會進行樹搖動。您可以使用可選的連結運算子來解決此問題:
if ( typeof proscenium . env ?. UNKNOWN !== "undefined" ) {
// do something if UNKNOWN is defined
}
提供了從config/locales/*.yml
匯入 Rails 區域設定檔並將其匯出為 JSON 的基本支援。
import translations from "@proscenium/i18n" ;
// translations.en.*
預設情況下,Proscenium 的輸出將利用 ES2022 規格及更早版本中的所有現代 JS 功能。例如, a !== void 0 && a !== null ? a : b
會變成a ?? b
縮小時(在生產中預設啟用),它使用 ES2020 版本的 JavaScript 語法。 ES2020 不支援的任何語法功能都將轉換為更廣泛支援的舊 JavaScript 語法。
Tree Shaking 是 JavaScript 社群用於消除死程式碼的術語,這是一種常見的編譯器最佳化,可以自動刪除無法存取的程式碼。在 Proscenium 中預設啟用 Tree Shaking。
function one ( ) {
console . log ( "one" ) ;
}
function two ( ) {
console . log ( "two" ) ;
}
one ( ) ;
上面的程式碼將轉換為下面的程式碼,丟棄two()
,因為它從未被呼叫。
function one ( ) {
console . log ( "one" ) ;
}
one ( ) ;
適用於
>=0.10.0
。
側面載入的資源會自動進行程式碼分割。這意味著,如果您有一個文件被導入並多次使用,並且被不同的文件導入,那麼它將被分割成一個單獨的文件。
舉個例子:
// /lib/son.js
import father from "./father" ;
father ( ) + " and Son" ;
// /lib/daughter.js
import father from "./father" ;
father ( ) + " and Daughter" ;
// /lib/father.js
export default ( ) => "Father" ;
son.js
和daughter.js
都會導入father.js
,因此兒子和女兒通常都會包含father 的副本,從而導致重複的程式碼和更大的包大小。
如果這些檔案是側面載入的,那麼father.js
將被分割成一個單獨的檔案或區塊,並且只下載一次。
多個入口點之間共享的程式碼被分成兩個入口點導入的單獨的共享檔案。這樣,如果用戶先瀏覽一個頁面,然後瀏覽另一個頁面,如果共享部分已經被瀏覽器下載並緩存,他們就不必從頭開始下載第二個頁面的所有 JavaScript。
透過非同步import()
表達式引用的程式碼將被拆分到一個單獨的檔案中,並且僅在計算該表達式時載入。這使您可以透過僅在啟動時下載所需的程式碼,然後在以後需要時延遲下載其他程式碼,從而縮短應用程式的初始下載時間。
如果沒有程式碼分割, import() 表達式將變成Promise.resolve().then(() => require())
。這仍然保留了表達式的非同步語義,但這意味著導入的程式碼包含在同一個套件中,而不是拆分到單獨的檔案中。
預設情況下啟用程式碼分割。您可以透過在應用程式的/config/application.rb
中將code_splitting
配置選項設為false
來停用它:
config . proscenium . code_splitting = false
就 JavaScript 而言,有一些重要的注意事項。這些在 esbuild 網站上有詳細介紹。
CSS 是 Proscenium 中一流的內容類型,這意味著它可以直接捆綁 CSS 文件,而無需從 JavaScript 程式碼匯入 CSS。您可以使用url()
@import
其他 CSS 文件和參考圖像和字體文件,Proscenium 會將所有內容捆綁在一起。
請注意,預設情況下,Proscenium 的輸出將利用所有現代 CSS 功能。例如, color: rgba(255, 0, 0, 0.4)
在生產中縮小後將變為color: #f006
,使用了 CSS Color Module Level 4 的語法。
支援新的 CSS 巢狀語法,並將其轉換為適用於舊版瀏覽器的非巢狀 CSS。
Proscenium 也會自動插入供應商前綴,以便您的 CSS 可以在舊版瀏覽器中運作。
您也可以從 JavaScript 匯入 CSS。當您執行此操作時,Proscenium 會自動將每個樣式表作為<link>
元素附加到文件的頭部。
import "./button.css" ;
export let Button = ( { text } ) => {
return < div className = "button" > { text } < / div > ;
} ;
Proscenium 實作了 CSS 模組的子集。它支援:local
和:global
關鍵字,但不支援composes
屬性。 (建議您使用 mixins 而不是composes
,因為它們可以在任何地方使用,甚至在純 CSS 檔案中也是如此。)
為任何 CSS 檔案提供.module.css
副檔名,Proscenium 會將其視為 CSS 模組,並使用該檔案特有的後綴轉換所有類別名稱。
. title {
font-size : 20 em ;
}
上述輸入產生:
. title-5564cdbb {
font-size : 20 em ;
}
您現在擁有一個幾乎可以在任何地方使用的唯一類別名稱。
您可以使用css_module
幫助程式從 Rails 視圖、部分視圖和版面配置 CSS 模組,該裝置接受一個或多個類別名稱,並將傳回等效的 CSS 模組名稱 - 附加了唯一後綴的類別名稱。
透過側面加載設置,您可以使用css_module
幫助程序,如下所示。
< div >
< h1 class =" <%= css_module :hello_title %> " > Hello World </ h1 >
< p class =" <%= css_module :body , paragraph : %> " >
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</ p >
</ div >
css_module
接受多個類別名,並將傳回轉換後的 CSS 模組名稱的空格分隔字串。
css_module :my_module_name
# => "my_module_name-ABCD1234"
您甚至可以透過將 URL 路徑傳遞給檔案(作為類別名稱的前綴)來引用任何 CSS 檔案中的類別。這樣做會自動側面載入樣式表。
css_module '/app/components/button.css@big_button'
# => "big_button"
它還支援 NPM 套件(已安裝在 /node_modules 中):
css_module 'mypackage/button@big_button'
# => "big_button"
css_module
也接受一個path
關鍵字參數,它允許您指定 CSS 檔案的路徑。請注意,這將使用傳遞給css_module
實例的所有類別名稱的給定路徑。
css_module :my_module_name , path : Rails . root . join ( 'app/components/button.css' )
從 JS 匯入 CSS 模組會自動將樣式表附加到文件的頭部。導入的結果將是 CSS 類別到模組名稱的物件。
import styles from "./styles.module.css" ;
// styles == { header: 'header-5564cdbb' }
需要注意的是,CSS 模組名稱的導出物件實際上是一個 JavaScript 代理物件。所以解構物件是行不通的。相反,您必須直接存取屬性。
此外,將一個 CSS 模組導入另一個 CSS 模組將導致所有類別產生相同的摘要字串。
Proscenium 提供了將一個或多個 CSS 類別包含或「混合」到另一個類別的功能。這類似於 CSS 模組的composes
屬性,但適用於任何地方,並且不限於 CSS 模組。
使用@define-mixin
和@mixin
at-rules 支援 CSS mixin。
mixin 是使用@define-mixin
at 規則定義的。為其傳遞一個名稱,該名稱應遵循類別名稱語義,並聲明您的規則:
// /lib/mixins.css
@define-mixin bigText {
font-size : 50 px ;
}
使用@mixin
at 法則來使用 mixin。向其傳遞您要使用的 mixin 的名稱以及聲明 mixin 的 url。 url 用來解析 mixin,可以是相對的、絕對的、URL,甚至來自 NPM 套件。
// /app/views/layouts/application.css
p {
@mixin bigText from url ( "/lib/mixins.css" );
color : red;
}
上面產生了這個輸出:
p {
font-size : 50 px ;
color : red;
}
Mixins 可以在任何 CSS 檔案中聲明。它們不需要在使用它們的相同文件中聲明。但是,如果您在同一文件中聲明並使用 mixin,則無需指定聲明 mixin 的 URL。
@define-mixin bigText {
font-size : 50 px ;
}
p {
@mixin bigText;
color : red;
}
CSS 模組和 Mixins 完美配合。您可以在 CSS 模組中包含 mixin。
就 CSS 而言,有一些重要的注意事項。這些在 esbuild 網站上有詳細介紹。
Typescript 和 TSX 是開箱即用的,並且具有解析 TypeScript 語法和丟棄類型註解的內建支援。只需將檔案重新命名為.ts
或.tsx
即可。
請注意,Proscenium 不執行任何類型檢查,因此您仍然需要與 Proscenium 並行運行tsc -noEmit
來檢查類型。
就 Typescript 而言,有一些重要的警告。這些在 esbuild 網站上有詳細介紹。
使用 JSX 語法通常需要您手動匯入正在使用的 JSX 庫。例如,如果您使用 React,預設情況下您需要將 React 匯入到每個 JSX 檔案中,如下所示:
import * as React from "react" ;
render ( < div / > ) ;
這是因為 JSX 轉換將 JSX 語法轉換為對React.createElement
的調用,但它本身並不會導入任何內容,因此 React 變數不會自動存在。
Proscenium 為您產生這些導入語句。請記住,這也完全改變了 JSX 轉換的工作方式,因此如果您使用的 JSX 庫不是 React,則可能會破壞您的程式碼。
在[不太遙遠]的將來,您將能夠設定 Proscenium 以使用不同的 JSX 庫,或完全停用此自動導入。
導入 .json 檔案會將 JSON 檔案解析為 JavaScript 對象,並將該對像作為預設導出導出。使用它看起來像這樣:
import object from "./example.json" ;
console . log ( object ) ;
除了預設導出之外,JSON 物件中的每個頂級屬性也有命名導出。直接匯入命名匯出表示 Proscenium 可以自動從套件中刪除 JSON 檔案中未使用的部分,只留下您實際使用的命名匯出。例如,此程式碼在捆綁時僅包含版本欄位:
import { version } from "./package.json" ;
console . log ( version ) ;
Phlex 是一個以純 Ruby 建立快速、可重複使用、可測試視圖的框架。 Proscenium 與 Phlex 完美配合,支援側面加載、CSS 模組等。只需編寫您的 Phlex 類別並從Proscenium::Phlex
繼承即可。
class MyView < Proscenium :: Phlex
def view_template
h1 { 'Hello World' }
end
end
在您的佈局中,包含Proscenium::Phlex::AssetInclusions
,並呼叫include_assets
幫助程式。
class ApplicationLayout < Proscenium :: Phlex
include Proscenium :: Phlex :: AssetInclusions # <--
def view_template ( & )
doctype
html do
head do
title { 'My Awesome App' }
include_assets # <--
end
body ( & )
end
end
end
您可以使用include_stylesheets
和include_javascripts
幫助程式專門包含 CCS 和 JS 資源,讓您可以控制它們包含在 HTML 中的位置。
任何繼承Proscenium::Phlex
的 Phlex 類別都會自動被側面載入。
Phlex 類別完全支援 CSS 模組,如果需要,可以存取css_module
幫助器。然而,有一種更好、更簡單的方法來在 Phlex 類別中引用 CSS 模組類別。
在您的 Phlex 類別中,任何以@
開頭的類別名稱都將被視為 CSS 模組類別。
# /app/views/users/show_view.rb
class Users :: ShowView < Proscenium :: Phlex
def