【问题标题】:Why is contextBridge not being imported in my Electron app?为什么我的 Electron 应用程序中没有导入 contextBridge?
【发布时间】:2021-05-03 20:23:58
【问题描述】:

我正在使用 Webpack、Babel 和 ESLint 编写一个带有 React 和 Typescript 的 Electron 应用程序,但我在设置时遇到了问题:

  mainWindow = new BrowserWindow({
    title: "Biomech",
    webPreferences: {
      nodeIntegration: false,
      contextIsolation: true,
      enableRemoteModule: false,
      sandbox: true,
      preload: path.join(__dirname, "./preload.js"),
      nativeWindowOpen: true,
    },
  });

我想要作为一种安全措施。

原因是,如果我按照上面指定的方式设置 webPreferences,我需要使用contextBridge 和一个将使用 IPC 的函数绑定到window 的预加载脚本。问题是contextBridge 没有正确导入我的preload.ts

import { contextBridge, ipcRenderer } from "electron";
import { readFileSync } from 'fs';
import { History } from 'history';
import { LIST_DRIVE_FILES_CHANNEL, LOAD_PREV_TEST_RESULTS_CHANNEL, OAUTH2_ACCESS_TOKEN_REQUEST_CHANNEL } from "../src/constants/ipcChannels";
import { VISUALIZE_RESULTS_PATH } from "../src/constants/urls";


// Expose protected methods that allow the renderer process to use
// the ipcRenderer without exposing the entire object
contextBridge.exposeInMainWorld(
    "api", {
        exchangeCodeForAccessToken: (code: string) => {
            ipcRenderer.invoke(OAUTH2_ACCESS_TOKEN_REQUEST_CHANNEL, code);
        },
        listDriveFiles: (accessToken: string) => {
            ipcRenderer.invoke(LIST_DRIVE_FILES_CHANNEL, accessToken);
        },
        openDirectoryDialog: (history: History) => {
            ipcRenderer.invoke(LOAD_PREV_TEST_RESULTS_CHANNEL).then((dialog: any) => {
                if (dialog.canceled) {  // canceled with one l is correct
                    return;
                } else {
                    const selectedDirectory = dialog.filePaths[0];
                    console.log(readFileSync(selectedDirectory + "/README.md", 'utf-8'));
                    history.push(VISUALIZE_RESULTS_PATH);
                }
            })
        }
    }
);

我认为它使用得当,就像我在 SO 或 Electron 文档中看到的示例一样。但是当我运行我的主进程脚本时:npm run dev:electron 在我的 package.json 中指定:

{
  "name": "electron-react-ts-app",
  "version": "1.0.0",
  "description": "Electron + React + Typescript",
  "main": "./dist/main.js",
  "preload": "./dist/preload.js",
  "scripts": {
    "dev": "concurrently --success first \"npm run dev:electron\" \"npm run dev:react\" -k",
    "dev:electron": "NODE_ENV=development webpack --config webpack.electron.config.js --mode development && electron .",
    "dev:react": "NODE_ENV=development webpack serve --config webpack.react.config.js --mode development",
    "build:electron": "NODE_ENV=production webpack --config webpack.electron.config.js --mode production",
    "build:react": "NODE_ENV=production webpack --config webpack.react.config.js --mode production",
    "build": "npm run build:electron && npm run build:react",
    "pack": "electron-builder --dir",
    "dist": "electron-builder",
    "lint": "eslint .",
    "format": "prettier --write \"**/*.+(js|jsx|json|yml|yaml|css|md|vue)\""
  },
  "keywords": [],
  "license": "MIT",
  "build": {
    "files": [
      "dist/",
      "node_modules/",
      "package.json"
    ],
    "productName": "Example",
    "appId": "com.example.app",
    "directories": {
      "output": "dist"
    }
  },
  "devDependencies": {
    "@babel/preset-env": "^7.9.5",
    "@babel/preset-react": "^7.9.4",
    "@babel/preset-typescript": "^7.9.0",
    "@types/electron-devtools-installer": "^2.2.0",
    "@types/react-router-dom": "^5.1.7",
    "@types/regenerator-runtime": "^0.13.0",
    "dpdm": "^3.6.0",
    "electron": "^11.2.1",
    "electron-builder": "^22.7.0",
    "electron-devtools-installer": "^3.1.1",
    "eslint": "^7.18.0",
    "file-loader": "^6.2.0",
    "html-webpack-plugin": "^4.5.1",
    "husky": "^4.3.8",
    "lint-staged": "^10.5.3",
    "prettier": "^2.2.1",
    "react-router-dom": "^5.2.0",
    "webpack": "^5.11.1",
    "webpack-cli": "^4.3.1",
    "webpack-dev-server": "^3.11.1"
  },
  "dependencies": {
    "@babel/core": "^7.12.10",
    "@popperjs/core": "^2.6.0",
    "@types/node": "^14.14.22",
    "@types/react": "^17.0.0",
    "@types/react-dom": "^17.0.0",
    "axios": "^0.21.1",
    "babel-loader": "^8.2.2",
    "bootstrap": "^4.5.3",
    "chokidar": "^3.5.1",
    "core-js": "^3.8.3",
    "css-loader": "^5.0.1",
    "electron-fetch": "^1.7.3",
    "fsevents": "^2.3.1",
    "ini": "^2.0.0",
    "jquery": "^3.5.1",
    "react": "^17.0.1",
    "react-bootstrap": "^1.4.0",
    "react-dom": "^17.0.1",
    "react-google-login": "^5.2.2",
    "style-loader": "^2.0.0"
  },
  "husky": {
    "hooks": {
      "pre-commit": "npm run lint && npm run format"
    }
  },
  "lint-staged": {
    "*.+(js|jsx)": "eslint --fix",
    "*.+(json|css|md)": "prettier --write"
  }
}

我收到以下错误:无法读取未定义的属性“exposeInMainWorld”

webpack 5.19.0 compiled successfully in 1793 ms
App threw an error during load
TypeError: Cannot read property 'exposeInMainWorld' of undefined
    at Object../electron/preload.ts (/Users/lucas_sg/Documents/ITBA/PF/pf-biomech/dist/preload.js:24:53)
    at __webpack_require__ (/Users/lucas_sg/Documents/ITBA/PF/pf-biomech/dist/preload.js:128:41)
    at /Users/lucas_sg/Documents/ITBA/PF/pf-biomech/dist/preload.js:252:11
    at Object.<anonymous> (/Users/lucas_sg/Documents/ITBA/PF/pf-biomech/dist/preload.js:254:12)
    at Module._compile (internal/modules/cjs/loader.js:1152:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1173:10)
    at Module.load (internal/modules/cjs/loader.js:992:32)
    at Module._load (internal/modules/cjs/loader.js:885:14)
    at Function.f._load (electron/js2c/asar_bundle.js:5:12738)
    at Module.require (internal/modules/cjs/loader.js:1032:19)

我已经查看了 this repo 关于 Electron 安全性的信息,看起来他做的事情并没有太大的不同,至少乍一看,但我显然搞砸了。

这是我的 webpack 配置 (webpack.electron.config.js),以备不时之需:

const path = require("path");

module.exports = [
  {
    resolve: {
      extensions: [".tsx", ".ts", ".js"],
    },
    devtool: "source-map",
    entry: {
        main: {
            import: "./electron/main.ts",
            dependOn: "preload"
        },
        preload: "./electron/preload.ts"
    },
    output: {
      path: path.resolve(__dirname, "dist"),
      filename: "[name].js",
    },
    target: "electron-main",
    module: {
      rules: [
        {
          test: /\.(js|ts|tsx)$/,
          exclude: /node_modules/,
          use: {
            loader: "babel-loader",
          },
        },
        {
          test: /\.css$/,
          use: ["style-loader", "css-loader"],
        },
      ],
    },
    node: {
      __dirname: false,
    },
  },
];

【问题讨论】:

    标签: node.js reactjs typescript webpack electron


    【解决方案1】:

    我认为问题出在你的 webpack.config 文件中:

    entry: {
        main: {
            import: "./electron/main.ts",
            dependOn: "preload"
        },
        preload: "./electron/preload.ts"
    },
    

    可能是 webpack 在编译时尝试解析所有“preload.ts”依赖项,并尝试将其与 main.ts 文件捆绑在一起。在这种情况下,它将永远无法访问 contextBridge。 preload.js 文件只能在单独的“上下文”中运行——文档中提到的“孤立世界”。

    我会尝试的是:

    1. 从您的 webpack 配置中删除 dependOn:preload: 行。
    2. preload.ts 文件转换为 JS(即转换为 CommonJS 格式) - 现在尝试使用 TS CLI 进行此操作

    您的 preload.js 文件应大致如下所示:

    const { contextBridge, ipcRenderer } = require('electron')
    
    contextBridge.exposeInMainWorld(
      'electron',
      {
        doThing: () => ipcRenderer.send('do-a-thing')
      }
    )
    
    1. preload.js 复制到 __dirname(在我的例子中是 /dist)
    2. 再次运行电子

    这是我可以访问“contextBridge”对象的唯一方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-01-04
      • 2021-12-28
      • 2023-03-16
      • 2020-10-10
      • 2020-12-29
      • 2021-08-07
      • 1970-01-01
      • 2023-02-10
      相关资源
      最近更新 更多