Electron 是由 GitHub 开发的开源库, 用于构建拥有 html,CSS 和 JavaScript 的跨平台桌面应用程序. Electron 通过把 Chromium 和 Node.JS 组合到一个运行时来实现这一点, 并且可以为 Mac,Windows 和 Linux 打包应用程序 .
为了快速入门, 我使用了 Greg Holguin 的 electron-vue 样板. 它给开发人员提供了 vue-cli scanffolding, 常见的 Vue 插件, 打包器, 测试, 开发工具和其他功能.
我们要构建什么?
我们要构建一个跟上一篇文章中所述的一样的应用程序, 基于 OpenWeatherMap API 的该应用程序用来查看用户所选择城市的天气情况.
如果只想查看 Electron 支持的应用程序最终代码, 请点击这里:
安装
Electron-vue 样板是作为 VueCLI 2.x 的模板构建的, 包括自定义应用程序的选项. 因此, 需要进行全局安装:
NPM install -g vue-cli
如果喜欢用 VueCLI 的最新版本, 就需要安装全局网桥:
NPM install -g @vue/cli @vue/cli-init
然后, 初始化你的项目:
vue init simulatedgreg/electron-vue weather-App
这将启动一个安装项目, 其中包括需要你做出的几个选择.
其中很酷的是, 如果需要一些常用的插件和库, 比如 axios, 可以在安装过程中选择它们.
几乎所有的选择都很明确, 但是有一个问题:
我决定搜索一下, 在 Stack Overflow 上找到了一个有用的线索. 根据这个线索, 看起来 electron 构建器 (electron-builder) 更适合我, 因此, 我就改用它了.
设置项目后, 需要打开应用程序所在的文件夹, 运行 NPM install 或 yarn install, 现在我们准备好了.
了解应用程序结构
安装完成后, 可以在 src 中看到两个文件夹 main 和 renderer,Electron 主进程需要用到第一个.
根据 electron-vue 的文档, 在 Electron 中运行 package.JSON 主脚本的进程被称为主进程. 在主进程中运行的脚本可以通过创建 web 页面来显示 GUI.
在 main 文件夹中有两个文件: index.JS 和 index.dev.JS. 第一个是你的应用程序的主文件, 是 electron 启动用的文件. 它也被用做 webpack 的生产入口文件. 所有主要的流程工作都应该从这里开始.
而 index.dev.jsis 专门用于开发, 因为它安装了 electron-debug 和 vue-devtools. 在开发应用程序时, 可以不用理睬它.
另外, 渲染器 (renderer) 进程需要 renderer 文件夹.
由于 Electron 使用 Chromium 来显示 Web 页面, 因此也用到了 Chromium 的多进程架构. Electron 中的每个 Web 页面都在自己的进程中运行, 这些进程被称为渲染器进程.
正如你可能注意到的是, 它是个 "正常的"Vue 应用程序结构, 拥有 assets 和 components 文件夹: main.JS 和 App.vue 文件. 这是后者的结构:
- <template>
- <div id="app">
- <landing-page></landing-page>
- </div>
- </template>
- <script>
- import LandingPage from '@/components/LandingPage'
- export default {
- name: 'weather-app',
- components: {
- LandingPage
- }
- }
- </script>
- <style>
- /* CSS */
- </style>
如果尝试运行 dev 任务, 将会得到以下结果:
因此, 这里有一个登录页面 (landing-page) 组件, 也开启了开发工具(devtools). 现在, 我们可以改变它了.
快速构建 (scaffolding) 应用程序
与 Vuido 不同, Electron 支持的应用程序是用 HTML 标签而不是用本机组件构建的. 因此, 我们将创建一个与通常的 Web 应用程序类似的结构, 并用 CSS 来进行样式设计.
请注意: 我没有特意安装任何 CSS 框架或组件库: 我希望在没有添加任何不同的依赖项的情况下, 比较一下包的大小. 在两个项目中唯一用到的库是 axios.
第一步是去掉登录页面组件. 然后我添加了一个简单的输入字段和一个按钮:
- <div id="app">
- <p>Enter the city name to check current weather in it</p>
- <section class="weather-input">
- <input type="text" v-model="query">
- <button :disabled="!query.length">Check</button>
- </section>
- </div>
我们在数据中有个查询 (query) 属性来处理用户输入, 我们将把该查询作为参数, 进行 API 调用.
进行 API 调用
我使用了 OpenWeatherMap 当前天气 API. 它提供很多不同的信息, 可以点击这里查看 JSON 响应的示例.
在安装过程中, 我们已经将 axios 包含在我们的应用程序中. 我们来看看 src/renderer/main.JS:
- import Vue from 'vue';
- import axios from 'axios';
- import App from './App';
- if (!process.env.IS_WEB) Vue.use(require('vue-electron'));
- Vue.http = Vue.prototype.$http = axios;
- Vue.config.productionTip = false;
因此, 我们可以在组件实例中把 axios 方式用作 this.$http. 在这里, 我们将只为我们的 API 调用添加一个基本 URL:
axios.defaults.baseURL = 'http://api.openweathermap.org/data/2.5';
现在, 在 App.vue 中, 我们将创建一组数据属性来显示不同的天气数据:
- data() {
- return {
- query: '',
- error: false,
- city: '',
- country: '',
- weatherDescription: '',
- temp: null,
- tempMin: null,
- tempMax: null,
- humidity: null,
- icon: '',
- };
- },
与 Vuido 版本相比, 我添加了一个额外属性, 它是个图标. API 提供天气图标, 但是我们无法在 Vuido 应用程序中使用, 因为目前不支持显示图像.
我们还创建了一种方法来获取数据:
- methods: {
- showWeather() {
- this.$http
- .get(`/weather?q=${this.query}&units=metric&&appid=${API_KEY}`)
- .then(response => {
- this.city = response.data.name;
- this.country = response.data.sys.country;
- this.weatherDescription = response.data.weather[0].description;
- this.temp = response.data.main.temp;
- this.tempMin = response.data.main.temp_min;
- this.tempMax = response.data.main.temp_max;
- this.humidity = response.data.main.humidity;
- this.icon = `http://openweathermap.org/img/w/${
- response.data.weather[0].icon
- }.PNG`;
- this.error = false;
- })
- .catch(() => {
- this.error = true;
- this.city = '';
- });
- },
- },
并把它添加到按钮的单击回调中:
<button :disabled="!query.length" @click="showWeather">Check</button>
现在, 如果在输入字段输入文本, 单击按钮, 就可以在 "网络(Network)" 选项卡上观察 API 调用:
显示天气数据
我们把这些数据添加到模板上:
- <template>
- <main id="app">
- <p>Enter the city name to check current weather in it</p>
- <section class="weather-input">
- <input type="text" v-model="query">
- <button :disabled="!query.length" @click="showWeather">Check</button>
- </section>
- <section v-if="error" class="weather-error">
- There is no such city in the database
- </section>
- <section v-if="city.length" class="weather-result">
- <h1>{{city}}, {{country}}</h1>
- <p><em>{{weatherDescription}}</em></p>
- <div class="weather-result__main">
- <img :src="icon" alt="Weather icon">
- <div class="weather-result__temp">
- {{temp}}°C
- </div>
- </div>
- <div class="weather-result__details">
- <p>Min: {{tempMin}}°C</p>
- <p>Max: {{tempMax}}°C</p>
- <p>Humidity: {{humidity}}%</p>
- </div>
- </section>
- </main>
- </template>
我们的应用程序视图:
image
太棒了, 我们能看到真实的天气状况了! 但是, 它看起来像是 1999 年的...... 我们给它添加点 CSS 魔法吧(事实上, 是很多 CSS 魔法)!
- <style lang="scss">
- * {
- margin: 0;
- padding: 0;
- }
- HTML,
- body,
- #App {
- height: 100%;
- }
- #App {
- font-family: Arial, Helvetica, sans-serif;
- font-size: 16px;
- padding: 10px;
- background: rgb(212, 228, 239);
- background: -moz-radial-gradient(
- center,
- ellipse cover,
- rgba(212, 228, 239, 1) 0%,
- rgba(134, 174, 204, 1) 100%
- );
- background: -webkit-radial-gradient(
- center,
- ellipse cover,
- rgba(212, 228, 239, 1) 0%,
- rgba(134, 174, 204, 1) 100%
- );
- background: radial-gradient(
- ellipse at center,
- rgba(212, 228, 239, 1) 0%,
- rgba(134, 174, 204, 1) 100%
- );
- filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#d4e4ef', endColorstr='#86aecc',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */
- }
- .weather-input {
- display: flex;
- align-items: center;
- padding: 20px 0;
- }
- .weather-result {
- text-align: center;
- &__main {
- display: flex;
- align-items: center;
- justify-content: center;
- padding-top: 5px;
- font-size: 1.3rem;
- font-weight: bold;
- }
- &__details {
- display: flex;
- align-items: center;
- justify-content: space-around;
- color: dimgray;
- }
- }
- .weather-error {
- color: red;
- font-weight: bold;
- }
- input {
- width: 75%;
- outline: none;
- height: 20px;
- font-size: 0.8rem;
- }
- button {
- display: block;
- width: 25%;
- height: 25px;
- outline: none;
- border-radius: 5px;
- white-space: nowrap;
- margin: 0 10px;
- font-size: 0.8rem;
- }
- </style>
最终, 我们拥有了这个美妙的全功能应用程序:
在打包之前, 最后要做的事是把它的尺寸缩小到窗口大小. 如果我们查看一下 src/main/index.JS 文件, 可以找到它的设置选项:
- mainWindow = new BrowserWindow({
- height: 563,
- useContentSize: true,
- width: 1000
- })
我们把宽度改为 450, 高度改为 250.
打 包
你可以把你的应用程序构建为 Web 应用程序了! 如果运行 build:Web 任务, 可以在 dist 文件夹中找到构建 Web 应用程序.
但是, 让我们回到我们的桌面应用程序并运行构建任务. 结果, 你在构建文件夹中会有一个文件夹, 该文件夹的名字是根据你的平台命名的(对我而言是 Mac), 里面有一个应用程序文件. 它的大小是 133Mb!
对于这么小的应用程序来说, 这文件太大了. 另外, 如果运行一下, 你就会注意到, 它的启动比 Vuido 支持的应用程序慢.
最终的样子:
结 论
优点:
容易上手;
出色的文档;
提供 Web 应用程序构建;
可以通过 CSS 样式自定义.
缺点:
打包后的文件超大;
比用本机 GUI 组件构建的应用程序速度慢;
如果你的应用程序需要独特的外观, 并且你不在乎;
打包后的文件大小和性能, 那么, Electron-vue 是个不错的选择.
更新
如果你的 Web 应用程序是用 Vue CLI 3 构建的, 则可以用 Vue CLI 插件 Electron Builder 把它简化成桌面应用程序. 你只需在项目根文件夹中运行以下命令:
vue add electron-builder
运行结束后, 你将会拥有两个额外的 NPM 任务: serve:electron 和 build:electron, 与桌面应用程序一起工作.
来源: http://www.jianshu.com/p/4196ee739f01