1. 程式人生 > >【原創】從零開始搭建Electron+Vue+Webpack專案框架(五)預載入和Electron自動更新

【原創】從零開始搭建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、打包配置修改

為什麼突然扯到打包配置修改了呢,因為牽扯到小版本的更新,那我們打包的時候就得把這個“小版本”給打出來,不然更新個