Electron, React, Webpack ve Babel Kullanarak Hem Masaüstü Hem Tarayıcı Uygulaması Geliştirme

reactelectronwebpackbabel

6 yıl önce 7 yorum

Merhaba, bildiğiniz üzere neredeyse web üzerinde yapılabilecek tüm programları masaüstü programı yapan electron projesi var. Bu konuda detaylı anlatım yapan bir makale bulamadığım için react, webpack, babel, electron kullanarak ve bunu paketleyerek bir örnek yapmak istedim. Umarım yararlı olur.

Başlangıç için kısa bir bilgi vermeliyim. Şimdi işletim sistemi ile client arasında veri paylaşımı yapmayı anlatacağım.

Bu konuda yine tercih ettiğim üzere kurulum dışında sadece bu konuyu anlatacağım. Electron kurulumu yapmak için diğer kaynaklardan faydalanabilirsiniz.

Electron’un en sevdiğim yönü bize nodejs’in işletim sisteminde yapabildiği özellikleri sunması. Biz de bu özellikleri ui üzerinde görebiliyoruz.

Tamam peki biz nodejs üzerinde yaptığımız işleri ui’da bulunan değişkenlerle nasıl kontrol ederiz. (yani veri paylaşımı nasıl yaparız)

Bu konuyu dokümanından bakarak bulabildim bkz. Burada native ile ui’ı haberleştirmek için iki fonksiyon kullanılıyor. Bunlar ipcMain ve ipcRenderer, isimlerinden de anlaşılacağı üzere ipcMain nodejs’in erişebildiği işlemleri yapıyor, ipcRenderer ise ipcMain’e haber vererek veya dinleyerek işlem yapıyor.

Basit bir örnek ile açıklayacak olursak:

// Main process içinde tanımlıyoruz.
const os = require('os');
const {ipcMain} = require('electron')

// burada ui'ı (renderer'ı) dinleyeceğiz
// eğer çağrılırsa anında değer döndüreceğiz
ipcMain.on('isletimSisteminiGetir', (event, arg) => {
    event.returnValue = os.platform();
});
// UI içinde (yani aslında yazdığımız projenin içinde kullanımı)
const {ipcRenderer} = require('electron');
// burada sendSync ile anında değer gelmesi ve onu değişkene atmasını istiyoruz.
var sistem = ipcRenderer.sendSync('isletimSisteminiGetir')
alert('Bu bilgisayarın işletim sistemi:' + sistem);

Buraya kadar güzel görünüyor, fakat biz eğer gecikmeli bir işlem yapacaksak bunu şu şekilde yaparız:

// Main process tarafında 
const os = require('os');
const {ipcMain} = require('electron')

ipcMain.on('isletimSisteminiGetirIstersen', (event, arg) => {
    // bu şekilde uzun sürecek işlemleri tekrar yönlendirme yaparak giderebiliyoruz
    event.sender.send('buyurIsletimSistemin', os.platform());
});

// aynı şekilde renderer (ui) tarafında
const {ipcRenderer} = require('electron');

ipcRenderer.on('buyurIsletimSistemin', (event, arg) => {
    alert('Bu bilgisayarın işletim sistemi:' + arg));
})

// bu şekilde istersek bize veri dönmek zorunda olmadığı için hemen diğer satıra geçer
ipcRenderer.send('isletimSisteminiGetirIstersen');

Artık bunu da öğrendiğimize göre, Nodejs ile ne yapmak istiyorsak bunu ui üzerinde görüntüleyebiliriz demektir.

React Uygulamasını Oluşturma ve Electron’da Görüntüleme

Bir örnek üzerinde hemen deneyelim.
Bir react projesi oluşturalım. Tüm ayarlarını kendimiz yapalım. Önce yeni bir klasör oluşturuyoruz.

mkdir sistem-ne
cd sistem-ne/

Hemen package.json dosyasını oluşturmak için npm init -f komutunu çalıştırıyoruz. Uygulamamız için gerekli olan bağımlılıkları package.json dosyasında bulunan ‘dependencies’ içine bunları yazıyoruz:

{
    "react": "^15.4.1",
    "react-dom": "^15.4.1"
}

Geliştirme yapmak için ise devDependencies içine bunları yazıyoruz:

{
    "babel-core": "^6.21.0",
    "babel-loader": "^6.2.10",
    "babel-preset-es2015": "^6.18.0",
    "babel-preset-react": "^6.16.0",
    "electron": "^1.7.6",
    "html-webpack-plugin": "^2.26.0",
    "webpack": "^1.14.0",
    "webpack-dev-server": "^1.16.2"
}

Uygulamayı tarayıcı üzerinde test etmek için ise webpack-dev-server kullanacağız. Bunun için package.json dosyasına start script’ini ekliyoruz:

"scripts": {
    "start": "webpack-dev-server"
}

Ardından .babelrc dosyasını touch .babelrc ile oluşturuyoruz ve içine şu ayarları yazıyoruz:

{
    "presets": ["es2015", "react"]
}

Şimdi 2 adet webpack ayarı kullanmamız gerekecek. İlk önce webpack.config.js dosyasını yazalım:

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: './src/index.js',
    output: {
        path: './dist',
        filename: 'index_bundle.js'
    },
    module: {
        loaders: [
            { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ },
            { test: /\.jsx?$/, loader: 'babel-loader', exclude: /node_modules/ }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html',
            filename: 'index.html',
            inject: 'body'
        })
    ]
}

Sonra electron için çalıştırmamız gereken target: 'electron-renderer' özelliğini kullanmak için electron.webpack.config.js dosyasını oluşturalım çünkü UI tarafında require('electron') kullandığınız anda Error: Cannot resolve module ‘fs’ with electron and js hatası ile karşılaşırız. Çünkü biz bir browser uygulaması yapıyoruz ve doğal olarak dosya sistemine erişemiyoruz. Webpack’de target adında bir ayarlama var ve bu ayarlama ile uygulamayı neye göre derleyeceğini belirtiyoruz. webpack.config.js dosyasının aynısı fakat target özelliği ekliyoruz:

…
module.exports = {
    target: 'electron-renderer',
    …
}

Projemizde require(‘electron’) kullanmak için webpack’in bize sağladığı alias özelliğini kullanarak sanki electron varmış gibi davranmasını sağlayarak biraz önce bahsettiğim hatanın çıkmamasını sağlıyoruz. webpack.config.js dosyasına bir özellik daha ekliyoruz:

…
module.exports = {
    resolve: {
        alias: {
            // electron alias'ı için herhangi bir yol göstermemiz gerekiyor
            // biz ana dizine aliases.js adında boş bir dosya oluşturuyoruz
            electron: __dirname + '/aliases.js'
        }
    },
    …
}

Electron’u başlatmak için main.js dosyası ve (sadece electron üzerinde kullanılan bağımlılıkların olduğu) package.json dosyasına ihtiyacımız olacak o yüzden iki dosya oluşturmamız gerekiyor. İlk önce electron.package.json dosyasını oluşturalım:

{
    "name": "sistem-ne",
    "version": "1.0.0",
    "description": "",
    "main": "main.js",
    "dependencies": {}
}

İkinci dosyamız ise electron.main.js dosyası (örneğin aynısı):

const electron = require('electron')
const app = electron.app
// Tarayıcı penceresi için gerekli
const BrowserWindow = electron.BrowserWindow

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

/* pencere için global bir referans oluşturuyoruz */
let mainWindow

function createWindow () {
    // yeni tarayıcı penceresi.
    mainWindow = new BrowserWindow({width: 800, height: 600})

    // sonra index.html dosyasını uygulamaya ekliyoruz.
    mainWindow.loadURL(url.format({
        pathname: path.join(__dirname, 'index.html'),
        protocol: 'file:',
        slashes: true
    }))

    // Başlangıçta geliştirme penceresini açabilirsiniz
    // mainWindow.webContents.openDevTools()

    // Pencere kapatıldığında
    mainWindow.on('closed', function () {
        // pencere bellekte yer kaplamaması için boşaltıyoruz.
        mainWindow = null
    })
}

// Electron'a ait herşeyin yüklenmesi bittiğinde çalışır
app.on('ready', createWindow)

// Tüm pencereler hapatıldığında
app.on('window-all-closed', function () {
    // Eğer OSX değilse bu şekilde çıkabiliyor.
    // OSX için sadece Cmd + Q ile çıkış yapılabilir.
    if (process.platform !== 'darwin') {
        app.quit()
    }
})

// Uygulama penceresi her aktif olduğunda tetiklenir
app.on('activate', function () {
    // Eğer window yoksa yeniden oluşturuyor
    if (mainWindow === null) {
        createWindow()
    }
})

Şimdi bu dosyaları inşa etmek ve electron dosyalarını kopyalamak için işlemler yapalım. build, start:electron ve copy:electron script’lerini package.json dosyasına girmenin zamanı geldi:

{
    "scripts": {
        …,
        "copy:electron": "cp electron.package.json ./dist/package.json && cp electron.main.js ./dist/main.js",
        "build": "webpack .",
        "start:electron": "webpack --config electron.webpack.config.js && npm run copy:electron && electron ./dist"
    }
}

Uygulama, eğer npm run start:electron komutunu çalıştırırsak başarılı bir şekilde çalışacak. Hatırladığınız üzere örneğe başlamadan önce bir bilgi vermiştik hemen onu deneyelim. İlk önce electron.main.js dosyasında işletim sisteminin bilgisini almamız için gerekli olan kodu yazalım:

…
/* pencere için global bir referans oluşturuyoruz */
let mainWindow

const os = require('os');
const {ipcMain} = require('electron')

// burada ui'ı (renderer'ı) dinleyeceğiz
// eğer çağrılırsa anında değer döndüreceğiz
ipcMain.on('isletimSisteminiGetir', (event, arg) => {
    event.returnValue = os.platform();
});

function createWindow () {
…

Artık bunu yaptığımıza göre projemizde bulunan src/index.js dosyasında işletim sistemini alert ile gösterebiliriz:

var electron = require('electron');

if (electron.ipcRenderer) { // eğer bu kontrolü yapmazsak tarayıcı üzerinde Cannot read property 'sendSync' of undefined hatasını alırız
    const ipcRenderer = electron.ipcRenderer;
    // burada sendSync ile anında değer gelmesi ve onu değişkene atmasını istiyoruz.
    var sistem = ipcRenderer.sendSync('isletimSisteminiGetir');
    alert('Bu bilgisayarın işletim sistemi:' + sistem);
}

Ve npm run start:electron komutunu çalıştırdığımızda şu görüntü ile karşılaşırız:

e.k.1
Fakat tarayıcı üzerinde bu işlem gerçekleşmeyecektir. Çünkü tarayıcı üzerinden (native erişimli) nodejs modülü çalıştıramayız.

Electron Projesini Paketleme

Her işletim sistemi için ayrı paketleme yöntemi var. Tüm işletim sistemlerini paketleyebilecek bir proje var. Biz de onu kullanacağız. Yalnız eğer windows derlemesi yapmak istiyorsanız ve unix (benzeri OS) kullanıyorsanız bilgisayarınızda Winekurulu olması gerekir. electron-packager’i kurmak için şu komutu çalıştırıyoruz:

npm i electron-packager --save-dev

electron-packager paketleme işlemi yaparken, belirttiğiniz dizini temel alır. Yani eğer node_modules dosyası olmayan bir klasörü temel alırsanız main.js dosyasında belirttiğiniz bağımlılıklar çalışmayacaktır. Dikkat ederseniz yaptığımız projede build komutuyla herşeyi dist dizinine çıkarıyoruz. Yani bu dizinde node_modules yok! “Peki biz node_modules’i nasıl oraya taşırız. node_modules çok büyük” diyorsak şu görsele inanmalıyız :)

 

e.k.2

Bunun çözümü sembolink link eklemek olacaktır, yani dist dosyasına node_modules adında bir sembolink adres ekleyeceğiz. package.json dosyasına iki script daha ekleyelim:

{
    "scripts": {
        …,
        "link:electron": "rm -rf ./dist/node_modules && ln -s ../node_modules ./dist",
        "pack:electron": "webpack --config electron.webpack.config.js && npm run copy:electron && npm run link:electron && electron-packager ./dist"
    }
}

Kısaca açıklayacak olursak link:electron ile dist dizinine bir üst dizindeki node_modules klasörünü sembol alacak link ekliyoruz.
pack:electron ile de uygulamayı webpack ile derledikten ve gerekli işlemleri yaptıktan sonra electron-packager ./distkomutu ile uygulamayı paketliyoruz ve ana dizinde ‘sistem-ne-darwin-x64’ dosyasında paketlenmiş halini buluyoruz.

electron-packager’in dokümanını okuyarak diğer işlemleri kullanabilirsiniz. Şu an sadece bulunduğu işletim sistemine göre paketleme yapıyor. Siz –platform özelliğini kullanarak istediğiniz işletim sistemine derleyebilirsiniz.

Bu projeyi indirmek ve kurmak için:

git clone https://github.com/abdurrahmanekr/electron-react-example.git
cd electron-react-example
npm i
npm start

Aslında bu projede react ile ilgili bir işlem yapılmadı. Herhangi bir proje ile anlatmak isterdim fakat nasıl bir proje olacağı hakkında bir fikrim olmadığı için, olurda yarın projeye dönüştürme düşüncem olursa react uyumlu olsun diye react ile uyumlu yaptım. Okuduğunuz ve zaman ayırdığınız için teşekkürler :) (Proje fikriniz varsa yorumlarda belirtebilirsiniz.)

Bugün anlatacaklarım bu kadardı aklınıza takılan bir soru olursa çekinmeyin. Eğer bu konu hakkında veya konu dışı olarak bana soru sormak istiyorsanız https://github.com/abdurrahmanekr/bana-istedigini-sor repository’sinde bir issue açabilirsiniz. Youtube kanalıma abone olarak ve yayınladığım bu makaleyi paylaşarak bana destek olabilirsiniz. Diğer makalelerde görüşmek üzere :)

Yorumlar ({{totalCommentCount}})

  • Serhat

    {{commentLike42Count}} beğenme 6 yıl önce

    Keyifli ve oldukça ilgi çekici bir makale olmuş. Kitap bile çıkar bunlardan :)
    Beğen Beğendin
  • Abdurrahman Eker

    {{commentLike43Count}} beğenme 6 yıl önce

    Yazarken ben de keyif aldım :) Proje ile anlatsam daha keyifli olurdu. Güzel bir fikir bekliyorum diğer makaleler için
    Beğen Beğendin
  • ahmet

    {{commentLike56Count}} beğenme 6 yıl önce

    npm run build yazmadan otomatik build oluştursa güzel olurdu
    Beğen Beğendin
  • Abdurrahman Eker

    {{commentLike57Count}} beğenme 6 yıl önce

    Merhaba Ahmet, Bunu package.json'daki script değeri ile oynayarak yapabilirsin. "npm run start" için build komutuna && npm run build derdin böylelikle start edince aynı zamanda build de etmiş olurdu gibi. Çok şey yapılabilir kendine göre özelleştirebilirsin.
    Beğen Beğendin
  • ahmet

    {{commentLike58Count}} beğenme 6 yıl önce

    Hayır demek sitedigim npm start ederken build etmesi degil işlemi kaydettikden sonra anında build etmesi
    Beğen Beğendin
  • Abdurrahman Eker

    {{commentLike59Count}} beğenme 6 yıl önce

    Anladım, livereload'dan bahsediyorsun değil mi? Bu aslında çeşitli yollar ile yapılabilir. GitHub'da bulunan diğer kütüphanelerden faydalanılarak yapılabilir. electron.main.js dosyasındaki "loadURL" kısmını localhost'a (uygulamanın electron için yayınlandığı kısma) bağlayarak yapılabilir. Belki ihtiyaç duyulursa veya bir proje fikri çıkarsa bu makaleyi güncelleyebilirim ya da yeni bir makale yayınlayabilirim. Önemli bir konu gibi görünüyor. Önerin için teşekkürler :)
    Beğen Beğendin
  • feyyaz özdemir

    {{commentLike91Count}} beğenme 5 yıl önce

    hocam ben webpack anlayamadım buna özel bir makale yazar mısınız? bir de React,React Desktop ve React tabanlı framworkler için Gulp veya Grunt kullansak olmuyor mu?
    Beğen Beğendin
  • Düşündüklerin nedir ?

    Abdurrahman Eker

    (1010 Eylül 11111001100)

  • Full Stack Developer Turkey/Sivas
  • İnternette Avare Kodcu
  • coffee
  • github
  • instagram
  • linkedin
  • youtube
  • Yeni içeriklerden haberdar olmak ister misin ?