This article has participated in the good article call order activity, click to see: back end, big front end double track submission, 20,000 yuan prize pool for you to challenge!

The introduction

Two commonly used solutions for introducing Vue into Electron projects are Vue CLI Plugin Electron Builder and electronic-VUE. In terms of weekly downloads, Vue CLI Plugin Electron Builder is about three times more widely used than Electron Vue. Next, we will introduce how to introduce Vue into Electron engineering based on Vue CLI Plugin Electron Builder.

The next application to build, UI and functionality is based on the original Tasky project (introduction Electron, hand to hand teach you to write a complete practical case). Here is a brief introduction.

Effect of case

Functional analysis:

1. Keep track of tasks to be completed and tasks completed

2. Add a task to be completed and set the time

3. Click the finished task to jump to the finished interface; Click Delete to delete the task

4. Click the “x” button in the upper right corner to close the main interface. To open the main interface again, you can use the system tray

5. When the set time is up, a reminder box will pop up in the lower right corner, as shown in the picture below.

Although is the original function, but the technology stack has the native JS changed to Vue, basically rewrote the whole project, the project architecture and coding logic are brand-new, so let us start learning with a new perspective!

Project structures,

Vue CLI Plugin Electron Builder is based on Vue CLI, so the construction of the project is very convenient.

Create the Vue project

First, install: npmi@vue /cli -g

Next, create the project: vue create tasky-vue

Running this command gives us a list of options to choose from, which in our case is as follows:

? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, Router, Vuex, CSS Pre-processors, Linter
? Choose a version of Vue.js that you want to start the project with: 3.x (Preview)
? Use history mode for router? (Requires proper server setup for index fallback in production) No
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): Sass/SCSS (with dart-sass)
? Pick a linter / formatter config: Standard
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In package.json
? Save this as a preset for future projects? (y/N) n
Copy the code

When done, the tasky-vue folder is generated.

Install the Vue CLI Plugin Electron Builder

cd tasky-vue
vue add electron-builder
Copy the code

You will be prompted to select the version of Electron. Select the latest version.

In this process, the Electron installation may fail due to network reasons. At this time, if the node_modules folder already has the Electron folder (which is an incomplete Electron package and cannot be run), delete this folder. The electron can then be re-installed using CNPM.

cnpm i electron --S
Copy the code

Note that if the above is not the latest version selected, you need to specify the version installed here, such as CNPM I [email protected] –S

Start the project

After the installation is complete, start the program with the following command:

npm run electron:serve
Copy the code

The running effect is as follows:

The initial project directory

If you have any experience in Vue development, you may find that the entire project directory is a familiar recipe, with the business code in the SRC folder.

The rendering process of the page was handed to VUE for rendering, the development process and we usually use vUE to develop web pages are similar. The electron main process code is placed in background.js.

Coding

The project supports hot update. After code modification, there is no need to manually refresh the code, which is much more convenient than DIY from zero before. We can focus more on the development of business logic

Vue project architecture analysis

The project has two main rendering processes, corresponding to two pages (main and remind), so we use multi-page packaging here.

The vue-CLI builds packages that are single-page packages by default, so we’ll redo them in vue.config.js:

Module. Exports = {module. Exports = {main: {// js entry: 'SRC /modules/main/main.js', // exports 'public/main.html', // filename generated in dist '. The main HTML ', / / title of the template HTML tag needs to be < title > < % = htmlWebpackPlugin. Options. The title % > < / title > title: 'Main Page' }, remind: { entry: 'src/modules/remind/remind.js', template: 'public/remind.html', filename: 'remind.html', title: 'Remind Page' } } }Copy the code

The project directory at this time:The main pageFor example, itsmain.jsandMain.vueThe contents are as follows:

  • main.js
import { createApp } from 'vue' import App from './Main.vue' import router from '.. /.. /router' import store from '.. /.. /store' createApp(App).use(store).use(router).mount('#app')Copy the code
  • Main.vue
<template> <div id="nav"> <div class="date">{{dateStr}}</div> <div class="nav-text"> <router-link To ="/"> To-do items </router-link> <router-link to="/finished"> Finished </router-link> </div> <router-link To ="/add"><span> create </span></ div> <div class="content"> <span class="close enable-click" </div> </div> </div> </template> <script> import { closeMain } from '.. /.. /utils/useIPC.js' export default { setup () { const date = new Date() const dateStr = `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}` return { closeMain, dateStr } } } </script>Copy the code

Route and state management

The structure and data of the remind window are simple, and the main window is analyzed here.

routing

The page architecture of the main window is mainly composed of three Tabs, corresponding to three VUE components.

The code for using vue-router is as follows:

// src/router/index.js import { createRouter, createWebHashHistory } from 'vue-router' import Todo from '.. /views/Todo.vue' const routes = [ { path: '/', name: 'Todo', component: Todo }, { path: '/finished', name: 'Finished', component: () => import(/* webpackChunkName: "finished" */ '../views/Finished.vue') }, { path: '/add', name: 'Add', component: () => import(/* webpackChunkName: "add" */ '../views/Add.vue') } ] const router = createRouter({ history: createWebHashHistory(), routes }) export default routerCopy the code

State management

The main data here are todoArray and finishedArray. These two data are operated on in each of the above three components, so vuEX is used for state management.

In this example, the task data needs to be stored using localStorage. We chose vuex to manage the data. We can use the plug-in to persist the data in VUex.

import { createStore } from 'vuex' import createPersistedState from 'vuex-persistedstate' export default createStore({ // The plugins will persist the data in vuex // You can configure which data to persist in localStorage, sessionStorage or cookie: [createPersistedState()], state: {todoArray: [], // Array of completed tasks finishedArray: [], // Array of completed tasks keepTimes: Mutations: {SET_TODO_ARRAY: (state, todoArray) => {state. TodoArray = todoArray}, SET_FINISHED_ARRAY: (state, finishedArray) => { state.finishedArray = finishedArray }, SET_KEEP_TIMES: (state, keepTimes) => { state.keepTimes = keepTimes } } })Copy the code

Composition Api

I haven’t used Vue3 before, but I used it for the first time in this project. The biggest experience I had was the Composition Api, so I’ll write it briefly here.

When using the traditional option configuration method to write components, with the increasing complexity of the business, the amount of code will continue to increase; The Composition Api is designed to solve the problem of code reusability due to the need to write code to a specific VUE instance.

In VUE2, we know that data written in data and computed is responsive, and functions written in Methods are used in template nodes.

So reactive data and page methods are tied to the VUE instance. If you have multiple vue instances, each of which has a MSG property and a changeMsg method, if you don’t define it separately for each instance, the more elegant way is to mix it in with a mixin.

The Composition Api decouples reactive data from the VUE instance. You can define reactive data anywhere by calling specific methods (reactive, ref, computed) and then use it in the VUE instance’s Setup method.

Case description

In this project, there are actually two main data structures to maintain: todoArray and finishedArray.

An operation on an array is reading the array and updating the array.

In addition, the todo.vue, Finished, and New Tasks (add.vue) components can operate data.

If you do not use the Composition Api, you define methods in each component to retrieve and update each data data. Using the Composition Api, we write all data operations in a single file (usedata.js) that can be imported into the component if needed.

import { computed, Export function useTodo () {const {proxy} = getCurrentInstance() // Get the vUE instance that calls this method const todoArray = computed(() => proxy.$store.state.todoArray) // Define the computed attribute todoArray const updateTodo = (payload) => {// define method: proxy.code.com MIT ('SET_TODO_ARRAY', payload)} return {todoArray, }} export function useFinished () {//... } export function useKeepTimes () {//... Similar to todoArrayCopy the code

Used in a component (for example, Finished. Vue) :

< span style =" box-sizing: border-box; color: RGB (74, 74, 74); line-height: 22px; font-size: 13px! Important; word-break: inherit! Important;"  :key="index"> <span class="task-text">{{item.name}}</span> <span class="flag-icon"></span> </li> </ul> <p style =" margin-top: 0px; margin-bottom: 0px; margin-bottom: 0px; margin-bottom: 0px; margin-bottom: 0px; </p> </template> <script> <script> import { useFinished, useKeepTimes } from '.. /utils/useData.js' export default { setup () { const { finishedArray } = useFinished() const { keepTimes } = UseKeepTimes () return {// return finishedArray, keepTimes}}} </script>Copy the code

The main process

The code in the main process is essentially the same as in the previous project. You can see the main process features in the Getting Started section.

In previous projects, the HTML files corresponding to the rendering process were loaded using the file:// protocol, and here you need to distinguish between the development and production environments.

In the development environment, because of the use of webpack-dev-server, you need to access the dev server address (process.env.webpack_dev_server_url) to get the updated page content in real time, whereas in the production environment, you use the file:// protocol.

//background.js app.on('ready', async () => { mainWindow = new BrowserWindow({ frame: false, resizable: false, width: 800, height: 600, icon: iconPath, webPreferences:{ backgroundThrottling: false, nodeIntegration:true, contextIsolation: False}}) if (process.env.webpack_dev_server_URL) {// in development environment, Mainwindow.loadurl (process.env.webpack_dev_server_URL + '/main.html')} else { CreateProtocol ('app') Mainwindow.loadurl (' file://${__dirname}/main.html ')} mainwindow.removemenu () setTray ()})Copy the code

packaging

Execute commands directly:

npm run electron:build
Copy the code

Package The generated content in the dist_electron folder based on the default configuration. The generated dist_electron folder contains the following contents:

Clicking tasky-Vue Setup 0.1.0 defaults to a direct one-click installation, and you can see the app icon on the desktop by default.

How to customize packaging, such as making ICONS, packaging format, installation behavior and so on, can refer to Electron application packaging and automatic update – case practice, very detailed.

Since we are using Vue CLI Plugin Electron Builder here, the packaged configuration needs to be placed in vue.config.js.

// exports of vue.config.js module.exports = {pages: {// multipage package... }, pluginOptions: { electronBuilder: { builderOptions: { // options placed here will be merged with default configuration and passed to electron-builder "appId": "This.is.tasky ", "productName":" tasky", "Copyright ": "Copyright © 2021 Alaso", "Directories ": {"buildResources": "build" }, "mac": { "category": "public.app-category.utilities" }, "dmg": { "background": "build/background.jfif", "icon": "build/icons/icon.icns", "iconSize": 100, "contents": [ { "x": 380, "y": 180, "type": "link", "path": "/Applications" }, { "x": 130, "y": 180, "type": "file" } ], "window": { "width": 540, "height": 380 } }, "win": { "target": [ "msi", "nsis" ], "icon": "build/icons/icon.ico" }, "nsis": { "oneClick": false, "language": "2052", "perMachine": true, "allowToChangeInstallationDirectory": true } } } } }Copy the code

conclusion

That’s all for today’s sharing, thank you for reading, if you feel good, welcome to like oh ❤️❤️!

For more technical exchanges, please follow my official account: Alasolala