使用ES6导入配置Electron的Webpack?

6

我正在尝试使用Webpack,因为我想在我的Electron应用程序中使用ES模块,但是遇到了一些障碍。我只想在我的mainrenderer进程中使用import

我的应用程序结构如下 -

- src/                  // contains basic html, css & js
  - index.html          // <h1>Hello World</h1>
  - style.css           // is empty
  - app.js              // console.log('it works ')
- app/                  // contains electron code
  - main_window.js
  - custom_tray.js
- index.js              // entry point for electron application
- dist/                 // output bundle generated from webpack
  - bundle.js

我的 index.js 文件看起来像-

import path from "path";
import { app } from "electron";

import MainWindow from "./app/main_window";
import CustomTray from "./app/custom_tray";

let win = null,
    tray = null;

app.on("ready", () => {
    // app.dock.hide();
    win = new MainWindow(path.join("file://", __dirname, "/src/index.html"));

    win.on("closed", () => {
        win = null;
    });

    tray = new CustomTray(win);
});

我的main_window.js文件看起来像这样 -
import { BrowserWindow } from "electron";

const config = {
    width: 250,
    height: 350,
    show: false,
    frame: false,
    radii: [500, 500, 500, 500],
    resizable: false,
    fullscreenable: false
};

class MainWindow extends BrowserWindow {
    constructor(url) {
        super(config);

        this.loadURL(url);
        this.on("blur", this.onBlur);
        this.show();
    }

    onBlur = () => {
        this.hide();
    };
}

export default MainWindow;

我的 custom_tray.js 看起来是这样的 -
import path from "path";
import { app, Tray, Menu } from "electron";

const iconPath = path.join(__dirname, "../src/assets/iconTemplate.png");

class CustomTray extends Tray {
    constructor(mainWindow) {
        super(iconPath);
        this.mainWindow = mainWindow;

        this.setToolTip("Thirsty");

        this.on("click", this.onClick);
        this.on("right-click", this.onRightClick);
    }

    onClick = (event, bounds) => {
        const { x, y } = bounds;
        const { width, height } = this.mainWindow.getBounds();

        const isMac = process.platform === "darwin";

        if (this.mainWindow.isVisible()) {
            this.mainWindow.hide();
        } else {
            this.mainWindow.setBounds({
                x: x - width / 2,
                y: isMac ? y : y - height,
                width,
                height
            });
            this.mainWindow.show();
        }
    };

    onRightClick = () => {
        const menuConfig = Menu.buildFromTemplate([
            {
                label: "Quit",
                click: () => app.quit()
            }
        ]);
        this.popUpContextMenu(menuConfig);
    };
}

export default CustomTray;

我的webpack.main.config.js看起来像这样 -

const path = require("path");

const config = {
    entry: "./index.js",
    output: {
        path: path.resolve(__dirname, "dist"),
        filename: "bundle.js"
    },
    module: {
        rules: [{ test: /\.js$/, exclude: /node_modules/, use: "babel-loader" }]
    },
    stats: {
        colors: true
    },
    target: "electron-main",
    devtool: "source-map"
};

module.exports = config;

我的webpack.renderer.config.js看起来像这样 -
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");

const config = {
    entry: "./src/app.js",
    output: {
        path: path.resolve(__dirname, "dist/renderer"),
        filename: "app.js"
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: "babel-loader"
            },
            {
                test: /\.css$/,
                use: {
                    loader: "css-loader",
                    options: {
                        minimize: true
                    }
                }
            },
            {
                test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
                use: {
                    loader: "url-loader",
                    query: {
                        limit: 10000,
                        name: "imgs/[name].[ext]"
                    }
                }
            },
            {
                test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
                use: {
                    loader: "url-loader",
                    query: {
                        limit: 10000,
                        name: "fonts/[name].[ext]"
                    }
                }
            }
        ]
    },
    stats: {
        colors: true
    },
    target: "electron-renderer",
    devtool: "source-map",
    plugins: [
        new CopyWebpackPlugin([
            { from: "src/app.css" },
            { from: "src/assets", to: "assets/" }
        ]),
        new HtmlWebpackPlugin({
            filename: "index.html",
            template: path.resolve(__dirname, "./src/index.html"),
            minify: {
                collapseWhitespace: true,
                removeAttributeQuotes: true,
                removeComments: true
            }
        })
    ]
};

module.exports = config;

我的 package.json 中的脚本看起来像这样

"scripts": {
    "dev:main": "webpack --mode development --config webpack.main.config.js",
    "dev:renderer": "webpack --mode development --config webpack.renderer.config.js",
    "dev:all": "npm run dev:main && npm run dev:renderer",
    "build:main": "webpack --mode production --config webpack.main.config.js",
    "build:renderer": "webpack --mode production --config webpack.renderer.config.js",
    "build:all": "npm run build:main && npm run build:renderer",
    "prestart": "npm run build:all",
    "electron": "electron dist/index.js",
    "start": "npm run electron",
}

目前我的应用程序创建了一个 dist/bundle.js 文件,但是当我运行 electron dist/bundle.js 时它无法工作。我知道可能是因为它不包含 src 文件夹,但是当我将 src 文件夹复制到 dist 中时仍然无法工作。
首先,我运行 npm run dev:main 来生成 dist/bundle.js,然后我运行 npm run dev:renderer 来生成 dist/renderer/bundle.js,最后我运行 npm run start 来启动我的 electron 应用程序。
它给我一个错误 "Uncaught Exception: Error: Requires constructor call at new MainWindow",这在 index.js 中调用构造函数 new MainWindow()
我只想在所有的 JS 文件中使用 ES6。是否有任何样板文件可以使用?因为我找到的那些都有大量附加内容,比如 React JS 等等,还有很多优化。

我从未见过有人像你这样设置他们的webpack.config文件,特别是模块部分。你是否尝试过将配置文件设置为类似es6文档中的那样,特别是让模块对象包含一个加载器数组。如果这不起作用,您可以发布任何输出的错误吗? - Stephen Agwu
我从未使用过Electron,但当我在knockout.js框架中使用es6时,有必要将knockout排除在解析之外。你可以看一下我如何制作我的webpack.config [here](https://github.com/osa10928/Udacity_FullStack_API_APP/blob/master/webpack.config.js),也许会有所帮助。 - Stephen Agwu
你能解释一下你实际收到的错误吗?你期望得到什么行为?你只说了 “它不起作用”。此外,请注意手动复制 src -> dist 永远不会生效,因为 Webpack 必须捆绑您的代码才能执行。Webpack 不是一个模块加载器,它是一个模块打包工具。 - Aluan Haddad
@stephenagwu 我正在使用Webpack 4,因此它是这样使用的。在构建Webpack时没有错误。当我运行Electron应用程序时会出现“new MainWindow不是构造函数”的错误。实际上问题是Electron和Webpack的结合。我知道单独使用Electron和Webpack,但结合起来我在过去两天里找不到任何东西。首先我运行npm run dev生成dist/bundle.js,然后我运行npm start,但在Electron应用程序中它会抛出错误。它给出了上面的错误。 - deadcoder0904
也可以尝试移除 transform-class 插件,因为它不需要转译。 - reoh
显示剩余5条评论
1个回答

9
在8天之后,我终于找到了答案。它可以在Electron中使用ESM。
我创建了一个最小化的repo,让您可以使用Electron编写ESM。
完整的代码可以在https://github.com/deadcoder0904/electron-webpack-sample找到。
它非常简洁,因此应该很容易理解。

您可能还需要为两个进程定义外部变量,以便webpack不解析您的依赖项。由于Electron主进程和渲染器进程都具有node集成,因此这样做是不必要的。 - m4heshd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接