//Description: Electron、Vue和C++相互调用
//Create Date: 2021-02-01 13:27:33
//Author: channy
背景:需求已有electron项目,要求使用element-ui,而element-ui是基于vue 的,所以只能使用electron+vue的结构。
ps: 安装好后可以输入npm -v查看版本
pps: 考虑到墙的问题,可使用淘宝镜像,后续用cnpm代替npm
npm install -g cnpm --registry=https://registry.npm.taobao.org
npm install -g electron
cnpm install @vue/cli -g
cnpm install i -g @vue/cli-init
npm i element-ui -S -g
或者在工程目录下安装element-ui
npm i element-ui -S
vue init webpack vueinit
? Project name vueinit
? Project description A Vue.js project
? Author
? Vue build standalone
? Install vue-router? Yes
? Use ESLint to lint your code? No
? Set up unit tests No
? Setup e2e tests with Nightwatch? No
? Should we run `npm install` for you after the project has been created? (recommended) no
vue-cli · Generated "vueinit".
# Project initialization finished!
# ========================
To get started:
cd vueinit
npm install (or if using yarn: yarn)
npm run dev
Documentation can be found at https://vuejs-templates.github.io/webpack
vue create projectname
electron自身的目录结构比较简单,一个配置文件package.json,一个主页面脚本main.js和主页面main.html即可。
一般package.json里面配置了启动命令”start”: “electron ./js/main.js”指定了入口。而main.js里面有createWindow新建窗口win.loadFile(‘../main.html’)。
也可以把vue放入原electron目录中,不过需要调整vue build后的目录和文件位置。
npm install
npm run builds
npm install
npm run start
vue create新建项目后的目录
public
src
assets 资源目录
components 组件,类似于c++项目中自定义的控件
router 页面跳转
views vue页面
App.vue 主页面
main.js 主函数入口
babel.config.js
package.json vue的配置文件,包含各种依赖库。其中自定义编译命令可在scripts里面设置,如npm build对应”build”: “vue-cli-service build”
在vue的目录下手动添加vue.config.js。内容如下,指定了vue的output路径。vue-cli-service build会在该路径下生成electron需要的js文件及其它。
module.exports = {
publicPath: './',
outputDir: './dist'
};
生成的文件放到electron指定的目录,就可以用vue开发electron的页面了。
如安装nan、node-gyp等。如果需要node.js和c++相互调用,需要node-gyp
在工程目录下编译
node-gyp rebuild --target=11.2.1 --arch=ia32 --dist-url=https://electronjs.org/headers --abi=83 --build-from-sourc
其中11.2.1为本机electron的版本;83为本机node.js版本对应的abi版本(官网可查)
一种方案是使用ffi,听说性能有折损,改用第二种方案,用c++写node addon
c++写node addon,需要熟悉v8。加载dll,注册函数
//xxx.cpp,加载dll中的checkDeviceAvaiable函数并封装注册给外部调用
void CheckDeviceAndFunc(const FunctionCallbackInfo<Value>& args)
{
std::string str = "xxx.dll";
HINSTANCE hDLL = LoadLibrary(str.c_str());
bool bRes = false;
if (hDLL != nullptr)
{
typedef bool(*TYPE_CheckDeviceAvaiableDLL)();
TYPE_CheckDeviceAvaiableDLL checkDeviceAvaiableDLL = (TYPE_CheckDeviceAvaiableDLL)GetProcAddress(hDLL, "checkDeviceAvaiable");
if (checkDeviceAvaiableDLL == nullptr)
{
bRes = false;
}
else
{
bRes = (checkDeviceAvaiableDLL());
}
}
Isolate* isolate = args.GetIsolate();
Local<Number> num = Number::New(isolate, bRes);
args.GetReturnValue().Set(num);
}
void RegisterModule(Local<Object> exports, Local<Object> module, Local<Context> context) {
NODE_SET_METHOD(exports, "CheckDeviceAndFunc", CheckDeviceAndFunc);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, RegisterModule)
然后写binding.gyp文件并用node-gyp编译,生成.node文件供js调用。
//binding.gyp
{
"targets": [
{
"target_name": "ForNodeAddOn",
"include_dirs": [
"forNodeAddOn/includeDirs",
"<!(node -e \"require('nan')\")"
],
"sources": [ "forNodeAddOn/nodeAddOnMain.cpp" ,
"forNodeAddOn/managerAddOn.cpp"
],
"win_delay_load_hook":"true"
}
]
}
//
const Interface = require('./build/Release/ForNodeAddOn.node');
Interface.CheckDeviceAndFunc()
理论上,electron有两个进程:主进程(node.js)和渲染进程(vue.js),经测试暂未找到直接在vue上引用.node文件的方法。故考虑vue-js-c++路线
vue可以通过import的方式直接调用js
//xxx.vue
import FingerRecog from "@/../fingerRecog.js"
//fingerRecog.js
const FingerRecog = {
CheckDeviceAndFunc:function(){
console.log('CheckDeviceAndFunc be called in fingerRecog.js ');
}
};
module.exports = FingerRecog;
使用node addon
electron主进程和渲染进程通信用ipcMain和ipcRenderer
在electron的main.js中增加preload预加载ipc并编写ipcMain的监听函数ipcMain.on,同步sendSync异步send。
//main.js
function createWindow () {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true,
preload: __dirname + '/ipc.js'
}
})
win.loadFile('../main.html');
}
ipcMain.on('CheckDeviceAndFunc', (event, arg) => {
event.returnValue = true;
//event.sender.send('CheckDeviceAndFunc', Interface.CheckDeviceAndFunc());
})
//ipc.js
window.ipcRenderer = require('electron').ipcRenderer;
window.ipcMain = require('electron').ipcMain;
在vue调用的js中编写ipcRenderer
fingerRecog.js
const FingerRecog = {
CheckDeviceAndFunc:function(){
var res = window.ipcRenderer.sendSync('CheckDeviceAndFunc');
console.log('CheckDeviceAndFunc be called in fingerRecog.js ' + res);
return res;
/*window.ipcRenderer.once('checkDeviceAndFunc', (event, args) => {
console.log('checkDeviceAndFunc call in fingerRecog.js ' + args);
});*/
}
};
module.exports = FingerRecog;
即可以实现vue-electron-c++通信
安装electron-packager
npm install electron-packager --save-dev
设置镜像
//.npmrc
ELECTRON_MIRROR=https://npm.taobao.org/mirrors/electron/
ELECTRON_BUILDER_BINARIES_MIRROR=http://npm.taobao.org/mirrors/electron-builder-binaries/
打包发布
"packager": "electron-packager ./ outAppName --platform=win32 --arch=ia32 --out ./OutApp --electron-version 11.2.1 --overwrite --icon=./dist/favicon.ico"
上述命令打包后的结果会把node_modules也打包进去导致打包后的整个文件夹十分庞大。。。