Nu este necesară nici o configurație webpack sau „expulzare”.

Am construit recent un Electron aplicație folosind creați-reacționați-aplicație. Nici nu a trebuit să-mi bat jocul cu Webpack, nici să „scot” aplicația mea. Vă voi prezenta cum am realizat acest lucru.

M-a atras ideea de a folosi creați-reacționați-aplicație deoarece ascunde detaliile de configurare webpack. Dar căutarea mea de ghiduri existente pentru utilizarea Electron și create-react-app împreună nu a dat niciun fruct, așa că am intrat și am descoperit-o eu.

Dacă te simți nerăbdător, poți să te scufunzi direct și să te uiți la codul meu. Iată Repo GitHub pentru aplicația mea.

Înainte de a începe, permiteți-mi să vă spun despre Electron și React și de ce create-react-app este un instrument atât de grozav.

Electron și React

React este cadrul de vizualizare JavaScript al Facebook.

O bibliotecă JavaScript pentru construirea interfețelor utilizator – Reacționează
O bibliotecă JavaScript pentru construirea interfețelor utilizatorfacefacebook.github.io

Și Electron este cadrul GitHub pentru construirea de aplicații desktop multiplataforma în JavaScript.

Electron
Construiți aplicații desktop pe mai multe platforme cu JavaScript, HTML și CSS.electron.atom.io

Majoritatea folosesc webpack pentru configurația necesară dezvoltării React. webpack este un instrument de configurare și construire pe care majoritatea comunității React l-a adoptat în comparație cu alternative precum Înghiţitură și Grunt.

Configurația generală variază (mai multe despre aceasta mai târziu) și există multe generatoare de aplicații și aplicații disponibile, dar în iulie 2016 Incubator Facebook a lansat un instrument, creați-reacționați-aplicație. Ascunde cea mai mare parte a configurației și permite dezvoltatorului să utilizeze comenzi simple, cum ar fi npm start și npm run build pentru a rula și a construi aplicațiile lor.

Ce este expulzarea și de ce doriți să o evitați?

create-react-app face anumite presupuneri despre o configurare tipică React. Dacă aceste ipoteze nu sunt pentru dvs., există o opțiune pentru scoate o aplicatie (npm run eject). Scoaterea unei aplicații copiază întreaga configurație încapsulată a create-react-app în proiectul dvs., oferind o configurație de tip boilerplate pe care o puteți modifica după cum doriți.

Dar acesta este un Într-un fel călătorie. Nu puteți anula scoaterea și reveniți înapoi. Au existat 49 de versiuni (începând cu acest post) de create-react-app, fiecare făcând îmbunătățiri. Dar pentru o aplicație evacuată, ar trebui să renunțați la aceste îmbunătățiri sau să aflați cum să le aplicați.

O configurație ejectată este de peste 550 de linii care acoperă 7 fișiere (începând cu acest post). Nu înțeleg totul (bine, majoritatea, de fapt) și nu vreau.

Obiective

Obiectivele mele sunt simple:

  • evitați să scoateți aplicația React
  • reduceți adezivul pentru ca React și Electron să lucreze împreună
  • păstrați valorile implicite, ipotezele și convențiile făcute de Electron și create-react-app / React. (Acest lucru poate face mai ușoară utilizarea altor instrumente care presupun / necesită astfel de convenții.)

Rețetă de bază

  1. alerga create-react-app pentru a genera o aplicație de bază React
  2. alerga npm install --save-dev electron
  3. adăuga main.js din electron-quick-start (îl vom redenumi în electron-starter.js, pentru claritate)
  4. modificați apelul către mainWindow.loadURL (în electron-starter.js) a folosi localhost:3000 (webpack-dev-server)
  5. adăugați o intrare principală la package.json pentru electron-starter.js
  6. adăugați o țintă de rulare pentru a porni Electron package.json
  7. npm start urmată de npm run electron

Pașii 1 și 2 sunt destul de simpli. Iată codul pentru pașii 3 și 4:

const electron = 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');

// 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;

function createWindow() {
    // Create the browser window.
    mainWindow = new BrowserWindow({width: 800, height: 600});

    // and load the index.html of the app.
    mainWindow.loadURL('https://localhost:3000');

    // 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
    })
}

// 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()
    }
});

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

(Gist)

Și pentru pașii 5 și 6:

{
  "name": "electron-with-create-react-app",
  "version": "0.1.0",
  "private": true,
  "devDependencies": {
    "electron": "^1.4.14",
    "react-scripts": "0.8.5"
  },
  "dependencies": {
    "react": "^15.4.2",
    "react-dom": "^15.4.2"
  },
  "main": "src/electron-starter.js",
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject",
    "electron": "electron ."
  }
}

(Gist)

Când executați comenzile npm la pasul 7, ar trebui să vedeți acest lucru:

Construirea unei aplicatii Electron cu create react app

Puteți face modificări live codului React și ar trebui să le vedeți reflectate în aplicația Electron care rulează.

1611880507 506 Construirea unei aplicatii Electron cu create react app

Acest lucru funcționează bine pentru dezvoltare, dar are două neajunsuri:

  • producția nu se va folosi webpack-dev-server. Trebuie să utilizeze fișierul static pentru a construi proiectul React
  • (mic) deranj pentru a rula ambele comenzi npm

Specificarea loadURL în Producție și Dev

În curs de dezvoltare, o variabilă de mediu poate specifica adresa URL pentru mainWindow.loadURL (în electron-starter.js). Dacă există var var, îl vom folosi; altfel vom folosi fișierul HTML static de producție.

Vom adăuga o țintă de rulare npm (la package.json) după cum urmează:

"electron-dev": "ELECTRON_START_URL=http://localhost:3000 electron ."

Actualizare: Utilizatorii Windows vor trebui să facă următoarele: (mulțumită @bfarmilo)

”electron-dev”: "set ELECTRON_START_URL=http://localhost:3000 && electron .”

În electron-starter.js, vom modifica fișierul mainWindow.loadURL sunați după cum urmează:

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

(Gist)

Există o problemă cu acest lucru: create-react-app (implicit) construiește un index.html care folosește căi absolute. Acest lucru va eșua atunci când îl încărcați în Electron. Din fericire, există o opțiune de configurare pentru a schimba acest lucru: setați un homepage proprietate în package.json. (Documentația Facebook despre proprietate este aici.)

Deci putem seta această proprietate la directorul curent și npm run build îl va folosi ca o cale relativă.

"homepage": "./",

Folosirea lui Foreman pentru a gestiona procesele de reacție și electroni

Pentru comoditate, prefer să nu

  1. lansează / gestionează atât React dev server, cât și procesele Electron (aș prefera să mă ocup de unul)
  2. așteptați pornirea serverului React dev și apoi lansarea Electron

Maistrii este un bun instrument de gestionare a proceselor. Îl putem adăuga,

npm install --save-dev foreman

și adăugați următoarele Procfile

react: npm startelectron: npm run electron

(Gist)

Aceasta se referă la (1). Pentru (2), putem adăuga un script de nod simplu (electron-wait-react.js) care așteaptă pornirea serverului React dev, apoi pornește Electron.

const net = require('net');
const port = process.env.PORT ? (process.env.PORT - 100) : 3000;

process.env.ELECTRON_START_URL = `http://localhost:${port}`;

const client = new net.Socket();

let startedElectron = false;
const tryConnection = () => client.connect({port: port}, () => {
        client.end();
        if(!startedElectron) {
            console.log('starting electron');
            startedElectron = true;
            const exec = require('child_process').exec;
            exec('npm run electron');
        }
    }
);

tryConnection();

client.on('error', (error) => {
    setTimeout(tryConnection, 1000);
});

(Gist)

NOTĂ: Foreman va compensa numărul portului cu 100 pentru procesele de diferite tipuri. (Vedea aici.) Asa de, electron-wait-react.js scade 100 pentru a seta corect numărul de port al serverului React dev.

Acum modificați fișierul Procfile

react: npm startelectron: node src/electron-wait-react

(Gist)

În cele din urmă, vom schimba obiectivele de rulare în package.json a inlocui electron-dev cu:

"dev" : "nf start"

Și acum, putem executa:

npm run dev

ACTUALIZARE (25.01.17): Am adăugat următoarea secțiune ca răspuns la unele comentarii ale utilizatorilor (aici și aici). Au nevoie de acces la Electron din aplicația React și o simplă cerere sau import aruncă o eroare. Observ o soluție mai jos.

Accesarea Electron din aplicația React

O aplicație Electron are două procese principale: gazda / împachetarea Electron și aplicația dvs. În unele cazuri, doriți accesul la Electron din aplicația dvs. De exemplu, s-ar putea să doriți să accesați sistemul de fișiere local sau să utilizați Electron’s ipcRenderer. Dar dacă faceți următoarele, veți primi o eroare

const electron = require('electron')
//or
import electron from 'electron';

Există unele discuții despre această eroare în diferite probleme GitHub și Stack Overflow, cum ar fi aceasta unu. Majoritatea soluțiilor propun modificări de configurare webpack, dar acest lucru ar necesita scoaterea aplicației.

Cu toate acestea, există o soluție simplă / hack.

const electron = window.require('electron');
const electron = window.require('electron');
const fs = electron.remote.require('fs');
const ipcRenderer  = electron.ipcRenderer;

Încheierea

Pentru comoditate, iată un Repo GitHub care are toate modificările de mai sus, cu etichete pentru fiecare pas. Dar, nu este mult de lucru să bootstrap o aplicație Electron care utilizează create-react-app. (Această postare este mult mai lungă decât codul și modificările de care ai avea nevoie pentru a le integra pe cele două.)

Și dacă utilizați create-react-app, vă recomandăm să verificați postarea mea, Testele de depanare în WebStorm și create-react-app.

Mulțumesc pentru lectură. Puteți consulta mai multe postări mele la justideas.io

ACTUALIZARE (02.02.17). Un cititor, Carl Vitullo, sugerat de utilizat npm start in loc de npm run dev și a trimis o cerere de extragere cu modificările, pe GitHub. Aceste modificări sunt disponibile în acest sens ramură.