ElectronDemo

  |   0 评论   |   0 浏览

Electron

js 写 windows/mac app 的框架

typescript react antd

创建项目

安装脚手架:yarn global add create-react-app

创建 react antd 项目:create-react-app my-project --scripts-version=react-scripts-ts-antd

以下是完整的 package.json

{
 // 项目名称
  "name": "typescript-sonar-client",
  // 版本
  "version": "1.0.3-alpha",
  "description": "null",
  // 入口
  "main": "main.js",
  // 作者
  "author": "ferried <harlancui@outlook.com>",
  "license": "MIT",
  // 环境(开发/部署)
  "DEV": true,
  // 主页
  "homepage": ".",
  // 脚本
  "scripts": {
      // 启动react
    "start": "react-scripts-ts-antd start",
    // 打包react && 编译ts 编译配置在tsconfig.json
    "build": "react-scripts-ts-antd build && yarn tsc",
    "test": "react-scripts-ts-antd test --env=jsdom",
    "eject": "react-scripts-ts-antd eject",
    // 编译ts,使用node_modules里的脚本编译两个ts
    "tsc": "./node_modules/typescript/bin/tsc main.ts ./public/render.ts",
    "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 --pkg package.json",
    // electron 启动命令
    "electron": "electron .",
    // 编译页面+ts+electron并生成app
    "electron-package": "yarn build && electron-packager . app --darwin --out myapp --arch=x64 --overwrite --ignore=node_modules --ignore=myapp --electron-version=4.0.1"
  },
  "dependencies": {
    "@types/jest": "^23.3.12",
    "@types/node": "^10.12.18",
    "@types/react": "^16.7.18",
    "@types/react-dom": "^16.0.11",
    "antd": "^3.12.1",
    "conventional-changelog-cli": "^2.0.11",
    "electron": "^4.0.1",
    "electron-packager": "^13.0.1",
    "husky": "^1.3.1",
    "react": "^16.7.0",
    "react-dom": "^16.7.0",
    "react-scripts-ts-antd": "2.17.0",
    "tslint": "^5.12.0",
    "typescript": "^3.2.2"
  }
}

创建入口 Main

import { app, BrowserWindow } from "electron";
import * as path from "path";
import * as url from "url";
// tslint:disable-next-line: no-var-requires
// 读取 package.json
const pkg = require("./package.json");
export class MyWindow {
  private mainWindow: BrowserWindow | null;
  // 创建window
  public create(): void {
    this.mainWindow = new BrowserWindow({
      autoHideMenuBar: true,
      fullscreenable: false,
      height: 600,
      width: 800,
      // tslint:disable-next-line: object-literal-sort-keys
      webPreferences: {
        javascript: true,
        plugins: true,
        // tslint:disable-next-line: object-literal-sort-keys
        nodeIntegration: false, // 不集成 Nodejs
        webSecurity: false,
        preload: path.join(__dirname, "./public/render.js") // 但预加载的 js 文件内仍可以使用 Nodejs 的 API
      }
    });
    // 如果是开发模式,react是由脚手架启动 窗口加载localhost
    if (pkg.DEV) {
      this.mainWindow.loadURL("http://localhost:3000");
    } else {
        // 如果不是开发模式则打包编译后的文件
      this.mainWindow.loadURL(
        url.format({
          pathname: path.join(__dirname, "./build/index.html"),
          protocol: "file:",
          slashes: true
        })
      );
    }
    this.mainWindow.webContents.openDevTools();
    this.mainWindow.on("closed", () => {
      this.mainWindow = null;
    });
  }

  public start(): void {
    app.on("ready", this.create);
    app.on("window-all-closed", () => {
      if (process.platform !== "darwin") {
        app.quit();
      }
    });
    app.on("activate", () => {
      if (this.mainWindow === null) {
        this.create();
      }
    });
  }
}

const my: MyWindow = new MyWindow();
my.start();

创建链接文件 render.js

由于 webpack 打包无法将 electron 生 API 打包(无法同构),所以创建一个 js 将 react app 和 electron app 连接起来

// public/render.js


import * as electron from 'electron';
import * as fs from 'fs';
// tslint:disable-next-line: no-string-literal no-var-requires
global["electron"] = electron;
// tslint:disable-next-line: no-string-literal no-var-requires
global["fs"] = fs;

创建好了之后在 index.html 中引用实现连接

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="theme-color" content="#000000">
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
    <title>React App</title>
  </head>
  <body>
  <!-- 实现连接 -->
    <script>require('./render.js')</script>
    <div id="root"></div>
  </body>
</html>

在 tsx 中调用electron/nodejs

import { Col } from "antd";
import { Row } from "antd";
import { Input } from "antd";
import { Select } from "antd";
import * as React from "react";
import "./App.css";

// tslint:disable-next-line: no-string-literal no-var-requires
// 从连接js中获取electron
const electron = global["electron"];
// tslint:disable-next-line: no-string-literal no-var-requires
// 从连接js中获取 nodejs /fs
const fs = global["fs"];
const Option = Select.Option;
class App extends React.Component {
  public render() {
    // tslint:disable-next-line: no-console
    console.log(fs);
    // tslint:disable-next-line: no-console
    console.log(electron.remote);
    const notification = new electron.remote.Notification({
      body: "this is an message body",
      title: "this is an message title"
    });
    notification.show();
    notification.show();
    notification.show();
    return (
      <div className="App" style={{ backgroundColor: "rgb(249, 249, 251)" }}>
        <Row type="flex" justify="center">
          <img src="sonar.png" />
          <div style={{ marginTop: "5%" }}>
            <Col span={24} push={6}>
              <Select defaultValue="typescript">
                <Option value="javascript">javascript</Option>
                <Option value="typescript">typescript</Option>
              </Select>
            </Col>
            <Col span={24} push={6}>
              <div style={{ width: "50%", marginTop: "10px" }}>
                <Input addonBefore="项目目录" defaultValue="" />
              </div>
            </Col>
            <Col span={24} push={6}>
              <div style={{ width: "50%", marginTop: "10px" }}>
                <Input addonBefore="组织" defaultValue="" />
              </div>
            </Col>
            <Col span={24} push={6}>
              <div style={{ width: "50%", marginTop: "10px" }}>
                <Input addonBefore="项目名称" defaultValue="" />
              </div>
            </Col>
            <Col span={24} push={6}>
              <div style={{ width: "50%", marginTop: "10px" }}>
                <Input addonBefore="项目版本" defaultValue="" />
              </div>
            </Col>
            <Col span={24} push={6}>
              <div style={{ width: "50%", marginTop: "10px" }}>
                <Input addonBefore="编码" defaultValue="UTF-8" />
              </div>
            </Col>
            <Col span={24} push={6}>
              <div style={{ width: "50%", marginTop: "10px" }}>
                <Input addonBefore="源目录" defaultValue="src/app" />
              </div>
            </Col>
            <Col span={24} push={6}>
              <div style={{ width: "50%", marginTop: "10px" }}>
                <Input
                  addonBefore="排除后缀"
                  defaultValue="**/node_modules/**,**/*.spec.ts"
                />
              </div>
            </Col>
            <Col span={24} push={6}>
              <div style={{ width: "50%", marginTop: "10px" }}>
                <Input addonBefore="测试目录" defaultValue="src/test" />
              </div>
            </Col>
            <Col span={24} push={6}>
              <div style={{ width: "50%", marginTop: "10px" }}>
                <Input addonBefore="测试后缀" defaultValue="**/*.spec.ts" />
              </div>
            </Col>
            <Col span={24} push={6}>
              <div style={{ width: "50%", marginTop: "10px" }}>
                <Input addonBefore="Lint路径" defaultValue="eslint/tslint" />
              </div>
            </Col>
            <Col span={24} push={6}>
              <div style={{ width: "50%", marginTop: "10px" }}>
                <Input
                  addonBefore="Sonar地址"
                  defaultValue="http://localhost:8080"
                />
              </div>
            </Col>
          </div>
        </Row>
      </div>
    );
  }
}

export default App;

开发/打包模式切换

dev

release

Git

源码地址