摘要:三配置環節目的一是為之后的環節初始化工作流參數,二是準備好應用文件夾內容即要打包的目標文件夾做的事解析命令行參數,初始化工作參數,填充配置文件,把配置文件和相關依賴文件導入到文件夾內合適的
首發于酷家樂前端博客,作者@摘星(segmentfault @StinsonZhao)
我們能從很多地方學習到怎么起一個 Electron 項目,有些還會介紹怎么打包或構建你的代碼,但距離「真正地發行一款 Electron 產品」這一目標,還有很多工作需要做...
這是 Electron 系列文章的第二篇,這一篇文章將和大家分享我是怎么去構建自動化的 Electron 開發構建工程的,說白了,就是怎么把敲的代碼變成一個用戶可以下載安裝的包,當然隨著之后應用復雜度的提升和技術再選型,工程體系可能隨時會重構或演進,但至少可以給大家一些參考,歡迎留言交流。
這是一篇很長的文章(手冊),寫得比較「唐僧」(知我者可以說我寫得比較用心),至少會花你一天時間(沒開玩笑),適用場景是「用 Electron 打造 Windows 或 Mac 應用」,是的,你沒看錯,同時會講清楚兼容 Win 和 Mac 兩個系統的流程。文中提及的技術方案絕對不是最佳的(我保證),因為幾乎每隔幾天我都會發現某個環節可以做得更好,但要明白要唱多大的戲,就先搭多大的臺,夠用就好,不要為了搭臺耽誤演出時間。
工程自動化,應該是所有開發者的一種基礎追求,當你搭建建好工程體系,以后你將專注于產品功能的開發,而不會花大量不必要的時間去手動構建。作為前端,可能我們已經熟悉了 web 應用的構建和部署,但是客戶端程序有其本身的特點,相比較 web 應用最大也是我認為最根本的一點區別在于「你的應用是被用戶下載過去安裝在用戶本地再跑起來的」。
這一區別對工程的影響在于,你不可能把你的代碼部署到「用戶的電腦」,你需要構建安裝包,你需要針對不同的用戶系統構建不同的安裝包,你需要讓你的應用被系統認為是安全的...
本文需要做的是,把客戶端的打包構建發行這一流程做到像「把大象放進冰箱」一樣的簡單:打開命令行,敲一個 npm run xxx,喝一口咖啡,咪哩嘛哩哄,安裝包出現(一開始打造這個流程時,劇本可能是「喝一口咖啡,啪,Error 了,又 Error 了」,take it easy,生活需要慢慢品味 —— 來自一位25歲的仙風道骨白胡子程序員)。
本文將分以下小節和大家分享「從本地的代碼到云端可下載的安裝包」這一路的風景,你會有漫步月球般的感覺(因為月球全是坑啊,還沒氧氣):
第一節是關于目錄結構的討論,合適的目錄結構會是一個良好的開端
第二節是之后幾個小節的概述,闡述了怎么把這一整個過程分成多個環節,每個環節又大致要做什么事
第三到七節分別詳細描述了「配置」、「打包」、「代碼簽名」、「構建安裝包」、「發行安裝包」這幾個環節要做哪些事,有什么講究
第八節是簡述一些可進一步研究或優化的點
附:這樣設計的 gulpfile 文件結構
下面一一展開進行闡述,再次強調,文中很多依賴的技術或包,你都可以嘗試替換成自己相中的,不必在意是選「翠花」還是「桂花」,多處處就知道了。
一、目錄結構以下目錄結構供參考,沒有很詳細地展開,因為每個應用可能不同,最想表達的是這是一個「雙 package.json 結構」,你可以看到根目錄下有一個package.json,app目錄下還有一個package.json。
/ // 項目根目錄 ├── app/ // 應用源碼目錄,打包就針對app目錄進行打包 │ ├── assets/ // 應用需要的圖片、icon等資源 │ ├── config/ // 配置文件存放 │ ├── consts/ // 應用運行需要的常量,如ipcChanel │ ├── lib/ // 引入的庫文件,如jquery │ ├── plugins/ // 應用運行需要的插件,如flash │ ├── utils/ // 常用的工具方法 │ ├── view/ // 視圖,html、css和js │ ├── app_config.js // 整個app的配置,引入config文件夾下文件 │ ├── main.js // 應用入口 │ ├── package.json // 內部的package,定義應用的版本、運行依賴等 │ └── yarn.lock ├── build_resource/ // 構建需要的一些工具、資源或者腳本的目錄 ├── config/ // 環境配置文件目錄,會選擇一個寫入到app/config ├── deploy/ // 部署腳本,用戶部署文件到cdn或上傳文件到OSS ├── reserve/ // 保留目錄,存放一些文件用于寫入到app內 ├── dist/ // 打包和構建的目標目錄 ├── release/ // 發行的目標目錄 ├── .gitignore ├── gulpfile.js // gulp配置 ├── package.json // 外部package.json,用于定義開發依賴和腳本 └── yarn.lock
這是因為,我們的應用在運行時需要一些第三方依賴,這些依賴我們需要打包到應用內,也就是說/app/node_modules目錄內的內容是要被打包到應用內的,用戶使用的時候才不會缺失「運行時依賴」,而如果我們只有一個package.json,那么所有的依賴都被下載和安裝到同一個node_modules文件夾下,我們沒法把我們需要打包進去的依賴樹提取出來。所以這樣雙 package.json的結構最清晰明了和簡單易用,dependencies和devDependencies有了明確劃分。
再大致解釋下其他目錄的作用:
app目錄:是我們應用的源碼目錄,我們所說的打包針對的就是這個目錄,其他目錄和文件不會被打包進去,而app目錄內的子目錄和文件就見仁見智了,在不同的復雜度下有不同的設置,這里還有一些東西是需要從外面復制進來的,因為不同的平臺下你可能需要打包進去的東西是不同的。
config:配置文件目錄,可能因為你想打包的應用所處的階段(開發、內測、眾測、正式發行)和平臺(Windows、Mac),那么可能需要不同的配置,比如一些資源的名稱和路徑等,這里你可以把不同情況下都一樣的配置寫到一個配置文件,而根據情況不一樣的配置文件是從外部腳本寫進來的,這就是為什么你會在app目錄外面看到一個config文件夾的原因
plugins:是插件文件夾,你可能需要給自己的應用加一些插件,比如 flash,而一個 flash 插件有 40M 左右,Win(32bit)、Win(64bit) 和 Mac 需要的 flash 插件文件都是不一樣的,所以如果全部打包進你的應用,再用「if - else」去選顯然是不科學的,Mac 下的應用肯定是用不到 Win 版本的插件的,所以這里的文件也是從外面腳本寫進來的
view:是視圖文件夾,也可以說是渲染進程對應的代碼文件夾
build_resource:構建資源或工具文件夾,這個文件夾下放打包到發行這一流程中需要用到的資源和工具,比如程序主圖標、構建安裝包的配置腳本(win)、代碼簽名工具等
deploy:存放部署腳本的文件夾,這里的腳本負責把你的應用安裝包上傳到云存儲(OSS),我們會在 gulp 中的發行環節引入這里寫的腳本進行自動上傳安裝包
dist和release:前者是打包和構建安裝包這兩步的 output 目錄,后者是最終我們會上傳到云端的安裝包目錄,構建和發行環節的差別我們后面會講到
二、把整個流程拆分成段這個部分沒法正向推導,我是從一個亂七八糟的 windows 開發流程開始的,然后修改成一個合適的 windows 開發流程,再因為要兼容 Mac 的開發,再改成現在這樣的流程設計的,所以我沒法從一開始就說因為什么所以要考慮什么,然后慢慢構建出一個合適的工作流,這是上帝視角,這個偏實踐經驗的過程一定是實踐越多,感受越多的。
所以我會先說我的做法,再說這么做的好處,所用的工具是 gulp(如果不熟悉,可以去 gulp 官網看一下,很容易上手),利用 gulp 的 task 串起整條流程,我把工程中的一個階段稱為一個環節,是為了和應用本身的階段(開發、內測、眾測或正式發行)做一個區分,不然都不知道說的階段是指啥:
配置環節:設定需要打包構建針對的系統、位數(Mac 版不考慮 32 bit)和這個版本所處的階段(開發、內存、眾測或正式發行)這些變量,然后把相關配置寫入配置文件模板,再導入 app 文件夾內相應位置,把其他相應的文件也寫入 app 文件夾內相應位置,如此 app 文件夾就 Ready 了。
打包環節:根據不同的平臺打出不同的可執行程序,這一步輸出的是可運行的程序
代碼簽名環節:客戶端特殊的一步,你的應用需要被系統所信任,那就需要代碼簽名,獲取對應平臺下的代碼簽名 CA 然后進行應用簽名,這樣你的應用才能被系統信任
構建安裝包環節:根據不同的系統利用不同的技術和依賴構建安裝包,Windows 下的 .exe 和 Mac下的 .dmg,并且對這兩個安裝包也需要代碼簽名,這一步后你的應用可以被分發安裝啦
發行環節:對構建的安裝包進行最后一步修飾,比如修改合適的文件名,然后上傳到云存儲服務器,獲取到可下載的鏈接,如此,你的應用已經可以經獲取到的 url 訪問進行下載安裝了
以上每一步,Mac 版和 Windows 版的開發都需要經歷,只是所用的方法不同,這樣做的好處,一是統一了 Mac 和 Win 下開發工作流的生命周期,二是簡單和直觀,每一環節目的是什么,輸出是什么很明確。
如此,我在package.json中的script就可以這么寫:
... ... "start": "cross-env NODE_ENV=dev gulp dev", "packDev": "cross-env NODE_ENV=dev gulp pack", "buildDev": "cross-env NODE_ENV=dev gulp build", "releaseDev": "cross-env NODE_ENV=dev gulp release", ... ...
當然這里的NODE_ENV你也可以寫成命令行參數(我只是習慣了用這個),利用這個參數去指定需要針對的應用階段,像以上這樣就配好了「dev」階段的相關腳本,可以用npm run packDev -- --platform="xxxx" --arch="xxxx" --sign這樣形式的命令行去執行不同的 gulp 任務,后面的參數,是需要我們在 gulpfile 文件中解析的,以上3個參數分別表示「系統平臺」、「系統位數」、「是否需要代碼簽名」,我們可以在 gulpfile 文件中給這些參數合適的默認值,使操作更人性化。
三、配置環節目的:一是為之后的環節初始化工作流參數,二是準備好應用文件夾內容(即要打包的目標文件夾 —— app)
做的事:解析命令行參數,初始化工作參數,填充配置文件,把配置文件和相關依賴文件導入到app文件夾內合適的地方
1. 初始化工作參數所用工具:yargs
yargs 是一款優秀的命令行參數解析工具,我們要初始化的工作參數包括以下 3 個:「系統平臺」、「系統位數」、「需不需要簽名」,你也可以把應用的所處階段(開發、內測、眾測、正式)設計成參數。
// 以下 3 個變量在 gulpfile 內全局聲明 // 這里的 detectPlatform() 需要自己寫,利用 node 的 os 模塊去檢測開發機環境從而給出 // 為了理解上直觀一些,把 32 位的 win 寫成 win32,64 位的 win 寫成 win64 // node os.platform() 沒有 win64 的返回的,只有在返回 win32 基礎上,你再使用 os.arch() 去確定是否是win64 // 可能的合法值:darwin、win64、win32 platform = yargs.argv.platform || detectPlatform() || "win32"; // 系統位數,如果是 Mac OS X,不考慮 32位 // 可能的合法值:x64、ia32 arch = platform === "darwin" ? "x64" : (yargs.argv.arch || "ia32"); // 布爾值,指定是否需要代碼簽名 needSign = yargs.argv.sign || process.env.NODE_ENV === "prod" || platform === "darwin";
看到上面的參數初始化,可能會有疑問,既然已經在platform中區分了 win32(32bit) 和 win64(64bit),而且darwin下不考慮 32bit(因為 OS X 10.6 之后就全是 64 位的),arch參數是否多余?這是可以認為是多余的,但是有的話更完整,而且如果你以后又想兼容 linux 了呢?
2. 填充并導入配置文件所用工具:gulp API、gulp-replace、gulp-rename
首先我會在根目錄下的 config 文件夾下放幾個不同的配置文件模板,分別對應應用不同的階段的配置(比如dev.js、alpha.js、beta.js、prod.js),然后利用gulp-replace去替換掉里面的一些占位字符串(也就是填充模板),最后利用gulp-rename重命名為比如env.js后,利用gulp.dest寫入文件到 app/config 目錄下,于是配置文件 Ready。
3. 二進制文件導入(以 flash 為例)所用工具:gulp API、del
以 flash 插件為例,首先你要找到需要的插件文件,electron 官網所說的打開chrome://plugins已經沒法用了,從 chrome 的某個版本開始,chrome://plugins Is Not Available。
所以用系統的搜索功能吧,記得先裝下 chrome 瀏覽器,Mac 搜索「PepperFlashPlayer.plugin」,Windows 搜索「pepflashplayer」,Windows下如果搜到多個,記得選擇和 chrome 目錄有關的那個「.dll」文件,此外 win32bit 和 win64bit 所用的 flash 也是不同的,Mac 下的「PepperFlashPlayer.plugin」本質是一個文件夾,整個文件夾都需要。所有的3個插件放進根目錄下 reserve 文件夾。
接下來需要做的就是,根據不同的平臺讀不同的 flash 插件( .dll 文件或 .plugin 文件夾)到 app/plugin 文件夾下。
這里有一個需要注意的是,每次你構建時,如果 app/plugin 下的 flash 不是你要的,那么你需要先刪除那個舊的,否則你的 app/plugin 文件夾下會躺著一個你不會用的 flash 插件,但會被打包進去,你的文件大小突然多了 40M,我這里用的刪除工具是 del。
經過配置環節,app文件夾已經準備就緒,所以以開發模式(不需要打包)運行應用也就沒啥大問題,可以另寫一個「dev」的 gulp task,利用 node 的child_process模塊下的exec調用下electron app --debug就可以運行應用了,沒啥可以多說的,我們繼續進入下一步 —— 打包。
四、打包環節目的:產出一個可執行程序,簡單來說,就是能有一個應用,雙擊能運行起來
做的事:利用electron-packager打包,補充應用信息(only for win)
1. 利用electron-packager打包利用electron-packager打包,只需要針對不同系統平臺給出不同的配置,然后調用其 API 就可以了。
// Mac const options = { dir: "./app", name: "應用名字", platform: "darwin", arch: arch, // 這就是工作參數 arch overwrite: true, appVersion: "Copyright(C) 2017 Qunhe", asar: { unpackDir: "plugins" // plugins 內的文件我們不希望打進 asar 格式包內 }, out: "./dist", icon: "./build_resource/logo.icns" // Mac 下 icon 格式是 .icns }; // Win const options = { dir: "./app", platform: "win32", // 不管是 32bit 還是 64bit 的 win,這里都是 win32 arch: arch, // 這里依靠 x64 或 ia32 去區分位數 overwrite: true, asar: { unpackDir: "plugins" }, out: "./dist", icon: "./build_resource/logo.ico" // Win 下 icon 格式是 .ico };
Mac 下各處(Dock、任務欄、進程名等地)展示的應用名字只要指定了name選項,就是處處一樣的,所以你可以用 name 指定一個中午名字,而且 Mac 下默認編碼都是 UTF-8,問題不大。
而對于 Windows,首先其中文默認編碼是 GBK 的,而所以如果指定中文名字可能會有奇怪的問題,所以 Windows 應用一般我不填name項,這樣它會去找你 app 目錄下的 package.json 文件中的productName或name字段值,這個字段一般設置是英文的,第二個不去設置中文的原因是,Windows 下應用的展示名字是 exe 主程序的FileDescription配置項決定的,如果不去設置,那么可能你的應用用任務管理器打開,顯示的進程是「Electron」,而不是你的應用名字。
關于應用的實際名字和展示名字,Win 和 Mac 下都有自己的一套,這里不細展開。而基于目前的實踐,我給的建議是,Mac 下的開發,你可以直接指定name為一個你要的中文應用名,而對于 Win,你最好像下面那樣操作。
2. 補充應用信息(for win)所用工具:rcedit
Command line tool to edit resources of exe file on Windows. 翻譯過來就是一個用于編輯 exe 文件信息的windows 命令行工具,當然它已經有了 node 版本,叫 node-rcedit,也就是說你可以用 node 子進程的exec去執行,也可以調用 node 版本的 API。
可以這么用:
execSync(` . ode_modules ceditin cedit // 調用rcedit ./dist/xxxxxx.exe // 目標文件(剛打包出來的主程序) --set-version-string "LegalCopyright" "Copyright(C) 2017 Health" // 版權信息 --set-version-string "CompanyName" "仙風道骨養生俱樂部" // 公司名字 --set-version-string "ProductName" "養生" // 產品名字 --set-version-string "FileDescription" "養生寶典" // 這個很重要,因為這個就是你打開任務管理器看到的進程名字 `);
大部分信息,你可以右鍵主程序(.exe)文件,「屬性 —— 詳細信息」中看到,這么做還有一個考慮是,這樣你的應用看上去會更加規范。
這里肯定有人說,為什么不用electron-builder,因為我首先接觸到的是electron-packager,我覺得夠用(因為我有一臺 win 和一臺 mac,跨平臺打包,不存在的),第二,electron-packager完成打包的事就夠了,后面構建安裝包等過程可以讓我們有更多的選擇,符合本文的工作流設定,每個環節做每個環節該做的事就好,當然你也可以選擇electron-builder,能達到目的就好。
五、代碼簽名環節目的:使應用被系統所認可,能正常安裝
做的事:給應用進行代碼簽名
1. 為什么需要代碼簽名,沒有會怎樣代碼簽名的目的就是為了安全,你的應用一旦經過了代碼簽名,如果發行過程中被篡改,你的用戶會看到系統給出的警告提示,而對于發行方而言,代碼簽名后,應用才能被系統認可,很大概率不會被殺毒軟件做掉,而且如果你要提交一些軟件市場,一些軟件市場要求應用需要有合法的代碼簽名。
而如果作為鐵頭娃的你鐵定不簽名,這應用就不能跑了么?不是的,還是可以跑的,只不過對你的用戶來說很不友好。
1.1 Windows 下有和沒有代碼簽名的差別Windows 下代碼簽名的限制沒有 Mac 那么嚴,你選擇「是」都是可以安裝使用的,但是從你產品的用戶角度,有一個代碼簽名會更可靠,此外,這樣的沒有簽名的安裝包在一些軟件市場可能都提交不上去。
1.2 Mac 下有和沒有代碼簽名的差別Mac 下有和沒有代碼簽名的差別就很大了,沒有合法的代碼簽名,你的 .dmg 安裝包根本沒法打開。
如果沒有代碼簽名,Mac 下的 .dmg 安裝包打開,首先會提示你「該應用來自身份不明的開發者,是否確認打開」,然后你點「確認」,再根據你的安全設定(系統偏好設置 —— 安全和隱私 —— 允許從以下位置的應用下的設置)去決定,而絕大部分的 Mac 用戶都是勾選「App Store 和 被認證的開發者」,于是就算你點了「打開」,直接會告訴你「打不開XXX,因為它來自身份不明的開發者」,這個時候只能去改變「系統偏好設置 —— 安全和隱私 —— 允許從以下位置的應用下的設置」才能打開。
典型的盜版軟件安裝方式啊,所以作為一款要發行的產品,我們一定是需要代碼簽名的。
2. Windows 下的代碼簽名總體建議:個人的小項目就不用 Windows 代碼簽名了,因為很貴,2K+/年,而且 Windows 下代碼簽名沒有問題不是非常大(和 Mac 相比),公司的產品,那就必須要的。
2.1 購買微軟代碼簽名證書可以向權威的 CA 機構購買代碼簽名證書,這里就我了解的做一個建議:建議向賽門鐵克購買簽名普通軟件(非驅動)的微軟代碼簽名證書,大概幾百刀一年。
背景說明:目前我們用的是沃通的代碼簽名證書,賽門鐵克的只是咨詢過,沒用過。
就以上的建議做一個解釋,為什么我這么建議:
我們需要代碼簽名,進一步,需要把 Windows 代碼簽名這一環節也做到自動化流程中,這是我們的需求
沃通的代碼簽名證書是封死在 U 盤里,所以可認為這是物理證書,更安全,但很不方便,不可能導出來進行簽名的
了解到的,賽門鐵克頒發的如果是針對普通軟件(非驅動的),那么是可以給頒發文件格式的真·電子證書的
意味著沃通的證書我們要簽名,需要依靠一個物理U盤
最坑爹的:沃通的代碼簽名時,要手輸密碼,如果一個 Windows 應用我們選擇 SHA1 + SHA256 的簽名方式,那么應用和安裝包,我們需要輸4次密碼,氣到拉閘,他們官方說有自己的命令行,實際是命令行喚起他們的 GUI 圖形界面來簽名,還不是需要人工操作
所以,顯然這和我們的「自動化」目標相去甚遠,我建議普通的應用,沒有涉及到高度安全的,不要選擇購買封死在 U 盤中的 Windows 代碼簽名證書。
2.2 簽名當你購買了證書后,就可以利用signtool 命令行進行簽名了,命令怎么寫,這些都在你購買證書的 CA 網站上找到或者 google 一下,這里要說的就兩點:
Windows 代碼簽名我們目前選擇 SHA1 簽名后再追加 SHA2(SHA256) 簽名,這樣的組合方式,安全和兼容性最好
代碼簽名可以在 gulpfile 文件中封裝成一個方法(參數是需要簽名的文件路徑),因為我們會多次調用
2.3 查看簽名信息查看 Windows 代碼簽名信息很簡單,右鍵你簽名的文件,簽名后的文件,屬性打開會有一個「數字簽名」的 tab,點擊切換到「數字簽名」可以看到代碼簽名信息。
3. Mac 下的代碼簽名總體建議:Mac 下應用要代碼簽名,因為很方便,也不是很貴,個人開發者 99 USD 一年,如果公司有 Apple Develop Team,你可以直接加入,關鍵是 Mac 下如果你不進行可供分發的代碼簽名,你的應用很難被他人安裝啊。
3.1 利用 Xcode 申請證書,各個證書間差別證書是可以在 Xcode 下申請的,Xcode —— Preference —— Account 下,選擇一個Team(之前要先加入),如果是獨立開發者,就選自己 Apple ID 的那個,點擊「Manage Certificates」,彈出的彈窗中左下角點加號,可以選擇需要的證書。
我看到之后的第一反應是:尼瑪,哪些是我要的啊。下面簡單說明下(摘自Mac App 發布的最后 1km):
Developer Certificate
Mac Development :這個只用來開發,Debug,不是正式發布的版本
Production Certificate
Mac App Store
Mac App Distribution :這個用于 Xcode 自己把 .app 文件上傳到 Mac App Store
Mac Installer Distribution :這個沒用過,但可以肯定的,也是上傳 Mac App Store 用的
Developer ID
Developer ID Application:這個用于開發者使用開發者帳號簽名,導出一個線下發布版本的 .app 文件,脫離了蘋果的 Mac App Store。
Developer ID Installer:用于開發者打包,同時加上開發者帳號簽名,打包工具在下面介紹。
我們主要需要的就是「Developer ID Application」這個類型的證書,「Mac Development」只是用于開發的,而前者可以供分發,也就是簽名后,別人下載安裝,就是來自「被認證的開發者」的應用啦。
如果是在一個 Team 中,不是個人獨立開發者,那么這個「Developer ID Application」證書的申請你是沒有權限的,就算你們 Team 的 Agent 設置你為 admin(管理員),你還是沒有權限的,因為一個「Developer ID Application」只有一個 Team 的 agent(owner) 才能申請,你需要做的是利用你 Mac 上的鑰匙串工具(具體怎么做,google 下就可以了),生成「CertificateSigningRequest」(簡稱 CSR),然后發給你的 team agent,讓他幫你生成證書,發回給你,你再安裝到自己機子上,搞定。
你可以在終端調用security find-identity -p codesigning -v來看一下你可用的代碼簽名證書,其中那個Developer ID Application開頭的就是我們要的。
3.2 簽名所用工具:electron-osx-sign
Mac 下的簽名簡直是紅紅火火開開心心嘿嘿哈哈啊,你可以從electron-osx-sign 指導這里獲得完全的指導,你在這個頁面右邊可以根據你的項目進行填寫,頁面最后會根據你的配置,給你一段你都可以直接復制的簽名代碼,完美。
而且簽名還能集成到打包階段,不過我建議還是拿出來好,比較清真。
3.3 查看簽名信息Mac 下查看文件簽名信息,你可以終端運行codesign --display --verbose=4 "文件路徑"。
六、構建安裝包環節目的:使你的應用可以被安裝(如果沒有這一步,你能怎么辦,壓縮整個應用文件夾,然后分發這個壓縮包,呃,你能接受也可以啊)
做的事:把經歷了打包和簽名環節后的應用程序文件夾(Mac 下的.app其實也是文件夾)打成一個安裝包文件
為什么要構建安裝包,這有很多的原因,可能你也會想到很多,其中值得強調的兩點,一是構建安裝包會直接便利于應用的自動更新,具體我們下一篇文章里再說,二是 Win 下安裝包的體積相比原先的文件夾,體積明顯小很多,在硬盤容積很大的時代,下載體積才是最影響用戶體驗的,而安裝后的體積不是最需要考慮的體積。
安裝包這個事和代碼簽名類似,兩個不同的系統(Win 和 Mac)實現完全不同,Windows 下我們習慣.exe或.msi這樣的安裝包格式,習慣點下一步到完成或一鍵安裝,而 Mac 下除了 Store 下載安裝的,我們習慣的.dmg格式的,掛載后打開,將里面的應用拖入到Application文件夾就完成了安裝。
這里我們實現的就是經典的 Windows exe 安裝和 Mac dmg 安裝,相比較而言,Windows 下的繁瑣得多得多。
1. Windows 下利用 inno setup 進行安裝包構建 1.1 為什么用這個 inno setup最終說服我使用 inno setup 來構建應用安裝包的理由是,VS Code 也是這么做的。因為按照程序這個領域離一個小前端已經很遙遠了,對于跨度大的未知東西,一般都會做充足的調研,最后發現 VS Code 也是這么做的,好,干!
而使用了一段時間后,我可以說幾點不后悔的理由(當然我沒使用過其他的安裝包構建工具,所以僅一些偏見):
inno setup 應該是 windows 下構建安裝程序的老牌工具了,你可以去進他們的官網,一股「老牌可靠」的風格撲面而來,可靠
它有 GUI 和 命令行工具,有 unicode 版本(意味著完全支持中文),gulp 有別人寫好的現成的插件(對于中文應用需要修改)
基本使用的話,學習成本不大,基本去找一些案例配置文件去學一下就可以了
進階使用,需要寫 pascal 腳本,但是功能是真的強大
還有一點我感受很好的是,這個工具的支持很好,stackoverflow 上有足夠的問答資源,如果還是沒有你滿意的,官網有一個看上去很很很簡陋的論壇,但是很有用啊,我問過 2 個問題,睡一覺起來都有回應了
1.2 怎么學習 inno setup先可以自己去搜一下 inno setup,進入官網逛一逛,下載安裝一下(記得安裝 unicode 版本,即括號里有 u 的版本),瀏覽后有幾個基本認知需要具備:
inno setup 是完全根據配置文件(.iss)來構建安裝程序的,你用 GUI 其實也是去編寫 .iss 文件,然后利用這個配置構建的
inno setup 可以用 pascal 腳本控制安裝向導的行為,這是進階的使用方式,足夠你安裝自己的設想優化安裝程序了
inno setup 構建出來的安裝包運行時可以添加參數,使安裝有不同的表現,比如完全靜默的后臺安裝(Amazing,這里的參數對于自動更新很有用)
有了上面的幾點認知,可以給出「學習和使用 inno setup 路徑」的建議:
下載安裝后,找幾篇 inno setup GUI 使用教程,嘗試構建一個安裝包(要可以安裝的)
找一些 inno setup 配置文件的案例,對于 inno setup 配置方式有一個印象,分多個[section],每個[section]有很多配置項,每個配置項可能有多個字段
可以把 inno setup 官方文檔 瀏覽一遍,跳過「pascal scripting」部分
到這里,你應該能看得懂他人的 .iss 文件里除了 [code] 這個 section 外的配置了
把安裝向導的語言換成中文(先要導入中文語言包,再改配置,具體做法也有一些文章說到了,不多說,這一步對于你之后步驟也是有用的)
可以嘗試正式結合到你的 gulp 工作流了
1.3 怎么結合到 gulp 工作流中所用工具:修改后的 gulp-inno
如果按照之前的步驟花了個把小時大概學習了下 inno setup 的話,那么到這里你應該可以嘗試把 inno setup 構建安裝包做到你的 gulp 工作流中了,如果還不熟悉 inno setup 配置文件,沒關系,你可以從仿照開始,不要慫,就是干,都到這一步了,誰慫誰尷尬。
配置文件的詳解不是這里的重點,所以不再展開,把 inno setup 整合進腳本中,因為它本身提供命令行工具,勤快和好學的你可以根據官方或其他渠道的指導自己封裝一個 node 模塊,而我就比較懶了,搜到一個已有的 gulp 插件 —— 「gulp-inno」,高興地一匹。
然而,事情總不會那么順利,該吃的shi躲不掉,該經歷的坑繞不過,這才叫「歷shi」。我利用「gulp-inno」根據其指導怎么都不能正確編譯,大概提示是有不合法的字符的意思。
明白了,絕壁是「gulp-inno」里包的 inno setup 不是 unicode 版本,所以一旦有中文等字符,就出錯了,我看到這個包里的 inno 文件夾完全就是和我的 inno setup 文件夾沒差嘛,于是我把我本地安裝的 inno setup 文件夾里內容復制替換到 gulp-inno 的 inno 的文件夾內,問題解決。
因為我之前導入過中文語言包,所以我復制過去的時候,中文語言包也復制過去了,可以愉快地配置安裝向導界面為中文了。
一旦修改好「gulp-inno」包(替換成 unicode 版本 & 加入簡體中文語言包),就可以怎么操作:
// 1. 準備 iss 文件:填充你的 iss 配置文件模板,并輸出到 dist 目錄下 const appInfo = require("./app/package.json"); // 所有和應用相關的信息從 package.json 讀取 const bom = require("gulp-bom"); // 這是為了解析中文的 const outputName = `${appInfo.name}-${platform}-${appInfo.version}-${process.env.NODE_ENV}`; const outputIssName = `${appInfo.name}-${platform}-${process.env.NODE_ENV}.iss` gulp .src(`./build_resource/installer_win_config_${platform}.iss`) .pipe(bom()) .pipe(replace("${version}", appInfo.version)) .pipe(replace("${appExe}", `${appInfo.name}.exe`)) .pipe(replace("${sourcePath}", `${appInfo.name}-${platform}`)) .pipe(replace("${outputName}",outputName)) .pipe(rename(outputIssName)) .pipe(gulp.dest("./dist")) .on("end", () => { // .iss file is ready }) // 2. 交給 inno setup const inno = require("my-gulp-inno"); // 修改后的 gulp-inno gulp .src(`./dist/${outputIssName}`) .pipe(inno()) .on("end", () => { // you have an installer now });1.4 未來可以做什么
當時還有一個看中 inno setup 的理由是,它可以讓我們定制我們的安裝向導步驟和外觀,也就是說你可以讓你的應用也像其他一些優秀的產品一樣,在安裝的時候可以定制酷炫的外觀,可以優化安裝流程,支持一鍵安裝,inno setup 還是可以玩出一些花樣的,enjoy。
1.5 對安裝包也進行代碼簽名同樣的,安裝包也需要代碼簽名,利用之前封裝的簽名方法進行簽名就行了。
2. Mac 下的構建 dmg 安裝包所用工具:appdmg
相比于 windows 的安裝包構建,Mac 下的構建安裝包又是美滋滋啊,你看我下面小標題都沒有就知道了。
// 因為 appdmg 在 windows 下不能下載安裝的,所以放在外部 package.json 的 optionalDependencies 下 // 在 gulp 腳本中需要做 try...catch 處理,否則當你回到 windows 下使用這份 gulp 時會出報錯 let appdmg; try { appdmg = require("appdmg"); } catch (err) { appdmg = null; } const dmg = appdmg({ // 打出的目標 dmg target: `dist/balabala.dmg`, // 基準目錄,以下的資源都基于這個目錄 basepath: __dirname, // 具體的選項 specification: { // dmg 打開后的窗口名字 // 注意不要給中文,給中文會導致下面的 background 無效,不明白, github 上也有人提了這個 issue title: `myapp`, // dmg 掛載后的圖標,出現在桌面上 icon: "xxx.icns", // 背景圖,如果同時存在 bg.png 和 bg@2x.png,appdmg 會根據用戶屏幕自己找合適的圖 background: "bg.png", // 里面所有icon的尺寸 "icon-size": 96, // 窗口設置 window: { size: { width: 550, height: 320 } }, // 里面的內容,x 是指這個 icon 中心距離窗口最左邊的距離,y 是指這個 icon 中心距離窗口頂部的距離 // 這里可以指定一個name項,不要給中文,會導致圖標異常 contents: [ { "x": 400, "y": 128, "type": "link", "path": "/Applications" }, { "x": 150, "y": 128, "type": "file", "path": "你的應用.app" } ], // 對 dmg 進行代碼簽名 "code-sign": { "signing-identity": "你的代碼簽名證書" } } }); dmg.on("finish", function () { // you have a dmg now }); dmg.on("error", function (err) { // error });
其余的配置和所以配置影響的內容可以參加 appdmg githug 主頁,然后就是自己試試看了。
七、發行環節目的:使應用可以被下載(上一步只是能被安裝,但并不能被下載)
做的事:重命名應用安裝包供發行,上傳應用安裝包到云存儲服務器供下載
這一步根據每個人使用的云存儲方式不同而需要利用賣方提供的 API 編寫合適的腳本去上傳你的安裝包,因此具體的腳本不做展開,只是有幾點最佳實踐可以參考:
上傳前,把你的安裝包文件重命名成符合一定規范的,可能是「應用名-版本-階段-系統-尾數」,可能是「應用名-版本-系統-構建號」,可能是...這個就自己定,但一定要有一個合適的命名,這樣一看到名字就知道這個是啥,不會弄錯
你的 OSS 服務器上要針對應用安裝包的不同階段建立不同的文件夾,一方面可以方面管理,另一方面也便于做權限管理
當你上傳了你的安裝包后,也就意味著這個安裝包有了一個下載鏈接,你可以分發這個鏈接供用戶下載啦,至此終于走完了「代碼」到可下載「安裝包」的過程,鼓掌。
八、路漫漫這一路走來看上去已經很有成就感,但實際上還有許多事可以做得更好,不過工程化的東西,邏輯清晰、流程自動化、能滿足需求就可以了,而搭好工程,我們需要開始專注于 Electron 應用的功能開發了,才剛剛要邁上紅地毯,路還有很長,下期見。
附:gulp 文件和腳本看上去會是怎樣的對之前的工作流做一個小結(如果遇到有一些舊文件覆蓋不了,可以自己加一個清理環節或方法,去清理舊文件)
/* gulpfile.js START */ // 此處省略一堆需要引入的依賴 // 工作參數 let platform = "win32"; let arch = "ia32"; let needSign = false; // 配置環節 gulp.task("env", (cb) => { // ... }); // 開發調試 gulp.task("dev", ["env"], (cb) => { exec("electron app --debug", (err) => { if (err) return cb(err); cb(); }); }); // 打包環節 gulp.task("pack",["env"], (cb) => { if (platform === "darwin") { // ... } else { // ... } }); // 簽名環節 gulp.task("sign-pack", ["pack"], (cb) => { if (needSign) { if (platform === "win32" || platform === "win64") { // ... } else if (platform === "darwin") { // ... } } else { cb(); } }); // 構建環節 gulp.task("build", ["sign-pack"], (cb) => { if (platform === "darwin") { // ... } else { // ... } }); // 發行環節 gulp.task("release", ["build"], (cb) => { // ... }); const codeSignForWin = (filePath) => {...}; const codeSignForMac = (filePath) => {...}; const detectPlatform = () => {...}; /* gulpfile.js END */ // package.json 中配腳本 "scripts": { "yarnall": "yarn && (cd app && yarn)", "start": "cross-env NODE_ENV=dev gulp dev", "packDev": "cross-env NODE_ENV=dev gulp pack", "packAlpha": "cross-env NODE_ENV=alpha gulp pack", "packProd": "cross-env NODE_ENV=prod gulp pack", "buildDev": "cross-env NODE_ENV=dev gulp build", "buildAlpha": "cross-env NODE_ENV=alpha gulp build", "buildProd": "cross-env NODE_ENV=prod gulp build", "releaseDev": "cross-env NODE_ENV=dev gulp release", "releaseAlpha": "cross-env NODE_ENV=alpha gulp release", "releaseProd": "cross-env NODE_ENV=prod gulp release" } // 可選命令行參數: // sign: 是否簽名 // platform: 系統平臺 // arch: 系統位數
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/89589.html
摘要:開發教程步步為營,掌握基礎技能發布機器學習速成課程為了幫助更多的人了解與學習機器學習相關的知識技能,發布了人工智能學習網站。更多相關內容參考數據科學與機器學習實戰手冊。 showImg(https://segmentfault.com/img/remote/1460000013586587); 前端每周清單專注前端領域內容,以對外文資料的搜集為主,幫助開發者了解一周前端熱點;分為新聞熱...
摘要:壓縮圖片桌面應用基于制作一個壓縮圖片的桌面應用下載地址項目源碼準備工作我們來整理一下我們需要做什么壓縮圖片模塊獲取文件路徑桌面應用生成壓縮圖片我們需要使用這個庫來壓縮圖片,這里我們把這個庫封裝成壓縮模塊。 壓縮圖片桌面應用imagemin-electron 基于electron制作一個node壓縮圖片的桌面應用 下載地址:https://github.com/zenoslin/imag...
摘要:首發于酷家樂前端博客標題是我以第一視角基于開發客戶端產品的體驗,我將在之后分一系列文章向有興趣的朋友一步一步介紹我是怎么從玩玩具的心態開始接觸到去開發客戶端產品,最后隨著業務和功能的復雜度提升再不斷地優化客戶端。 首發于酷家樂前端博客 標題是我以第一視角基于 Electron 開發客戶端產品的體驗,我將在之后分一系列文章向有興趣的朋友一步一步介紹我是怎么從玩玩具的心態開始接觸 Ele...
摘要:軟件跨平臺支持以及,運行流暢,可謂是微軟的良心之作微軟有這個宇宙最強,自然也不會弱宇宙最強編輯器說到代碼編輯器,我們有必要提一提還有。 原文鏈接:VS Code上手與超實用插件安利 工欲善其事必先利其器 Visual Studio Code (簡稱 VS Code / VSC) 是一款免費開源的現代化輕量級代碼編輯器,支持幾乎所有主流的開發語言的語法高亮、智能代碼補全、自定義熱鍵、括號...
閱讀 3690·2021-11-22 15:24
閱讀 1593·2021-09-26 09:46
閱讀 1904·2021-09-14 18:01
閱讀 2601·2019-08-30 15:45
閱讀 3526·2019-08-30 14:23
閱讀 1866·2019-08-30 12:43
閱讀 2915·2019-08-30 10:56
閱讀 800·2019-08-29 12:20