構建和渲染您的第一個操縱桿組件
如何使用 CheatCode 的 @joystick.js/ui
構建一個簡單的應用程序並編寫一個組件 框架並使用 @joystick.js/node
將其呈現給瀏覽器 .
開始使用
在本教程中,我們將使用 CheatCode 的全棧 JavaScript 框架 Joystick。 Joystick 將前端 UI 框架與用於構建應用的 Node.js 後端結合在一起。
首先,我們要通過 NPM 安裝 Joystick。確保在安裝之前使用 Node.js 16+ 以確保兼容性(如果您需要學習如何安裝 Node.js 或在計算機上運行多個版本,請先閱讀本教程):
終端
npm i -g @joystick.js/cli
這將在您的計算機上全局安裝操縱桿。安裝好之後,接下來我們新建一個項目:
終端
joystick create app
幾秒鐘後,您將看到一條消息已註銷到 cd
進入你的新項目並運行 joystick start
:
終端
cd app && joystick start
在此之後,您的應用應該可以運行了,我們可以開始了。
創建組件
當你創建你的應用程序時,如果你打開 package.json
在項目根目錄下的文件中,您將看到列出了兩個依賴項:@joystick.js/ui
和 @joystick.js/node
.儘管這些是單獨的包,但它們旨在協同工作。為了實現這一點,我們使用 @joystick.js/cli
上面安裝的包。當我們運行 joystick start
上面,該連接已建立。
在我們創建的項目中,您將看到一個文件夾 /ui
在項目的根目錄下,其中包含三個文件夾:/ui/components
, /ui/layouts
, 和 /ui/pages
.使用 @joystick.js/ui
在 Joystick 中創建組件時 包,我們使用這三種類型來保持井井有條:
/ui/components
包含旨在與其他組件一起呈現或在頁面中組合在一起的各種操縱桿組件。/ui/layouts
包含操縱桿組件,這些組件旨在作為呈現靜態內容(例如,導航元素或頁腳)以及動態頁面的包裝器。/ui/pages
包含代表應用程序中的頁面或 URL 的操縱桿組件,這些組件旨在組合 HTML 和映射到路由的其他組件。
對於本教程,我們將重點關注最後一種類型,頁面。我們將要創建的頁面將呈現一些虛擬元素,以展示操縱桿組件的所有功能。
首先,讓我們為組件創建文件夾和文件。我們將其稱為儀表板並將其存儲在 /ui/pages/dashboard/index.js
:
/ui/pages/dashboard/index.js
import ui from '@joystick.js/ui';
const Dashboard = ui.component({
render: () => {
return `
<div class="dashboard">
<h4>Dashboard</h4>
</div>
`;
},
});
export default Dashboard;
首先,我們想為我們的組件設置一個骨架。上面,我們正在導入 ui
從 @joystick.js/ui
導出的對象 我們之前暗示過的包。為了設置我們的組件,我們創建一個新變量 Dashboard
並將其分配給對 ui.component()
的調用 ,傳遞一個包含我們組件定義的對象。在我們文件的底部,我們確保導出 Dashboard
變量作為默認值,因為 Joystick 要求我們這樣做(稍後我們會看到原因)。
專注於render
我們在傳遞給 ui.component()
的對像上設置的屬性 , 這被分配給一個函數,該函數負責為我們的組件呈現 HTML 標記。在 Joystick 中,組件是用純 HTML 構建的。您可以用普通 .html
編寫的任何 HTML 文件將在操縱桿組件中工作。
在我們的 render()
函數,我們返回一個字符串——使用反引號 ``
這樣我們就可以利用 JavaScript 字符串插值(允許我們在 HTML 中嵌入動態值,如變量或調用函數的結果)。
在該字符串中,我們為我們的組件編寫 HTML ——這裡只是一個 <div></div>
帶有類和 <h4></h4>
的標籤 在其中添加標籤以幫助我們開始。雖然看起來可能不多,但如果我們現在渲染它,我們會看到我們的 <h4></h4>
呈現在屏幕上。
在我們這樣做之前,讓我們進一步充實我們的 HTML 並添加一些 CSS:
/ui/pages/dashboard/index.js
import ui from '@joystick.js/ui';
const Dashboard = ui.component({
css: `
.dashboard {
width: 100%;
max-width: 1000px;
margin: 0 auto;
}
.dashboard h4 {
margin-bottom: 20px;
}
.dashboard input {
display: block;
padding: 20px;
font-size: 16px;
border: 1px solid #ddd;
margin-bottom: 20px;
}
.dashboard button {
border: none;
background: #000;
color: #fff;
font-size: 16px;
padding: 20px;
border-radius: 3px;
}
`,
render: () => {
return `
<div class="dashboard">
<h4>Dashboard</h4>
<input type="text" />
<button class="say-hello">Say Hello</button>
</div>
`;
},
});
export default Dashboard;
相同的組件,只是添加了一些東西。在render()
中 ,我們添加了一個 <input />
和一個 <button></button>
(我們稍後會用到這些)。這裡重要的部分是新的 css
屬性。
同樣,使用 ``
反引號(除了插值,這允許我們在 JavaScript 中做一個多行字符串),我們在 render()
中為標記編寫了一些 CSS 功能。
這裡的想法是我們希望在每個組件的基礎上隔離 CSS。這讓我們保持井井有條,同時也避免了在使用單個 CSS 文件(或將多個 CSS 文件導入單個文件)時的樣式衝突。
在幕後,當我們的組件被渲染時,Joystick 將使用這個 CSS 並自動將其作用於我們的組件。這就是我們避免在 CSS 中創建重疊或破壞樣式的級聯問題的方法。樣式直接映射到您的組件。
除了動態作用域,Joystick 還會自動將這個 CSS 注入到 <head></head>
我們在瀏覽器中呈現的 HTML,這意味著樣式會自動與組件的 HTML 一起呈現。關注 CSS 本身,注意我們在組件的 HTML 中引用元素和類名——不需要任何特殊的東西;操縱桿會為我們處理棘手的事情。
/ui/pages/dashboard/index.js
import ui from '@joystick.js/ui';
const Dashboard = ui.component({
state: {
name: 'Friend',
},
methods: {
sayHello: (component) => {
window.alert(`Hello, ${component.state.name}!`);
},
},
css: `
...
`,
render: ({ state }) => {
return `
<div class="dashboard">
<h4>Dashboard</h4>
<p>I'm going to say "Hello, ${state.name}!"</p>
<input type="text" />
<button class="say-hello">Say Hello</button>
</div>
`;
},
});
export default Dashboard;
接下來,為了使我們的組件具有交互性,我們將向我們的組件添加一個通用函數,稱為方法。 methods
這裡的屬性被分配了一個具有自定義命名函數的對象,該函數可以從組件的其他地方調用。我們定義的每個方法都傳遞了整個 component
實例作為最後一個可用參數(例如,如果我們調用一個方法並傳遞一個值,該值將成為第一個參數和 component
將成為第二個)。
在這裡,我們定義了一個方法 sayHello
我們希望在調用時顯示一個警報對話框。在內部,我們希望它顯示一條消息“你好,<name>
是 name
的當前值 組件的 state
上的屬性 對象。
在操縱桿組件內部,state
表示當前的視覺 組件的狀態(想想“視覺狀態”)。那個state
可以是數據,我們 UI 的一部分的設置——任何你想要的。初始化我們的 state
值(也稱為設置我們的“默認”狀態),我們添加一個 state
我們組件的選項,也傳遞了一個對象,帶有我們要在 state
上設置的值的名稱 當組件加載時。
對於我們的組件,我們要設置 name
在 state
.在這裡,我們將默認值設置為 'Friend'
.所以很明顯,如果我們調用 sayHello
按原樣運行,我們會看到彈出一個警告框,上面寫著“你好,朋友!”現在讓我們使用我們組件的 lifecycle
來連接它 方法。
/ui/pages/dashboard/index.js
import ui from '@joystick.js/ui';
const Dashboard = ui.component({
state: {
name: 'Friend',
},
lifecycle: {
onMount: (component) => {
component.methods.sayHello();
},
},
methods: {
sayHello: (component) => {
window.alert(`Hello, ${component.state.name}!`);
},
},
css: `
...
`,
render: ({ state }) => {
return `
<div class="dashboard">
<h4>Dashboard</h4>
<p>I'm going to say "Hello, ${state.name}!"</p>
<input type="text" />
<button class="say-hello">Say Hello</button>
</div>
`;
},
});
export default Dashboard;
當我們在瀏覽器中渲染 Joystick 組件時,它會經歷幾個“生命階段”,我們稱之為它的生命週期。在這裡,我們將一個對象添加到我們的組件 lifecycle
可以分配三個功能:
onBeforeMount
在瀏覽器中呈現 Joystick 組件之前立即調用的函數。onMount
在瀏覽器中呈現 Joystick 組件後立即調用的函數。onBeforeUnmount
在從瀏覽器中刪除操縱桿組件之前立即調用的函數。
演示我們的 sayHello
方法,我們將使用 onMount
生命週期方法/函數(名稱“方法”是用於描述 JavaScript 中定義在對像上的函數的術語)來調用它。所有 lifecycle
方法通過 component
實例,這意味著我們可以訪問我們的 methods
通過那個對象。在我們的 onMount
內部 函數,我們調用 component.methods.sayHello()
說“當這個組件在屏幕上呈現時,顯示一個警告窗口並問候用戶。”
快完成了。為了在繼續路由之前包裝我們的組件,我們要做的最後一件事是連接一些 DOM 事件處理程序。
/ui/pages/dashboard/index.js
import ui from '@joystick.js/ui';
const Dashboard = ui.component({
state: { ... },
lifecycle: { .. },
methods: { ... },
css: `
...
`,
events: {
'keyup input': (event, component) => {
component.setState({ name: event.target.value });
},
'click .say-hello': (event, component) => {
component.methods.sayHello();
},
},
render: ({ state }) => {
return `
<div class="dashboard">
<h4>Dashboard</h4>
<p>I'm going to say "Hello, ${state.name}!"</p>
<input type="text" />
<button class="say-hello">Say Hello</button>
</div>
`;
},
});
export default Dashboard;
首先,讓我們關注events
我們添加到組件中的屬性。這就是我們如何定義並自動將 DOM 事件偵聽器作用於我們的組件。監聽器是通過設置一個回調函數到一個屬性來定義的,該屬性的名稱是一個具有某種 DOM 事件類型的字符串,後跟一個空格,然後是 DOM 選擇器以將事件附加到。
在這裡,我們添加了兩個事件監聽器:首先,一個 keyup
<input />
上的監聽器 第二個 click
<button></button>
上的監聽器 使用它的類名 say-hello
.對於我們的 keyup 事件,我們想要動態更新我們的 state.name
我們在輸入中輸入的值。為此,我們為函數分配了兩個參數,event
它表示來自 DOM 和 component
的 keyup 事件 (我們的組件實例)作為第二個。
在 component
例如,一個 .setState()
方法被定義,它接受一個包含我們想要在狀態上設置(或覆蓋)的屬性的對象。在這種情況下,我們要覆蓋 name
,將其設置為我們輸入的當前值。在這裡,我們使用純 JavaScript event.target.value
屬性來訪問 event.target
的值 等於觸發事件的 HTML 元素和 value
是的當前值 那個目標。
在我們的 click
中 事件處理程序,我們使用相同的參數結構,這次跳過 event
的使用 並訪問我們的 sayHello()
通過 component.methods
的方法 我們實例上的對象。這裡的想法是,每當我們點擊按鈕時,我們的 window.alert()
在 sayHello()
將被觸發,顯示最新的值(假設我們在輸入中輸入了一些內容,我們希望看到)。
在我們繼續之前,我們想對我們的 render()
進行一個小改動 函數的 HTML。請注意,我們添加了一個 <p></p>
其中嵌入了 state.name
的當前值 使用 JavaScript 插值表達式 ${state.name}
.您會注意到我們在 render()
上使用了 JavaScript 解構 函數,“拔掉”state
該對象的值。該對像是我們的組件實例。在這裡,我們使用解構來消除鍵入 component.state
的需要 而只是摘掉 state
直接。
這就是我們的組件定義。接下來,讓我們跳轉到服務器並連接一條路由,以便我們可以在瀏覽器中看到它。
定義路由並使用 res.render() 渲染組件
路由是在我們的應用程序中呈現某些內容的 URL 的技術名稱。要定義路由,我們需要轉到 index.server.js
中在應用程序服務器端運行的代碼 項目根目錄下的文件。
/index.server.js
import node from "@joystick.js/node";
import api from "./api";
node.app({
api,
routes: {
"/dashboard": (req, res) => {
res.render("ui/pages/dashboard/index.js");
},
"/": (req, res) => {
res.render("ui/pages/index/index.js", {
layout: "ui/layouts/app/index.js",
});
},
"*": (req, res) => {
res.render("ui/pages/error/index.js", {
layout: "ui/layouts/app/index.js",
props: {
statusCode: 404,
},
});
},
},
});
在 Joystick 應用程序中,@joystick.js/ui
的服務器端對應項 是 @joystick.js/node
.這個包負責設置我們的後端,特別是啟動一個 Express.js 實例並為我們的應用程序運行一個 HTTP 服務器(默認情況下,它在端口 2600 上啟動,但如果我們願意,可以自定義)。從那個包中,導出了一個我們在上面的代碼中作為 node
導入的對象 .在那個對像上,我們有一個函數 .app()
它負責設置我們的後端。
當我們調用它時,我們向它傳遞了幾個不同的選項,我們在本教程中關心的是 routes
它設置為我們要在應用程序中定義的路由對象。上面,我們預定義了兩條路線(這些路線自動包含在 joystick create
通過 @joystick.js/cli
):/
和 *
, 一個索引路由和一個包羅萬象的 404 路由 *
.
我們這里關心的是 /dashboard
我們添加的路線(我們選擇了這個名稱,因為它與我們定義的頁面的名稱相匹配,但我們可以稱之為 /pizza
如果我們願意)。
routes
上定義的路由 object 只不過是一個 Express.js 路由(例如,app.get()
)。這裡的區別純粹是句法和組織。為了清晰起見並保持代碼一致,我們將所有路由一起定義。就像普通的 Express.js 路由一樣,我們有一個回調函數,當我們的路由被訪問時調用(稱為“匹配”瀏覽器中的 URL)。
在我們的回調內部,我們調用了 Express res
上 Joystick 定義的特殊函數 ponse 對象,res.render()
, 傳入我們要渲染的頁面的路徑(Joystick 要求我們傳遞整個路徑,包括 .js
擴大)。在幕後,Joystick 會自動做一些事情:
- 將我們的組件呈現為 HTML(稱為 SSR 或服務器端呈現)以作為初始響應發送回瀏覽器。
- 找到對應的被
@joystick.js/cli
編譯的JS文件(意思是瀏覽器安全的代碼) 並嵌入到 SSR 的 HTML 中。 - 在
development
, Joystick 還包括一些實用功能和 HMR(熱模塊重新加載)腳本,用於在我們更改代碼時自動刷新瀏覽器。 - 在我們的組件樹中定位所有 CSS(我們的樹只有一個級別,但如果我們嵌套組件,這些組件也會被掃描)並將其嵌入到
<head></head>
我們的 HTML 標籤。
完成所有這些後,生成的 HTML 將返回到瀏覽器並為我們的用戶呈現。在我們頁面組件的瀏覽器安全 JavaScript 文件中,Joystick 自動包含在瀏覽器中“掛載”我們的組件所需的腳本。
這是一個稱為保濕的過程。我們最初發送一些幹的 , 服務器端為初始請求返回 HTML,然後在瀏覽器中加載一些 JavaScript 以 水合物 通過使其再次交互(即在瀏覽器中加載 JavaScript 的動態部分)來乾燥 HTML。
而已。如果我們打開瀏覽器並前往 http://localhost:2600/dashboard
,我們應該看到我們的警報對話框顯示,點擊確定後,看到我們的組件。嘗試在框中輸入您的姓名,然後單擊“打個招呼”按鈕以查看它的實際效果。
總結
在本教程中,我們學習瞭如何安裝操縱桿 CLI (@joystick.js/cli
),創建一個新應用,並使用 @joystick.js/ui
構建一個操縱桿組件 .我們了解了組件的不同特性,例如狀態、CSS、DOM 事件和方法,以及如何通過 res.render()
定義路由和渲染該組件 服務器上的方法。