使用electron和vue,以ipc通信的方式构建客户端版本的掘金首页

6,955 阅读1分钟

1. 使用electron-vue新建基于vueelectron环境,更多配置请访问源项目

npm i -g vue-cli
vue init simulatedgreg/electron-vue my-project
cd my-project
npm i
npm run dev

2. 打通electron主进程与渲染进程的通信

定义常量作为channel,也就是事件类型名称

const CLIENT_NORMAL_MSG = 'CLIENT_NORMAL_MSG'  // 渲染进程发出消息类型
const CRAWLER_NORMAL_MSG = 'CRAWLER_NORMAL_MSG' // 主进程发出消息类型
渲染进程

在渲染进程中,使用vue plugin的形式,具体参见vue官方文档插件

ipcRenderereventEmitter的一个实例,在渲染进程中使用,你可以通过它来像主进程发送同步和异步消息,也可以通过它来接收来自主进程的消息

  const { ipcRenderer } = require('electron')
  const ipcService = Object.create(null)
  const callbackCache = []

  ipcService.install = Vue => {
    Vue.prototype.$ipcRenderer = {
      send: (msgType, msgData) => {
        ipcRenderer.send(CLIENT_NORMAL_MSG, {
          type: msgType,
          data: msgData,
        })
      },
      on: (type, callback) => {
        callbackCache.push({
          type,
          callback,
        })
      }
    }
    ipcRenderer.on(CRAWLER_NORMAL_MSG, (sender, msg) => {
      callbackCache.forEach(cache => {
        if (cache.type === msg.type) {
          cache.callback && cache.callback(msg.data)
        }
      })
    }) // 监听主进程的消息
  }

  export default ipcService

vue项目中通过this.$ipcRenderer.on的方式添加主进程发送消息的监听,通过this.$ipcRenderer.send的方式向主进程发送消息,保证发出消息均为CLIENT_NORMAL_MSG类型,收到消息均为CRAWLER_NORMAL_MSG,通过消息中的二级固定参数type来区分具体类型,并可以通过detach的方式来取消特定的类型的消息的监听

最后在Vue的入口文件,也就是渲染进程的入口文件使用上面定义的插件 Vue.use(ipcService) 渲染进程中的配置完成


主进程

使用class的方式来定义,需要传入两个参数来实例化这个class,需要传入两个参数,listener为监听消息者,sender为发送消息者

ipcMsgHandler中包含了所有的handler,为其传入this以便能够在其中向渲染进程发送消息

  import ipcMsgHandler from './ipcMsgHandler'

  export default class Ipc {
    constructor(listener, sender) {
      this.listener = listener
      this.sender = sender
      this.addListener(CLIENT_NORMAL_MSG, this.handleFn.bind(this))
      this.handlerList = ipcMsgHandler(this)
    }

    handleFn(event, data) {
      try {
        this.handlerList[data.type](event, data.data)
      } catch (error) {
        console.error('handler event error:' + error.message)
      }
    }

    addListener(chanel, cb) {
      this.listener.on(chanel, cb)
    }


    _sendMsg(chanel, msgBody) {
      this.sender.send(chanel, msgBody)
    }

    sendToClient(type, data) {
      this._sendMsg(CRAWLER_NORMAL_MSG, {
        type,
        data,
      })
    }
  }

初始状态下ipcMsgHandler.js文件

export default _ipc => ({})

在主进程的入口文件(/src/main/index.js)中对Ipc这个class实例化,其中需要使用的listeneripcMainipcMainipcRenderer不同,它只负责对消息的监听,不复杂发送消息,这里需要入口文件中的mainWindow下的webContents作为消息的发送者,所以需要在mainWindow创建成功之后再进行Ipc的实例化

// ...
function createWindow() {
  mainWindow = new BrowserWindow({
    // ...
  });
  new IpcMgr(ipcMain, mainWindow.webContents)
}

3. 完成具体功能开发

引入element-ui,使用babel配置css按需加载,具体配置方式element-ui官网

分析掘金首页数据来源

掘金首页七种分类下数据均来自一个接口,https://timeline-merger-ms.juejin.im/v1/get_entry_by_rank?src=web&limit=20&category=xxx,通过category进行分类的区分

渲染进程开发

App.js中,在进入页面和切换分类时向主进程发送消息

// ...
  methods: {
    startRequest() {
      this.$ipcRenderer.send('start-request', this.activeCategory)
    },
    onRequestBack() {
      this.$ipcRenderer.on('request-back', data => {
        // todo...
      })
    },
  }

主进程开发

现在渲染进程中已经定义了两种消息类型start-requestrequest-backstart-request告诉主进程开始执行任务,完成后request-back告诉渲染进程任务完成,渲染进程收到消息后通过收到的数据来对页面进行操作。

ipcMsgHandler.js中进行拓展,使用axios对接口内容进行抓取

  import axios from 'axios'
  const handlerList = _ipc => ({
    ['start-request'](event, category) {
      const requestBack = data => {
        _ipc.sendToClient('request-back', data)
      }
      axios.get(`https://timeline-merger-ms.juejin.im/v1/get_entry_by_rank?src=web&limit=20&category=${category}`)
        .then(r => {
          if(r.status === 200) {
            requestBack({
              success: true,
              rows: r.data.d.entrylist
            })
          } else {
            requestBack({
              success: false,
              error: `server error code: ${r.status}`
            })
          }
        })
        .catch(e => requestBack({
          success: false,
          error: `server error code: ${e.status} msg: ${e.message}`
        }))
    }
  })

请求完成后,通过requestBack向渲染进程发送消息,渲染页面,操作样式调整,页面看起来大概是这样。

4. 增加链接跳转到默认浏览器

在该项目中如果直接使用window.open的方式来打开的话,它会新弹出一个electron窗口,所有连接跳转也需要用到ipc的方式

electron中,有一个shell对象,它提供一个openExternal的方法来在默认浏览器中打开链接

ipcMsgHandler.js中新增以下内容

  const { shell } = require('electron')
  export default _ipc => ({
    ['open-shell'](event, url) {
      shell.openExternal(url)
    },
    // ...
  })

现在就可以在vue中通过this.$ipcRenderer.send('open-shell', url)的方式来在默认浏览器中打开链接了

一个通过打通IPC通信方式的掘金首页概览客户端就完成了,通过npm run build就可以对这个应用进行打包了

PS: 其实在electron中是可以支持跨域访问的,只需要在创建窗口的时候加上一下配置项就行了,不过结果其实并不重要,重要的是过程了

webPreferences: {
  webSecurity: false,
}

以上代码都可以在github上找到,欢迎star~~~