从零创建基于Vue框架的Electron项目

之前写过一篇文章简单介绍了以下Electron项目《使用Electron创建简单的原生应用》。但是在实际工程项目中发现,Vue框架用的比较多。甚至Electron-Build脚手架中推荐的最小项目electron-webpack中也用了Vue框架。因此,我特地咨询了前端开发专家如何使用Vue框架创建一整套Electron项目。

本文章节较多,建议先马后看:

主要模块和工具

基于Vue框架的Electron项目,主要可以包括以下几个重要模块:

从零创建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等插件。
基于Vue框架的Electron项目-结构图

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项目的内容会被编译添加到appid的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页面。
在视图中使用router-view

到目前为止,我们已经成功地在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文件夹中,我们可以分别根据不同的页面,创建对应的数据类。以下是本例中的文件夹结构:
vue data store

创建model文件

随后,我们在model文件夹中新建user.js文件。该文件中将定义两个数据字段userNameip

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视图组件中直接使用变量,如图所示:
Vue使用Vuex state

效果如下:
Vue使用Vuex state结果

进阶:动态更新Vuex Store中的state数据

上例中我们在user数据store中定义了两个静态的数据变量userNameip。在实际使用中,我们需要动态更新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定义的更新方法。如图红色框部分:
Vue中更新Vuex state数据

效果如下(用户名称James变为Henry):
Vue中更新Vuex state结果

添加axios异步数据访问

axios提供一套简单易用的HTTP/HTTPS异步数据访问工具。接下来,我们将在本示例中使用axios访问远程服务,获得当前的IP地址。

  • 新建service文件夹
  • 新建service工具类
  • 在Vuex Store中异步更新数据
  • 在HelloWorld.vue组件中调用异步函数

创建service文件夹

此步骤不是必要的,但是为了更好的维护项目结构,仍然推荐创建一个service文件夹。

新建service工具类

在service文件夹中创建一个user.js工具类,该工具类将实现异步调用远程服务获取本地IP地址。此时我们使用axios库,直接发起异步访问。
Vue中service工具类

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数据字段。代码更新部分如图所示:
Vuex中调用Service访问远程服务

在HelloWorld.vue组件中调用异步函数

最后,我们在HelloWorld.vue视图组件中调用之前在user.js中创新的新方法getIP,从而更新IP数据。
Vue中调用store异步方法

这里我们在测试过程中会遇到一个错误:

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代码中修改以下部分:
electron fix cross domain

效果如下(IP变为公网IP):
Electron中使用Vue调用远程服务

axios高级应用之拦截

axios能够非常方便的在Vue项目中调用Ajax,并且提供了非常好的拦截方式。通过对请求和响应添加拦截器,我们可以统一处理一些数据全局工作。这里就不一一展开了。有兴趣的朋友可以在Github中查看axios被封装后添加拦截器使用方案。

本文中涉及到的所有源码已经上传至Github中:https://github.com/jamesliu668/electron-vue-browser

Captain QR Code

扫码联系船长

发表回复

您的电子邮箱地址不会被公开。