前端项目打包成Electron-傻瓜式操作

前提是你得有一个前端项目且能成功运行起来哈!

安装electron相关包

# 这行命令很重要,否则会导致electron打包下载的zip一直失败

pnpm install -D concurrently cross-env wait-on
pnpm install -D electron electron-builder electron-reloader 
pnpm install @electron/remote

如果在安装electron相关包的时候出现node install.js卡住的问题,可以跳过该脚本的执行,安装完包之后,再按顺序运行如下命令修复一下问题:

pushd node_modules/electron

# 在这一步,会下载适配你自己电脑的electron压缩包

node install.js

popd

最后执行popd后返回你当前项目的根目录在电脑中的目录地址的话,此时,相关包均安装完成。

配置package.json

{
  "private": true,
  "author": "EmmaLu",
  "description": "这是一个示例 Electron 应用程序",
  "name": "sdf-web-electron",
  "version": "2.1.0",
  "main": "app/main.js",
  "scripts": {
    "dev": "umi dev",
    "build": "umi build",
    "postinstall": "umi setup",
    "setup": "umi setup",
    "start": "npm run dev",
    "electron": "pnpm build && NODE_ENV=production electron app/main.js",
    "electron-dev": "concurrently \"cross-env BROWSER=none pnpm dev\" \"wait-on http://localhost:8000 && cross-env NODE_ENV=development electron app/main.js\"",
    "pre-electron-build:win": "pnpm build && electron-builder install-app-deps",
    "pre-electron-build:mac": "pnpm build && electron-builder install-app-deps",
    "electron-build:win": "pnpm build && electron-builder build --win --x64",
    "electron-build:mac": "pnpm build && electron-builder build --mac --arm64"
  },
  "build": {
    "electronDownload": {
      "cache": "./.electron-cache"
    },
    "directories": {
      "output": "dist-app/${os}-${arch}"
    },
    "icon": "app/assets/favicon.ico",
    "mac": {
      "target": [
        {
          "target": "dmg",
          "arch": [
            "x64"
          ]
        }
      ],
      "artifactName": "${productName}_setup_${version}.${ext}"
    },
    "win": {
      "target": [
        {
          "target": "nsis",
          "arch": [
            "x64"
          ]
        }
      ],
      "artifactName": "${productName}_setup_${version}.${ext}"
    },
    "nsis": {
      "oneClick": false,
      "allowToChangeInstallationDirectory": true,
      "createDesktopShortcut": true,
      "installerIcon": "app/assets/favicon.ico",
      "uninstallerIcon": "app/assets/favicon.ico"
    }
  },
  "dependencies": {
    ...
  },
  "devDependencies": {
    ...
  }
}

说明:

“electron-dev”:本机桌面端的开发模式;

“electron”:本地桌面端实时展示;

“pre-electron-build:win”:安装windows版应用程序依赖项;

“pre-electron-build:mac”:安装MAC版应用程序依赖;

“electron-build:win”:打包成windows桌面端应用可执行文件.exe;

“electron-build:mac”:打包成MAC桌面端应用可执行文件.dmg;

实现HTTP服务与Electron窗口创建

 目录结构如下:

说明:本地启动 umi 项目的 HTTP 服务

在项目根目录下创建app文件夹,创建www.js文件,其具体内容如下:

const http = require('http');
const url = require('url');
const path = require('path');
const fs = require('fs');
// const debug = require('debug')('server');

let server = null;

// let port = process.argv[2] || 8080;
let port = process.env.ELECTRON_SERVER_PORT || process.argv[2] || 8000;
console.log('port-www.js', port)
/**
 * Event listener for HTTP server "error" event.
 */
function onError(error) {
    if (error.syscall !== 'listen') {
        throw error;
    }

    let bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;

    // handle specific listen errors with friendly messages
    switch (error.code) {
        case 'EACCES':
            console.error(bind + ' requires elevated privileges');
            process.exit(1);
            break;
        case 'EADDRINUSE':
            console.error(bind + ' is already in use');
            process.exit(1);
            break;
        default:
            throw error;
    }
}

/**
 * Event listener for HTTP server "listening" event.
 */
function onListening() {
    let addr = server.address();
    let bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
    // debug('Listening on ' + bind);
}

const startServer = (customPort) => {
    /**
     * Listen on provided port, on all network interfaces.
     */
    port = customPort || port;

    if (server) {
        server.close();
    }
    server = http
        .createServer(function (request, response) {
            console.log('request', request.url)
            let uri = url.parse(request.url).pathname;
            let filename = path.join(__dirname, '../dist', uri);

            let contentTypesByExtension = {
                '.html': 'text/html',
                '.css': 'text/css',
                '.js': 'text/javascript',
            };

            fs.exists(filename, function (exists) {
                if (!exists) {
                    response.writeHead(404, { 'Content-Type': 'text/plain' });
                    response.write('404 Not Found\n');
                    response.end();
                    return;
                }

                if (fs.statSync(filename).isDirectory()) filename += '/index.html';

                fs.readFile(filename, 'binary', function (err, file) {
                    if (err) {
                        response.writeHead(500, { 'Content-Type': 'text/plain' });
                        response.write(err + '\n');
                        response.end();
                        return;
                    }
                    let headers = {};
                    let contentType = contentTypesByExtension[path.extname(filename)];
                    if (contentType) headers['Content-Type'] = contentType;
                    response.writeHead(200, headers);
                    response.write(file, 'binary');
                    response.end();
                });
            });
        })
        .listen(parseInt(port, 10), '0.0.0.0');

    // server.listen(port);
    server.on('error', onError);
    server.on('listening', onListening);
};
const getServer = () => server;

// 暴露一个函数,用于启动服务
module.exports = { startServer, getServer };

在app目录下创建main.js文件,用于创建一个 electron 窗口,其具体内容如下:

// electron打包配置
const {
    app,
    BrowserWindow,
    globalShortcut,
    ipcMain
} = require('electron');
const path = require('path');
const { startServer, getServer } = require('./www');
const fs = require('fs');

const isPro = process.env.NODE_ENV !== 'development';
const remote = require('@electron/remote/main');
remote.initialize();

let mainWindow;
let port = process.argv[2] || 8000;
console.log('port-main.js', port)



function createWindow() {
    startServer(port);
    const userDataPath = app.getPath('userData');
    const configPath = path.join(userDataPath, 'config.json');

    let savedConfig = {};
    if (fs.existsSync(configPath)) {
        savedConfig = JSON.parse(fs.readFileSync(configPath));
        port = savedConfig.port || port; // 修改服务器端口
    }

    mainWindow = new BrowserWindow({
        minWidth: 800,
        minHeight: 600,
        title: 'ElectronDemo桌面版',
        // autoHideMenuBar: true,
        // frame: false, // 设置为false后为无边框窗口,即无法拖拽,拉伸窗体大小,没有菜单项
        icon: path.join(__dirname, './assets/logo.ico'),
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            preload: path.join(__dirname, 'preload.js')
        },
    });
    remote.enable(mainWindow.webContents);

    globalShortcut.register('CommandOrControl+M', () => {
        mainWindow.maximize();
    });

    globalShortcut.register('CommandOrControl+T', () => {
        mainWindow.unmaximize();
    });

    globalShortcut.register('CommandOrControl+H', () => {
        mainWindow.close();
    });

    if (isPro) {
        mainWindow.loadURL(`http://localhost:${port}/`);
        console.log('Production URL:', `http://localhost:${port}/`);
        mainWindow.webContents.openDevTools();
    } else {
        mainWindow.loadURL('http://localhost:8000/');
        console.log('development')
        mainWindow.webContents.openDevTools();
    }

    // 解决应用启动白屏问题
    mainWindow.on('ready-to-show', () => {
        mainWindow.show();
        mainWindow.focus();
    });

    mainWindow.on('close', (e) => { });

    mainWindow.on('closed', () => {
        mainWindow = null;
    });
}

const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
    app.quit();
} else {
    app.on('second-instance', (event, commandLine, workingDirectory) => {
        if (mainWindow) {
            if (mainWindow.isMinimized()) mainWindow.restore();
            mainWindow.focus();
        }
    });
}

app.on('ready', () => {
    createWindow();
    ipcMain.on('update-server-port', (event, newPort) => {
        const server = getServer();

        if (server) {
            server.close(() => {
                console.log(`Old server on port ${port} closed`);

                // 更新端口
                port = newPort;

                // 启动新服务
                startServer(port);

                // 保存到本地文件
                const configPath = path.join(app.getPath('userData'), 'config.json');
                fs.writeFileSync(configPath, JSON.stringify({ port }));
            });
        }
    });

});

try {
    require('electron-reloader')(module, {});
} catch (_) { }

app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        app.quit();
    }
});

app.on('activate', () => {
    if (mainWindow === null) {
        createWindow();
    }
});

app.on('before-quit', (event) => { });

运行项目
 

注意package.json中的脚本命令!

启动

开发环境:

pnpm dev:启动前端项目服务

pnpm electron-dev:启动本地electron应用

生产环境:

pnpm electron:会根据你电脑系统来安装相应的可执行包

构建:

pnpm electron-build:mac:打包为mac系统可执行文件dmg

pnpm electron-build:win:打包为windows系统可执行文件.exe

最后打包成功的终端截图与文件目录结构:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值