【原创】从零开始搭建Electron+Vue+Webpack项目框架(五)预加载和Electron自动更新

导航:

(一)Electron跑起来
(二)从零搭建Vue全家桶+webpack项目框架
(三)Electron+Vue+Webpack,联合调试整个项目
(四)Electron配置润色
(五)预加载及自动更新
(六)构建、发布整个项目(包括client和web)(未完待续)

摘要:到目前为止,我们的项目已经具备了PC客户端该有的一些基础功能和调试环境,但是总感觉缺了灵魂,那就是结合实际项目、实际业务的细节处理,缺着吧。。。这篇文章就介绍一下预加载和自动更新,文字功底有限,如有介绍的不清楚的地方,欢迎留言指正,或者跳过文字,直接去看代码,项目完整代码:https://github.com/luohao8023/electron-vue-template,随博客更新。

一、预加载

1、什么是预加载?什么场景能用到? 

preload String (可选) -在页面运行其他脚本之前预先加载指定的脚本 无论页面是否集成Node, 此脚本都可以访问所有Node API 脚本路径为文件的绝对路径。 当 node integration 关闭时, 预加载的脚本将从全局范围重新引入node的全局引用标志。

摘自electron官网的一段介绍,https://www.electronjs.org/docs/api/browser-window

preload是BrowserWindow类的参数webPreferences的一个可选配置项,我们解读一下官网的介绍:

在页面运行其他脚本之前预先加载的指定的脚本:首先是个js文件没错了,再看加载时机,在页面运行其他脚本之前预先加载,这个页面不是普通的某个h5页面,而是指某个渲染进程(需要预加载js的渲染进程,因为渲染进程可能有多个,每个就是一个窗口),我们new一个BrowserWindow,打开了一个窗口,就是启动了一个渲染进程,如果我们不给这个窗口指定页面,那它就是空白的,如果指定了页面,那么窗口就会加载这个页面:

    const win = new BrowserWindow({
        width: 800,
        height: 600
    });
    win.loadURL(‘https://www.baidu.com‘);

如上面代码,我们创建了一个窗口,然后加载百度首页,而preload脚本的加载时机就是窗口创建后,百度首页加载之前。如果有人问,如果不调用loadURL方法,不加载页面,preload脚本会加载吗?答案是会,但有什么用呢?你起个壳子不给人家看页面是什么鬼?不管这些,重要的是我们理解这个加载时机就好了;

无论页面是否集成Node,此脚本都可以访问所有Node API:首先要说明的一点是,Electron5.x以上版本,默认无法在渲染进程中访问Node API,如需使用,需要预先配置:

    const win = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            nodeIntegration: true
        }
    });

然后还要清楚一点,preload脚本是运行在渲染进程中的,可以仔细考虑一下。再有一点就是,preload脚本中可以访问window对象(渲染进程其实就是起了个浏览器壳子),preload脚本运行在渲染进程,提前于页面和其他所有js的加载,又能访问Node API;

脚本文件路径为绝对路径,当node integration关闭时,预加载的脚本将从全局范围重新引入node的全局引用标志:结合前面两点理解就好了。

那么,到底什么是预加载?用白话定义一下:

某一个渲染进程,在页面加载之前加载一个本地脚本,这个脚本能访问所有Node API、能访问window对象。用法如下:

    const win = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            preload: path.join(__dirname, ‘preload.js‘)
        }
    });

理解应该差不多了,但什么场景能用到这玩意儿呢?按正常的逻辑来想,主进程启动后启动渲染进程,渲染进程加载页面就完事儿了,哪会用到这个preolad呢?

想一下,如果我们有以下场景:

a、如果我们启动了一个窗口(渲染进程),加载了一个线上的页面,本地没有页面文件,但要做一些错误处理,比如网络错误,页面加载失败,然后在页面空白但时候插入一些元素;

b、如果我们的一套代码部署在web端和客户端,需要用一个变量判断是在web端还是客户端;

...........

感觉举的例子好勉强啊,不要见怪,就是大概这么个意思,没准哪天就遇到了非preload解决不了的问题呢,毕竟这玩意儿还是有它的特殊之处的;

上面两个场景如果用preload来解决的话,思路是利用prelaod中能访问window对象的特点,比如b,代码中可以用window.isClient来判断是否在客户端,默认为false,然后在preload中把window.isClient设置为true,而对于部署在web端的代码来说,这个值就是false。

2、怎么用?

上面说了怎么引用preload脚本,现在说一下怎么写,下面开始xxoo乱写乱画了:

// 访问electron对象
const {
    remote,
    ipcRenderer
} = require(‘electron‘);
// 访问node模块
const fs = require(‘fs‘);
const path = require(‘path‘);
// 访问window对象
window.isClient = true;
window.sayHello = function() {
    console.log(‘hello‘);
};
// 操作dom
const div = document.createElement(‘div‘);
div.innerText = ‘I am a div‘;
document.body.appendChild(div);
// ...

如果preoad里面逻辑比较复杂,有可能还要用webpack打包一下,单独拎出来打包就行了,webpack单文件打包,注意targer要"electron-renderer":

/*
Tip:  preload 打包配置
 */
const path=require(‘path‘);
const { dependencies } = require(‘../package.json‘);
module.exports = {
    mode:process.env.NODE_ENV,
    entry: {
        preload:[‘./src/preload/index.js‘]
    },
    output: {
        path: path.join(__dirname, ‘../app/‘),
        libraryTarget: ‘commonjs2‘,
        filename: ‘./[name].js‘
    },
    optimization: {
        runtimeChunk: false,
        minimize: true
    },
    node: {
        fs: ‘empty‘,
        __dirname:false
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                loader: ‘babel-loader‘,
                exclude: /node_modules/
            }
        ]
    },
    externals: [
        ...Object.keys(dependencies || {})
    ],
    resolve: {
        extensions: [‘.js‘],
        alias: {
            ‘@‘: path.resolve(__dirname, "../src"),
            ‘@public‘: path.resolve(__dirname, "../public")
        }
    },
    plugins:[],
    target:"electron-renderer"
}

我相信,总会遇到使用preload就能迎刃而解的问题。

二、自动更新

我们都知道,electron其实是封了个chrome内核,抛开壳子不说,里面运行的其实就是我们的h5页面,而就算我们跑了个空项目,没有任何内容,打包后的安装包也得30M左右,我们希望自己的程序有自动更新功能,但是更新机制是怎样的呢?

如果我们只改动了页面某一处的文本,却要用户更新整个安装包,那显然太不合理了,一是体验不好,二是我们的流量啊......

基于这种考虑,加上electron主进程和渲染进程的划分,那我们可以考虑如下更新机制:

主进程有改动时,那没的说,用户需要更新整个客户端(当然有精力有条件的可以做动态更新,官方好像是说支持,主要是我不会);渲染进程有改动时,我们只需要把h5包下载到本地然后加载就行了,当然这需要我们打包的时候能把h5包区分出来,在更新后能打开对应版本的h5包。

这里我们称主进程的更新为大版本更新,渲染进程的更新为小版本更新。

1、打包配置修改

为什么突然扯到打包配置修改了呢,因为牵扯到小版本的更新,那我们打包的时候就得把这个“小版本”给打出来,不然更新个??。因为下面还有一篇文章是专门介绍这个Electron-vue项目的打包,所以这里呢就只讲一下怎么把小版本的压缩包给打出来。

修改build.js,思路是:使用webpack打包主进程、打包preload、打包渲染进程,得到可执行文件目录app,然后引入electrin-builder对app目录进行打包,产生一个安装包,然后把渲染进程的文件压缩并标记版本号。这里我们只拣和本节相关的说,就是打包渲染进程和压缩小版本文件,为什么能拆出来说呢,当然是分模块封装的好处啦,各个进程的打包逻辑封装一下拆出来,能随意组合还能复用,否则一个又臭又长的打包脚本文件,很难维护。

具体代码就不贴出来了,太占篇幅,也没什么用,可以到https://github.com/luohao8023/electron-vue-template看完整代码。

2、增加启动页,启动页显示欢迎语等,在这里检查更新

这里我们暂且叫它检查更新页,这个检查更新页是个独立的渲染进程,用户打开程序时首先显示检查更新窗口,但是这个窗口也不一定显示检查更新字样,偷偷的检查就行了,有新版本就提示更新,没有新版本就显示欢迎语。

这儿的逻辑是单独拆分出来的,不能是自动更新的时候把自动更新逻辑本身也给更新了,容易乱套。

修改主进程代码,程序启动时首先启动自动更新窗口:

app.on(‘ready‘, () => {
    //注册快捷键打开控制台事件
    shortcut.register(‘Command+Control+Alt+F5‘);
    mainWindow = updateWin.create();
});

然后注册监听事件,因为自动更新窗口逻辑完成之后需要呼起主窗口,需要主进程来调度:

//启动主窗体
ipcMain.on(‘create-main‘,(event,arg) => {
    // h5页面指向指定版本
    // global.wwwroot.path = arg.newVersionPath ? arg.newVersionPath : __dirname;
    // if (arg.version) setVal(‘version‘,‘smallVersion‘, arg.version);
    indexWin.create();
    mainWindow.destroy();
});

自动更新窗口只需专注于更新逻辑就行了,逻辑结束后呼起主窗口:

        // 更新逻辑看下面伪代码
        const v1 = getOnlineVersion();
        const v2 = getLocalVersion();
        const needUpdate = checkVersion(v1, v2);
        if (needUpdate) {
            downloadVersion();
        }

        this.runMain();

在呼起主窗口的同时给主窗口传递参数,并告知主窗口有没有更新版本,以及主窗口需要加载哪个小版本的包,而主窗口在loadURL时也要做下改动:

    let wwwroot = global.wwwroot.path ? global.wwwroot.path : __dirname;
    let filePath = url.pathToFileURL(path.join(wwwroot, ‘index.html‘)).href;

而wwwrot就是当前小版本包的根路径,由主进程来维护,自动更新小版本后会修改这个值,以告诉主进程加载哪个版本。

好了,啰嗦了一大堆,好多地方没贴代码,感觉贴了代码的话,篇幅就不受控制了,还是去github看完整项目吧,自动更新这一块是伪代码,只实现了渲染进程的切换(即自动更新窗口呼起主窗口),具体的更新逻辑实现起来的话还要拿线上版本去比较,这个还是留给大家在实际项目中去调试吧,就是上面这个思路。

好啦,有什么问题可以留言交流,也可以直接去看代码https://github.com/luohao8023/electron-vue-template

原文地址:https://www.cnblogs.com/kakayang/p/12386918.html

时间: 2024-10-14 20:11:37

【原创】从零开始搭建Electron+Vue+Webpack项目框架(五)预加载和Electron自动更新的相关文章

【原创】从零开始搭建Electron+Vue+Webpack项目框架,一套代码,同时构建客户端、web端(二)

导航: (一)Electron跑起来(二)从零搭建Vue全家桶+webpack项目框架(三)Electron+Vue+Webpack,联合调试整个项目(未完待续)(四)Electron配置润色(未完待续)(五)预加载及自动更新(未完待续)(六)构建.发布整个项目(包括client和web)(未完待续) 摘要:上篇文章说到了如何新建工程,并启动一个最简单的Electron应用.“跑起来”了Electron,那就接着把Vue“跑起来”吧.有一点需要说明的是,webpack是贯穿这个系列始终的,我也是

Android 加载gif图片强大框架(支持预加载、缓存,还支持显示静态图片,一行代码全搞定)

之前项目中没有涉及到显示gif图片的功能,也没有着重研究过,最近项目中要用到显示gif图片,于是就在网上一顿搜,用过之后发现如下几个缺点. 1.加载大的gif图片会出现oom. 2.没有预加载和缓存功能,比较消耗内存. 3.功能比较单一,显示gif了但是不能显示静态图片. 最后无意间发现了一个强大的gif加载框架,能够满足上面的所有缺陷,支持预加载和缓存,同时支持显示静态图片和gif图片.下面是一些源代码: //xml布局就是一个ImageView //在需要的地方直接调用 Glide.with

基于spring的web项目启动时预加载数据到ServletContext

1.要在web启动时预加载数据到ServletContext,实现方法有很多,一种比较简单的方案就是: 1)新建一个bean,定义其初始化方法: <bean id="beanId" init-method="初始化方法" />或者使用@PostConstruct注解到初始化方法上面 2)获取ServletContext实例对象,如何获取呢? 方法1: @Autowired private ServletContext application; 方法2:

vue-用Vue-cli从零开始搭建一个Vue项目

Vue是近两年来比较火的一个前端框架(渐进式框架吧). Vue两大核心思想:组件化和数据驱动.组件化就是将一个整体合理拆分为一个一个小块(组件),组件可重复使用:数据驱动是前端的未来发展方向,释放了对DOM的操作,让DOM随着数据的变化自然而然的变化(尤神原话),不必过多的关注DOM,只需要将数据组织好即可.本文用Vue-cli从零开始搭建一个Vue项目. 准备工作 1.下载安装Node.js 下载地址:https://nodejs.org/en/download/ ,选择合适自己的版本下载即可

从零开始:一个正式的vue+webpack项目的目录结构是怎么形成的

每天学习一点点 编程PDF电子书.视频教程免费下载:http://www.shitanlife.com/code 如何从零开始一个vue+webpack前端工程工作流的搭建,首先我们先从项目的目录结构入手.一个持续可发展,不断加入新功能,方便后期维护的目录结构究竟是长什么样子的?接下来闰土大叔带你们一起手摸手学起来. 初级前端初始化目录篇 项目伊始,我们肯定是先在terminal终端命令行(以下简称terminal)cd进入<project name>根目录,然后输入 npm init 初始化

vue+webpack项目结构介绍

对vue已经有了一定的了解,所以希望可以在github上找到一些项目实践,但是无奈不知道vue的整体架构是如何的,往往不知道该如何着手看,所以这里在网上找了相关的文章作为参考,对结构有一个大致的了解.参考文章如下: 用vue+webpack搭建的前端项目结构 vue+webpack项目实践 这里插一句话,搜索谷歌做的确实好,支持原创,搜索精准,不知道比度娘高到哪里去~

webpack入坑之旅(五)加载vue单文件组件

这是一系列文章,此系列所有的练习都存在了我的github仓库中vue-webpack,在本人有了新的理解与认识之后,会对文章有不定时的更正与更新.下面是目前完成的列表: webpack入坑之旅(一)不是开始的开始 webpack入坑之旅(二)loader入门 webpack入坑之旅(三)webpack.config入门 webpack入坑之旅(四)扬帆起航 webpack入坑之旅(五)加载vue单文件组件 webpack入坑之旅(六)配合vue-router实现SPA 需要什么? 在经过前面的四

python web开发之flask框架学习(2) 加载模版

上次学习了flask的helloword项目的创建,这次来学习flask项目的模版加载: 第一步:创建一个flask项目 第二步:在项目目录的templates文件夹下创建一个html文件 第三步: 加载模版文件这里会用到flask包下的一个模版渲染器render_template因此要倒入这个render_template 第四步:运行项目就可以看到模版加载的效果了 以上就是flask框架的模版加载学习了,有什么问题欢迎留言! 简书地址: Code人生 原文地址:https://www.cnb

基于Vue的SPA如何优化页面加载速度

常见的几种SPA优化方式 减小入口文件体积 静态资源本地缓存 开启GZip压缩 使用SSR ..... 减小入口文件体积,常用的手段是路由懒加载,开启路由懒加载之后,待请求的页面会单独打包js文件,使得入口文件index.js变小,开启懒加载之后,js是这样请求加载的: 2. 静态文件本地缓存有两种方式 HTTP缓存,设置Cache-Control,Last-Modified,Etag等响应头,很多文章讲的比较详细,推荐:https://www.cnblogs.com/chinajava/p/5