zoukankan      html  css  js  c++  java
  • [转] electron实战开发详细流程

    【From】 http://www.myk3.com/arc-8856.html

    很久没有更新博客,人越来越懒了,唉 说好做的electron教程距离上次玩electron已经过去了好几个月了。。

        这次对electron做一个实战流程的介绍,关于electron的介绍,可以看这里 http://www.myk3.com/arc-4486.html 

        关于electron的demo安装,请看 http://www.myk3.com/arc-4483.html

        使用 electron 您需要有一定的nodejs使用基础,能熟练使用js语言。那么,以这篇文档http://www.myk3.com/arc-4486.html 的项目为例,来介绍其中用到的技巧和流程。

        思想:项目的界面采用html+css+js完成,关于顶部的标题栏和最大化最小化 关闭按钮也是用html完成。后面说他们与electron 的交互。而electron 提供包含在线网页的容器框架,一个electron窗口包含一个在线网页。

        假如我们写好了登录页面,这里面有如下的地方需要雨electron 交互 甚至与node模块交互。分别是 点击页面中的新窗口打开的连接,会弹出新的无标题栏窗口,我们拦截点击事件,让其用默认浏览器打开。然后是自己写的标题栏的拖动,最大化,最小化,关闭按钮的响应。点击登录打开新窗口的响应。以及穿够页面加载完毕才显示。 如果你的网页载入很慢,建议html本地化,我这里的服务器性能不错,所以直接在线加载,速度也不错。

        那么对上面页面中需要与electron 响应的功能,下面一一说明。

        先创建一个登录窗口,并加载登录页面作为gui驱动。在main.js中写。修改你安装的demo中的createWindow方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function createWindow () {
      mainWindow = new BrowserWindow({
           500,//窗口宽度
           height: 600,//窗口高度
           resizable: false,//能否改变窗体大小
            'accept-first-mouse'true,
            title: '佳乐社区',
            frame: false,
            show: false,//是否显示界面 先设置否
            //icon:'3.ico',
            hasShadow : true
      })
      mainWindow.loadURL(baseUrl+'user.php?a=basic&f=login&app=1')
    }

        我们让其加载完毕显示,加上在载入完毕显示的代码

    1
    2
    3
    mainWindow.webContents.on( 'did-finish-load'function () {
          mainWindow.show();
      });

        这样登录窗口就显示了。接下来标题栏的拖动。很简单在标题栏的html元素上加上 css

    1
    -webkit-app-region: drag;

        对于加上-webkit-app-region: drag;的元素,只能拖动,而我们的最大化最小化,关闭都包含在其中,我们让他们可以点击,单独设置这几个按钮加上css

    1
    -webkit-app-region: no-drag;

        如图:QQ截图20160918100100.png

        

        当然,你也可以保留window的标题栏,这样就不用自己写事件处理了,不过,小安觉得自带的太丑了,所以重写。

        接下来对于页面中target="blank"a连接弹出的新窗口做拦截,让其用自带的浏览器打开,如果用无标题栏的electron打开,很丑,具体思想,调用node的exec函数执行cmd,不过需要先拦截,具体代码:

    1
    2
    3
    4
    5
    6
    mainWindow.webContents.on( 'new-window'function (event,url,fname,disposition,options) {
          var exec = require('child_process').exec; //加载node模块  url是将要跳转的地址
          //拦截url调用外部浏览器打开
          exec('start '+url, function(err,stdout,stderr){});
          event.preventDefault();
      });

        然后我们来看 最大化,最小化,关闭的处理

        这里要介绍下electron的ipc通信,具体的专业术语不知道,反正就是让main.js和各个electron窗口之间能通信,互相控制,官方有个规则,一切的electron窗口原生操作都尽量在main.js中处理,这是安全的,虽然在各个electron窗口的html页面也可以写窗口的相关操作代码。

        用实例来演示ipc的用法。main.js中创建了登录的electron窗口,在点击窗口中html写的最小化按钮时,触发页面js,我用jquery来绑定点击事件

    1
    2
    3
    4
    5
    var ipc = require('electron').ipcRenderer;
    //绑定最小化
    f.$doc.delegate('.window_min','click',function(e){
            ipc.send('login-window-min');//发送了一个login-window-min的消息,这个消息将在 main.js中接受,名称可以自定义。
        })

        记得在html页面中使用ipc一定要先引用 

    1
    require('electron').ipcRenderer;

        而在main.js中不一样,用这句 这里语法是es6,新一代的js语法,童鞋们可以学习下

    1
    const ipc = electron.ipcMain

        在main.js中处理接受到消息login-window-min 代码如下:

    1
    2
    3
    4
    //登录窗口最小化
    ipc.on('login-window-min',function(){
        mainWindow.minimize();
    })

        mainWindow是我们的登录窗口创建后返回的句柄,它是全局的,所以在main.js的任何地方都可以用,它的定义是全局的

    1
    let mainWindow

        而最大化和关闭都差不多。

        完整代码登录的html中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
            f.$doc.delegate('.window_close','click',function(e){
            ipc.send('login-window-close');
        })
        f.$doc.delegate('.window_min','click',function(e){
            ipc.send('login-window-min');  
        })
        var is_window_max = false;
        f.$doc.delegate('.window_max','click',function(e){
            ipc.send('login-window-max');
            if(is_window_max){
                is_window_max = false;
                $(this).find('div').css('fontSize','24px');
            }else{
                $(this).find('div').css('fontSize','20px');
                is_window_max = true;
            }  
        })

        main.js中的处理过程

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    //关闭所有窗口 调用ipc模块 主进程
    ipc.on('login-window-close',function(){
        app.quit();
    })
    //登录窗口最小化
    ipc.on('login-window-min',function(){
        mainWindow.minimize();
    })
    //登录窗口最大化
    ipc.on('login-window-max',function(){
        if(mainWindow.isMaximized()){
            mainWindow.restore();  
        }else{
            mainWindow.maximize(); 
        }
    })

        应为登录窗口是主窗体,所以当登录窗体关闭,应用结束,用 app.quit();

        接下来介绍登录按钮点击后创建新electron窗口,在登录html页面中写绑定,在ajax登录验证成功的回调中打开新窗体

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
                                success:function(r){
                        $('#login-error').hide();
                        $('#login-error').fadeIn(500); 
                        $('.notice-descript').text(r.msg);
                        if(r.status){
                            //检查是否记住密码
                            lo.remember_pwd();
                            //如果是软件登录则打开内容窗口
                            if(f.app){
                                ipc.send('content-window-show',r.data);
                                setTimeout(function(){
                                    $('#fm-login-submit').attr('disabled',false);  
                                },4000);
                                return false
                            }
                             
                            $('.icon-notice').removeClass('icon-error').addClass('icon-success');
                            $('.loading-mask').show();
                            if(f.backurl){//如果是其他地方跳转过来的登录
                                location.href = 'http://'+f.backurl;
                            }else{
                                location.href = 'user.php?a=index&f=home&app='+f.app;
                            }
                        }else{
                            $('#fm-login-submit').attr('disabled',false);
                        }
                    }

        主要是这一句

    1
    ipc.send('content-window-show',r.data);

        发送打开新窗体的命令,带了参数r.data.

        在main.js中我们来写监听并响应,其实和socket.io的编写方式差不多。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    //内容窗口显示 一般登录成功才行
    var contentWindow = null;
    var remindWindow = null;
    ipc.on('content-window-show',function(event,u){
        UserInfo = u;
        console.log(u);
        //先隐藏登录窗口
        setTimeout(function(){
            mainWindow.hide(); 
        },1800);
        //创建提醒窗口
        creatRemindWindow();
        //创建内容窗口   
        if(contentWindow){
            contentWindow.focus();
            return;
        }
        contentWindow = new BrowserWindow({
            frame: false,
            height: 700,
            resizable: false,
             1240,
            //icon:'3.ico',
            frame: false,
            show: false,
        });
        contentWindow.loadURL(baseUrl+'user.php?a=index&f=home&app=1');
        contentWindow.webContents.on( 'did-finish-load'function () {
            contentWindow.show();
        });
        contentWindow.setSkipTaskbar(true);
        //contentWindow.webContents.openDevTools()
        contentWindow.on('closed'function () {
            contentWindow = null;
        });
        contentWindow.webContents.on( 'new-window'function (event,url,fname,disposition,options) {
          var exec = require('child_process').exec; 
          //拦截url调用外部浏览器打开
          exec('start '+url, function(err,stdout,stderr){});
          event.preventDefault();
      });
        ipc.on('content-window-min',function(){
            contentWindow.minimize();
        })
    })

        我们看见了其实创建过程和创建登录窗口差不多,通过加载不同的

    1
    loadURL(baseUrl+'user.php?a=index&f=home&app=1'

        url来显示不同的页面作为GUI。

        到这里,如果你能看明白,那么已经可以用electron做项目开发了,毕竟,核心的东西就这些,其他是一些小技巧,比如右下角图标和右键菜单的使用,代码如下:

        载入模块

    1
    const {Menu,Tray, MenuItem} = electron;

        实现代码

    1
    2
    3
    4
    5
    6
    7
    8
    //显示右下角图标
      tray = new Tray(path.join(__dirname, '3.ico'))//右下角的图标
      const contextMenu = Menu.buildFromTemplate([//右键菜单项 可以是多个 这里只有关闭一个项
        {label: '关闭', click: function(){
            app.quit();
        }},
        //{label: 'Item2', type: 'radio'},
      ]);

        给右下角图标绑定右键菜单

    1
    tray.setContextMenu(contextMenu);

     绑定双击事件,让其窗口显示

    1
    2
    3
    4
    5
    6
    7
    tray.on('double-click',function(){
            if(contentWindow){
                contentWindow.show();  
            }else{
                mainWindow.show();
            }
      })

        上面这些代码需要写在createWindow 创建登录窗口的函数体中。

    如图:QQ截图20160918103416.png

        如果使用右下角图标,那么下方的任务栏不应该在显示程序信息,隐藏方法为

    1
    2
    //隐藏任务栏
      mainWindow.setSkipTaskbar(true);

        右键菜单不仅可以绑定给右下角图标,也可以是html元素的任何位置,具体的代码我看看 比如绑定到h1标签上,在html中写js代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //右键菜单
    const {remote} = require('electron');
    const {Menu,Tray, MenuItem} = remote;
     
    const menu = new Menu();
    menu.append(new MenuItem({label: '查看', click() { console.log('item 1 clicked'); }}));
    //menu.append(new MenuItem({type: 'separator'}));
    menu.append(new MenuItem({label: '编辑', type: 'checkbox', checked: true}));
     
    var h1 = document.getElementById('rightButton');
     
    h1.addEventListener('contextmenu', (e) => {
      e.preventDefault();
      menu.popup(remote.getCurrentWindow());
    }, false);

        当然也可以在html中(非main.js)写关闭等操作,虽然是不安全的,不推荐

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    const BrowserWindow = require('electron').remote.BrowserWindow
    const path = require('path')
     
    const newWindowBtn = document.getElementById('openNewWindwo')
    const clo = document.getElementById('close')
     
     
    newWindowBtn.addEventListener('click'function (event) {
      const modalPath = path.join('file://', __dirname, 'modal.html')
     
      let win = new BrowserWindow({id:2,  400, height: 320,title:'test' })
       
      win.loadURL(`file://${__dirname}/model.html`)//指定渲染的页面
      //win.show()//打开一个窗口
      //win.webContents.openDevTools()
      win.webContents.on( 'did-finish-load'function () {
            win.show();
        });
       
      win.on('move', updateReply) 
      win.on('resize', updateReply)
      win.on('closed'function () { win = null })
       
      function updateReply () {
        const mangageWindowReply = document.getElementById('manage-window-reply')
        const message = `Size: ${win.getSize()} Position: ${win.getPosition()}`
     
        mangageWindowReply.innerText = message
      
    })

        在html页面中(非main.js)枚举所有窗体 

    1
    2
    3
    const {dialog} = require('electron').remote;
        win = BrowserWindow.getAllWindows();
        win[0]就是第一个创建的窗口

        最后介绍下文件对话框的使用 在html中写

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //打开文件框 方便读写文件操作
    $('#dialog').on('click',function(e){
        const {dialog} = require('electron').remote;
        win = BrowserWindow.getAllWindows();
        dialog.showOpenDialog(win[0],{properties: ['openFile''multiSelections'],filters: [
        //{name: 'Images', extensions: ['jpg', 'png', 'gif']},
        //{name: 'Movies', extensions: ['mkv', 'avi', 'mp4']},
        //{name: 'Custom File Type', extensions: ['as']},
        {name: 'All Files', extensions: ['*']}
      ]},function(r){
              console.log(r);
             
          })
    })

        好了,使用上大概我也就会这些,更多的操作可以去看官方英文文档,比较详细,希望能让你更快的学会electron,下一篇文档小安会抽时间做electron的打包教程,以及跨平台,其实只要打包后,下载对应的版本,跨平台修改的代码很少,至少对我我的这个项目而言,搬运到mac系统上只删除了几行代码就能运行了,祝你好运。感谢阅读

     
     

    本文来自:绵阳史安平个人博客.励志于每一位朋友 
    欢迎转载,转载请注明本文链接: electron实战开发详细流程 
    http://www.myk3.com/arc-8856.html

  • 相关阅读:
    GPS定位的实现
    app启动监听网络类型
    tableview 刷新某一行跟某一组
    给app 添加手势验证
    类似支付宝启动页面的实现
    xcode 插件安装路径
    Windows上Dart安装
    Skynet通讯遇到的奇怪问题
    与流氓的斗争
    Skynet:Debug Console的扩展
  • 原文地址:https://www.cnblogs.com/pekkle/p/8018939.html
Copyright © 2011-2022 走看看