【原創】從零開始搭建Electron+Vue+Webpack項目框架(五)預加載和Electron自動更新

導航:html

(一)Electron跑起來
(二)從零搭建Vue全家桶+webpack項目框架
(三)Electron+Vue+Webpack,聯合調試整個項目
(四)Electron配置潤色
(五)預加載及自動更新
(六)構建、發佈整個項目(包括client和web)(未完待續)vue

摘要:到目前爲止,咱們的項目已經具有了PC客戶端該有的一些基礎功能和調試環境,可是總感受缺了靈魂,那就是結合實際項目、實際業務的細節處理,缺着吧。。。這篇文章就介紹一下預加載和自動更新,文字功底有限,若有介紹的不清楚的地方,歡迎留言指正,或者跳過文字,直接去看代碼,項目完整代碼:https://github.com/luohao8023/electron-vue-template,隨博客更新。node

1、預加載webpack

一、什麼是預加載?什麼場景能用到? git

preload String (可選) -在頁面運行其餘腳本以前預先加載指定的腳本 不管頁面是否集成Node, 此腳本均可以訪問全部Node API 腳本路徑爲文件的絕對路徑。 當 node integration 關閉時, 預加載的腳本將從全局範圍從新引入node的全局引用標誌。

摘自electron官網的一段介紹,https://www.electronjs.org/docs/api/browser-windowgithub

preload是BrowserWindow類的參數webPreferences的一個可選配置項,咱們解讀一下官網的介紹:web

在頁面運行其餘腳本以前預先加載的指定的腳本:首先是個js文件沒錯了,再看加載時機,在頁面運行其餘腳本以前預先加載,這個頁面不是普通的某個h5頁面,而是指某個渲染進程(須要預加載js的渲染進程,由於渲染進程可能有多個,每一個就是一個窗口),咱們new一個BrowserWindow,打開了一個窗口,就是啓動了一個渲染進程,若是咱們不給這個窗口指定頁面,那它就是空白的,若是指定了頁面,那麼窗口就會加載這個頁面:chrome

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

如上面代碼,咱們建立了一個窗口,而後加載百度首頁,而preload腳本的加載時機就是窗口建立後,百度首頁加載以前。若是有人問,若是不調用loadURL方法,不加載頁面,preload腳本會加載嗎?答案是會,但有什麼用呢?你起個殼子不給人家看頁面是什麼鬼?無論這些,重要的是咱們理解這個加載時機就行了;json

不管頁面是否集成Node,此腳本均可以訪問全部Node API:首先要說明的一點是,Electron5.x以上版本,默認沒法在渲染進程中訪問Node API,如需使用,須要預先配置: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。

二、怎麼用?

上面說了怎麼引用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就能迎刃而解的問題。

2、自動更新

咱們都知道,electron實際上是封了個chrome內核,拋開殼子不說,裏面運行的其實就是咱們的h5頁面,而就算咱們跑了個空項目,沒有任何內容,打包後的安裝包也得30M左右,咱們但願本身的程序有自動更新功能,可是更新機制是怎樣的呢?

若是咱們只改動了頁面某一處的文本,卻要用戶更新整個安裝包,那顯然太不合理了,一是體驗很差,二是咱們的流量啊......

基於這種考慮,加上electron主進程和渲染進程的劃分,那咱們能夠考慮以下更新機制:

主進程有改動時,那沒的說,用戶須要更新整個客戶端(固然有精力有條件的能夠作動態更新,官方好像是說支持,主要是我不會);渲染進程有改動時,咱們只須要把h5包下載到本地而後加載就好了,固然這須要咱們打包的時候能把h5包區分出來,在更新後能打開對應版本的h5包。

這裏咱們稱主進程的更新爲大版本更新,渲染進程的更新爲小版本更新。

一、打包配置修改

爲何忽然扯到打包配置修改了呢,由於牽扯到小版本的更新,那咱們打包的時候就得把這個「小版本」給打出來,否則更新個🔨。由於下面還有一篇文章是專門介紹這個Electron-vue項目的打包,因此這裏呢就只講一下怎麼把小版本的壓縮包給打出來。

修改build.js,思路是:使用webpack打包主進程、打包preload、打包渲染進程,獲得可執行文件目錄app,而後引入electrin-builder對app目錄進行打包,產生一個安裝包,而後把渲染進程的文件壓縮並標記版本號。這裏咱們只揀和本節相關的說,就是打包渲染進程和壓縮小版本文件,爲何能拆出來講呢,固然是分模塊封裝的好處啦,各個進程的打包邏輯封裝一下拆出來,能隨意組合還能複用,不然一個又臭又長的打包腳本文件,很難維護。

具體代碼就不貼出來了,太佔篇幅,也沒什麼用,能夠到https://github.com/luohao8023/electron-vue-template看完整代碼。

二、增長啓動頁,啓動頁顯示歡迎語等,在這裏檢查更新

這裏咱們暫且叫它檢查更新頁,這個檢查更新頁是個獨立的渲染進程,用戶打開程序時首先顯示檢查更新窗口,可是這個窗口也不必定顯示檢查更新字樣,偷偷的檢查就好了,有新版本就提示更新,沒有新版本就顯示歡迎語。

這兒的邏輯是單獨拆分出來的,不能是自動更新的時候把自動更新邏輯自己也給更新了,容易亂套。

修改主進程代碼,程序啓動時首先啓動自動更新窗口:

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

相關文章
相關標籤/搜索