之前写过一篇文章简单介绍了以下Electron项目《使用Electron创建简单的原生应用》。但是在实际工程项目中发现,Vue框架用的比较多。甚至Electron-Build脚手架中推荐的最小项目electron-webpack中也用了Vue框架。因此,我特地咨询了前端开发专家如何使用Vue框架创建一整套Electron项目。
本文章节较多,建议先马后看:
主要模块和工具
基于Vue框架的Electron项目,主要可以包括以下几个重要模块:
- vuejs(首先要使用Vue-cli创建Vue项目)
- vue-cli-plugin-electron-builder(该工具会把Vue项目整合到Electron项目中)
- vuex
- vue-router
- axios
- element-ui
从零创建Electron项目
接下来我们一步一步基于以上工具创建一个基于Vue框架的Electron项目。
创建Vue项目
写这篇文章时,我们使用Yarn 1.22和Vue-Cli 4.3.1。具体使用方法可以参考官方网站,这里简单举例安装和创建一个Vue项目。
yarn global add @vue/cli #安装Vue-cli vue create electron-vue-browser #创建基于Vue的electron-vue-browser项目 cd electron-vue-browser yarn add vuex #安装Vuex yarn add vue-router #安装Vue Router yarn add axios #安装axios yarn add element-ui #安装element-ui vue add electron-builder #添加electron-builder,注意这里是vue的插件
在以上过程中我遇到了一个错误:
Error: Cannot find module ‘@vue/cli-shared-utils’
通过添加这个模块解决:
yarn add @vue/cli-shared-utils
基于Vue框架的Electron项目分析
到目前为止,我们已经添加了所有的Vue项目所需要的库和集成Electron项目的库。使用以下命令可以调式当前Electron项目:
yarn electron:serve
目录结构说明
当前我们的目录结构还是非常简单的。Vue项目中还没有使用vuex,vue-router,element-ui等插件。
background.js
该文件为Electron项目的入口JS文件。
public/index.html
该文件为Electron项目的入口页面,可以理解为整个程序的界面起始页。(由于纯HTMl代码贴进来显示有问题,暂时不贴了,大家可以在自己的项目中看到)
src/main.js
该文件为Vue项目的入口JS文件,在我们什么都没有添加的情况下,代码如下:
import Vue from 'vue' import App from './App.vue' Vue.config.productionTip = false new Vue({ render: h => h(App), }).$mount('#app')
如代码所示,整个Vue项目的内容会被编译添加到app
为id
的HTMl控件中。在index.html
文件中,我们可以找到该HTML控件为一个DIV。
App.vue
该文件为Vue的项目代码,后期我们要添加自己的页面和功能,基本上都是从这个文件开始。当引入vue-router
库以后,该文件也会变得非常简单。后文中我会给出具体的示例代码。
components文件夹
components文件夹中包含了项目用到的所有控件。当前只有一个自动生成的HelloWorld.vue组件。
添加vue-router路由支持
到目前为止,我们已经创建了一个几乎Vue框架的Electron项目。但是Vue中需要的几个重要的库都还没有支持,例如vue-router。vue-router库主要是起到一个单页应用的路由功能,具体可以参考官方文档。使用vue-router可以中心化的管理页面结构,并且方便在Vue项目中动态调用。要添加vue-router的支持,下面我们就把上节中提到的HelloWorld控件,添加到一个新页面Home中,并通过vue-router管理新页面Home的路由。步骤:
- 新建Views视图文件夹
- 新建Home.vue视图页面
- 新建router路由文件夹
- 新建route.js路由
- 在main.js中引用路由
- 在App.vue中使用路由
创建views视图文件夹
首先创建一个views文件夹,新建这个文件夹的目的是把以后所有的视图类都放在这个文件夹中。下一步,我们将在该文件夹中创建一个Home页面。
创建Home页面
Vue项目中的页面文件都是vue
结尾。因此在该文件夹中创建一个Home.vue
文件,并把上一节中App.vue
的内容剪贴复制到该文件中。由于之前App.vue
文件和Home.vue
文件的路径层级不同,因此在代码中需要注意相对路径的引用问题。如图:
创建router路由文件夹
为了保证项目更加结构化,我们同时创建一个router文件夹,这个文件夹中保存和路由相关的文件。
创建router文件
随后在router文件夹中创建一个route.js
文件,在文件中定义一个指向Home页面的路由。示例代码如下:
import Vue from 'vue' import VueRouter from 'vue-router' //定义路由表 const routes = [ { path: '/', name: 'homePage', meta: { title: '首页', hideInMenu: true, }, component: () => import('../views/Home'), } ] //重定义push函数 const routerPush = VueRouter.prototype.push VueRouter.prototype.push = function push(location) { return routerPush.call(this, location).catch(error => error) } //注册router-view Vue.use(VueRouter) //创建VueRouter实例 const router = new VueRouter({ mode: process.env.IS_ELECTRON ? 'hash' : 'history', // mode: 'history', routes, }) export default router
大家可以看到,这个文件包含了一个调用Home页面的路由。我的当前理解(可能不对)是这个项目的路由都应该定义在这个文件中,方便统一管理。因此是否需要在上一步创建文件夹也是值得讨论的。
在main中引用vue-router类
在src/main.js
中引用vue-router类。示例代码如下:
import Vue from 'vue' import App from './App.vue' import router from './router/route' Vue.config.productionTip = false new Vue({ router, render: h => h(App), }).$mount('#app')
在App中使用路由
在上一步中引用了路由类router以后,我们就能方便在项目的视图页面中通过路由调用各种页面跳转了。例如,我们在App.vue
中调用Home页面。
到目前为止,我们已经成功地在Vue项目中启用了vue-router的路由功能。下一章节中,我们将讨论vuex的状态管理功能和axios的异步网络访问功能。
添加vuex数据支持
Vue项目的一个设计思路是数据驱动。Vuex能够帮助我们统一管理数据状态(可以理解为一个状态机),并且根据不同的数据,启动Vue页面的显示状态。以下样例中,我们将定义一个username
的数据字段并且绑定到HelloWorld.vue组件上。
- 创建store文件夹及其子文件夹
- 创建model文件
- 在main.js中引用Vuex
- 在HelloWorld.vue组件中引用Vuex Store
创建store文件夹
创建一个store文件夹,并在其中创建一个model文件夹。在model文件夹中,我们可以分别根据不同的页面,创建对应的数据类。以下是本例中的文件夹结构:
创建model文件
随后,我们在model文件夹中新建user.js
文件。该文件中将定义两个数据字段userName
和ip
。
export default { namespaced: true, state: { userName: "James", ip: "127.0.0.1" } }
接下来,我们在store文件夹中新建index.js
文件。该文件将初始化Vuex.Store
并在Vue中启用Vuex类。
import Vue from 'vue' import Vuex from 'vuex' import user from './model/user' Vue.use(Vuex) export default new Vuex.Store({ state: {}, mutations: {}, actions: {}, modules: { user } })
在main.js中引用Vuex Store
我们初始化好Vuex Store以后,需要在项目中引入Vuex对象。方法极其简单,就是在初始化Vue对象时将Vuex Store对象作为参数传入Vue的构造函数即可。
import Vue from 'vue' import App from './App.vue' import router from './router/route' import store from './store' Vue.config.productionTip = false new Vue({ router, store, render: h => h(App), }).$mount('#app')
在vue组件中引用Vuex Store
如果要在Vue视图中引用Vuex Store中的数据,首先需要把Vuex中的对象import进来。以下代码中我们只import了state对象,因为在当前示例中我们暂时只用到state对象。后面的示例还会用到mapMutations和mapActions对象。代码如下:
import { mapState } from 'vuex'
随后使用state中的数据定义Vue组件中的变量,并在Vue视图组件中直接使用变量,如图所示:
效果如下:
进阶:动态更新Vuex Store中的state数据
上例中我们在user数据store中定义了两个静态的数据变量userName
和ip
。在实际使用中,我们需要动态更新Vuex中的数据。因此就需要用到mutations(同步更新)和actions(异步更新)。在实际使用中,建议即使是需要同步更新的情况,也通过调用异步函数完成(前端大佬给的建议,没有问具体根据)。因此,我们更新一下user.js
文件。
export default { namespaced: true, state: { userName: "James", ip: "127.0.0.1" }, mutations: { setUserName(state, userName) { state.userName = userName } }, actions: { init(context) { console.log('page init') context.commit("setUserName", "Henry") } } }
进阶:在Vue视图组件中更新Vuex state数据
随后我们可以在Vue视图组件中引用并调用Vuex state定义的更新方法。如图红色框部分:
效果如下(用户名称James变为Henry):
添加axios异步数据访问
axios提供一套简单易用的HTTP/HTTPS异步数据访问工具。接下来,我们将在本示例中使用axios访问远程服务,获得当前的IP地址。
- 新建service文件夹
- 新建service工具类
- 在Vuex Store中异步更新数据
- 在HelloWorld.vue组件中调用异步函数
创建service文件夹
此步骤不是必要的,但是为了更好的维护项目结构,仍然推荐创建一个service文件夹。
新建service工具类
在service文件夹中创建一个user.js
工具类,该工具类将实现异步调用远程服务获取本地IP地址。此时我们使用axios库,直接发起异步访问。
user.js
代码如下:
import axios from 'axios' export function getIP(params) { var instance = axios.create() return instance.request({ url: `http://101.132.238.47/ip.php`, params, }) }
在Vuex Store中异步更新数据
新建service以后,我们可以在Vuex的Data Model中调用Service工具获取远端数据。由于axios是异步访问,因此在actions中定义一个getIP方法,等axios数据返回后在执行mutations中的setIP方法更新ip数据字段。代码更新部分如图所示:
在HelloWorld.vue组件中调用异步函数
最后,我们在HelloWorld.vue视图组件中调用之前在user.js中创新的新方法getIP,从而更新IP数据。
这里我们在测试过程中会遇到一个错误:
Access to XMLHttpRequest at ‘http://101.132.238.47/ip.php’ from origin ‘http://localhost:8080’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
这是一个cross domain的老问题,我们需要在background.js代码中修改以下部分:
效果如下(IP变为公网IP):
axios高级应用之拦截
axios能够非常方便的在Vue项目中调用Ajax,并且提供了非常好的拦截方式。通过对请求和响应添加拦截器,我们可以统一处理一些数据全局工作。这里就不一一展开了。有兴趣的朋友可以在Github中查看axios被封装后添加拦截器使用方案。
本文中涉及到的所有源码已经上传至Github中:https://github.com/jamesliu668/electron-vue-browser
扫码联系船长