[英]import inside Electron renderer script
我遵循了 Electron typescript 快速啟動代碼結構。 基本上我克隆了回購協議。 在我想將我的代碼拆分為 multiple.ts 文件並將它們導入渲染器腳本之前,它工作正常。
然后我得到Uncaught ReferenceError: exports is not defined
。 由於 renderer.ts 頂部的這一行:
import { stuff } from "./otherfile.ts";
挖掘更多信息后,似乎原因是來自 tsconfig 的"module": "commonjs"
...但是如果我將其更改為esnext
,則 Electron 將不再加載預加載腳本!
有沒有人真正設法讓 Electron 和 typescript 充分發揮作用? 我的意思是,能夠跨多個文件使用導入之類的東西?
文件結構:
/dist
/dist/main.js
/dist/preload.js
/dist/renderer.js
/dist/stuff.js
/src
/src/main.ts
/src/preload.ts
/src/renderer.ts
/src/stuff.ts
/index.html
/src/main.ts:
import { ipcMain } from "electron"; // imports work fine in main
...
ipcMain.on(...
/src/preload.ts:
import { contextBridge, ipcRenderer} from "electron"; // imports work fine in preload
contextBridge.exposeInMainWorld("my-api", { ....
/src/renderer.ts
import stuff from "./stuff.ts"; // import fails in renderer (exports is not defined error)
/src/stuff.ts
const stuff = { ... };
export default stuff;
/index.html
<html>
...
<script src="./dist/renderer.js"></script>
</html>
配置文件:
如果我在 ts 編譯文件后在renderer.js
中手動添加var exports = {}
,那么我會得到一個不同的錯誤“require is not defined”
我相信你的錯誤是由混淆 CommonJS (CJS) 和 ES 模塊 (ESM) 引起的。 聽起來您正在使用 CJS 導出和不兼容的 ESM 導入。 下一節是一個有效的 Election 示例,之后是 CJS 和 ESM 導出(export)和 require(導入)的比較。
您的 OP 不包含任何代碼,因此這是我對問題所在的最佳猜測,並提供了可行的解決方案。 從設置代碼開始:
# Clone this repository
git clone https://github.com/electron/electron-quick-start-typescript
# Go into the repository
cd electron-quick-start-typescript
# Install dependencies
npm install
接下來創建以下文件:
// src/otherfile.ts
export const stuff = {
fullOf: 'stuff'
}
export function shareLocation(loc: string): void {
console.log(`I was imported by: ${loc}`);
}
現在打開src/main.ts
文件並進行以下更改:
// Add import to top of file.
import {stuff, shareLocation} from "./otherfile"
// Add this code to the end of the createWindow() function.
shareLocation('main.ts (main.js)');
現在,如果您以npm start
運行此示例,您將在終端中看到I was imported by: main.ts (main.js)
。
如果您嘗試使用src/preload.ts
文件執行此操作,您將因沙盒而收到錯誤。 要查看這一點,請在src/preload.ts
中進行以下更改:
// Add to first line.
import {stuff, shareLocation} from "./otherfile";
// Add after the for loop inside the DOMContentLoaded function.
shareLocation('preload.ts (preload.js)');
現在,如果運行npm start
,您將在 electron window(網絡瀏覽器)控制台中收到錯誤消息。 這是因為 Electron 中的重要安全設置。對src/main.ts
文件進行以下更改:
// Modify the webPreferences of the new BrowserWindow.
const mainWindow = new BrowserWindow({
height: 600,
webPreferences: {
preload: path.join(__dirname, "preload.js"),
sandbox: false // <=== ADD THIS LINE
},
width: 800,
});
您的導入現在將按預期工作,您應該看到I was imported by: preload.ts (preload.js)
在 electron window(網絡瀏覽器)控制台中。 請記住此代碼是不安全的! 您應該使用 IPC而不是禁用沙箱。
如果您仍然遇到錯誤,我相信這是因為您混淆了 CommonJS (CJS) 和 ES 模塊 (ESM)。 聽起來您正在使用 CJS 導出和 ESM 導入。 請參閱下一節演示差異。
CommonJS 是在 Node.js 中導出和要求(導入)模塊的原始方法。 這是 Mozilla 工程師 Kevin Dangoor 在 2009 年推出的一項標准。您可以在一個文件中編寫一些代碼,如下所示:
// example.js
function hello() {
console.log('World!');
}
module.exports = hello;
然后將代碼要求(導入)到另一個模塊/文件中,如下所示:
// main.js
const hello = require('./example.js');
hello();
就像更新標准的 ES 模塊一樣,“您可以導出functions
、 var
、 let
、 const
和classes
”( MDN 文檔)。 這是一個更大的示例,它使用單個導出object
導出兩個函數:
// example.js
function hello() {
console.log('World!');
}
function foo() {
console.log('Bar!');
}
module.exports = {
hello,
foo
};
我們甚至可以更改導出的object
以使用不同的名稱來引用我們的函數。 例如,我們可以將導出代碼更改為:
module.exports = {
H: hello,
F: foo
};
然后我們會像這樣需要(導入)這段代碼:
// main.js
const {hello, foo} = require('./example.js');
hello();
foo();
// OR with the other export example
const {H, F} = require('./example.js');
H();
F();
這是將一個文件/模塊中的 JavaScript 代碼包含到另一個文件/模塊中的現代方法,旨在在現代瀏覽器中工作。 如果您手動為 web 編寫 JavaScript 模塊,則必須將type="module"
屬性添加到加載模塊的腳本標記中。
在您的設置和許多其他設置中,您有某種類型的捆綁器(例如 webpack)通過為您編譯/轉譯代碼來為您處理此問題。 不過,這個主題超出了這個問題的 scope,所以讓我們看一個例子。
使用 ESM,我們的簡單示例現在變為:
// example.js
function hello() {
console.log('World!');
}
export default hello;
請注意我們的導出語句發生了怎樣的變化。 為了簡單起見,我在導出單個項目時提供default
導出。 我們還可以內聯導出:
// example.js
export default function hello() {
console.log('World!');
}
然后我們將代碼導入另一個模塊/文件,如下所示:
// main.js
import hello from "example";
hello();
如果您不想在導出的模塊中使用default
,則必須使用不同的語法來導入代碼。 正如Darryl Noakes提到的,那將是Named Exports 。 由於這是一個 TypeScript 項目,這包括更改您的 TypeScript 配置。 This SO answer涵蓋了如果你想 go 這個項目的這條路線要做什么。
如果導出多個項目,則不必使用default
語句:
// example.js
function hello() {
console.log('World!');
}
function foo() {
console.log('Bar!');
}
export {
hello,
foo
}
然后您以類似於object destructing的方式導入代碼:
// main.js
import {hello, foo} from "example";
hello();
foo();
對於import
方面的額外幫助,此MDN 文檔以及此免費代碼訓練營文章是一個很好的開始。 JavaScript Tutorial 有一個很棒的教程,介紹什么是 object 破壞。 這個SO Q&A更多地涉及模塊導入,包括為什么他們實際上沒有使用 destructing 。 再次感謝Darryl Noakes指出這一點!
Electron 還不完全支持 ECMAScript 模塊,但這應該不會影響您的 Typescript 代碼,它可以是非常現代的。
選項 1(松散文件)
在您的 index.html 中執行此操作以引導您的渲染器進程的 Typescript 代碼,就像我的這個初始示例一樣。 這將使您快速啟動並運行,盡管它使用節點集成,在您發布到生產環境之前應該禁用它:
<body>
<div id='root' class='container'></div>
<script type='text/javascript'>
require('./built/renderer');
</script>
</body>
選項 2(捆綁)
要擺脫 require 語句,並更接近 SPA 操作,符合 Electron 安全建議,您可以使用 webpack 捆綁和參考構建 Typescript,如下所示,如我的更新示例所示:
<body>
<div id='root' class='container'></div>
<script type='module' src='vendor.bundle.js'></script>
<script type='module' src='app.bundle.js'></script>
</body>
設置
我在 package.json 中指定了type=commonjs
,在 tsconfig.json 文件中指定了module=commonjs
,而在非 Electron 項目中,我使用 ECMAScript 模塊設置來構建更好的 output。
它的藝術當然是產生一個現代代碼設置,您可以根據自己的喜好進行擴展,而不是局限於啟動項目所做的事情。
抱歉,樣板文件的配置方式是不可能的。
要在renderer
進程內部使用import
/ require
,您必須降低最新版本 Electron 中的默認安全設置,或者使用轉譯器,以便renderer
執行的最終代碼不包含import
/ require
。
在electron-quick-start-typescript
項目中,你會發現這個文件確認了這個問題。
// 這個文件是 index.html 文件所需要的
// 在 window 的渲染器進程中執行。
// 沒有 Node.js API 在此過程中可用,除非
// nodeIntegration 在 webPreferences 中設置為 true。
// 使用 preload.js 有選擇地啟用功能
// 在渲染器進程中需要。
Node.js API 包括import
和require
。
此外,當您訪問require
時,如果啟用了沙箱,那么您實際上獲得了一個 polyfilled 版本,而不是底層require
- 請參閱有關預加載腳本沙箱的這些說明
除了更改樣板之外,您唯一的其他選擇是通過預加載腳本訪問您需要的任何內容,例如window.api.yourFunction
。
您可能希望使用包含通過 Webpack 進行轉譯的樣板,以使您能夠在渲染器進程中使用import
。
我相信這兩個樣板都可以容納它(如果你被其中之一困住,我可能會建議你一些)
PS - 如果您編寫的代碼旨在分發給其他人,請不要啟用nodeIntegration
,因為它會破壞 Electron 的安全性 model!
PPS - 您也可以嘗試"module": "ES2020"
,這是我們使用的 - 但它不會解決您無法在renderer
進程中import
/ require
的問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.