互联网移动化以后,催生了手机端各种app,两大手机系统上需要分别开发一个app。我一直在想,本来网页就是最便捷的跨平台方式,只要手机上有浏览器,打开对应网址就可以了,只需要开发一次,什么系统都通用。为什么不是有一个可以安装到不同系统中的容器,里面可以跑网页呢?为什么要分开开发两个app呢?
更进一步的想法是,以后所有的操作系统,都将是一个浏览器,我们所有的文件不储存在本地,而是储存在云端。所以将来我们的系统里面看见的,其实全都是使用web前端技术写的UI,网页浏览器的概念将不复存在,因为我们从一开始,就是在上网。
2015年逐渐出现一些实现前一种想法的桌面应用,gitHub团队开发的Atom编辑器和大牌Adobe开发的Brackets编辑器就是其中代表。而React-Native的出现,更加是让开发人员雀跃不已。Electron其实就是gitHub团队开发Atom时的框架,之前叫Atom-Shell。
React-Native和Electron异曲同工的地方,就是提供出一个公共的容器,以及能够调用对应系统事件的API。只是React-Native主打移动端,而Electron目前只针对桌面端而已。
下面的示例写于2016年1月,如果日后Electron的API有更新导致报错的话,请自行查询官方最新的文档。
创建文件夹并安装开发依赖包
创建文件夹electron-get-started
,或命名为其它自己喜爱的名字。在文件夹下创建下列文件,全部留空即可。
1 2 3
| . ├── index.html └── main.js
|
执行npm init
命令,生成package.json
文件。其中scripts
部分设置为"start": "electron main.js"
。
接下来安装所需的依赖包(npm默认源链接缓慢的,请使用淘宝国内镜像):
1
| npm install --save-dev electron-prebuilt
|
完成后package.json
文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| { "name": "electron-get-started", "version": "0.0.1", "description": "", "main": "main.js", "scripts": { "start": "electron main.js" }, "author": "", "license": "ISC", "devDependencies": { "electron-prebuilt": "^0.36.2" } }
|
完成以上步骤之后文件夹目录结构如下:
1 2 3 4
| . ├── index.html ├── main.js └── package.json
|
开玩
既然是个桌面程序,第一步自然是程序启动后要打开个窗口吧。在main.js
中添加下面的代码:
1 2 3 4 5 6 7 8 9
| var electron = require('electron'); var app = electron.app; var BrowserWindow = electron.BrowserWindow; app.on('ready', function () { var mainWindow = new BrowserWindow({ width: 800, height: 600 }); });
|
很明显electron
为开发者提供了方便好用的API可供调用。app
控制整个程序的生命周期,BrowserWindow
可以打开一个浏览器窗口。
注:Electron引入了两个新的概念Main Process
和Renderer Process
。以目前浅显的理解,Main Process
指的就是浏览器窗口,Renderer Process
指的就是网页。具体解释请参考官网作出的详细解释。
在main.js
中添加好上面的代码后保存,在命令行运行npm start
,应该就会打开一个800*600像素的空白窗口。从而也证明程序可以正常跑起来了。
加载网页或本地文件
既然打开了一个浏览器窗口,第一个想法自然就是,咱们开个网页吧。在main.js
里面使用一个简单的方法,就可以达到目的啦。
1 2 3 4 5 6 7 8
| app.on('ready', function () { var mainWindow = new BrowserWindow({ width: 800, height: 600 }) mainWindow.loadURL('https://github.com') })
|
重新启动程序之后就可以见到效果。
可是我们在建一个桌面程序呢,肯定是要加载自己写的文件吧。loadURL
同样可以为我们做到。把传入的参数改为本地文件路径就可以啦。
首先在index.html
里面加点简单的内容,好让渲染成功之后能看到点东西。
1 2 3 4 5 6 7 8 9
| <html> <head> <meta charset="UTF-8"> <title>electron-quick-start</title> </head> <body> <div class="main">hello world</div> </body> </html>
|
main.js
里面修改成加载本地文件:
1 2 3 4 5 6 7 8
| app.on('ready', function () { var mainWindow = new BrowserWindow({ width: 800, height: 600 }) mainWindow.loadURL('file://' + __dirname + '/index.html') })
|
重启程序应该就可以看见页面渲染出hello world
字符串了。
引入额外的js文件
打开html
文件成功的话,下一步自然就是引入一下jquery
和其它的js
吧。下面在文件夹根目录放入jquery-2.1.4.min.js
(当然也可以是另外的版本,引入的时候文件名对应就可以了),再创建一张app.js
。并在index.html
里面引入:
1 2 3 4 5 6 7 8 9 10 11
| <html> <head> <meta charset="UTF-8"> <title>electron-quick-start</title> </head> <body> <div class="main"></div> </body> <script>require('./jquery-2.1.4.min.js')</script> <script>require('./app.js')</script> </html>
|
不要问我为什么可以直接require
,因为我也没有深究。Electron就是支持哟。
有了jquery
,下面的步骤自然就是操作一下dom了。我们在app.js
里面简单地搞一下:
1
| $('.main').text('hello kitty world')
|
重启程序。
嗯,如果一切步骤正确的话,你应该是得到一个空白的窗口。哈哈哈。
打开开发者工具
好了,这应该是哪里出错了吧。是不是开始怀念chrome dev tool
了?
既然我们是打开了一个浏览器窗口,那应该也能打开dev tool
才是。Bingo!Electron可以为你做到!
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| app.on('ready', function () { var mainWindow = new BrowserWindow({ width: 800, height: 600 }) mainWindow.loadURL('file://' + __dirname + '/index.html') mainWindow.webContents.openDevTools({detach: true}) })
|
重启程序就可以在dev tool
中看到报错了。
Uncaught ReferenceError: $ is not defined
查到正确的引入方法应该是:
1 2 3 4 5 6 7 8 9 10 11
| <html> <head> <meta charset="UTF-8"> <title>electron-quick-start</title> </head> <body> <div class="main"></div> </body> <script>window.$ = require('./jquery-2.1.4.min.js')</script> <script>require('./app.js')</script> </html>
|
原因:
看一下jquery
源码:
1 2 3 4 5
| if ( typeof module === "object" && typeof module.exports === "object" ) { } else { }
|
在app.js
里面简单console.log
一下可以知道,module
和module.exports
都已经定义,所以jQuery
挂到了module
对象下,而不是全局的window.$
或window.jQuery
。
参考资料如下:
读取本地文件内容
之前loadURL
方法可以帮我们直接在浏览器中打开本地html
文件。由于有nodeJS
的加持,我们还能直接读取本地文件的内容。
html
中加一个fileContent
1 2 3 4 5 6 7 8 9 10 11 12
| <html> <head> <meta charset="UTF-8"> <title>electron-quick-start</title> </head> <body> <div class="main"></div> <div class="fileContent"></div> </body> <script>window.$ = require('./jquery-2.1.4.min.js')</script> <script>require('./app.js')</script> </html>
|
app.js
中使用nodeJS
的API,读取package.json
文件内容:
1 2 3
| var fs = require('fs') var pckJson = fs.readFileSync('./package.json', 'utf8') $('.fileContent').text(pckJson)
|
能做到这一点,就代表我们可以在app本地写一些数据来使用了。
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 46 47 48 49 50 51 52 53 54 55 56 57
| import java.util.concurrent.TimeUnit; import Nature.Time; public class mainClass { public static void main(String[] argv) { Nature.Time n = new Nature.Time(0); int maxRabbitNumber = 1024; int i = 0,k = 0; Rabbit[] r = new Rabbit[maxRabbitNumber]; int adultRabbit = 0; int initRabbitNum = 1; int existRabbitNum = initRabbitNum; int oldAdultRabbit = 0; for(;n.getTimeByMonth() <= 100;) { try { if(i <= (existRabbitNum - 1)) System.out.print("[INFO] Adding rabbit in the r."); for(;i <= (existRabbitNum - 1) && i < maxRabbitNumber;i++) { r[i] = new Rabbit(); TimeUnit.MILLISECONDS.sleep(50); System.out.print("."); } TimeUnit.MILLISECONDS.sleep(50); System.out.println(""); oldAdultRabbit = adultRabbit; for(k = adultRabbit;k <= (existRabbitNum - 1);k++) { if(r[k].getAgeByMonth() >= r[k].getAdultAgeByMonth()) { adultRabbit += 1; } } if((adultRabbit - oldAdultRabbit) != 0) System.out.println("[INFO] There are " + (adultRabbit - oldAdultRabbit) + " rabbit become adult."); n.add((int) Nature.Time.month); System.out.println("[TIME] Time passed 1 month, and this is "+ n.getTimeByMonth() +" month."); for(k = 0;k <= (existRabbitNum - 1); k++) { r[k].syncAge((int) n.getTimeByDay()); } for(k = 0;k < (adultRabbit);k++) { existRabbitNum++; } System.out.println(existRabbitNum); }catch(Exception e) { System.out.println("[ERR] Finally, time passes " + n.getTimeByDay() + " days, it means " + n.getTimeByMonth() + " months, about " + n.getTimeByYear() + " years, and " + (existRabbitNum - initRabbitNum) +" rabbits born, " + adultRabbit + " rabbits become adult. Bye."); System.exit(0); } } } }
|
参考资料:
[1] Building a Package Featuring Electron as a Stand-Alone Application
[2] electron github docs
[3] electron官方教程