electron5+vue3.0 build a desktop application

Posted by predhtz on Thu, 10 Feb 2022 04:32:16 +0100

Before, because the project needs to do a desktop application, I learned some basic knowledge on bilibili and then referred to it[ https://zhuanlan.zhihu.com/p/75764907 ]This blog has built a desktop price query application of electron+vue. This article records the construction process and some problems encountered

I Fundamentals of electron

I think electron is a browser. What is displayed in it How to write is the same as how to write the usual page, but one is to open it directly with the browser and the other is to open it with the method of desktop application

preparation
1.cnpm init -y create package json
2. cnpm i electron -D installation
3. Create a new main JS entry file, write the main process code of electron here
4. Modify package Add JSON in script
"Package": "electron packer. / hello -- platform = Win32 -- arch = x64 -- icon = favicon. ICO -- out =. / out -- ASAR -- app version = 0.0.1 -- overwrite -- ignore = node_modules" use cnpm run package to package
5. Create a new html and display it on the electron window
Create an electron window to start formal learning
main.js directly refers to the main process, and html refers to the rendering process. The rendering process cannot directly use some properties of the main process, and remote is required

// main.js
var electron = require('electron')

var app = electron.app // Reference app

var globalShortcut = electron.globalShortcut // Global shortcut

var BrowserWindow = electron.BrowserWindow // Window reference

var mainWindow = null // Declare the main window to open

app.on('ready', () => {
    // Set up the window
    mainWindow = new BrowserWindow({
        width: 900, height: 800,
        webPreferences: {
            nodeIntegration: true,// So that all node s can be used in our rendering process
            contextIsolation: false,
            enableRemoteModule: true // remote can only be used after configuration 
        }
    })

    // Register global functions
    globalShortcut.register('ctrl+e',()=>{
        mainWindow.loadURL('https://jspang.com')
    })

    // Judge whether this shortcut key is added successfully
    let isRegister = globalShortcut.isRegistered('ctrl+e')
    console.log('-----',isRegister,'-------')

    // If the debug mode of toggle cannot be opened in the debug panel below
    // mainWindow.webContents.openDevTools()

    // Import custom menu
    // require('./main/menu')
    // Add Web pages
    mainWindow.loadFile('demo7.html') //Load html page


    // BrowserView
    // Embed subpages in a page
    /* var BrowserView = electron.BrowserView
    var view = new BrowserView()
    mainWindow.setBrowserView(view) //view Put under the main window
    view.setBounds({//Set style location
        x:0,
        y:120,
        width:900,
        height:600
    })
    // What's in the page
    view.webContents.loadURL('https://www.baidu.com') */


    // window.open sub window BrowserWindow / / opens a window

    // When closing the window, set mainwindow to null, otherwise more and more memory will be saved
    mainWindow.on('closed', () => {
        mainWindow = null
    })
})

app.on('will-quit',function(){
    // Log out of the shortcut method before exiting this application
    globalShortcut.unregister('ctrl+e') //Cancel single shortcut key

    // globalShortcut.unregisterAll() / / cancel all shortcut keys
})
// menu.js
const { Menu, BrowserWindow } = require('electron')

var template = [
    {
        label: "Feng Laiyi",
        submenu: [
            {
                label: 'Boutique spa',
                accelerator:'ctrl+n',
                click: () => {
                    var win = new BrowserWindow({
                        width: 500,
                        height: 500,
                        webPreferences: {
                            nodeIntegration: true,
                            contextIsolation: false,
                        }
                    })

                    win.loadFile('yellow.html')
                    win.on('closed', () => {
                        win = null
                    })

                }
            },
            { label: "Thai massage" }
        ]
    },
    {
        label: "mighty wave crashing on a sandy shore",
        submenu: [
            { label: 'Milk rose bath' },
            { label: "foot massage" }
        ]
    }
]

var m = Menu.buildFromTemplate(template) // Build a menu to use
Menu.setApplicationMenu(m) //Setup menu


1. Test text

...
<body>
    <button id="btn">Test text come in</button>
    <div id="myTest"></div>
</body>
<script>
var fs = require('fs')

window.onload = function(){

    var btn = this.document.querySelector('#btn')
    var myTest = this.document.querySelector('#myTest')

    btn.onclick = function(){
        fs.readFile('test.txt',(err,data) => {
            myTest.innerHTML = data
        })
    }
}
</script>

2. Open a new window

...
<body>
    <button id="btn">Open a new window</button>
    <div id="myTest"></div>
</body>
<script>
const btn = this.document.querySelector('#btn')
// This allows you to share main BrowserWindow in JS
// main.js directly refers to the main process, and html refers to the rendering process. The rendering process cannot directly use some properties of the main process, and remote is required
const BrowserWindow = require('electron').remote.BrowserWindow

window.onload = function(){
    btn.onclick = function(){
        newWin  = new BrowserWindow({
            width:500,
            height:500
        })

        newWin.loadFile('yellow.html')
        newWin.on('closed',()=>{
            newWin = null
        })
    }
}
const {remote} = require('electron')

var rightTemplate = [
    {label:"paste",accelerator:'ctrl+c',},
    {label:"copy",accelerator:'ctrl+v',}
]

var m = remote.Menu.buildFromTemplate(rightTemplate)

// Right click menu
window.addEventListener('contextmenu',(e)=>{
    // alert(1)
    e.preventDefault()
    m.popup({window:remote.getCurrentWindow()})
})
</script>

3. Open sub window

<body>
    <h1>
        <a id="aHref" href="https://www.baidu. Com "> Baidu</a>
    </h1>
    <button id="mybtn">Open sub window</button>
    <div id="mytext"></div>
</body>
<script>
var {shell} = require('electron')

var aHref = this.document.querySelector('#aHref')
var mybtn = document.querySelector('#mybtn')
var mytext = document.querySelector('#mytext')

aHref.onclick = function(e){
    e.preventDefault() // The default link will open in the current application
    var href = this.getAttribute('href')
    // This allows you to open in browser and Google browser
    shell.openExternal(href)
}

mybtn.onclick = function(){
    window.open('./popup_page.html')
}

window.addEventListener('message',(msg)=>{
    // console.log(msg)
    mytext.innerHTML = JSON.stringify(msg.data)
})
</script>
...
<body>
    <h2>I'm the child window that pops up</h2>
    <button id="popbtn">Pass information to parent window</button>
</body>
<script>
    var popbtn = document.querySelector('#popbtn')
    popbtn.onclick = (e)=>{
        // alert(1)
        window.opener.postMessage('I am the message transmitted by the sub window')
    }
</script>

4. Open the pop-up dialog box of picture saving file

<body>
    <button id="openBtn">Open picture</button>
    <button id="saveBtn">Save file</button>
    <button id="messageBtn">Pop up dialog box</button>
    <img style="width:100%" id="images">
    <script>
        const {dialog} = require('electron').remote
        const fs = require('fs')

        // Upload file
        const openBtn = document.querySelector('#openBtn')
        // You can also use input file js, which is electron ic's own way
        openBtn.onclick = function(){
            dialog.showOpenDialog({
                title:"Please choose your favorite photos",
                defaultPath:'./2.png',//You can choose to fill in the default photo path
                filters: [{name:'img',extensions:['png']}],
                buttonLabel:'Open my favorite pictures',// Set picture open button text
            }).then(res => {
                console.log(res)
                let images = document.querySelector('#images')
                images.setAttribute('src',res.filePaths[0])
            }).catch(err=>{
                console.log(err)
            })
        }

        // Saving a file is equivalent to creating a new file. After the new file is created successfully, you can write content in it
        const saveBtn = document.querySelector('#saveBtn')
        saveBtn.onclick = function(){
            dialog.showSaveDialog({
                title:"Save file"
            }).then(res => {
                fs.writeFileSync(res.filePath,'jspang.com')

            }).catch(err=>{
                console.log(err)
            })
        }

        // Pop up dialog box
        const messageBtn = document.querySelector('#messageBtn')
        messageBtn.onclick = function(){
            dialog.showMessageBox({
                type:"warning",
                title:'It's up to you whether you go or not',
                message:"Are you going",
                buttons:['I want to go','i'm not coming' ]
            }).then(res=>{
                console.log(res)
                // The subscript of buttons is returned
            })
        }
    </script>

5. Disconnection reminder function

<body>
    <h2>JSPANG.com Disconnection reminder test</h2>
</body>
<script>
// You can cut off the network and try it on the F12 debugging tool
    // online offline disconnection
    window.addEventListener('online',function(){
        // It will only be triggered when the network is disconnected again
        alert('Come on')
    })
    window.addEventListener('offline',function(){
        alert('The network is disconnected,Leave first for a while~~')
    })
</script>

6. Bottom message notification

<body>
   <button id="notifyBtn">Notification message</button>
</body>
<script>
    var notifyBtn = document.querySelector('#notifyBtn')
    var option = {
        title:'waiter,Here comes the order,Come out to pick up guests!',
        body:'A senior official turned over your sign'
    }

    notifyBtn.onclick = function(){
        new window.Notification(option.title,option)
    }
</script>

7. Shear board function

<body>
    <div>
        Activation code: <span id="code">jasdfdlfjdsklfjkldsfj</span>
        <button id='btn'>Copy activation code</button>
    </div>
    <script>
        // The clipboard can be used in both the rendering process and the main process
        const {clipboard} = require('electron')
        var btn = document.querySelector('#btn')
        var code = document.querySelector('#code')
        btn.onclick = function(){
            clipboard.writeText(code.innerHTML)
            alert('Copy successful')
        }
    </script>

II Build vue+electron project

Create vue project

vue create electron-vue-demo
custom installation

Automatic installation of electron

vue add electron-builder
During the installation process, it may get stuck due to network problems. We can forcibly end it and then execute it again
After installation, background. Will be automatically generated in src directory JS (electron code) and modify the package json

Modify package json

In package Add in JSON
"main": "background.js",
Add in scripts
"electron:build": "vue-cli-service electron:build",
"electron:serve": "vue-cli-service electron:serve",
"postinstall": "electron-builder install-app-deps",
"postuninstall": "electron-builder install-app-deps"
Add in devDependencies
"electron": "^5.0.6",
"vue-cli-plugin-electron-builder": "^1.3.5",

After installing the dependency package

cnpm install

Compile and start APP

npm run electron:serve

Compilation and packaging

npm run electron:build

vue packaged separately

npm run build

Modify the electron configuration file

The configuration files of electron and vue are put together in vue config. JS configuration
Or in package Add a build: {} configuration under JSON
In Vue config. Add more configurations in JS. Other configurations can be queried on the official website

pluginOptions: {
    electronBuilder: {
      builderOptions: {
        "appId": "com.example.app",
        "productName": "Price-look-up",//The project name is also the name of the generated installation file, namely ademo exe
        "copyright": "Copyright © 2021",//Copyright information
        "directories": {
          "output": "./dist_out"//Output file path
        },
        "extraResources":  {
          "from": "./public/config.js", // Put config Copy the JS file to the resources folder so that it is not packaged
          "to": "./config.js"
        },
        "win": {//win related configuration
          "icon": "./favicon.ico",//Icon. The current icon is in the root directory. Note that there are two pits here
          "target": [
            {
              "target": "nsis",//Making installation program with nsis
              "arch": [
                "x64"//64 bit
              ]
            }
          ]
        },
        "nsis": {
          "oneClick": false, // One click installation
          "allowElevation": true, // Allow request for promotion. If false, the user must restart the installer with elevated privileges.
          "allowToChangeInstallationDirectory": true, // Allow to modify the installation directory
          "installerIcon": "./favicon.ico",// Installation Icon
          "uninstallerIcon": "./favicon.ico",//Uninstall Icon
          "installerHeaderIcon": "./favicon.ico", // Head Icon during installation
          "createDesktopShortcut": true, // Create desktop icon
          "createStartMenuShortcut": true,// Create start menu icon
          "shortcutName": "Price-look-up", // Icon name
        }
      }
    }
  }

In background JS can modify the webPreFerences configuration and customize the header, such as

...
async function createWindow() {
  // Create the browser window.
  const win = new BrowserWindow({
    width: 1000,
    height: 800,
    // frame: false, / / cancel the default header and customize the header
    webPreferences: {
      // Use pluginOptions.nodeIntegration, leave this alone
      // See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
      // nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
      nodeIntegration: true, // node can be used on render templates
      contextIsolation: false, // In the previous version of electron, contextIsolation was false by default, but now it is true by default, so if you want to use node, you have to change contextIsolation to false
      enableRemoteModule: true,// You can use remote
      
    },
    icon: `${__static}/favicon.ico`
  })

  // Register global shortcut key Ctrl+ F12 to open development tool debugging
  globalShortcut.register('ctrl+F12', () => {
    win.webContents.openDevTools()
  })

  if (process.env.WEBPACK_DEV_SERVER_URL) {
    // Load the url of the dev server if in development mode
    await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
    if (!process.env.IS_TEST) win.webContents.openDevTools()
  } else {
    createProtocol('app')
    // Load the index.html when not in development
    win.loadURL('app://./index.html')
  }

  createMenu()
}
...

Problems encountered:

1. Use require(electron) to display on the rendering template__ dirname is undefined or FS existsSync is not a function
terms of settlement:

hold
 const {getCurrentWindow} = require('electron').remote
 Change to
const {getCurrentWindow} = window.require('electron').remote

webpack Will compile automatically js The document should read window.require Time webpack Will not compile until node To compile

The browser will still prompt window Require is not a function error, but if you open it in the way of electron ic desktop application, it will display normal

window.require is not a function
at eval (Header_menu.vue?a063:19)
at Module.../node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader-v16/dist/index.js?!./src/views/Header_menu.vue?vue&type=script&lang=js

2. We often failed in the process of running npm run electron:build packaging before. After checking, it is probably because of network problems. Some packages required for downloading electron packaging can not be downloaded. We can download them manually and then package them, or we can change them to Taobao image packaging. I changed it to Taobao image packaging and then package them successfully

Topics: Front-end Vue.js Electron