新聞中心
1.你為什么要這樣做?
盡管我很喜歡 .NET 生態(tài)系統(tǒng),但有些事情,JavaScript 生態(tài)系統(tǒng)做得更好。其中之一就是任何事情都能找到一個(gè)庫,特別是涉及到網(wǎng)絡(luò)時(shí)。

以語法高亮為例。這可以直接用 C# 來做,但這不是一個(gè)特別流暢的體驗(yàn)。例如,TextMateSharp 項(xiàng)目為 TextMate 語法提供了一個(gè)解釋器。這些文件是 VS Code 用來為一種語言添加基本語法高亮的。然而,如果你想部署應(yīng)用程序,它包裝了一個(gè)本地依賴,這就增加了一些復(fù)雜性。
相比之下,JavaScript 有大量成熟的語法高亮庫。僅舉幾例,有 highlight.js、Prism.js(在本博客中使用)和 shiki.js。尤其是前兩個(gè),非常成熟,有多個(gè)插件和主題,而且有簡單的 API。
作為一個(gè) .NET 開發(fā)者,JavaScript 的明顯問題是,你需要學(xué)習(xí)并選擇進(jìn)入一個(gè)完整的獨(dú)立工具鏈,與 Node.js 和 NPM 一起工作。這似乎是一個(gè)很大的開銷,只是為了使用一個(gè)小功能。
因此,我們陷入了一個(gè)困境。我們要么走 C#(+ Native)路線,要么就得轉(zhuǎn)用 JavaScript。
或者......我們直接從我們的 .NET 應(yīng)用程序中調(diào)用 JavaScript 。
2.在 .NET 中運(yùn)行 JavaScript
一旦你決定在你的 .NET 代碼中運(yùn)行 JavaScript,你就會(huì)考慮幾個(gè)選擇。你可以借用 JavaScript 引擎,讓它為你運(yùn)行你的 JavaScript,但你并沒有真正解決問題,你仍然需要安裝 Node.js。
另一個(gè)選擇是在你的庫中直接捆綁 JavaScript 引擎。這并不像聽起來那么瘋狂,有幾個(gè) NuGet 包采用了這種方法,然后暴露出一個(gè) C# 層來與引擎進(jìn)行交互。
下面是你可以使用的一些包的列表。
Jering.Javascript.NodeJS
這個(gè)庫采取了上述的第一種方法。它不包括包中的 Node.js。相反,它為執(zhí)行 JavaScript 代碼提供了一個(gè) C# API,并調(diào)用了安裝在你機(jī)器上的 Node.js。這在你知道兩者都已安裝的環(huán)境中可能很有用,但它并沒有真正解決我想避免的問題。
ChakraCore
ChakraCore 是 Edge 轉(zhuǎn)為基于 Chromium 引擎之前最初使用的 JavaScript 引擎。根據(jù) GitHub 項(xiàng)目的介紹:
ChakraCore 是一個(gè)帶有 C 語言 API 的 JavaScript 引擎,你可以用它來為任何 C 語言或 C 語言兼容項(xiàng)目添加對(duì) JavaScript 的支持。它可以在 Linux macOS 和 Windows 上針對(duì) x64 處理器進(jìn)行編譯。而 x86 和 ARM 只適用于 Windows。
因此,ChakraCore 包括一個(gè)本地依賴,但由于 C# 可以 P/Invoke 到本地庫,這本身并不是一個(gè)問題。但它會(huì)帶來一些部署方面的挑戰(zhàn)。
ClearScript (V8)
Node.JS、Chromium、Chrome 和最新的 Edge 使用的都是 V8 JavaScript 引擎。Microsoft.ClearScript 包為該庫提供了一個(gè)封裝,為調(diào)用 V8 庫提供了一個(gè) C# 接口。就像 ChakraCore 一樣,V8 引擎本身是一個(gè)本地依賴。ClearScript 庫負(fù)責(zé) P/Invoke 調(diào)用,提供了一個(gè)很好的 C# API,但你仍然要確保你在目標(biāo)平臺(tái)上部署了正確的本地庫。
Jint
Jint 很有意思,因?yàn)樗且粋€(gè)完全在 .NET 中運(yùn)行的 JavaScript 解釋器,沒有任何本地的依賴!它完全支持 ECMAScript 5.1 (ES5),并支持 .NET Standard 2.0,所以你可以在你的所有項(xiàng)目中使用它!
Jurassic
Jurassic 是另一個(gè) JavaScript 引擎的 .NET 實(shí)現(xiàn),類似于 Jint。也和 Jint 類似,它支持所有的 ES5,而且似乎也部分支持 ES6。與 Jint 不同的是,Jurassic 不是一個(gè)解釋器,它將 JavaScript 編譯成 IL,這使得它的速度非??欤宜鼪]有本地的依賴性。
那么,在所有這些選擇中,你應(yīng)該選擇哪一個(gè)?
3.JavaScriptEngineSwitcher:當(dāng)一個(gè) JS 引擎不夠用的時(shí)候
還有一個(gè)偉大的項(xiàng)目可以讓你簡單地嘗試上面其中的任何一個(gè)庫。雖然所有的庫都允許你運(yùn)行 JavaScript,但它們都有略微不同的 C# API 來與之交互。這可能會(huì)使比較它們變得有點(diǎn)痛苦,因?yàn)槟惚仨殲槊總€(gè)庫學(xué)習(xí)不同的 API。
JavaScriptEngineSwitcher 這個(gè)庫為我提到的所有庫和更多的庫提供了封裝:
- Jering.Javascript.NodeJS
- ChakraCore
- Microsoft ClearScript.V8
- Jint
- Jurassic
- MSIE JavaScript Engine for .NET
- NiL.JS
- VroomJs
每個(gè)庫都在一個(gè)單獨(dú)的包中(有本地依賴關(guān)系的引擎需要一個(gè)額外的本地包),還有一個(gè) Core 包,它提供通用的 API。即使你不打算切換 JS 引擎,我也傾向于盡可能地使用 JavaScriptEngineSwitcher 封裝庫,這樣你就不必在以后需要切換引擎時(shí)弄清楚一個(gè)新的 API 了。
在 .NET 項(xiàng)目中改變使用的 JavaScript 引擎在我看來是完全可能的。例如,我開始使用 Jint,但當(dāng)我需要執(zhí)行更大的腳本時(shí),我遇到了性能問題,于是換成了 Jurassic。JavaScriptEngineSwitcher 讓這一切變得很簡單,只需在我的項(xiàng)目中添加一個(gè)新的包并改變一些初始化代碼即可。
我最近才發(fā)現(xiàn) JavaScriptEngineSwitcher 這個(gè)庫,但最新版本的下載量已接近一百萬,它被用于 .NET 靜態(tài)網(wǎng)站建設(shè)者 Statiq 中。在這篇文章的最后部分,我將舉一個(gè)最基本用法的例子。
4.案例:用 JavaScriptEngineSwitcher 在控制臺(tái)應(yīng)用中運(yùn)行 prism.js
在這篇文章的開頭,我討論了一個(gè)特定的場景--代碼塊的語法高亮。在本節(jié)中,我將展示如何使用 prism.js 高亮一小段代碼,并在一個(gè)控制臺(tái)應(yīng)用程序中運(yùn)行。
開始之前請(qǐng)?zhí)砑?JavaScriptEngineSwitcher.Jurassic NuGet 包的引用。
dotnet add package JavaScriptEngineSwitcher.Jurassic
接下來,下載你想運(yùn)行的 JavaScript 文件。例如,我從 Prism.js 的官網(wǎng)下載了 prism.js 文件,并將 C# 添加到默認(rèn)支持高亮的語言集。在把文件放到項(xiàng)目文件夾的根目錄后,我把文件更新為嵌入資源。你可以在你的 IDE 中操作,也可以手動(dòng)編輯項(xiàng)目文件:
Exe
net6.0
enable
enable
剩下的就是編寫代碼,在我們的程序中運(yùn)行腳本。下面的代碼段設(shè)置了 JavaScript 引擎,從程序集中加載嵌入的 prism.js 庫,并執(zhí)行它。
using JavaScriptEngineSwitcher.Jurassic;
// Create an instance of the JavaScript engine
IJsEngine engine = new JurassicJsEngine();
// Execute the embedded resource called JsInDotnet.prism.js from the provided assembly
engine.ExecuteResource("JsInDotnet.prism.js", typeof(Program).Assembly);
現(xiàn)在我們可以在同一個(gè)上下文中運(yùn)行我們自己的 JavaScript 命令。我們可以通過使用 SetVariableName、Execute 和 Evaluate 從 C# 向 JavaScript 引擎?zhèn)鬟f數(shù)值:
// This is the code we want to highlight
string code = @"
using System;
public class Test : ITest
{
public int ID { get; set; }
public string Name { get; set; }
}";
// set the JavaScript variable called "input" to the value of the c# variable "code"
engine.SetVariableValue("input", code);
// set the JavaScript variable called "lang" to the string "csharp"
engine.SetVariableValue("lang", "csharp");
// run the Prism.highlight() function, and set the result to the "highlighed" variable
engine.Execute($"highlighted = Prism.highlight(input, Prism.languages.csharp, lang)");
// "extract the value of "highlighted" from JavaScript to C#
string result = engine.Evaluate("highlighted");
Console.WriteLine(result);
當(dāng)你把它們放在一起運(yùn)行時(shí),高亮的代碼會(huì)被打印到控制臺(tái):
using System;
public class Test : ITest
{
public int ID { get; set; }
public string Name { get; set; }
}
渲染后,看起來像這樣:
我對(duì)整個(gè)過程的簡單程度感到驚訝。啟動(dòng)一個(gè) JavaScript 引擎,加載 prism.js 文件,并執(zhí)行我們的自定義代碼是如此順利。這是我面臨問題的完美解決方案。
我顯然不建議所有的應(yīng)用程序都這樣做。如果你需要運(yùn)行大量的 JavaScript,那么直接使用 Node.js 生態(tài)系統(tǒng)及工具可能更容易。但如果你只是想利用一個(gè)小型的、獨(dú)立的工具(如 prims.js),那么這是一個(gè)不錯(cuò)的選擇。
5.總結(jié)
在這篇文章中,我展示了如何使用 JavaScriptEngineSwitcher NuGet 包來在 .NET 應(yīng)用程序中運(yùn)行 JavaScript。這個(gè)包為許多不同的 JavaScript 引擎提供了一個(gè)一致的接口。其中一些引擎(如 Chakra Core 和 V8)需依賴一個(gè)本地組件,而其他引擎(如 Jint 和 Jurassic)只使用托管代碼。最后,我展示了你如何使用 JavaScriptEngineSwitcher 在 .NET 應(yīng)用程序內(nèi)部運(yùn)行 Prims.js 代碼高亮庫。
原文:bit.ly/38awq7W
作者:Andrew Lock
翻譯:精致碼農(nóng)
標(biāo)題名稱:在.NET應(yīng)用程序中運(yùn)行JavaScript,你會(huì)了嗎?
網(wǎng)頁網(wǎng)址:http://m.5511xx.com/article/coipgej.html


咨詢
建站咨詢
