Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Node.js 为Electron React JS应用程序创建安装程序-React JS组件不会';安装后运行时无法加载_Node.js_Reactjs_Installation_Electron - Fatal编程技术网

Node.js 为Electron React JS应用程序创建安装程序-React JS组件不会';安装后运行时无法加载

Node.js 为Electron React JS应用程序创建安装程序-React JS组件不会';安装后运行时无法加载,node.js,reactjs,installation,electron,Node.js,Reactjs,Installation,Electron,我正在使用Electron和ReactJS进行一个新项目。该项目在开发模式下运行良好,但我正在尝试为Windows创建一个安装程序,但无论我尝试了什么,在Google上发现了什么,都不起作用。我只是得到一个空白的白色屏幕 下面是我的pacakge.json { "name": "MyApp", "description": "My App Description", "version"

我正在使用Electron和ReactJS进行一个新项目。该项目在开发模式下运行良好,但我正在尝试为Windows创建一个安装程序,但无论我尝试了什么,在Google上发现了什么,都不起作用。我只是得到一个空白的白色屏幕

下面是我的pacakge.json

{
  "name": "MyApp",
  "description": "My App Description",
  "version": "0.1.2",
  "private": true,
  "homepage": "./",
  "dependencies": {
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.5.0",
    "@testing-library/user-event": "^7.2.1",
    "@types/jest": "^26.0.14",
    "@types/node": "^14.11.2",
    "@types/react": "^16.9.50",
    "@types/react-dom": "^16.9.8",
    "bootstrap": "^4.5.2",
    "electron-is-dev": "^1.2.0",
    "electron-settings": "^4.0.2",
    "electron-squirrel-startup": "^1.0.0",
    "react": "^16.13.1",
    "react-bootstrap": "^1.3.0",
    "react-dom": "^16.13.1",
    "react-icons": "^3.11.0",
    "react-json-pretty": "^2.2.0",
    "react-scripts": "3.4.3",
    "react-tooltip": "^4.2.10",
    "typescript": "^4.0.3"
  },
  "main": "src/electron-starter.js",
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "electron-start": "set ELECTRON_START_URL=http://localhost:3000 && electron .",
    "package-win": "electron-packager . --asar --out=release-builds --platform=win32 --arch=x64 --no-prune --ignore=/e2e --overwrite",
    "create-installer-win": "node installers/windows/createInstaller.js"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {

    "electron": "^10.1.3",
    "electron-packager": "^12.0.1",
    "electron-winstaller": "^4.0.1",
    "react-router-dom": "^5.2.0",
    "react-toastify": "^6.0.8"
  }
}
下面是我的electron-start.js脚本

const {electron, Menu, app, BrowserWindow} = require('electron');
// Module to control application life.
//const app = electron.app;
// Module to create native browser window.
//const BrowserWindow = electron.BrowserWindow;


const path = require('path');
const url = require('url');

if (require('electron-squirrel-startup')) app.quit()
// if first time install on windows, do not run application, rather
// let squirrel installer do its work
const setupEvents = require('../installers/setupEvents')
if (setupEvents.handleSquirrelEvent()) {
    console.log("Squirrel event returned true");
    process.exit()
    //return;
}

console.log("Starting main program");

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;

/*const env = process.env.NODE_ENV;

let windowUrlBase = "";

if (env === "production")
{
    windowUrlBase = "/";
}
else
{
    windowUrlBase = 'http://localhost:3000';
}*/

let windowUrlBase = 'http://localhost:3000';

function returnMainWindow()
{
    const mainWindow =  new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            nodeIntegration: true,
            //preload: __dirname + '/preload.tsx'
        }
    });

    //const env = process.env.NODE_ENV;
    //console.log("Environment: " + env);
    const isDev = require('electron-is-dev');

    windowUrlBase = "";
    console.log("Not electron dev");
    console.log("dir name: " + __dirname);

    const startUrl = process.env.ELECTRON_START_URL || url.format({
        //pathname: path.join(__dirname, '/../build/index.html'),
        pathname: path.join(__dirname, '../index.html'),
        protocol: 'file:',
        slashes: true,
        webSecurity: false
    });
    mainWindow.loadURL(startUrl);
    
    return mainWindow;
}


function createWindow() {
    // Create the browser window.

    mainWindow = returnMainWindow();



    mainWindow.maximize();

    // and load the index.html of the app.


    // Open the DevTools.
    //mainWindow.webContents.openDevTools();

    // Emitted when the window is closed.
    mainWindow.on('closed', function () {
        // Dereference the window object, usually you would store windows
        // in an array if your app supports multi windows, this is the time
        // when you should delete the corresponding element.
        mainWindow = null
    })
    setMainMenu();
}

function setMainMenu()
{
    const template = [
        {
            label: 'File',
            submenu: [
                {
                    label: 'Exit',
                    accelerator: "ctrl+f4",
                    click() {
                        app.quit();
                    }
                }
            ]
        },
        {
            label: 'Edit',
            submenu: [
                {
                    label: 'Settings',
                    click() {
                        mainWindow.loadURL(windowUrlBase + "/settings");
                    }
                }
            ]
        },
        {
            label: 'Help',
            submenu: [
                {
                    label: 'Show Dev Console',
                    accelerator: "f11",
                    click() {
                        mainWindow.webContents.openDevTools();
                    }
                }
            ]
        }
    ];

    Menu.setApplicationMenu(Menu.buildFromTemplate(template));
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);

// Quit when all windows are closed.
app.on('window-all-closed', function () {
    // On OS X it is common for applications and their menu bar
    // to stay active until the user quits explicitly with Cmd + Q
    if (process.platform !== 'darwin') {
        app.quit()
    }
});

app.on('activate', function () {
    // On OS X it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (mainWindow === null) {
        createWindow()
    }
});
const createWindowsInstaller = require('electron-winstaller').createWindowsInstaller
const path = require('path')

getInstallerConfig()
    .then(createWindowsInstaller)
    .catch((error) => {
        console.error(error.message || error)
        process.exit(1)
    })

function getInstallerConfig () {
    console.log('creating windows installer')
    const rootPath = path.join('./')
    const outPath = path.join(rootPath, 'release-builds')

    return Promise.resolve({

        appDirectory: path.join(outPath, 'crash-catch-control-panel-win32-x64'),
        authors: 'Boardies IT Solutions',
        noMsi: true,
        outputDirectory: path.join(outPath, 'windows-installer'),
        exe: 'crash-catch-control-panel.exe',
        setupExe: 'crash-catch-control-panel-installer.exe'
        //setupIcon: path.join(rootPath, 'assets', 'images', 'logo.ico')
    })
}
下面是我的创建安装程序脚本

const {electron, Menu, app, BrowserWindow} = require('electron');
// Module to control application life.
//const app = electron.app;
// Module to create native browser window.
//const BrowserWindow = electron.BrowserWindow;


const path = require('path');
const url = require('url');

if (require('electron-squirrel-startup')) app.quit()
// if first time install on windows, do not run application, rather
// let squirrel installer do its work
const setupEvents = require('../installers/setupEvents')
if (setupEvents.handleSquirrelEvent()) {
    console.log("Squirrel event returned true");
    process.exit()
    //return;
}

console.log("Starting main program");

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;

/*const env = process.env.NODE_ENV;

let windowUrlBase = "";

if (env === "production")
{
    windowUrlBase = "/";
}
else
{
    windowUrlBase = 'http://localhost:3000';
}*/

let windowUrlBase = 'http://localhost:3000';

function returnMainWindow()
{
    const mainWindow =  new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            nodeIntegration: true,
            //preload: __dirname + '/preload.tsx'
        }
    });

    //const env = process.env.NODE_ENV;
    //console.log("Environment: " + env);
    const isDev = require('electron-is-dev');

    windowUrlBase = "";
    console.log("Not electron dev");
    console.log("dir name: " + __dirname);

    const startUrl = process.env.ELECTRON_START_URL || url.format({
        //pathname: path.join(__dirname, '/../build/index.html'),
        pathname: path.join(__dirname, '../index.html'),
        protocol: 'file:',
        slashes: true,
        webSecurity: false
    });
    mainWindow.loadURL(startUrl);
    
    return mainWindow;
}


function createWindow() {
    // Create the browser window.

    mainWindow = returnMainWindow();



    mainWindow.maximize();

    // and load the index.html of the app.


    // Open the DevTools.
    //mainWindow.webContents.openDevTools();

    // Emitted when the window is closed.
    mainWindow.on('closed', function () {
        // Dereference the window object, usually you would store windows
        // in an array if your app supports multi windows, this is the time
        // when you should delete the corresponding element.
        mainWindow = null
    })
    setMainMenu();
}

function setMainMenu()
{
    const template = [
        {
            label: 'File',
            submenu: [
                {
                    label: 'Exit',
                    accelerator: "ctrl+f4",
                    click() {
                        app.quit();
                    }
                }
            ]
        },
        {
            label: 'Edit',
            submenu: [
                {
                    label: 'Settings',
                    click() {
                        mainWindow.loadURL(windowUrlBase + "/settings");
                    }
                }
            ]
        },
        {
            label: 'Help',
            submenu: [
                {
                    label: 'Show Dev Console',
                    accelerator: "f11",
                    click() {
                        mainWindow.webContents.openDevTools();
                    }
                }
            ]
        }
    ];

    Menu.setApplicationMenu(Menu.buildFromTemplate(template));
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);

// Quit when all windows are closed.
app.on('window-all-closed', function () {
    // On OS X it is common for applications and their menu bar
    // to stay active until the user quits explicitly with Cmd + Q
    if (process.platform !== 'darwin') {
        app.quit()
    }
});

app.on('activate', function () {
    // On OS X it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (mainWindow === null) {
        createWindow()
    }
});
const createWindowsInstaller = require('electron-winstaller').createWindowsInstaller
const path = require('path')

getInstallerConfig()
    .then(createWindowsInstaller)
    .catch((error) => {
        console.error(error.message || error)
        process.exit(1)
    })

function getInstallerConfig () {
    console.log('creating windows installer')
    const rootPath = path.join('./')
    const outPath = path.join(rootPath, 'release-builds')

    return Promise.resolve({

        appDirectory: path.join(outPath, 'crash-catch-control-panel-win32-x64'),
        authors: 'Boardies IT Solutions',
        noMsi: true,
        outputDirectory: path.join(outPath, 'windows-installer'),
        exe: 'crash-catch-control-panel.exe',
        setupExe: 'crash-catch-control-panel-installer.exe'
        //setupIcon: path.join(rootPath, 'assets', 'images', 'logo.ico')
    })
}
下面是我的setupEvents.js

const electron = require('electron')
const app = electron.app

module.exports = {
    handleSquirrelEvent: function() {
        if (process.argv.length === 1) {
            return false;
        }

        const ChildProcess = require('child_process');
        const path = require('path');

        const appFolder = path.resolve(process.execPath, '..');
        const rootAtomFolder = path.resolve(appFolder, '..');
        const updateDotExe = path.resolve(path.join(rootAtomFolder, 'Update.exe'));
        const exeName = path.basename(process.execPath);
        const spawn = function(command, args) {
            let spawnedProcess, error;

            try {
                spawnedProcess = ChildProcess.spawn(command, args, {detached: true});
            } catch (error) {}

            return spawnedProcess;
        };

        const spawnUpdate = function(args) {
            return spawn(updateDotExe, args);
        };

        const squirrelEvent = process.argv[1];
        switch (squirrelEvent) {
            case '--squirrel-install':
            case '--squirrel-updated':
                // Optionally do things such as:
                // - Add your .exe to the PATH
                // - Write to the registry for things like file associations and
                // explorer context menus

                // Install desktop and start menu shortcuts
                spawnUpdate(['--createShortcut', exeName]);

                setTimeout(app.quit, 1000);
                return true;

            case '--squirrel-uninstall':
                // Undo anything you did in the --squirrel-install and
                // --squirrel-updated handlers

                // Remove desktop and start menu shortcuts
                spawnUpdate(['--removeShortcut', exeName]);

                setTimeout(app.quit, 1000);
                return true;

            case '--squirrel-obsolete':
                // This is called on the outgoing version of your app before
                // we update to the new version - it's the opposite of
                // --squirrel-updated

                app.quit();
                return true;
        }
    }
}
import * as React from 'react';
import {BrowserRouter, Route, Switch} from 'react-router-dom'
import './Stylesheet.css'
import Dashboard from "./Components/Dashboard";
import Settings from "./Components/Settings";
import './ComponentStyles/BreadcrumbNav.css'
import 'react-toastify/dist/ReactToastify.min.css';
import { ToastContainer, toast } from 'react-toastify';
import CustomerDetails from "./Components/CustomerDetails";

toast.configure({
    position: 'top-center',
    hideProgressBar: true
});
function App() {
  return (

      <BrowserRouter>
          <div>
              <Switch>
                  <Route path="/" render={() => <Dashboard  /> } exact />
                  <Route path="/customer-information/:customer_id" render={(props) => <CustomerDetails {...props} />  } exact />
                  <Route path="/settings" render={() => <Settings /> } exact />
              </Switch>
          </div>
      </BrowserRouter>
  );
}

export default App;
下面是我的App.js

const electron = require('electron')
const app = electron.app

module.exports = {
    handleSquirrelEvent: function() {
        if (process.argv.length === 1) {
            return false;
        }

        const ChildProcess = require('child_process');
        const path = require('path');

        const appFolder = path.resolve(process.execPath, '..');
        const rootAtomFolder = path.resolve(appFolder, '..');
        const updateDotExe = path.resolve(path.join(rootAtomFolder, 'Update.exe'));
        const exeName = path.basename(process.execPath);
        const spawn = function(command, args) {
            let spawnedProcess, error;

            try {
                spawnedProcess = ChildProcess.spawn(command, args, {detached: true});
            } catch (error) {}

            return spawnedProcess;
        };

        const spawnUpdate = function(args) {
            return spawn(updateDotExe, args);
        };

        const squirrelEvent = process.argv[1];
        switch (squirrelEvent) {
            case '--squirrel-install':
            case '--squirrel-updated':
                // Optionally do things such as:
                // - Add your .exe to the PATH
                // - Write to the registry for things like file associations and
                // explorer context menus

                // Install desktop and start menu shortcuts
                spawnUpdate(['--createShortcut', exeName]);

                setTimeout(app.quit, 1000);
                return true;

            case '--squirrel-uninstall':
                // Undo anything you did in the --squirrel-install and
                // --squirrel-updated handlers

                // Remove desktop and start menu shortcuts
                spawnUpdate(['--removeShortcut', exeName]);

                setTimeout(app.quit, 1000);
                return true;

            case '--squirrel-obsolete':
                // This is called on the outgoing version of your app before
                // we update to the new version - it's the opposite of
                // --squirrel-updated

                app.quit();
                return true;
        }
    }
}
import * as React from 'react';
import {BrowserRouter, Route, Switch} from 'react-router-dom'
import './Stylesheet.css'
import Dashboard from "./Components/Dashboard";
import Settings from "./Components/Settings";
import './ComponentStyles/BreadcrumbNav.css'
import 'react-toastify/dist/ReactToastify.min.css';
import { ToastContainer, toast } from 'react-toastify';
import CustomerDetails from "./Components/CustomerDetails";

toast.configure({
    position: 'top-center',
    hideProgressBar: true
});
function App() {
  return (

      <BrowserRouter>
          <div>
              <Switch>
                  <Route path="/" render={() => <Dashboard  /> } exact />
                  <Route path="/customer-information/:customer_id" render={(props) => <CustomerDetails {...props} />  } exact />
                  <Route path="/settings" render={() => <Settings /> } exact />
              </Switch>
          </div>
      </BrowserRouter>
  );
}

export default App;
import*as React from'React';
从“react router dom”导入{BrowserRouter,Route,Switch}
导入“./Stylesheet.css”
从“/Components/Dashboard”导入仪表板;
从“/Components/Settings”导入设置;
导入“./ComponentStyles/BreadcrumbNav.css”
导入“react toastify/dist/react toastify.min.css”;
从'react toastify'导入{ToastContainer,toast};
从“/Components/CustomerDetails”导入CustomerDetails;
toast.configure({
位置:'上止点',
hideProgressBar:对
});
函数App(){
返回(
}精确的/>
}精确的/>
}精确的/>
);
}
导出默认应用程序;
当我在应用程序加载时查看chrome控制台时,我看到以下错误:

不允许加载本地资源: file:///C:/Users/Chris/AppData/Local/MyApp/app-0.1.2/resources/app.asar/index.html

如上所述,只有当我在安装electron应用程序时启动该应用程序时,问题才会发生。如果我将其作为Node dev服务器的一部分启动,那么它就可以正常工作

更新

目录结构如下所示


components目录包含实际的ReactJS组件,目录ComponentStyles是所有单独的组件样式表。组件是typescript,所以是tsx格式的。

经过大量的尝试和错误,我终于让它工作了

第一件事是更改我的App.js,这样我就不用使用BrowserRouter,而是使用HashRouter,如下所示,并使用react router dom中的历史记录

import {HashRouter, useHistory} from "react-router-dom";
const history = createBrowserHistory();

<HashRouter basename="/" history={history} >
          <Switch>
              <Route path="/" exact component={Dashboard} />
              <Route path="/customer-information/:customer_id" component={CustomerDetails} />
              <Route path="/settings" component={Settings} />
              <Route path="*" component={NotFound} />
          </Switch>
      </HashRouter>
当我加载浏览器窗口时,我现在有以下内容:

const history = useHistory();
history.push('/newlocation');
mainWindow.loadURL(isDev ? windowUrlBase : `file://${__dirname}/../build/index.html`);
const isDev = require('electron-is-dev');
并删除了下面的块

const startUrl = process.env.ELECTRON_START_URL || url.format({
        //pathname: path.join(__dirname, '/../build/index.html'),
        pathname: path.join(__dirname, '../index.html'),
        protocol: 'file:',
        slashes: true,
        webSecurity: false
    });
isDev的设置采用以下方法:

const history = useHistory();
history.push('/newlocation');
mainWindow.loadURL(isDev ? windowUrlBase : `file://${__dirname}/../build/index.html`);
const isDev = require('electron-is-dev');
对于使用IPC更改位置的设置菜单,请使用以下命令:

const history = useHistory();
history.push('/newlocation');
mainWindow.loadURL(isDev ? windowUrlBase : `file://${__dirname}/../build/index.html`);
const isDev = require('electron-is-dev');
在electron-start.js中

const { ipcMain } = require("electron");

    submenu: [
                    {
                        label: 'Settings',
                        click() {
                            //mainWindow.loadURL(windowUrlBase + "/settings");
                            //history.push('/settings')
                            //location.pathname = "/settings";
                            //ipcMain.send("change-location", "/settings");
                            mainWindow.webContents.send('change-location', '/settings')
                            //mainWindow.location.pathname = "/settings";
                        }
                    }
                ]
然后在整个项目中共享的组件中,我有以下内容来接收IPC事件

const {ipcRenderer} = window.require('electron')
const history = useHistory();

useEffect(() => {
        ipcRenderer.on('change-location', (event, arg) => {
            history.push(arg);
        });
    })

你确定这行是对的吗

const startUrl=process.env.ELECTRON_START_URL|URL.format({
//路径名:path.join(uu dirname,'/../build/index.html'),
路径名:path.join(_dirname,'../index.html'),
协议:“文件:”,
斜杠:对,
网站安全性:false
})
我认为路径名应该是
path.join(uu dirname,'./index.html')
double
使您在上面两级(红色),而不是父文件夹(橙色)

您可以使用文件夹/main/sub/test.js中的一个简单文件来测试这一点:

const path=require('path'))
console.log(uu dirname)
console.log(path.join(uu dirname,“../”))
console.log(path.join(uu dirname,./))
产出将是:

~/main/sub
~/main/
~/main/sub/
因此,在您的情况下,
./index.html
可以让您进入
/crash catch控制面板/
,但是
index.html
不在那里

我不熟悉使用asar,但我认为您的错误消息:
file:///C:/Users/Chris/AppData/Local/MyApp/app-0.1.2/resources/app.asar/index.html
表示您不在
src
文件夹中,您的
index.html
文件位于该文件夹中,但您正在查找其上一级的
index.html


特别是因为您在响应中的内容:
mainWindow.loadURL(isDev?windowUrlBase:`file://${\uu dirname}/./build/index.html`
上升一级,然后下降到
build
。所以我关心的是,在这里,您要跳入
build
文件夹,从
build
文件夹中获取index.html。但是
build
文件夹与您的
src
文件夹处于同一层次结构,您没有被指向该层次结构。

我需要您的项目文件结构。如果这是开源的,你应该链接repo URL。我已经按要求添加了我的目录结构-它不是开源项目当你说从“节点开发服务器”启动它时,你是说用“npm运行electron start”?你可以签出。我用它作为路由的一个例子。我从中构建没有任何问题。而且,通过您的更改,它看起来像“设置”除非您还更新了指向“设置”的菜单链接,否则“菜单项”可能无法在生产环境中使用。@Andrew我修复了我的答案,因为我删除了一块与正在加载的startURL相关的代码,正如您所提到的,“设置”菜单将无法使用我最初更新的设置。谢谢你指出我已经更新了我的答案,我确实移除了你提到的startURL块