Webpack: 開發 PWA、Node、Electron 應用

概述

毋庸置疑,對前端開發者而言,當下正是一個日升月恒的美好時代!在久遠的過去,Web 頁面的開發技術鏈條非常原始而粗糙,那時候的 JavaScript 更多用來點綴 Web 頁面交互而不是用來構建一個完整的應用。直到 2009年5月 Ryan Dahl 正式發布 NodeJS,JavaScript 終于有機會脫離 Web 瀏覽器獨立運行,隨之而來的是,基于 JavaScript 構建應用程序的能力被擴展到越來越多場景,我們得以用相同的語言、技術棧、工具獨立開發桌面端、服務端、命令行、微前端、PWA 等應用形態。

相應地,我們需要更好的構建、模塊化以及打包能力來應對不同形態的工程化需求,所幸 Webpack 提供的功能特性,能夠充分支撐這些場景。

前面兩個章節我們已經詳細介紹了如何使用 Webpack 構建 NPM Library,以及如何基于 Module Federation 搭建微前端架構。本文將繼續匯總這些特化場景需求,包括:

  • 如何使用 Webpack 構建 Progressive Web Apps 應用;
  • 如何使用 Webpack 構建 Node 應用;
  • 如何使用 Webpack 構建 Electron 應用。

構建 PWA 應用

PWA 全稱 Progressive Web Apps (漸進式 Web 應用),原始定義很復雜,可以簡單理解為 一系列將網頁如同獨立 APP 般安裝到本地的技術集合,借此,我們即可以保留普通網頁輕量級、可鏈接(SEO 友好)、低門檻(只要有瀏覽器就能訪問)等優秀特點,又同時具備獨立 APP 離線運行、可安裝等優勢。

實現上,PWA 與普通 Web 應用的開發方法大致相同,都是用 CSS、JS、HTML 定義應用的樣式、邏輯、結構,兩者主要區別在于,PWA 需要用一些新技術實現離線與安裝功能:

  • ServiceWorker: 可以理解為一種介于網頁與服務器之間的本地代理,主要實現 PWA 應用的離線運行功能。例如 ServiceWorker 可以將頁面靜態資源緩存到本地,用戶再次運行頁面訪問這些資源時,ServiceWorker 可攔截這些請求并直接返回緩存副本,即使此時用戶處于離線狀態也能正常使用頁面;

請添加圖片描述

  • manifest 文件:描述 PWA 應用信息的 JSON 格式文件,用于實現本地安裝功能,通常包含應用名、圖標、URL 等內容,例如:

    // manifest.json
    {"icons": [{"src": "/icon_120x120.0ce9b3dd087d6df6e196cacebf79eccf.png","sizes": "120x120","type": "image/png"}],"name": "My Progressive Web App","short_name": "MyPWA","display": "standalone","start_url": ".","description": "My awesome Progressive Web App!"
    }
    

我們可以選擇自行開發、維護 ServiceWorkermanifest 文件 ,也可以簡單點使用 Google 開源的 Workbox 套件自動生成 PWA 應用的殼,首先安裝依賴:

yarn add -D workbox-webpack-plugin webpack-pwa-manifest

其中:

  • workbox-webpack-plugin:用于自動生成 ServiceWorker 代碼的 Webpack 插件;
  • webpack-pwa-mainifest:根據 Webpack 編譯結果,自動生成 PWA Manifest 文件的 Webpack 插件。

之后,在 webpack.config.js 配置文件中注冊插件:

const HtmlWebpackPlugin = require("html-webpack-plugin");
const { GenerateSW } = require("workbox-webpack-plugin");
const WebpackPwaManifest = require("webpack-pwa-manifest");module.exports = {// ...plugins: [new HtmlWebpackPlugin({title: "Progressive Web Application",}),// 自動生成 Manifest 文件new WebpackPwaManifest({name: "My Progressive Web App",short_name: "MyPWA",description: "My awesome Progressive Web App!",publicPath: "/",icons: [{// 桌面圖標,注意這里只支持 PNG、JPG、BMP 格式src: path.resolve("src/assets/logo.png"),sizes: [150],},],}),// 自動生成 ServiceWorker 文件new GenerateSW({clientsClaim: true,skipWaiting: true,}),],
};

之后,執行編譯命令如 npx webpack 就可以生成如下資源:

├─ 8-1_pwa
│  ├─ src
│  │  ├─ xxx
│  ├─ dist
│  │  ├─ icon_150x150.119e95d3213ab9106b0f95100015a20a.png
│  │  ├─ index.html
│  │  ├─ main.js
│  │  ├─ manifest.22f4938627a3613bde0a011750caf9f4.json
│  │  ├─ service-worker.js
│  │  ├─ workbox-2afe96ff.js
│  └─ webpack.config.js

接下來,運行并使用 Chrome 打開頁面,打開開發者工具,切換到 Applicatios > Service Workers 面板,可以看到:

請添加圖片描述

這表明 Service Worker 已經正常安裝到瀏覽器上。此外,地址欄右方還會出現一個下載圖標:

請添加圖片描述

點擊該圖標可將應用下載到本地,并在桌面創建應用圖標 —— 效果如同安裝獨立 App 一樣。

提示:PWA 是一種復雜度較高的技術,前文只是介紹了一種 Webpack 構建 PWA 的簡單方法,感興趣的同學可以擴展閱讀:

  • developer.chrome.com/docs/workbo…
  • developers.google.com/web/fundame…

構建 Node 應用

注意,在開發 Node 程序時使用 Webpack 的必要性并不大,因為 Node 本身已經有完備的模塊化系統,并不需要像 Web 頁面那樣把所有代碼打包成一個(或幾個)產物文件!即使是為了兼容低版本 Node 環境,也可以使用更簡單的方式解決 —— 例如 Babel,引入 Webpack 反而增加了系統復雜度以及不少技術隱患。

不過,出于學習目的,我們還是可以了解一下使用 Webpack 構建 Node 程序的方法及注意事項,包括:

  1. 需要 Webpack 的 target 值設置為 node ,這能讓 Webpack 忽略 fs/path 等原生 Node 模塊;
  2. 需要使用 externals 屬性過濾 node_modules 模塊,簡單起見,也可以直接使用 webpack-node-externals 庫;
  3. 需要使用 node 屬性,正確處理 __dirname__filename 值。

一個典型的 Node 構建配置如下:

const nodeExternals = require("webpack-node-externals");module.exports = merge(WebpackBaseConfig, {// 1. 設置 target 為 nodetarget: "node",entry: ...,module: [...],// 2. 過濾 node_modules 模塊externals: [nodeExternals()],// 3. 設置 __dirname, __filename 值node: {__filename: false,__dirname: false,},
});

在此基礎上,我們可以復用大多數 Loader、Plugin 及 Webpack 基礎能力實現各種構建功能。

不過,需要特別注意,在 Node 代碼中請務必慎用動態 require 語句,你很可能會得到預期之外的效果!例如對于下面的示例目錄:

├─ example
│  ├─ src
│  │  ├─ foo.js
│  │  ├─ bar.js
│  │  ├─ unused.js
│  │  └─ main.js
│  ├─ package.json
│  └─ webpack.config.js

其中 main.js 為入口文件,代碼:

const modules = ['foo', 'bar'].map(r => require(`./${r}.js`));

可以看到在 main.js 中并沒有引用 unused.js ,但打包產物中卻包含了 src 目錄下所有文件:
請添加圖片描述

這是因為 Webpack 遇到示例中的 require 語句時,僅僅依靠詞法規則、靜態語義、AST 等手段并不能推斷出實際依賴情況,只能退而求其次粗暴地將所有可能用到的代碼一股腦合并進來,這種處理手段很可能會帶來許多意想不到的結果,很可能觸發 BUG!

綜上,建議盡量不要使用 Webpack 構建 Node 應用。

構建 Electron 應用

Electron 是一種使用 JavaScript、HTML、CSS 等技術構建跨平臺桌面應用開發框架,這意味著我們能用我們熟悉的大部分 Web 技術 —— 例如 React、Vue、Webpack 等開發桌面級應用程序。實際上,許多大名鼎鼎的應用如 VSCode、Facebook Messenger、Twitch,以及國內諸多小程序 IDE 都是基于 Electron 實現的。

與 Web 頁面不同,Electron 應用由一個 主進程 及若干 渲染進程 組成,進程之間以 IPC 方式通訊,其中:

  • 主進程是一個 Node 程序,能夠使用所有 Node 能力及 Electron 提供的 Native API,主要負責應用窗口的創建與銷毀、事件注冊分發、版本更新等;
  • 渲染進程本質上是一個 Chromium 實例,負責加載我們編寫的頁面代碼,渲染成 Electron 應用界面。

請添加圖片描述

  • 提示:Chromium 是一個非常簡潔的開源瀏覽器,許多瀏覽器都基于 Chromium 二次開發而成,例如 Chrome、Microsoft Edge、Opera 等。

Electron 這種多進程機構,要求我們能在同一個項目中同時支持主進程與若干渲染進程的構建,兩者打包需求各有側重。接下來我們將通過一個簡單示例,逐步講解如何使用 Webpack 搭建一套完備的 Electron 應用構建環境,示例文件結構如下:

8-3_electron-wp
├─ package.json
├─ webpack.main.config.js       // 主進程構建配置
├─ webpack.renderer.config.js   // 渲染進程構建配置
├─ src
│  ├─ main.js
│  ├─ pages
│  │  ├─ home
│  │     ├─ index.js
│  │  ├─ login
│  │     ├─ index.js

其中:

  • src/main.js 為主進程代碼;
  • src/pages/${page name}/ 目錄為渲染進程 —— 即桌面應用中每一個獨立頁面的代碼;
  • 由于主進程、渲染進程的打包差異較大,這里為方便演示,直接寫成兩個配置文件:webpack.main.config.jswebpack.renderer.config.js

Electron 主進程打包配置

主進程負責應用窗口的創建銷毀,以及許多跨進程通訊邏輯,可以理解為 Electron 應用的控制中心,簡單示例:

// src/main.js
const { app, BrowserWindow } = require("electron");// 應用啟動后
app.whenReady().then(() => {// 創建渲染進程實例const win = new BrowserWindow({width: 800,height: 600});// 使用 BrowserWindow 實例打開頁面win.loadFile("home.html");
});

代碼核心邏輯是在應用啟動后 (app.whenReady 鉤子),創建 BrowserWindow 實例并打開頁面。

  • 提示:建議結合 Electron 官方提供的 完整示例 一起學習。

Electron 主進程本質上是一個 Node 程序,因此許多適用于 Node 的構建工具、方法也同樣適用主進程,例如 Babel、TypeScript、ESLint 等。與普通 Node 工程相比,構建主進程時需要注意:

  • 需要將 target 設置為 electron-main ,Webpack 會自動幫我們過濾掉一些 Electron 組件,如 clipboardipcscreen 等;
  • 需要使用 externals 屬性排除 node_modules 模塊,簡單起見也可以直接使用 webpack-node-externals 包;
  • 生產環境建議將 devtools 設置為 false,減少包體積。

對應的配置腳本:

// webpack.main.config.js
const path = require("path");
const nodeExternals = require("webpack-node-externals");module.exports = {// 主進程需要將 `target` 設置為 `electron-main`target: "electron-main",mode: process.env.NODE_ENV || "development",// 開發環境使用 `source-map`,保持高保真源碼映射,方便調試devtool: process.env.NODE_ENV === "production"? false: "source-map",entry: {main: path.join(__dirname, "./src/main"),},output: {filename: "[name].js",path: path.join(__dirname, "./dist"),},externals: [nodeExternals()],
};

至此,一個非常簡單的主進程腳本與構建環境示例就搭建完畢了,執行下述命令即可完成構建工作:

npx webpack -c webpack.main.config.js

另外,安裝 Electron 過程中可能會遇到網絡超時問題,這是因為資源域已經被墻了,可以使用阿里云鏡像解決:

ELECTRON_MIRROR="https://cdn.npm.taobao.org/dist/electron/" npm i -D electron

Electron 渲染進程打包配置

Electron 渲染進程本質上就一個運行在 Chromium 瀏覽器上的網頁,開發方法基本等同于我們日常開發的普通 Web 頁面,例如我們可以用 React 開發 Electron 渲染進程:

// src/home/index.js
import React from "react";
import ReactDOM from "react-dom";const root = document.createElement("div");ReactDOM.render(<h1>Hello world!</h1>, root);document.body.append(root);

相應的,我們可以復用大部分普通 Web 頁面構建的方式方法,主要差異點:

  1. 需要將 Webpack 的 target 配置設置為 electron-renderer
  2. Electron 應用通常包含多個渲染進程,因此我們經常需要開啟多頁面構建配置;
  3. 為實現渲染進程的 HMR 功能,需要對主進程代碼稍作改造。

第一點很簡單:

// webpack.renderer.config.js
module.exports = {// 渲染進程需要將 `target` 設置為 `electron-renderer`target: "electron-renderer"
};

提示:Webpack 為 Electron 提供了三種特殊 target 值:electron-main/electron-renderer/electron-preload,分別用于主進程、Renderer 進程、Preload 腳本三種場景。

第二點可以用多 entry 配置實現,如:

// webpack.renderer.config.js
// 入口文件列表
const entries = {home: path.join(__dirname, "./src/pages/home"),login: path.join(__dirname, "./src/pages/login"),
};// 為每一個入口創建 HTMLWebpackPlugin 實例
const htmlPlugins = Object.keys(entries).map((k) =>new HtmlWebpackPlugin({title: `[${k}] My Awesome Electron App`,filename: `${k}.html`,chunks: [k],})
);module.exports = {mode: process.env.NODE_ENV || "development",entry: entries,target: "electron-renderer",plugins: [...htmlPlugins],// ...
};

第三點,由于 Webpack 的 HMR 功能強依賴于 WebSocket 實現通訊,但 Electron 主進程常用文件協議 file:// 打開頁面,該協議不支持 WebSocket 接口,為此我們需要改造主進程啟動代碼,以 HTTP 方式打開頁面代碼,如:

function createWindow() {const win = new BrowserWindow({//...});if (process.env.NODE_ENV === "development") {// 開發環境下,加載 http 協議的頁面,方便啟動 HMRwin.loadURL("http://localhost:8080/home");} else {// 生產環境下,依然使用 `file://` 協議win.loadFile(path.join(app.getAppPath(), "home.html"));}
}
  • 提示:在生產環境中,出于性能考慮,Electron 主進程通常會以 File URL Scheme 方式直接加載本地 HTML 文件,這樣我們就不必為了提供 HTML 內容而專門啟動一個 HTTP 服務進程。不過,同一份代碼,用 File URL Scheme 和用 HTTP 方式打開,瀏覽器提供的接口差異較大,開發時注意區分測試接口兼容性。

至此,改造完畢

總結

綜上,Webpack 不僅能構建一般的 Web 應用,理論上還適用于一切以 JavaScript 為主要編程語言的場景,包括 PWA、Node 程序、Electron 等,只是不同場景下的具體構建需求略有差異:

  • PWA:需要使用 workbox-webpack-plugin 自動生成 ServiceWorker 代碼;使用 webpack-pwa-mainifest Manifest 文件;
  • Node 程序:需要設置 Webpack 配置項 target = "node";需要使用 externals 屬性過濾 node_modules 模塊;需要使用 node 屬性正確處理 Node 全局變量;
  • Electron 桌面應用:需要為主進程、渲染進程分別設置不同的構建腳本;同時需要注意開發階段使用 HMR 的注意事項。

這種強大、普適的構建能力正是 Webpack 的核心優勢之一,同類工具無出其右者,雖然不能一招鮮吃天下,但也足夠覆蓋大多數前端應用場景。站在學習的角度,你可以將主要精力放在 Webpack 基礎構建邏輯、配置規則、常用組件上,遇到特殊場景時再靈活查找相應 Loader、Plugin 以及其它生態工具,就可以搭建出適用的工程化環境。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/37874.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/37874.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/37874.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

LINUX操作系統:Mx Linux,用虛擬機VMware Workstation安裝體驗

需求說明&#xff1a; 操作系統目前流行有Windows、Linux、Unix等&#xff0c;中國人應該要知道國有操作系統&#xff0c;也要支持國產操作系統&#xff0c;為了更好支持國產操作系統&#xff0c;我們也要知己知彼&#xff0c;那么今天就來體驗一把操作系統Mx_Linux_23.2的安裝…

分享一個下載windows系統鏡像包的網站

下載各種操作系統&#xff08;比如Windows、Linux、MacOS等&#xff09;比較快的鏡像站點&#xff0c;我嘗試過這個不錯&#xff0c;提供了BT連接&#xff0c;可以用迅雷軟件下載&#xff0c;速度很快的&#xff01; 入口地址&#xff1a;NEXT, ITELLYOU 1&#xff09;打開網站…

[XYCTF新生賽2024] pwn

用了一周來復現crypto部分(不能算是復現&#xff0c;拿著 糖醋小雞塊的WP一點點學了下)。 兩天時間復現PWN部分。相對來說PWN比密碼這塊要簡單&#xff0c;不過ARM,MIPS懶得學了&#xff0c;跳過。 malloc_flag 題目先打開flag將建0x100的塊&#xff0c;然后把flag讀入再fre…

[深度學習] Transformer

Transformer是一種深度學習模型&#xff0c;最早由Vaswani等人在2017年的論文《Attention is All You Need》中提出。它最初用于自然語言處理&#xff08;NLP&#xff09;任務&#xff0c;但其架構的靈活性使其在許多其他領域也表現出色&#xff0c;如計算機視覺、時間序列分析…

MySQL高級-SQL優化- limit優化(覆蓋索引加子查詢)

文章目錄 0、limit 優化0.1、從表 tb_sku 中按照 id 列進行排序&#xff0c;然后跳過前 9000000 條記錄0.2、通過子查詢獲取按照 id 排序后的第 9000000 條開始的 10 條記錄的 id 值&#xff0c;然后在原表中根據這些 id 值獲取對應的完整記錄 1、上傳5個sql文件到 /root2、查看…

libctk shared library的設計及編碼實踐記錄

一、引言 1.1 <libctk>的由來 1.2 <libctk>的設計理論依據 1.3 <libctk>的設計理念 二、<libctk>的依賴庫 三、<libctk>的目錄說明 四、<libctk>的功能模塊及使用實例說明 4.1 日志模塊 4.2 mysql client模塊 4.3 ftp client模塊 4…

鴻蒙開發設備管理:【@ohos.geolocation (位置服務)】

位置服務 說明&#xff1a; 本模塊首批接口從API version 7開始支持。后續版本的新增接口&#xff0c;采用上角標單獨標記接口的起始版本。 導入模塊 import geolocation from ohos.geolocation;geolocation.on(‘locationChange’) on(type: ‘locationChange’, request: L…

安卓開發自定義時間日期顯示組件

安卓開發自定義時間日期顯示組件 問題背景 實現時間和日期顯示&#xff0c;左對齊和對齊兩種效果&#xff0c;如下圖所示&#xff1a; 問題分析 自定義view實現一般思路&#xff1a; &#xff08;1&#xff09;自定義一個View &#xff08;2&#xff09;編寫values/attrs.…

poi-tl 生成 word 文件(插入文字、圖片、表格、圖表)

文章說明 本篇文章主要通過代碼案例的方式&#xff0c;展示 poi-tl 生成 docx 文件的一些常用操作&#xff0c;主要涵蓋以下內容 &#xff1a; 插入文本字符&#xff08;含樣式、超鏈接&#xff09;插入圖片插入表格引入標簽&#xff08;通過可選文字的方式&#xff0c;這種方…

俄羅斯防空系統

俄羅斯的S系列防空系統是一系列先進的地對空導彈系統&#xff0c;旨在防御各類空中威脅&#xff0c;包括飛機、無人機、巡航導彈和彈道導彈。以下是幾種主要的S系列防空系統&#xff1a; 1. **S-300系統**&#xff1a; - **S-300P**&#xff1a;最早期的版本&#xff0c;用…

翻譯造句練習

翻譯練習 翻譯 1&#xff1a;經常做運動會提高人的自信 翻譯 2&#xff1a;教學的質量對學生成績有很大的影響。 翻譯 3&#xff1a;家長和老師應該努力去減少小孩看電視的時間。 翻譯 4&#xff1a;經濟的下滑&#xff08;economic slowdown&#xff09;導致失業率的上升 翻譯…

大模型和數據庫最新結合進展

寫在前面 本文主要內容是上次接受 infoQ 訪談&#xff0c;百度智能云朱潔老師介紹了大模型和 AI 結合相關話題&#xff0c;這次整體再刷新下&#xff0c;給到對這個領域感興趣的同學。 當前&#xff0c;百度智能云云數據庫特惠專場開始&#xff01;熱銷規格新用戶免費使用&am…

Android中ViewModel+LiveData+DataBinding的配合使用(kotlin)

Android 中 ViewModel、LiveData 和 Data Binding 的配合使用&#xff08;Kotlin&#xff09; 摘要 本文將介紹如何在 Android 開發中結合使用 ViewModel、LiveData 和 Data Binding 進行數據綁定和狀態更新。我們將詳細探討這三者之間的關系&#xff0c;并展示如何在 Kotlin…

最逼真的簡易交通燈設計

最逼真的簡易交通燈設計 需要資料的請在文章末尾獲取&#xff08;有問題可以私信我哦~~&#xff09; 01 資料內容 Proteus仿真文件程序源碼實物制作&#xff0c;代碼修改&#xff0c;功能定制&#xff08;需額外收費&#xff0c;價格實惠&#xff0c;歡迎咨詢&#xff09; …

實驗場:在幾分鐘內使用 Elasticsearch 進行 RAG 應用程序實驗

作者&#xff1a;來自 Elastic Joe McElroy, Serena Chou 什么是 Playground&#xff08;實驗場&#xff09;&#xff1f; 我們很高興發布我們的 Playground 體驗 —- 一個低代碼界面&#xff0c;開發人員可以在幾分鐘內使用自己的私人數據探索他們選擇的 LLM。 在對對話式搜…

41割隊伍

上海市計算機學會競賽平臺 | YACSYACS 是由上海市計算機學會于2019年發起的活動,旨在激發青少年對學習人工智能與算法設計的熱情與興趣,提升青少年科學素養,引導青少年投身創新發現和科研實踐活動。https://www.iai.sh.cn/problem/387 題目描述 給定 ??n 個數字 ??1,?…

一周小計(1):實習初體驗

實習的第一周&#xff0c;從最開始的配環境做好準備工作&#xff0c;到拉項目熟悉項目&#xff0c;然后自己去寫需求&#xff0c;每一步都有很大收獲&#xff0c;得到很多人幫助真的好感謝&#xff0c;以下是個人這幾天的記錄與感想。 &#xff08;這個其實是我寫的周報&#x…

Hi3861 OpenHarmony嵌入式應用入門--LiteOS Semaphore做同步使用

信號量作為同步使用 創建一個Semaphore對象&#xff0c;并指定一個初始的計數值&#xff08;通常稱為“許可”或“令牌”的數量&#xff09;。這個計數值表示當前可用的資源數量或可以同時訪問共享資源的線程數。當一個線程需要訪問共享資源時&#xff0c;它會嘗試從Semaphore…

加油站可視化:打造智能化運營與管理新模式

智慧加油站可視化通過圖撲 HT 構建仿真的三維模型&#xff0c;將加油站的布局、設備狀態、人員活動等信息動態呈現。管理者可以通過直觀的可視化界面實時監控和分析運營狀況&#xff0c;快速做出決策&#xff0c;提高管理效率和安全水平&#xff0c;推動加油站向智能化管理轉型…

后端之路第三站(Mybatis)——結合案例講Mybatis怎么操作sql

先講一下準備工作整體流程要做什么 我們要基于一個員工管理系統作為案例&#xff0c;進行員工信息的【增、刪、改、查】 原理就是用Mybatis通過java語言來執行sql語句&#xff0c;來達到【增、刪、改、查】 一、準備工作 1、引入數據庫數據 首先我們把一個員工、部門表的數…