常見問題

本常見問題解答包含在 IRC 和郵件列表上提出的許多問題的答案。

如何編譯程式碼?

請參閱 Emscripten 教學emcc

為什麼我建置基本程式碼和測試時會出現錯誤?

Emscripten 測試套件 中的所有測試都已知在我們的測試基礎架構上建置並通過,因此如果您在本地看到失敗,則很可能是您的環境存在問題。(很少有臨時中斷,但永遠不會在已標記的發布版本上。)

首先呼叫 emcc --check,它會執行基本健全性檢查並印出有用的環境資訊。如果這沒有幫助,請按照 驗證 Emscripten 開發環境 中的說明進行操作。

您可能還想再次瀏覽 Emscripten 教學,因為它會隨著 Emscripten 的變更而更新。

另請確保您已具備執行 Emscripten 所需的要求,如 SDK 區段中所指定,包括依賴項的足夠新版本。

我嘗試了某個東西:為什麼它不起作用?

一些可能有助於找出問題的通用步驟

  • 看看是否在沒有最佳化的情況下發生問題(-O0,或未指定任何最佳化層級)。在沒有最佳化的情況下,emscripten 會在編譯和執行階段啟用許多斷言,這可能會捕獲問題並顯示錯誤訊息,其中包含有關如何修復它的建議。

  • 在本網站上搜尋文件。

  • 檢查 Emscripten 測試套件 中是否有失敗功能的測試(在 test/ 中執行 grep -r)。它們都應通過(只有極少數例外),因此它們提供了如何使用各種選項和程式碼的具體「已知良好」範例。

我是否需要變更我的建置系統才能使用 Emscripten?

在大多數情況下,您將能夠將專案目前的建置系統與 Emscripten 一起使用。請參閱 建置專案

為什麼程式碼編譯速度很慢?

Emscripten 做出了一些權衡,使得產生的程式碼更快、更小,但代價是連結時間更長。例如,我們使用 -flto(連結時間最佳化)建置標準程式庫的部分內容,這會啟用一些額外的最佳化,但可能需要更長的時間才能建置。我們也會(在最佳化的建置中)即使沒有 LTO,也會在整個輸出上執行 Binaryen 最佳化工具。

注意

您可以透過在環境中編譯 EMCC_DEBUG=1,然後檢閱除錯日誌(預設在 /tmp/emscripten_temp 中)來判斷哪個編譯步驟花費的時間最長。請注意,在除錯模式下編譯比正常情況下需要更長的時間,因為我們會在磁碟上印出許多中間步驟,因此它適用於除錯,但不適用於實際編譯。

改善建置時間的主要提示是

  • 使用 -O0 進行快速疊代建置。您仍然可以使用較高的最佳化層級進行編譯,但在連結期間指定 -O0 將使連結步驟更快。

  • 在具有更多核心的機器上編譯

    • 對於編譯您的原始檔,請使用並行建置系統(例如,在 make 中,您可以執行類似 make -j8 的操作來使用 8 個核心執行)。

    • 對於連結步驟,Emscripten 可以並行執行一些最佳化(具體來說,用於 Wasm 的 Binaryen 最佳化和我們的 JavaScript 最佳化)。增加核心數量會產生幾乎線性的改善。Emscripten 將在有更多核心可用時自動使用更多核心,但您可以使用環境中的 EMCC_CORES=N 來控制它(如果您有很多核心但相對較少的記憶體,這會很有用)。

為什麼我的程式碼執行速度很慢?

請確保透過使用 -O2 進行建置來最佳化程式碼(可以使用更多積極的最佳化,但代價是編譯時間顯著增加)。

為什麼我編譯的程式碼很大?

請確保使用 -O3-Os 進行建置,以便程式碼完全最佳化和最小化。您應該在您的網路伺服器上使用 Closure Compiler、gzip 壓縮等,請參閱最佳化程式碼中關於程式碼大小的章節

為什麼在另一台電腦上可以運作的編譯程式碼會給我錯誤?

請確保您使用的是 Emscripten 捆綁的系統標頭。預設情況下,使用 emcc 會這樣做,但如果您使用本地系統標頭搭配 emcc,則可能會發生問題。

如何減少啟動時間?

請確保您正在執行最佳化的建置(較小的建置啟動速度更快)。

網路延遲也是啟動時間的一個可能因素。請考慮將檔案載入程式碼放在與產生的程式碼不同的腳本元素中,以便瀏覽器可以與啟動程式碼庫並行開始網路下載(執行檔案打包器並將檔案載入程式碼放在一個腳本元素中,然後將產生的程式碼庫放在後來的腳本元素中)。

如何執行本地網路伺服器進行測試/為什麼我的程式在「正在下載...」或「正在準備...」中停滯不前?

當使用 file:// URL 載入頁面時可能會發生該錯誤,該 URL 在某些瀏覽器中有效,但在其他瀏覽器中無效。相反,最好使用本地網路伺服器。例如,Python 有一個內建的伺服器,Python 3 中為 python -m http.server,Python 2 中為 python -m SimpleHTTPServer。執行此操作後,您可以訪問 https://127.0.0.1:8000/。您也可以使用 emrun FILENAME.html(它會為您執行一個 python 網路伺服器)。

當進行快速的本地測試時,除了本地網頁伺服器之外,另一種選擇是使用 -sSINGLE_FILE 將所有內容打包成單一檔案(這樣就不會向 file:// URL 發出 XHR 請求)。

否則,要偵錯此問題,請查看網頁本身或瀏覽器開發人員工具(網頁控制台和網路標籤)或您的網頁伺服器日誌中報告的錯誤。

為什麼在連結時會出現 machine type must be wasm32unknown file type 的錯誤?

這表示其中一個或多個連結器輸入檔案不是由 Emscripten 建置的(或者更具體地說,不是為正確的目標架構建置的)。

通常,有問題的檔案會是為本機建置的 ELF 檔案或 Mach-O 檔案。您可以執行 file 命令列工具來查看它們實際包含的內容。

常見的問題是:

  • 嘗試連結針對主機系統建置的程式庫。例如,如果您的連結命令中有類似 -L/usr/lib 的內容,則幾乎總是會導致這些錯誤,因為那些系統目錄中存在的程式庫幾乎可以肯定不是使用/為 Emscripten 建置的。解決方案是使用 Emscripten 建置您所依賴的所有程式庫,並且永遠不要使用主機程式庫。

  • 您專案中的某些程式庫或物件檔案是使用主機編譯器而不是 emscripten 編譯器建置的。如果您使用 autoconf 或 cmake,請確保使用 emconfigure/emmake 包裝器,請參閱 建置專案

  • 來自舊後端的 LLVM IR,如果您在 1.39.0 之前的版本(預設使用舊後端)建置專案,並且現在正在執行增量重建。要解決此問題,請從頭開始完整重建專案的所有檔案,包括程式庫(如果您有來自第三方的預建程式庫,則通常會發生此錯誤;這些程式庫也必須使用新後端重新編譯)。

為什麼我的程式碼編譯失敗,並出現有關內嵌組譯(或 {"text":"asm"})的錯誤訊息?

Emscripten 無法編譯內嵌組譯碼(除非該組譯碼是專門針對 WebAssembly 編寫的)。

您需要找到使用內嵌組譯碼的地方,並停用它或將其替換為與平台無關的程式碼。

為什麼我的 HTML 應用程式會停滯?

瀏覽器事件模型使用協作式多工處理 — 每個事件都有一個執行的「回合」,然後必須將控制權返回給瀏覽器事件迴圈,以便可以處理其他事件。HTML 頁面停滯的常見原因是 JavaScript 沒有完成並且沒有將控制權返回給瀏覽器。

圖形 C++ 應用程式通常有一個無限的主迴圈,在其中執行事件處理、處理和渲染,然後延遲以保持正確的畫面播放速率(SDL 應用程式中的 SDL_DELAY)。由於主迴圈不會完成(是無限的),因此它無法將控制權返回給瀏覽器,並且應用程式將停滯。

使用無限主迴圈的應用程式應重新編碼,以將迴圈單次迭代的動作放入單個「有限」函數中。在本機建置中,此函數可以像以前一樣在無限迴圈中執行。在 Emscripten 建置中,它會設定為主迴圈函數,並且將由瀏覽器以指定的頻率呼叫。

有關此主題的更多資訊,請參閱Emscripten 執行環境

如何執行事件迴圈?

要重複執行 C 函數,請使用 emscripten_set_main_loop()(這在Emscripten 執行環境中有討論)。emscripten.h 中的相關函數也很有用,可讓您加入會封鎖主迴圈的事件等等。

要回應瀏覽器事件,請以正常方式使用 SDL API。在 SDL 測試中 (在 test/runner.py 中搜尋 SDL) 有範例。

另請參閱:為什麼我的 HTML 應用程式會停滯?

為什麼我的 SDL 應用程式無法運作?

如需運作範例,請參閱 SDL 自動測試:test/runner.py browser

音訊播放有哪些選項?

Emscripten 部分支援 SDL1 和 2 音訊以及 OpenAL。

要使用 SDL1 音訊,請將其包含為 #include <SDL/SDL_mixer.h>。您可以將其與 SDL1、SDL2 或另一個用於平台整合的程式庫一起使用。

要使用 SDL2 音訊,請將其包含為 #include <SDL2/SDL_mixer.h> 並使用 -sUSE_SDL_MIXER=2。格式支援目前僅限於 OGG、WAV、MID 和 MOD。

我編譯的程式如何存取檔案?

Emscripten 使用虛擬檔案系統,可以預先載入資料或連結到 URL 以進行延遲載入。如需更多詳細資訊,請參閱檔案系統概述

為什麼我的程式碼無法存取同一目錄中的檔案?

瀏覽器中執行的 Emscripten 產生程式碼無法存取本機檔案系統中的檔案。相反地,您可以使用 預先載入嵌入 來解決缺乏同步檔案 IO 的問題。如需更多資訊,請參閱 檔案系統概述

可以允許存取在 node.js 中執行的程式碼的本機檔案系統,請使用 NODEFS 檔案系統選項。

我如何知道頁面已完全載入,並且可以安全地呼叫編譯的函數?

(如果您看到錯誤訊息,例如 native function `x` called before runtime initialization,這是 ASSERTIONS 建置中啟用的檢查,您可能需要此答案。)

如果函數依賴可能不存在的檔案,則在頁面完全載入之前呼叫已編譯的函數可能會導致錯誤(例如,預先載入的檔案會非同步載入,因此,如果您只是在 --post-js 中放置一些呼叫已編譯程式碼的 JS,則該程式碼會在組合的 JS 檔案末尾同步呼叫,這可能會在非同步事件發生之前,這是不好的做法)。

找出載入何時完成的最簡單方法是加入 main() 函數,並在其中呼叫 JavaScript 函數以通知您的程式碼載入已完成。

注意

當啟動完成後,會呼叫 main() 函數,表示呼叫任何已編譯的方法都是安全的。

例如,如果 allReady() 是您希望在一切準備就緒時呼叫的 JavaScript 函數,您可以執行

#include <emscripten.h>

int main() {
  EM_ASM( allReady() );
}

另一個選項是定義 onRuntimeInitialized 函數,

Module['onRuntimeInitialized'] = function() { ... };

當執行階段準備就緒且可以呼叫已編譯的程式碼時,就會呼叫該方法。實際上,這與呼叫 main() 的時間完全相同,因此 onRuntimeInitialized 不會讓您執行任何新操作,但您可以從執行階段的 JavaScript 中以彈性的方式設定它。

以下是如何使用它的範例

<script type="text/javascript">
  var Module = {
    onRuntimeInitialized: function() {
      Module._foobar(); // foobar was exported
    }
  };
</script>
<script type="text/javascript" src="my_project.js"></script>

關鍵在於,在載入包含 emscripten 輸出的指令碼(在此範例中為 my_project.js)之前,Module 存在,並且具有屬性 onRuntimeInitialized

另一個選項是使用 MODULARIZE 選項,使用 -sMODULARIZE。這會將所有產生的 JavaScript 放入工廠函數,您可以使用該函數建立模組的執行個體。工廠函數會傳回一個 Promise,該 Promise 會使用模組執行個體解析。在可以安全地呼叫已編譯的程式碼之後,即在下載並具現化已編譯的程式碼之後,就會解析 Promise。例如,如果您使用 -sMODULARIZE -s 'EXPORT_NAME="createMyModule"' 建置,則可以執行以下操作

createMyModule(/* optional default settings */).then(function(Module) {
  // this is reached when everything is ready, and you can call methods on Module
});

請注意,在 MODULARIZE 模式下,我們不會尋找全域的 Module 物件來取得預設值。預設值必須作為參數傳遞給工廠函式。(詳情請參閱 settings.js)

「退出執行階段」是什麼意思?為什麼 atexit() 不會執行?

(如果您看到類似 atexit() called, but EXIT_RUNTIME is not setstdio streams had content in them that was not flushed. you should set EXIT_RUNTIME to 1 的錯誤訊息,您可能需要這個答案。)

預設情況下,Emscripten 設定 EXIT_RUNTIME=0,這表示我們不會包含關閉執行階段的程式碼。這表示當 main() 退出時,我們不會刷新 stdio 串流,也不會呼叫全域 C++ 物件的解構子,或呼叫 atexit 回呼。這讓我們能夠預設發出更小的程式碼,而且這通常是您在網路上想要的:即使 main() 已經退出,您可能還有一些稍後想要執行的非同步事件。

但在某些情況下,您可能想要更像「命令列」的體驗,也就是當 main() 退出時,我們會關閉執行階段。您可以使用 -sEXIT_RUNTIME 進行建置,然後我們將呼叫 atexits 等等。當您使用 ASSERTIONS 進行建置時,當您需要這個選項時,應該會收到警告。例如,如果您的程式在沒有換行符號的情況下印出某些內容,

#include <stdio.h>

int main() {
  printf("hello"); // note no newline
}

如果我們不關閉執行階段並刷新 stdio 串流,則不會印出「hello」。在 ASSERTIONS 建置中,您會收到通知,指出 stdio streams had content in them that was not flushed. you should set EXIT_RUNTIME to 1

為什麼當我編譯成 WebAssembly 時,我的 C/C++ 原始碼中的函式會消失?

Emscripten 會刪除未從已編譯程式碼呼叫的函式中的無用程式碼。雖然這樣可以最小化程式碼大小,但它可能會移除您打算自行呼叫的函式(在已編譯程式碼之外)。

為了確保可以從一般的 JavaScript 呼叫 C 函式,必須使用 emcc 命令列將其新增至 EXPORTED_FUNCTIONS。例如,為了避免移除/重新命名函式 my_func()main(),請使用以下方式執行 emcc

emcc -sEXPORTED_FUNCTIONS=_main,_my_func  ...

注意

如果您的程式碼中有 main() 函式,則 _main 應在匯出清單中,如同範例所示。否則,它會被當作無用程式碼移除;預設沒有特殊邏輯可以讓 main() 保持存活。

注意

EXPORTED_FUNCTIONS 會影響編譯成 JavaScript。如果您先編譯成物件檔,然後再將物件編譯成 JavaScript,則需要在第二個命令中加入該選項。

如果您的函式在其他函式中使用,LLVM 可能會將其內嵌,並且它不會在 JavaScript 中顯示為唯一的函式。若要防止內嵌,請使用 EMSCRIPTEN_KEEPALIVE 來定義函式。

void EMSCRIPTEN_KEEPALIVE yourCfunc() {..}

EMSCRIPTEN_KEEPALIVE 也會匯出函式,如同在 EXPORTED_FUNCTIONS 中一樣。

注意

  • 所有未透過 EXPORTED_FUNCTIONSEMSCRIPTEN_KEEPALIVE 保持存活的函式都可能會被移除。請確保您使用其中一種或兩種方法讓您需要的項目保持存活。

  • 匯出的函式需要是 C 函式(以避免 C++ 名稱修飾)。

  • 如果您不想明確追蹤要匯出的函式,以及這些匯出沒有變更時,使用 EMSCRIPTEN_KEEPALIVE 修飾程式碼會很有用。它不一定適合從其他程式庫匯出函式 — 例如,修飾和重新編譯 C 標準程式庫的原始碼並不是一個好主意。如果您以多種方式建置相同的原始碼並變更匯出的內容,那麼在命令列上管理匯出會比較容易。

  • 使用 -sLINKABLE 執行 emcc 也會停用連結時最佳化和無用程式碼刪除。不建議這樣做,因為它會使程式碼變大且較少最佳化。

程式碼遺失的另一個可能原因是 .a 檔案的連結不正確。.a 檔案只會連結命令列上先前檔案所需的內部物件檔,因此檔案的順序很重要,這可能會令人驚訝。如果您要連結 .a 檔案,請確保它們位於檔案清單的末尾,並且它們彼此之間的順序正確。或者,您可以直接在專案中使用 .so 檔案。

提示

將環境設定為 EMCC_DEBUG=1 來進行編譯會很有用(在 Linux 上是 EMCC_DEBUG=1 emcc ...,在 Windows 上是 set EMCC_DEBUG=1)。這會將編譯步驟分開並將其儲存在 /tmp/emscripten_temp 中。然後您可以查看程式碼在什麼階段消失(您需要在位元碼階段執行 llvm-dis 來讀取它們,或執行 llvm-nm 等)。

為什麼當我使用 closure 建置時,檔案系統 API 無法使用?

Closure Compiler 會縮減檔案伺服器 API 程式碼。使用檔案系統的程式碼必須使用 emcc 的 --pre-js 選項**與**檔案系統 API 一起最佳化。

為什麼當我使用 -O2 --closure 1 時,我的程式碼會損壞並產生奇怪的錯誤?

Closure Compiler 會縮減變數名稱,這會導致產生非常短的變數名稱,例如 ijxa 等等。如果其他程式碼在全域範圍內宣告了具有相同名稱的變數,可能會導致嚴重的問題。

如果您的程式碼可以使用設定的 -O2 和未設定的 --closure 成功執行,則這很可能是原因。

一個解決方案是停止在全域範圍內使用小的變數名稱(通常這是一個錯誤 — 在指派變數時忘記使用 var)。

另一個替代方案是將產生的程式碼(或您的其他程式碼)包裝在閉包中,如下所示:

var CompiledModule = (function() {
  .. GENERATED CODE ..
  return Module;
  })();

為什麼我會收到 TypeError: Module.someThing is not a function

Module 物件將包含匯出的方法。為了讓某些內容出現在那裡,您應該將其新增至已編譯程式碼的 EXPORTED_FUNCTIONS,或執行階段方法(例如 getValue)的 EXPORTED_RUNTIME_METHODS。例如,

emcc -sEXPORTED_FUNCTIONS=_main,_my_func ...

會匯出 C 方法 my_func(在此範例中,還有 main)。和

emcc -sEXPORTED_RUNTIME_METHODS=ccall ...

會匯出 ccall。在這兩種情況下,您都可以存取 Module 物件上匯出的函式。

注意

如果編譯器可以看到它們被使用,您可以直接使用執行階段方法,而無需匯出它們。例如,您可以透過直接呼叫,在 EM_ASM 程式碼或 --pre-js 中使用 getValue。最佳化工具不會移除該 JS 執行階段方法,因為它會看到它被使用。如果您想從編譯器看不到的 JS 程式碼外部呼叫該方法,則只需要使用 Module.getValue,然後您需要匯出它。

注意

Emscripten 過去預設會匯出許多執行階段方法。這增加了程式碼大小,因此我們變更了該預設值。如果您依賴於過去匯出的內容,您應該會在未最佳化的建置中或啟用了 ASSERTIONS 的建置中看到指向解決方案的警告,我們希望這能將任何惱人程度降到最低。詳情請參閱 ChangeLog.md

為什麼 Runtime 不再存在?為什麼我嘗試存取 Runtime.someThing 時會收到錯誤?

1.37.27 包含重構以移除 Runtime 物件。這使得產生的程式碼更有效率且更精簡,但如果您使用 Runtime.* API,則需要進行小的變更。您只需要移除 Runtime. 前綴,因為這些函式現在是頂層範圍中的簡單函式(在 -O0 中或啟用了判言的建置中的錯誤訊息會建議這一點)。換句話說,請將

x = Runtime.stackAlloc(10);

取代為

x = stackAlloc(10);

注意

上述方法適用於 --pre-js 或 JS 程式庫中的程式碼,也就是與 emscripten 輸出一起編譯的程式碼。如果您嘗試從已編譯程式碼外部存取 Runtime.* 方法,則必須匯出該函式(使用 EXPORTED_RUNTIME_METHODS),並在 Module 物件上使用它,請參閱 該常見問題解答條目

為什麼當我使用 -s 選項時,會得到 NameError 評估 "-s" 之後的內容時發生問題

如果您在 -s 參數中使用非簡單的字串,並且在取得正確的 shell 引號/跳脫字元時遇到問題,則可能會發生這種情況。

使用更簡單的列表形式(不帶引號、空格或方括號)有時可以幫助解決問題。

emcc a.c -sEXPORTED_RUNTIME_METHODS=foo,bar

也可以使用回應檔案,也就是:

emcc a.c -sEXPORTED_RUNTIME_METHODS=@extra.txt

其中 extra.txt 是一個純文字檔案,其中在不同的行包含 foobar

我如何在 CMake 專案中指定 -s 選項?

像這樣簡單的事情應該可以在 CMakeLists.txt 檔案中直接運作。

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -sUSE_SDL=2")

然而,某些 -s 選項可能需要引號,或者當使用 target_link_options 之類的功能時,-s 和下一個參數之間的空格可能會使 CMake 感到困惑。為了避免這些問題,您可以使用 -sX=Y 表示法,也就是說,不使用空格、方括號或引號。

# same as before but no space after -s
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -sUSE_SDL=2")
# example of target_link_options with a list of names
target_link_options(example PRIVATE "-sEXPORTED_FUNCTIONS=_main")

另請注意,即使 _main 是一個字串名稱,也不需要加上引號(emcc 知道 EXPORTED_FUNCTIONS 的參數是一個字串列表,因此它接受 aa,b 等)。

為什麼我在 file=.. 或以 f'..' 開頭的字串上得到 Python SyntaxError: invalid syntax

Emscripten 需要一個足夠新的 Python 版本。較舊的 Python 版本,例如 2.*,預設情況下將不支援 print 語法,因此它會在諸如 print('..', file=..) 之類的語法上產生錯誤。較舊的 3.* Python 可能不支援 f 字串,其看起來像 f'..'

請確保您已安裝足夠新的 Python 版本,如 SDK 指示中所指定,並確保 emcc 使用該版本(例如,通過使用該 Python 執行 emcc.py)。

在 CI 環境中,如果預設版本不夠新,您可能需要指定要使用的 Python 版本。例如,在 Netlify 上,您可以使用 PYTHON_VERSION

為什麼在優化時會得到堆疊大小錯誤:RangeError: Maximum call stack size exceeded 或類似錯誤?

您可能需要增加 node.js 的堆疊大小。

在 Linux 和 Mac macOS 上,您只需在 Emscripten 編譯器設定檔 (.emscripten) 中執行 NODE_JS = ['/path/to/node', '--stack_size=8192'] 即可。在 Windows 上(對於低於 v19 的 node 版本),您還需要 --max-stack-size=8192,並執行 editbin /stack:33554432 node.exe

我如何將 int64_t 和 uint64_t 值從 js 傳遞到 Wasm 函式?

如果您使用 -sWASM_BIGINT 標誌進行建置,則 int64_tuint64_t 將在 JS 中表示為 bigint 值。如果不使用 -sWASM_BIGINT 標誌,這些值將在 JS 中表示為 number,這無法表示 int64,因此會發生以下情況:在導出的函式中(您可以從 JS 中呼叫),我們會通過將 i64 參數轉換為兩個 i32(低位和高位),並將 i64 回傳值變成 i32 來「合法化」這些類型,並且您可以通過呼叫名為 getTempRet0 的輔助函式來存取高位。

我可以在一個網頁上使用多個 Emscripten 編譯的程式嗎?

Emscripten 的預設輸出只是一些程式碼。當放在 script 標籤中時,這意味著程式碼位於全域範圍中。因此,同一個頁面上的多個此類模組無法運作。

但是通過將每個模組放在函式範圍中,可以避免該問題。Emscripten 甚至有一個編譯標誌來做到這一點,即 MODULARIZE,它與 EXPORT_NAME 結合使用非常有用(詳細資訊請參閱 settings.js)。

但是,如果多個單獨的模組之間使用相同的 Module 物件(它定義了畫布、文字輸出區域等),仍然會存在一些問題。Emscripten 預設輸出甚至會在全域範圍中尋找 Module,但是當使用 MODULARIZE 時,您會得到一個必須使用 Module 作為參數呼叫的函式,這樣可以避免該問題。但是請注意,每個模組可能需要自己的畫布、文字輸出區域等;僅僅傳遞相同的 Module 物件(例如,從預設的 HTML shell 傳遞)可能無法運作。

因此,通過使用 MODULARIZE 並為每個模組建立一個正確的 Module 物件,然後將這些物件傳遞進去,多個模組可以正常運作。

另一個選項是使用 iframe,在這種情況下,預設的 HTML shell 將會正常運作,因為每個 iframe 都有自己的畫布等。但是對於小型程式而言,這有點過頭了,這些程式可以如上所述以模組化的方式執行。

我可以建置僅在 Web 上執行的 JavaScript 嗎?

可以,您可以在 settings.js 中使用 ENVIRONMENT 選項。例如,使用 emcc -sENVIRONMENT=web 進行建置將會發出僅在 Web 上執行的程式碼,並且不包含對 Node.js 和其他環境的支援程式碼。

這對於減少程式碼大小很有用,並且還可以解決 Node.js 支援程式碼使用 require() 的問題,Webpack 會處理該問題並包含不必要的程式碼。

為什麼這個專案的名稱這麼奇怪?

我不知道為什麼;這是一個完全 cromulent 的單字!