electron + vue 实践项目 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
bili
V2EX    分享创造

electron + vue 实践项目

  •  
  •   bili 2017-08-27 2008:35 +08:00 3827 次点击
    这是一个创建于 2971 天前的主题,其中的信息可能已经有所发展或是发生改变。

    github 地址

    本地安装环境准备

     npm install -g cnpm --registry=https://registry.npm.taobao.org 因为 npm 的默认仓库在国外,下载很慢,国内淘宝搞了个 CNPM,每 10 分钟同步一次,完全够用了 
    • 当然也可以使用 yarn 下载
     npm install -g yarn yarn install 

    依赖包安装

    • 进入项目目录
    • 执行 cnpm install

    安装问题

    • cnpm install 之后,可能会由于网络不好而导致一些包安装不完整,这里推荐使用 yarn 进行安装
    • 需要额外安装 vue-style-loader、vue-template-compiler,不然 vue-loader 会报错
    • electron 配置项( config.js )为 true 时,运行 npm run dev,浏览器访问会报错, Uncaught ReferenceError: require is not defined,原因可以去这里看,由于配置 config.electron 是开启状态,于是 require 被 browserified 化了,不是原先 node 原生 require 函数,所以在 browser 会出现此问题
    • 应用打包的时候,需要注意 package.json 的 main 配置项 main,必须指向 electron 的主线程文件,此处为 app/index.js

    字体引入问题

    对于 webpack 对于引入字体文件一直都会有问题,有时候你使用了 file-laoder,url-loader,但是在使用还是会存在一些问题,比如渲染进程入口文件 components/App.vue 希望引入common.scsscommon.scss会去@import iconfont.css(字体样式),这时候iconfont.css的字体路径就会出现问题,webpack 一直提示找不到依赖路径。在开发环境下,我是将 iconfont.cn 获取的字体文件远程地址写进 build/index.html,这样解决了问题。正式环境下,可以将字体文件代码引入到 App.vue 文件中去

    功能列表

    • [ ] mac 安装包生成
    • [ ] 新增各个模块功能
    • [x] windows 安装包生成 -- 完成
    • [x] 应用自动更新 -- 完成
    • [x] 中英文切换 -- 完成
    • [x] 全局快捷键绑定 -- 完成
    • [x] 即时通讯功能 -- 完成
    • [x] excel 表格导入导出 -- 完成
    • [x] 登录功能 -- 完成
    • [x] mock.easy 提供数据 -- 完成

    development:

     $ npm run dev # express 开启服务,可以通过`http://localhost:port`访问(热重载) # 原理:通过 electron 创建主体窗口,`mainWindow.loadURL( http://localhost:port)`,加载应用的 index.html $ npm run app # 运行`electron ./`,生成桌面应用 

    socket.io:

     $ npm run socket 使用 express + mongoDB + socket.io 引入基于 node 的即时通讯模块 

    本地调试时,只需运行npm run dev => npm run app,需要开启即时通讯的功能的需要npm run sock,这里需要注意即时通讯模块目前没有迁移至服务器,要在本地运行,需要使用 express 起一个服务(./socket/),这里的数据库集成使用的是 mongoDB,所以必须要安装mongoDB,然后配置环境变量(比如说我安装的目录是d:,我的环境变量这样配置,D:\Program Files\MongoDB\Server\3.4\bin),这样之后,便可以使用mongodmongo命令了,执行mongod命令,一般会报错,默认存储文档的目录没有,那可以这样,新建一个文件夹,用来存储 mongo 产生的文档对象,执行mongod --dbpath D:\mongodb\db,至于 mongo(models/sechemas)、socket.io 、express 如何搭配去实现即时通讯的的功能,具体可以看代码如何实现,对于这些新的东西,也只是了解个大概,后面准备花些时间去深入学习。

    production:

     $ npm run build #生成正式文件到 app/dist 目录( eletron 应用目录) 

    package:

     $ npm run package:mac $ npm run package:win $ npm run package:linux $ npm run package 将上一步`npm run build`后生成的正式文件,进行打包,生成程序,打包至`./package`目录中 

    img

    setup:

     $ npm run setup 这里生成安装包(仅适合于 window ),将上一步生成的 package,通过 grunt 和 grunt-electron-installer 完成打包,打包至`./package_dir` 

    img

    生成安装包的过程:

    • npm run build
    • npm run package:win (目前只支持 window )
    • npm run setup

    对于打包工具,这里使用的是electron-packager,安装命令:

    rimraf package && electron-packager . TEST --platform=win32 --arch=x64 --overwrite --icon=hosts.ico --out=./package --electron-version=1.6.11 --version-string.CompanyName=TEST --version-string.ProductName=TEST --ignore=\"(build|client$|static|theme|.gitignore|LICENSE|README.md|.editorconfig|.eslintrc|node_modules|gruntPackage.json|Gruntfile.js|yarn.lock|socket|package_dir|git_img)\" 

    参数:

    • . => 应用目录
    • TEST => 应用名称
    • --platform=win32 => 要打包的平台
    • --overwrite => 覆盖模式安装
    • --icon=hosts.ico => 应用图标( window 时可以是.ico.png,mac 时可以为.icns
    • --out=./package => 输出目录
    • --electron-version => electron 版本
    • --version-string.CompanyName=TEST --version-string.ProductName=TEST => 为了生成安装包的时候,应用名字为TEST,而不是默认的electron
    • --ignore=XXX => 忽略打包的目录

    详细可看这里

    打包成安装程序,需要使用到gruntgrunt-electron-installer,请保证事先安装好
    package.json设置:

    { "version": "1.0.0", // 这个是必须的,为了后面使用 electron updater 实现自动更新 "productName": "my-electron", "description": "My Superb Vue Project For Electron", ...... } 

    Gruntfile.js 文件如下详细

    var grunt = require('grunt') // 配置 grunt.config.init({ pkg: grunt.file.readJSON('package.json'), // 这里会去获取版本号 'create-windows-installer': { x64: { authors: 'xiaobinwu <[email protected]>', // 作者 projectUrl: '', appDirectory: './package/TEST-win32-x64', // 要打包的输入目录 outputDirectory: './package_dir', // grunt 打包后的输出目录 exe: 'TEST.exe', // 生成的 exe 文件 description: 'My Superb Vue Project For Electron', setupIcon: './app/hots.ico', // 图标 noMsi: true // 是否生成.msi } } }) // 加载任务 grunt.loadNpmTasks('grunt-electron-installer') // 设置为默认 grunt.registerTask('default', ['create-windows-installer']) 

    于是就会生成如上图所示的my-electronSetup.exe,点击运行,进入一个安装的过程,会有安装的小动画,如下图:
    gif
    而我们需要的是安装完后自动生成快捷方式,这里使用的electron-squirrel-startupnpm 包,然后在主线程文件中 app/index.js 中写入startupEventHandle方法,安装时触发 squirrel.window 的一些命令,将其放在创建主体窗口的回调函数中,代码如下:

    app.on('ready', function(){ ...... startupEventHandle() ...... }) ...... function startupEventHandle () { if (require('electron-squirrel-startup')) { return } // 安装和更新时添加快捷方式,删除和卸载时删除快捷方式 var handleStartupEvent = function () { if (process.platform !== 'win32') { return false } var squirrelCommand = process.argv[1] switch (squirrelCommand) { case '--squirrel-install': case '--squirrel-updated': install() return true case '--squirrel-uninstall': uninstall() app.quit() return true case '--squirrel-obsolete': app.quit() return true } // 安装 function install () { var cp = require('child_process') var updateDotExe = path.resolve(path.dirname(process.execPath), '..', 'update.exe') var target = path.basename(process.execPath) var child = cp.spawn(updateDotExe, ['--createShortcut', target], {detached: true }) child.on('close', function (code) { app.quit() }) } // 卸载 function uninstall () { var cp = require('child_process') var updateDotExe = path.resolve(path.dirname(process.execPath), '..', 'update.exe') var target = path.basename(process.execPath) var child = cp.spawn(updateDotExe, ['--removeShortcut', target], { detached: true }) child.on('close', function (code) { app.quit() }) } } if (handleStartupEvent()) { return } } 

    这样便可以在安装时生成快捷方式,卸载时删除快捷方式了,在这个过程中,有可能回报electron-squirrel-startup module not found类似的错误,那是electron-packager打包时,过滤掉了node_moudles目录,所以需要手动添加到生成的 package 里面。至于网上的一些教程说,是需要安装 vs2015 环境,并且将 msbuild 程序声明成环境变量,但是我觉得应该是缺少 npm 包的原因,大家也可以试试,我本地是本来就安装过 vs2015 的,而且安装包没办法自定义安装目录,默认都是安装在C:\Users\Wushaobin\AppData\Local\XXX下面的。

    lint:

     $ npm run lint # lint 项目(配置规则:.eslintrc ) 

    上面的 npm run script 命令可能有些多,涉及的内容也比较多,文章后面会一一讲解!下面上一波效果图:

    gif gif gif gif gif

    electron 自动更新

    前面我们也有提到过自动更新,这里使用的官方提供的electron.autoUpdater模块去更新,坑爹的是官方对这一功能的描述真是少之又少,autoUpdater 的一些方法和事件这里可以去了解清楚,autoUpdater.setFeedURL(url)这一方法是重中之重,url放着高版本的文件(.exe,.nupkg,RELEASES),这里我是存储在阿里 oss,然后autoUpdater.checkForUpdates()会去检查是否需要更新,它会触发error、checking-for-update、update-available、update-downloaded中的一些事件,而我们需要利用主进程跟渲染进程之间的通讯( ipc/remote/webContent ),来触发更新,具体代码如下:

    function updateHandle () { ipcMain.on('check-for-update', function (event, arg) { if (process.platform !== 'win32') { return false } let appName = '门店系统' let appIcon = __dirname + '/hots.ico' let message = { error: '检查更新出错', checking: '正在检查更新……', updateAva: '下载更新成功', updateNotAva: '现在使用的就是最新版本,不用更新', downloaded: '最新版本已下载,将在重启程序后更新' } const os = require('os') const { dialog } = require('electron') // 放最新版本文件的文件夹的服务器地址 // 阿里 oss autoUpdater.setFeedURL('http://electron20170815.oss-cn-beijing.aliyuncs.com/electron/') autoUpdater.on('error', function (error) { return dialog.showMessageBox(mainWindow, { type: 'info', icon: appIcon, buttons: ['OK'], title: appName, message: message.error, detail: '\r' + error }) }) .on('checking-for-update', function (e) { return dialog.showMessageBox(mainWindow, { type: 'info', icon: appIcon, buttons: ['OK'], title: appName, message: message.checking }) }) .on('update-available', function (e) { var downloadCOnfirmation= dialog.showMessageBox(mainWindow, { type: 'info', icon: appIcon, buttons: ['OK'], title: appName, message: message.updateAva }) if (downloadCOnfirmation=== 0) { return } }) .on('update-not-available', function (e) { return dialog.showMessageBox(mainWindow, { type: 'info', icon: appIcon, buttons: ['OK'], title: appName, message: message.updateNotAva }) }) .on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) { var index = dialog.showMessageBox(mainWindow, { type: 'info', icon: appIcon, buttons: ['现在重启', '稍后重启'], title: appName, message: message.downloaded, detail: releaseName + '\n\n' + releaseNotes }) if (index === 1) { return } autoUpdater.quitAndInstall() }) autoUpdater.checkForUpdates() }) } 

    如果内容对你有帮助的话,可以去github给个 star !!!!

    参考资料:
    https://segmentfault.com/a/1190000008287730
    https://segmentfault.com/a/1190000007616641
    https://juejin.im/entry/5805e39ad20309006854e58f
    https://github.com/hua1995116/webchat


    Generated by VuePack. vuePack Issue

    1 条回复    2017-08-31 14:05:36 +08:00
    hjdtl
        1
    hjdtl  
       2017-08-31 14:05:36 +08:00
    谢谢,正想了解一下 electron
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1574 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 16:30 PVG 00:30 LAX 09:30 JFK 12:30
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86