Criando aplicativos de desktop com Electron e Vue

Publicados: 2022-03-10
Resumo rápido ↬ Electron é uma estrutura de software de código aberto desenvolvida e mantida pelo GitHub. Ele permite o desenvolvimento de aplicativos GUI de desktop usando tecnologias da web. Neste tutorial, Timi Omoyeni explica o que você precisa ter em mente ao construir um aplicativo de desktop com Vue.js usando o Vue CLI Plugin Electron Builder.

JavaScript costumava ser conhecido como a linguagem para a construção de sites e aplicativos da Web, especialmente com alguns de seus frameworks, como React, Vue e Angular, mas com o tempo (já em 2009), tornou-se possível que o JavaScript fosse executado fora do navegador com o surgimento do Node.js, um ambiente de tempo de execução JavaScript de código aberto e multiplataforma que executa código JavaScript fora de um navegador da web. Isso levou à capacidade de usar JavaScript para muito mais do que apenas aplicativos da Web, e um dos quais é a criação de aplicativos de desktop usando o Electron.js.

O Electron permite que você crie aplicativos de desktop com JavaScript puro, fornecendo um tempo de execução com APIs nativas ricas (sistema operacional). Você pode vê-lo como uma variante do runtime do Node.js que se concentra em aplicativos de desktop em vez de servidores web.

Neste tutorial, vamos aprender a construir aplicativos de desktop usando Electron, também vamos aprender a usar Vuejs para construir aplicativos Electron.

Nota : É necessário conhecimento básico de Vue.js e Vue CLI para seguir este tutorial. Todo o código usado neste tutorial pode ser encontrado no meu GitHub. Sinta-se à vontade para clonar e brincar com ele!

O que são aplicativos de desktop?

Aplicativos de desktop são aplicativos executados de forma independente em computadores desktop ou laptop. São aplicativos que executam tarefas específicas e são instalados exclusivamente para esse fim.

Um exemplo de aplicativo de desktop é o Microsoft Word, que é usado para criar e digitar documentos. Outros exemplos de aplicativos de desktop comuns são navegadores da Web, Visual Studio Code e Adobe Photoshop. Os aplicativos de desktop são diferentes dos aplicativos da web porque você precisa instalar o aplicativo de desktop para poder acessá-lo e usá-lo, e às vezes eles não precisam de acesso à Internet para funcionar. Os aplicativos da Web, por outro lado, podem ser acessados ​​simplesmente visitando a URL em que esse aplicativo está hospedado e sempre precisam de acesso à Internet antes que você possa acessá-los.

Exemplos de estruturas usadas na criação de aplicativos de desktop incluem:

  1. Java
    Java é uma linguagem de programação de propósito geral que é baseada em classes, orientada a objetos e projetada para ter o menor número possível de dependências de implementação. Destina-se a permitir que os desenvolvedores de aplicativos escrevam uma vez, executem em qualquer lugar (WORA), o que significa que o código Java compilado pode ser executado em todas as plataformas que suportam Java sem a necessidade de recompilação.
  2. Java FX
    De acordo com sua documentação oficial, é uma plataforma de aplicativo cliente de código aberto e de próxima geração para sistemas desktop, móveis e embarcados construídos em Java.
  3. C#
    C# é uma linguagem de programação multiparadigma de propósito geral que abrange disciplinas de programação de tipagem forte, escopo lexical, imperativo, declarativo, funcional, genérico, orientado a objetos e orientado a componentes.
  4. .INTERNET
    O .NET é uma plataforma de desenvolvedor de código aberto, multiplataforma e gratuita para construir muitos tipos diferentes de aplicativos. Com o .NET, você pode usar vários idiomas, editores e bibliotecas para criar para Web, dispositivos móveis, desktop, jogos e IoT.
Mais depois do salto! Continue lendo abaixo ↓

O que é elétron?

Electron é uma estrutura de código aberto para a construção de aplicativos de desktop. Anteriormente era conhecido como 'Atom shell' e é desenvolvido e mantido pelo GitHub. Ele permite que você escreva aplicativos de desktop multiplataforma usando HTML, CSS e JavaScript. Isso significa que você pode criar aplicativos de desktop para Windows, MacOS e outras plataformas usando uma base de código. É baseado em Node.js e Chromium. Exemplos de aplicativos criados com Electron incluem o popular editor Atom, Visual Studio Code, Wordpress para desktop e Slack.

Instalação

Você pode instalar o Electron em seu projeto usando o NPM:

 npm install electron --save-dev

Você também pode instalá-lo globalmente se for trabalhar muito com aplicativos de elétrons usando este comando:

 npm install electron -g

Criando aplicativos Vuejs para desktop com Electron

Se você estiver familiarizado com a criação de aplicativos da Web usando o Vuejs, é possível criar aplicativos de desktop usando o Vuejs. Tudo que você precisa para isso é o Vue CLI Plugin Electron Builder.

O construtor eletrônico de plug-in Vue CLI

Esta ferramenta permite que você crie aplicativos Vue para desktop com Electron, isso significa que faz seu aplicativo Vue funcionar como um aplicativo eletrônico. Isso significa que seu aplicativo Vue, que possivelmente é um aplicativo da Web, pode ser estendido para funcionar em ambientes de desktop sem a necessidade de criar um aplicativo de desktop separado em outro framework. Isso dá aos desenvolvedores Vue a opção e o poder de ir além da web. No futuro, você pode trabalhar nessa ideia e oferecer aos usuários uma opção de aplicativo de desktop – que pode ser executada no Windows, macOS e Linux.

Para ver isso em ação, criaremos um aplicativo de notícias usando a API de notícias. O aplicativo fornecerá manchetes de notícias de última hora e permitirá que você pesquise artigos de fontes de notícias e blogs em toda a web com sua API. Tudo o que você precisa para começar com eles é sua chave de API pessoal, que pode ser obtida aqui.

Construiremos um aplicativo simples que oferece o seguinte:

  1. Uma página que exibe as principais manchetes e as últimas manchetes de um país selecionado com a opção de escolher um país usando seu ponto de extremidade /top-headlines . A API de notícias fornece notícias de uma lista de países que eles suportam, encontre a lista aqui.
  2. Notícias de uma categoria selecionada usando uma combinação de seu endpoint /everything e um parâmetro de consulta q com o qual especificaremos nossa categoria.

Depois de obter sua chave de API, podemos criar nosso aplicativo usando a Vue CLI. Certifique-se de ter o Vue CLI instalado em seu sistema, caso não tenha, instale-o usando este comando:

 npm install -g @vue/cli # OR yarn global add @vue/cli

Feito isso, crie seu aplicativo de notícias usando a CLI:

 vue create news-app

Buscaremos os dados da API de notícias usando o Axios para este tutorial, mas você pode usar qualquer alternativa com a qual se sinta mais confortável. Você pode instalar o Axios usando qualquer um dos seguintes comandos:

 //NPM npm install axios // YARN yarn add axios

A próxima etapa seria configurar uma instância do Axios para configuração global em nosso aplicativo. Vamos criar uma pasta de plugins na pasta src onde criaremos este arquivo axios.js . Após criar o arquivo, adicione as seguintes linhas de código:

 import axios from "axios"; let baseURL = `https://newsapi.org/v2`; let apiKey = process.env.VUE_APP_APIKEY; const instance = axios.create({ baseURL: baseURL, timeout: 30000, headers: { "X-Api-Key": apiKey, }, }); export default instance;

Aqui, definimos nossa baseURL e apiKey que obtivemos da API de notícias e as passamos para uma nova instância do Axios. Essa instância aceita o baseURL e apiKey junto com uma propriedade de timeout . A API de notícias exige que você adicione sua chave de API ao fazer uma solicitação à API e oferece três maneiras de anexá-la à sua solicitação, mas aqui estamos adicionando-a à propriedade X-Api-Key do cabeçalho, após a qual exportamos instance . Feito isso, agora podemos usar essa configuração para todas as nossas solicitações do Axios em nosso aplicativo.

Quando isso for feito, você pode adicionar o construtor Plugin Electron com a CLI usando este comando:

 vue add electron-builder

Você será solicitado a selecionar sua versão preferida do Electron, selecionei a versão 9.0.0 porque é a versão mais recente do Electron (no momento da redação).

Quando isso for feito, agora você pode servir seu aplicativo usando este comando:

 Using Yarn(strongly recommended) yarn electron:serve OR NPM npm run electron:serve

Isso levará algum tempo para compilar e veicular seu aplicativo. Quando isso for feito, seu aplicativo será aberto em seu sistema, devendo ficar assim:

estado aberto padrão do seu aplicativo de elétrons
Estado de abertura automática do seu aplicativo de elétrons. (Visualização grande)

Se você fechar o devtools do seu aplicativo, ele deve ficar assim:

andando na página do seu aplicativo
Página de destino do seu aplicativo. (Visualização grande)

Este plugin de elétrons é super útil e fácil de usar porque todas as partes do desenvolvimento deste aplicativo funcionam da mesma forma que um aplicativo Vue. Isso significa que você pode ter uma base de código para seu aplicativo da Web e para o aplicativo de desktop. Nosso aplicativo terá três partes:

  1. Uma página de destino que exibe as principais notícias de um país escolhido aleatoriamente.
  2. Uma página para renderizar as principais notícias do país de escolha do usuário.
  3. Uma página que renderiza as principais notícias de uma categoria de seleção do usuário.

Para isso, precisaremos de um componente de cabeçalho para todos os nossos links de navegação. Então, vamos criar um arquivo na pasta de componentes e nomeá-lo header.vue e depois adicionar as seguintes linhas de código a ele:

 <template> <header class="header"> <div class="logo"> <div class="logo__container"> <img src="../assets/logo.png" alt="News app logo" class="logo__image" /> </div> <h1>News App</h1> </div> <nav class="nav"> <h4 class="nav__link"> <router-link to="/home">Home</router-link> </h4> <h4 class="nav__link"> <router-link to="/top-news">Top News</router-link> </h4> <h4 class="nav__link"> <router-link to="/categories">News By Category</router-link> </h4> </nav> </header> </template> <script> export default { name: "app-header", }; </script> <style> .header { display: flex; flex-wrap: wrap; justify-content: space-between; } .logo { display: flex; flex-wrap: nowrap; justify-content: space-between; align-items: center; height: 50px; } .logo__container { width: 50px; height: 50px; } .logo__image { max-width: 100%; max-height: 100%; } .nav { display: flex; flex-wrap: wrap; width: 350px; justify-content: space-between; } </style>

Aqui, criamos um componente de cabeçalho que tem o nome e o logotipo do nosso aplicativo (a imagem pode ser encontrada no meu GitHub) junto com uma seção de navegação que contém links para as outras partes do nosso aplicativo. A próxima coisa seria importar esta página em nossa página de layout — App.vue para que possamos ver nosso cabeçalho em todas as páginas.

 <template> <div> <app-header /> <router-view /> </div> </template> <script> import appHeader from "@/components/Header.vue"; export default { name: "layout", components: { appHeader, }, }; </script> <style> @import url("https://fonts.googleapis.com/css2?family=Abel&family=Staatliches&display=swap"); html, #app { min-height: 100vh; } #app { font-family: "Abel", sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; background-color: #fff; } #app h1 { font-family: "Staatliches", cursive; } a { font-weight: bold; color: #2c3e50; text-decoration: none; } a:hover { text-decoration: underline; } a.router-link-exact-active { color: #42b983; } </style>

Aqui, substituímos o conteúdo padrão na seção de modelo pelo nosso componente de cabeçalho recém-criado após importá-lo e declará-lo na seção de script. Por fim, adicionamos alguns estilos para todo o aplicativo na seção de estilo.

Agora, se tentarmos visualizar nosso aplicativo, ele deve ficar assim:

página de destino vazia
Página de destino vazia. (Visualização grande)

O próximo passo seria adicionar conteúdo ao nosso arquivo Home.vue . Esta página hospedaria a primeira seção do nosso aplicativo; As principais notícias de um país selecionado aleatoriamente. Atualize seu arquivo Home.vue com as seguintes linhas de código:

 <template> <section class="home"> <h1>Welcome to News App</h1> <h4>Displaying Top News from {{ countryInfo.name }}</h4> <div class="articles__div" v-if="articles"> <news-card v-for="(article, index) in articles" :key="index" :article="article" ></news-card> </div> </section> </template> <script> import { mapActions, mapState } from "vuex"; import NewsCard from "../components/NewsCard"; export default { data() { return { articles: "", countryInfo: "", }; }, components: { NewsCard, }, mounted() { this.fetchTopNews(); }, computed: { ...mapState(["countries"]), }, methods: { ...mapActions(["getTopNews"]), async fetchTopNews() { let countriesLength = this.countries.length; let countryIndex = Math.floor( Math.random() * (countriesLength - 1) + 1 ); this.countryInfo = this.countries[countryIndex]; let { data } = await this.getTopNews( this.countries[countryIndex].value ); this.articles = data.articles; }, }, }; </script> <style> .articles__div { display: flex; flex-wrap: wrap; justify-content: center; } </style>

Na seção de script deste arquivo, importamos mapState e mapActions do Vuex, que usaremos posteriormente neste arquivo. Também importamos um componente NewsCard (vamos criá-lo a seguir) que renderizaria todas as manchetes desta página. Em seguida, usamos o método fetchTopNews para buscar as últimas notícias de um país selecionado aleatoriamente da matriz de countries em nossa loja. Este país é passado para nossa ação getTopNews , isso seria anexado ao baseURL como uma consulta para um país como baseURL/top-news?country=${randomCountry} . Feito isso, percorremos esses dados e os passamos para o article prop do nosso componente Newscard na seção de modelos. Também temos um parágrafo que indica de qual país são as principais notícias.

A próxima coisa seria configurar nosso componente NewsCard que exibirá essas notícias. Crie um novo arquivo dentro da pasta de componentes , nomeie-o como NewsCard.vue e adicione as seguintes linhas de código a ele:

 <template> <section class="news"> <div class="news__section"> <h1 class="news__title"> <a class="article__link" :href="article.url" target="_blank"> {{ article.title }} </a> </h1> <h3 class="news__author" v-if="article.author">{{ article.author }}</h3> <!-- <p class="article__paragraph">{{ article.description }}</p> --> <h5 class="article__published">{{ new Date(article.publishedAt) }}</h5> </div> <div class="image__container"> <img class="news__img" src="../assets/logo.png" :data-src="article.urlToImage" :alt="article.title" /> </div> </section> </template> <script> export default { name: "news-card", props: { article: Object, }, mounted() { this.lazyLoadImages(); }, methods: { lazyLoadImages() { const images = document.querySelectorAll(".news__img"); const options = { // If the image gets within 50px in the Y axis, start the download. root: null, // Page as root rootMargin: "0px", threshold: 0.1, }; const fetchImage = (url) => { return new Promise((resolve, reject) => { const image = new Image(); image.src = url; image.onload = resolve; image.onerror = reject; }); }; const loadImage = (image) => { const src = image.dataset.src; fetchImage(src).then(() => { image.src = src; }); }; const handleIntersection = (entries) => { entries.forEach((entry) => { if (entry.intersectionRatio > 0) { loadImage(entry.target); } }); }; // The observer for the images on the page const observer = new IntersectionObserver(handleIntersection, options); images.forEach((img) => { observer.observe(img); }); }, }, }; </script> <style> .news { width: 100%; display: flex; flex-direction: row; align-items: flex-start; max-width: 550px; box-shadow: 2px 1px 7px 1px #eee; padding: 20px 5px; box-sizing: border-box; margin: 15px 5px; border-radius: 4px; } .news__section { width: 100%; max-width: 350px; margin-right: 5px; } .news__title { font-size: 15px; text-align: left; margin-top: 0; } .news__author { font-size: 14px; text-align: left; font-weight: normal; } .article__published { text-align: left; } .image__container { width: 100%; max-width: 180px; max-height: 180px; } .news__img { transition: max-width 300ms cubic-bezier(0.4, 0, 1, 1), max-height 300ms cubic-bezier(0.4, 0, 1, 1); max-width: 150px; max-height: 150px; } .news__img:hover { max-width: 180px; max-height: 180px; } .article__link { text-decoration: none; color: inherit; } </style>

Aqui, exibimos os dados passados ​​para este componente usando a prop do objeto do article . Também temos um método que carrega com preguiça as imagens anexadas a cada artigo. Esse método percorre o número de imagens de artigos que temos e as carrega com preguiça quando elas se tornam visíveis. Finalmente, temos estilos direcionados a este componente na seção de estilo.

O próximo passo será montar nossa loja para que possamos começar a receber as últimas novidades. Adicione as seguintes linhas de código ao seu arquivo index.js :

 import Vue from "vue"; import Vuex from "vuex"; import axios from "../plugins/axios"; Vue.use(Vuex); const store = new Vuex.Store({ state: { countries: [{ name: "United States of America", value: "us", }, { name: "Nigeria", value: "ng", }, { name: "Argentina", value: "ar", }, { name: "Canada", value: "ca", }, { name: "South Africa", value: "za", }, ], categories: [ "entertainment", "general", "health", "science", "business", "sports", "technology", ], }, mutations: {}, actions: { async getTopNews(context, country) { let res = await axios({ url: `/top-headlines?country=${country}`, method: "GET", }); return res; }, }, }); export default store;

Estamos adicionando duas propriedades à nossa loja, uma dessas propriedades é countries . Esta propriedade contém um array de objetos de países. Também temos a propriedade de categories ; ele contém uma série de categorias disponíveis na API de notícias. O leitor vai gostar da liberdade de ver as principais notícias de países e categorias específicas; isso também será necessário em mais de uma parte do aplicativo e é por isso que estamos fazendo uso da loja. Na seção de ações de nossa loja, temos um método getTopNews que busca as principais notícias de um país (este país foi passado do componente que chamou esta ação).

Neste ponto, se abrirmos nosso aplicativo, devemos ver nossa página de destino que se parece com isso:

Página de destino atualizada
Página de destino atualizada. (Visualização grande)

O arquivo background.js

Este arquivo é o ponto de entrada para o Electron em seu aplicativo. Ele controla todas as configurações semelhantes ao aplicativo Desktop para este aplicativo. O estado padrão deste arquivo pode ser encontrado no meu GitHub.

Neste arquivo, temos algumas configurações predefinidas definidas para o aplicativo, como a height e a width padrão do seu aplicativo. Vamos dar uma olhada em algumas das coisas que você pode fazer neste arquivo.

Habilitando as devtools do Vuejs

Por padrão, você tem acesso às ferramentas de desenvolvimento no Electron, mas não é ativado após a instalação. Isso é resultado de um bug existente no Windows 10, portanto, se você abrir o arquivo background.js , encontrará algum código com comentários com comentários que informam por que eles foram comentados:

 // Install Vue Devtools // Devtools extensions are broken in Electron 6.0.0 and greater // See https://github.com/nklayman/vue-cli-plugin-electron-builder/issues/378 for more info // Electron will not launch with Devtools extensions installed on Windows 10 with dark mode // If you are not using Windows 10 dark mode, you may uncomment these lines // In addition, if the linked issue is closed, you can upgrade electron and uncomment these lines // try { // await installVueDevtools() // } catch (e) { // console.error('Vue Devtools failed to install:', e.toString()) // }

Então, se você não for afetado por esse bug, você pode descomentar o bloco try/catch e também procurar por installVueDevtools neste mesmo arquivo (linha 5) e também descomentá-lo. Feito isso, seu aplicativo será reiniciado automaticamente e, quando você verificar suas ferramentas de desenvolvimento, deverá ver o Vuejs Devtools.

Vuejs em devtools
Vuejs em devtools. (Visualização grande)

Selecionando um ícone personalizado para seu aplicativo

Por padrão, o ícone do Electron é definido como o ícone padrão do seu aplicativo e, na maioria das vezes, você provavelmente gostaria de definir seu próprio ícone personalizado. Para fazer isso, mova seu ícone para sua pasta pública e renomeie-o para icon.png . A próxima coisa a fazer seria adicionar a dependência necessária, electron-icon-builder .

Você pode instalá-lo usando qualquer um dos seguintes comandos:

 // With Yarn: yarn add --dev electron-icon-builder // or with NPM: npm install --save-dev electron-icon-builder

Feito isso, você pode executar este próximo comando. Ele converterá seu ícone no formato Electron e imprimirá o seguinte em seu console quando isso for feito.

informações geradas no terminal
Informações geradas no terminal. (Visualização grande)

A próxima coisa seria definir a opção de ícone no arquivo background.js . Esta opção vai dentro da opção BrowserWindow que é importada do Electron . Para fazer isso, atualize o BrowserWindow para ficar assim:

 // Add this to the top of your file /* global __static */ // import path import path from 'path' // Replace win = new BrowserWindow({ width: 800, height: 600 }) // With win = new BrowserWindow({ width: 800, height: 600, icon: path.join(__static, 'icon.png') })

Agora, se executarmos yarn run electron:build e visualizarmos nosso aplicativo, devemos ver o ícone atualizado sendo usado como ícone do aplicativo, mas isso não muda no desenvolvimento. Esse problema ajuda a resolver uma correção manual para ele no macOS.

Configurando o título para seu aplicativo

Você notará que o título do seu aplicativo está definido para o nome do aplicativo (app de notícias neste caso) e precisaremos alterá-lo. Para fazer isso, temos que adicionar uma propriedade title ao método BrowserWindow em nosso arquivo background.js assim:

 win = new BrowserWindow({ width: 600, height: 500, title: "News App", icon: path.join(__static, "icon.png"), 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, }, });

Aqui, estamos definindo o título do nosso aplicativo como 'Aplicativo de notícias'. Mas se o seu arquivo index.html tiver um título selecionado ou seu título não mudar para isso, tente adicionar este código ao seu arquivo:

 win.on("page-title-updated", (event) => event.preventDefault());

Estamos ouvindo um evento que é disparado quando nosso title é atualizado de BrowserWindow . Quando este evento é acionado, estamos dizendo ao Electron para não atualizar o título com o encontrado no arquivo index.html .

Outra coisa que pode valer a pena mudar é o productName , que controla o nome que aparece quando você passa o mouse sobre o ícone do seu aplicativo ou como seu computador reconhece o aplicativo. No momento, o nome do nosso aplicativo é Electron . Para alterar esse nome em produção, crie um arquivo vue.config.js e adicione as seguintes linhas de código a ele:

 module.exports = { pluginOptions: { electronBuilder: { builderOptions: { productName: "News App", }, }, }, };

Aqui, definimos productName como 'News App' para que, quando executarmos o comando de compilação para nosso aplicativo, o nome mude de 'Electron' para 'News App'.

Compilação multiplataforma

Por padrão, quando você executa o comando de compilação, o aplicativo criado depende da plataforma em que está sendo executado. Isso significa que se você executar o comando build no Linux, o aplicativo criado será um aplicativo de desktop Linux. O mesmo também se aplica a outras plataformas (macOS e windows). Mas o Electron vem com a opção de especificar uma plataforma (ou duas plataformas) que você deseja gerar. As opções disponíveis são:

  1. mac
  2. win
  3. linux

Portanto, para compilar a versão do Windows do seu aplicativo, execute o seguinte comando:

 // NPM npm electron:build -- --win nsis // YARN yarn electron:build --win nsis

Conclusão

O aplicativo completo pode ser encontrado no meu GitHub. A documentação oficial do Electron fornece informações e um guia que ajuda você a personalizar seu aplicativo de desktop da maneira que desejar. Algumas das coisas que experimentei, mas não estão incluídas neste tutorial, são:

  1. Personalizando seu dock no macOS — https://www.electronjs.org/docs/tutorial/macos-dock.
  2. Configuração redimensionável, maximizável e muito mais — https://github.com/electron/electron/blob/master/docs/api/browser-window.md#new-browserwindowoptions.

Portanto, se você deseja fazer muito mais com seu aplicativo Electron, seus documentos oficiais são um bom lugar para começar.

Recursos Relacionados

  1. Node.jshttps://en.wikipedia.org/wiki/Node.js
  2. Java (linguagem de programação)https://en.wikipedia.org/wiki/Java_(programming_language)
  3. Electron (estrutura de software)
  4. JavaFX 14
  5. elétronjs
  6. Documentação eletrônica
  7. Vue CLI Plugin Electron Builder
  8. Carregamento lento de imagens para desempenho usando o Observador de interseção por Chris Nwamba
  9. axios
  10. Introdução ao Axios no Nuxthttps://www.smashingmagazine.com/2020/05/getting-started-axios-nuxt/) por Timi Omoyeni