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!" }
我们可以选择自行开发、维护 ServiceWorker
及 manifest
文件 ,也可以简单点使用 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 程序的方法及注意事项,包括:
- 需要 Webpack 的
target
值设置为node
,这能让 Webpack 忽略fs/path
等原生 Node 模块; - 需要使用
externals
属性过滤node_modules
模块,简单起见,也可以直接使用webpack-node-externals
库; - 需要使用
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.js
与webpack.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 组件,如clipboard
、ipc
、screen
等; - 需要使用
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 页面构建的方式方法,主要差异点:
- 需要将 Webpack 的
target
配置设置为electron-renderer
; - Electron 应用通常包含多个渲染进程,因此我们经常需要开启多页面构建配置;
- 为实现渲染进程的 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 以及其它生态工具,就可以搭建出适用的工程化环境。
相关文章:

Webpack: 开发 PWA、Node、Electron 应用
概述 毋庸置疑,对前端开发者而言,当下正是一个日升月恒的美好时代!在久远的过去,Web 页面的开发技术链条非常原始而粗糙,那时候的 JavaScript 更多用来点缀 Web 页面交互而不是用来构建一个完整的应用。直到 2009年5月…...
python处理txt文件, 如果第一列和第二列的值在连续的行中重复,则只保留一行
处理txt文件, 如果第一列和第二列的值在连续的行中重复,则只保留一个实例,使用Python的内置函数来读取文件,并逐行检查和处理数据。 一个txt文件,里面的数据是893.554382324,-119.955825806,0.0299997832626,-0.133618548512,28.1155740884,112.876833236,46.7922,19.62582…...
C++17中引入了什么新的重要特性
C17是C标准的一个重要版本,它在语言核心和标准库中引入了许多新特性和改进,使得C编程更加现代化和高效。以下是C17中引入的一些重要新特性: 语言核心新特性 结构化绑定(Structured Bindings): 结构化绑定…...

Andrej Karpathy提出未来计算机2.0构想: 完全由神经网络驱动!网友炸锅了
昨天凌晨,知名人工智能专家、OpenAI的联合创始人Andrej Karpathy提出了一个革命性的未来计算机的构想:完全由神经网络驱动的计算机,不再依赖传统的软件代码。 嗯,这是什么意思?全部原生LLM硬件设备的意思吗?…...
用国内镜像安装docker 和 docker-compose (ubuntu)
替代方案,改用国内的镜像站(网易镜像) 1.清除旧版本(可选操作) for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do apt-get remove $pkg; done 2.安装docker apt-get update 首先安装依赖 apt-g…...

Linux多线程【线程互斥】
文章目录 Linux线程互斥进程线程间的互斥相关背景概念互斥量mutex模拟抢票代码 互斥量的接口初始化互斥量销毁互斥量互斥量加锁和解锁改进模拟抢票代码(加锁)小结对锁封装 lockGuard.hpp 互斥量实现原理探究可重入VS线程安全概念常见的线程不安全的情况常…...

os实训课程模拟考试(大题复习)
目录 一、Linux操作系统 (1)第1关:Linux初体验 (2)第2关:Linux常用命令 (3)第3关:Linux 查询命令帮助语句 二、Linux之进程管理—(重点) &…...
QT/QML国际化:中英文界面切换显示(cmake方式使用)
目录 前言 实现步骤 1. 准备翻译文件 2. 翻译字符串 3.设置应用程序语言 cmake 构建方式 示例代码 总结 1. 使用 file(GLOB ...) 2. 引入其他资源文件 再次生成翻译文件 5. 手动更新和生成.qm文件 其他资源 前言 在当今全球化的软件开发环境中,应用程…...
设计模式在Java项目中的实际应用
设计模式在Java项目中的实际应用 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿! 引言 设计模式是软件开发中重要的思想工具,它提供了解决特定问题…...
js制作随机四位数验证码图片
<div class"lable lable2"><div class"l"><span>*</span>验证码</div><div class"r"><input type"number" name"vercode" placeholder"请输入验证码"></div>&l…...

[开源软件] 支持链接汇总
“Common rules: 1- If the repo is on github, the support/bug link is also on the github with issues”" label; 2- Could ask questions by email list;" 3rd party software support link Note gcc https://gcc.gnu.org openssh https://bugzilla.mindrot.o…...

从零开始搭建spring boot多模块项目
一、搭建父级模块 1、打开idea,选择file–new–project 2、选择Spring Initializr,选择相关java版本,点击“Next” 3、填写父级模块信息 选择/填写group、artifact、type、language、packaging(后面需要修改)、java version(后面需要修改成和第2步中版本一致)。点击“…...

Iot解决方案开发的体系结构模式和技术
前言 Foreword 计算机技术起源于20世纪40年代,最初专注于数学问题的基本原理;到了60年代和70年代,它以符号系统为中心,该领域首先开始面临复杂性问题;到80年代,随着个人计算的兴起和人机交互的问题&#x…...

02.C1W1.Sentiment Analysis with Logistic Regression
目录 Supervised ML and Sentiment AnalysisSupervised ML (training)Sentiment analysis Vocabulary and Feature ExtractionVocabularyFeature extractionSparse representations and some of their issues Negative and Positive FrequenciesFeature extraction with freque…...
Stable Diffusion秋叶AnimateDiff与TemporalKit插件冲突解决
文章目录 Stable Diffusion秋叶AnimateDiff与TemporalKit插件冲突解决描述错误描述:找不到模块imageio.v3解决:参考地址 其他文章推荐:专栏 : 人工智能基础知识点专栏:大语言模型LLM Stable Diffusion秋叶AnimateDiff与…...
PCL 渐进形态过滤器实现地面分割
点云地面分割 一、代码实现二、结果示例🙋 概述 渐进形态过滤器:采用先腐蚀后膨胀的运算过程,可以有效滤除场景中的建筑物、植被、车辆、行人以及交通附属设施,保留道路路面及路缘石点云。 一、代码实现 #include <iostream> #include <pcl/io/pcd_io.h> #in…...

第十四届蓝桥杯省赛C++B组E题【接龙数列】题解(AC)
需求分析 题目要求最少删掉多少个数后,使得数列变为接龙数列。 相当于题目要求求出数组中的最长接龙子序列。 题目分析 对于一个数能不能放到接龙数列中,只关系到这个数的第一位和最后一位,所以我们可以先对数组进行预处理,将…...
Ubuntu 20.04.4 LTS 离线安装docker 与docker-compose
Ubuntu 20.04.4 LTS 离线安装docker 与docker-compose 要在Ubuntu 20.04.4 LTS上离线安装Docker和Docker Compose,你需要首先从有网络的环境下载Docker和Docker Compose的安装包,然后将它们传输到离线的服务器上进行安装。 在有网络的环境中:…...

vue3+ts 写echarts 中国地图
需要引入二次封装的echarts和在ts文件写的option <template><div class"contentPage"><myEcharts :options"chartOptions" class"myEcharts" id"myEchartsMapId" ref"mapEcharts" /></di…...

【设计模式】【行为型模式】【责任链模式】
系列文章目录 可跳转到下面链接查看下表所有内容https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5501文章浏览阅读2次。系列文章大全https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5501 目录…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...

shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...

MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...