由于 Blazor-WebAssembly 是在瀏覽器中運(yùn)行的,通常不需要執(zhí)行服務(wù)器代碼,只要有個(gè)“窩”能托管并提供相關(guān)文件的下載即可。所以,當(dāng)你有一個(gè)現(xiàn)成的 Blazor wasm 項(xiàng)目,沒(méi)必要用其他語(yǔ)言重寫(xiě),或者你不想用 ASP.NET Core 來(lái)托管(有些大材小用了),就可以試試用 node.js 來(lái)托管。
要實(shí)現(xiàn)這個(gè)不需要掌握什么新的知識(shí),所以咱們直接開(kāi)工干活。
首先,咱們做好 Blazor wasm 應(yīng)用的開(kāi)發(fā)。
dotnet new blazorwasm-empty -n Demo -o .
blazorwasm-empty 模板創(chuàng)建的項(xiàng)目只帶一些基本代碼和 Hello World,沒(méi)有演示代碼——無(wú)Counter無(wú)假天氣預(yù)報(bào)。
然后,Program.cs 文件也可以精簡(jiǎn)一下。
var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add<App>("#app"); await builder.Build().RunAsync();
#app 是CSS篩選器,即選擇 id 為 app 的元素來(lái)呈現(xiàn) Razor 組件。這個(gè)相信各位都懂。
為了更好地演示,咱們把 Index 組件改一下,加一點(diǎn)交互功能,以便后面可以驗(yàn)證 Blazor 是否正常啟動(dòng)。
@page "/" <h1>Hello, world!</h1> <button @onclick="ClickMe">點(diǎn)這里中大獎(jiǎng)</button> <div>@Message</div> @code{ private string? Message{get;set;} void ClickMe() { int xx = Random.Shared.Next(100, 700); Message = $"恭喜你獲得{xx}萬(wàn)假鈔!"; } }
這個(gè)不復(fù)雜,就是點(diǎn)擊一下按鈕,然后生成個(gè)隨機(jī)整數(shù),并修改 Message 屬性。處理 click 事件要注意加上 @,如果是 onclick 你只能用 js 去寫(xiě),要想用 C# 來(lái)寫(xiě)代碼,就得用 @onclick。
接著,試執(zhí)行一下,保證沒(méi)有錯(cuò)誤,能正常運(yùn)行。
?
現(xiàn)在,你打開(kāi)?\bin\Debug\net7.0\wwwroot 目錄,里面你看到有個(gè) _framework 目錄,這個(gè)目錄就是我們要的。不過(guò),這個(gè)體積太大,不適合。咱們將項(xiàng)目發(fā)布一下,這樣體積會(huì)變小很多。
我們不需要 wwwroot 目錄下的東東,把整個(gè)目錄“咔嚓”掉(這里指的是項(xiàng)目中的 wwwroot 目錄,不是輸出目錄的)。為了防止重新生成時(shí)有文件錯(cuò)誤(一般不會(huì)),可以把 obj 和 bin 目錄也刪除。
執(zhí)行發(fā)布命令。
dotnet publish -c PublishRelease
-c 參數(shù)也可以用 Release,差別不大。
另外新建一個(gè)目錄,路徑隨便,不要有非英文字符(防止出錯(cuò)),比如這里我命名為 Server。把剛才發(fā)布的整個(gè) _framework 目錄復(fù)制到 Server 目錄中。現(xiàn)在你可以關(guān)閉 Blazor 項(xiàng)目了,沒(méi)它什么事了。
在 Server 目錄下新建一個(gè)文件,叫 index.html。
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"/> <title>高級(jí)示例</title> </head> <body> <div id="app">正在加載……</div> <script src="_framework/blazor.webassembly.js"></script> </body> </html>
這里注意兩處:
1、要有一個(gè) id 為 app 的元素,它用來(lái)呈現(xiàn)組件。
2、<script> 要引用 blazor.webassembly.js 文件。
?
在 Server 目錄下再新建一個(gè)文件,名為 app.js。這個(gè)用來(lái)寫(xiě)服務(wù)器主程序(js 代碼)。
const url = require("node:url"); const path = require("node:path"); const http = require("node:http"); const fs = require("node:fs"); // 主機(jī) const host = 'localhost'; // 端口 const port = 6748; // MIME 映射 function getFileMap(fileExt) { switch(fileExt) { case ".js": case ".mjs": return "text/javascript"; case ".json": return "application/json"; case ".htm": case ".html": return "text/html"; case ".css": return "text/css"; case ".jpg": case ".jpeg": return "image/jpeg"; // 其他的自己看情況添加 default: // 其余的如.dll、.gz等,就是二進(jìn)制文件 return "application/octet-stream"; } } http.createServer((request, response)=> { // 獲取請(qǐng)求路徑 let reqPath = url.parse(request.url).pathname; // 去掉路徑開(kāi)頭的“/” let fileName = reqPath.substring(1); // 如果空白,默認(rèn)文件名 index.html if(fileName.length === 0) { fileName = "index.html"; } // 讀取文件內(nèi)容 fs.readFile(fileName, (err, data)=>{ // 如果出錯(cuò) if(err) { // 直接回個(gè)404 response.writeHead(404, {"Content-Type": "text/html"}); } else { // 獲取文件擴(kuò)展名,以決定MIME類型 let ext = path.extname(fileName); let mimeType = getFileMap(ext.toLowerCase()); // 發(fā)送HTTP頭 response.writeHead(200, {"Content-Type": mimeType}); // 發(fā)送正文 response.write(data); } // 這一行必須,結(jié)束響應(yīng)消息 response.end(); }); }) .listen(port, host); console.log(`服務(wù)器:${host}:${port}`);
運(yùn)行它,執(zhí)行:node app.js。接著在瀏覽器中輸入地址:http://localhost:6748。再驗(yàn)證 Blazor 應(yīng)用程序是否成功啟動(dòng)。
?
如果看到隨機(jī)數(shù)能正確生成,說(shuō)明運(yùn)行成功了。
?