1.探究webpack代码热更新原理
2.分析 vant4 源码,源码学会用 vue3 + ts 开发毫秒级渲染的源码倒计时组件,真是源码妙啊
3.用编程的方法禁用某一硬件设备.求代码
探究webpack代码热更新原理
一、前备知识
1.HMR-HotModuleReplacement热模块替换发生代码改动时,源码保持当前页面状态的源码同时,局部更新修改模块
2.piler?源码企业app制作源码=?webpack(config);?//?这里的server是全局变量?server?=?new?Server(compiler,?options,?log);?if?(options.socket)?{ ?server.listen(options.socket,?options.host,?(err)?=>?{ })?}?else?{ ?server.listen(options.port,?options.host,?(err)?=>?{ })?}?}
深入核心,了解如何通过compiler初始化服务器server对象,源码并且调用listen方法
//?源码webpack-dev-server/lib/Server.js?class?Server?{ ?constructor(compiler,?options?=?{ },?_log)?{ ?//?保存webpack实例?this.compiler?=?compiler;?//?保存用户的配置参数?this.options?=?options;?this.heartbeatInterval?=?;?//?socketServer参数?this.socketServerImplementation?=?getSocketServerImplementation(this.options);?this.sockets?=?[];?//?设置文件监听的目录范围?this.contentBaseWatchers?=?[];?//?开启代码热更新的必备参数?this.hot?=?this.options.hot?||?this.options.hotOnly;?//?文件监听配置?this.watchOptions?=?options.watchOptions?||?{ };?this.setupHooks();?this.setupApp();?this.setupDevMiddleware();?this.createServer();?}?//?使用文件编译结束的钩子?setupHooks()?{ ?const?addHooks?=?(compiler)?=>?{ ?done.tap('webpack-dev-server',?(stats)?=>?{ ?//?服务端编译结束通过websocket告知客户端,以及传递当前的源码hash值和ok?this._sendStats(this.sockets,?this.getStats(stats));?this._stats?=?stats;?})?}?if?(this.compiler.compilers)?{ ?this.compiler.compilers.forEach(addHooks);?}?else?{ ?addHooks(this.compiler);?}?}?_sendStats(sockets,?stats,?force)?{ ?this.sockWrite(sockets,?'hash',?stats.hash);?this.sockWrite(sockets,?'ok');?}?//?利用express初始化一个服务器,用于静态资源的源码路由?setupApp()?{ ?this.app?=?new?express();?}?//?配置express搭建后的服务器,确认使用的源码协议?createServer()?{ ?//?如果使用的协议是piler,?Object.assign({ },?this.options,?{ ?logLevel:?this.log.options.level?})?)?}//?创建websocket服务器,用于下发模块更新的源码通知到客户端?createSocketServer()?{ ?const?SocketServerImplementation?=?this.socketServerImplementation;?this.socketServer?=?new?SocketServerImplementation(this);?this.socketServer.onConnection((connection,?headers)?=>?{ })?}?//?监听对应的端口开启静态资源路由,同时部署另一个websocket服务器?源码listen(port,?hostname,?fn)?{ ?return?this.listeningApp.listen(port,?hostname,?(err)?=>?{ ?this.createSocketServer();?}?}?}?//?添加两个打包入口模块,利用webpack将相关代码注入到bundle.js中,源码用于客户端开启websokct以及处理热模块替换?源码Server.addDevServerEntrypoints?=?require('./utils/addEntries');?module.exports?=?Server;到这里webpack的HMR在node层做的处理基本完成了,这部分同样是让服务端拥有静态资源路由以及主动下发代码更新通知到客户端的能力,下面看一下如何实现客户端接收websocket通知后主动拉取更新后的服务端代码,并且替换执行新的每月账单源码模块代码
webpack客户端的代码肯定不会让开发人员自己去实现,不然就会出现千奇百怪的问题。这部分代码处理被黑盒处理,隐藏在了Server.addDevServerEntrypoints方法内,悄悄得在webpack带包过程中添加entry注入这部分代码处理
巧妙地划分客户端能力到两个模块中
//?webpack-dev-server/utils/addEntries.js?function?addEntries(config,?options,?server)?{ ?const?domain?=?createDomain(options,?app);?const?sockHost?=?options.sockHost?`&sockHost=${ options.sockHost}`?:?'';?const?sockPath?=?options.sockPath?`&sockPath=${ options.sockPath}`?:?'';?const?sockPort?=?options.sockPort?`&sockPort=${ options.sockPort}`?:?'';?//?引入搭建websocket客户端代码块module?const?clientEntry?=?`${ require.resolve(?'../../client/'?)}?${ domain}${ sockHost}${ sockPath}${ sockPort}`;?//?处理客户端从服务端获取新模块并且替换执行的代码块module?let?hotEntry;?if?(options.hotOnly)?{ ?hotEntry?=?require.resolve('webpack/hot/only-dev-server');?}?else?if?(options.hot)?{ ?hotEntry?=?require.resolve('webpack/hot/dev-server');?}?}?module.exports?=?addEntries;从这里可以看出来,客户端需要的两个能力被划分到了两个代码模块中,一个是搭建websocket客户端,一个是处理客户端的代码模块更新和替换
搭建websocket客户端
//?webpack-dev-server/client/index.js?var?socket?=?require('./socket');?var?sendMessage?=?require('./utils/sendMessage');?var?createSocketUrl?=?require('./utils/createSocketUrl');?var?reloadApp?=?require('./utils/reloadApp');?var?socketUrl?=?createSocketUrl(__resourceQuery);?var?onSocketMessage?=?{ ?//?接收websocket服务端返回的最新hash值?hash:?function?hash(_hash)?{ ?status.currentHash?=?_hash;?}?ok:?function?ok()?{ ?sendMessage('Ok');?reloadApp(options,?status);?}?}?socket(socketUrl,?onSocketMessage);当客户端收到服务端返回的ok消息推送时,会调用reloadApp,这里看一下具体是怎么处理的
//?webpack-dev-server/client/utils/reloadApp.js?function?reloadApp(_ref,?_ref2)?{ ?if?(hot)?{ ?log.info('[WDS]?App?hot?update...');?var?hotEmitter?=?require('webpack/hot/emitter');?hotEmitter.emit('webpackHotUpdate',?currentHash);?//?如果当前宿主是浏览器环境,则触发webpackHotUpdate消息推送?if?(typeof?self?!==?'undefined'?&&?self.window)?{ ?self.postMessage("webpackHotUpdate".concat(currentHash),?'*');?}?}?}?module.exports?=?reloadApp;所以调用this.postMessage("webpackHotUpdate".concat(currentHash),'*')
有发送就会有接收,找一下对应的回调处理,而处理这部分的代码被划分到了hot模块中,根据hash获取新的代码模块并进行替换执行
处理客户端的代码模块更新和替换
//?webpack/hot/dev-server.js?if?(module.hot)?{ ?var?lastHash;?var?check?=?function?check()?{ ?module.hot?.check(true)?.then(function(updatedModules)?{ ?//?容错,如果不存在待更新的模块,直接刷新页面?if?(!updatedModules)?{ ?log("warning",?"[HMR]?Cannot?find?update.?Need?to?do?a?full?reload!");?log(?"warning",?"[HMR]?(Probably?because?of?restarting?the?webpack-dev-server)"?);?window.location.reload();?return;?}?}?.catch(function(err)?{ ?window.location.reload();?}?}?hotEmitter.on("webpackHotUpdate",?function(currentHash)?{ ?lastHash?=?currentHash;?if?(!upToDate()?&&?module.hot.status()?===?"idle")?{ ?log("info",?"[HMR]?Checking?for?updates?on?the?server...");?//?调用check方法拉取更新后的模块代码并进行处理?check();?}?});?}这里的module.hot.check方法,其实是另一位隐藏的大佬进行的方法注入
HotModuleReplacementPlugin
由于涉及到另一个插件的解析,放到后面去扩展。秀才app源码感兴趣的读者可以去webpack/lib/hotModuleReplacement.js阅读源码。
这里重点介绍针对module.hot.check都注入了怎样的代码
//?webpack/lib/web/JsonpMainTemplate.runtime.jsfunction?hotCreateModule(moduleId)?{ ?var?hot?=?{ ?check:?hotCheck?}function?hotCheck(apply)?{ ?hotSetStatus("check");?return?hotDownloadManifest(hotRequestTimeout).then(function(update)?{ ?hotAvailableFilesMap?=?update.c;?hotUpdateNewHash?=?update.h;?hotSetStatus("prepare");})?}?function?hotDownloadManifest(requestTimeout)?{ ?requestTimeout?=?requestTimeout?||?;?return?new?Promise(function(resolve,?reject)?{ ?var?request?=?new?XMLHttpRequest();?var?requestPath?=?__webpack_require__.p?+?""?+?hotCurrentHash?+?".hot-update.json";?request.open("GET",?requestPath,?true);?request.timeout?=?requestTimeout;?request.send(null);?request.onreadystatechange?=?function()?{ ?var?update?=?JSON.parse(request.responseText);?resolve(update);?}?}?}这里之所以使用JSONP的方式获取新的模块代码,是因为JSONP获取的代码可以直接执行,而hash.hot-update.js代码里有个webpackHotUpdate函数调用,最后重点看一下这个函数是如何处理代码模块替换和执行的
//?webpack/lib/HotModuleReplacement.runtime.js?window["webpackHotUpdate"]?=?function?(chunkId,?moreModules)?{ ?hotAddUpdateChunk(chunkId,?moreModules);?};?function?hotAddUpdateChunk(chunkId,?moreModules)?{ ?//?更新的模块moreModules赋值给全局全量hotUpdate?for?(var?moduleId?in?moreModules)?{ ?if?(Object.prototype.hasOwnProperty.call(moreModules,?moduleId))?{ ?hotUpdate[moduleId]?=?moreModules[moduleId];?}?}?//?调分析 vant4 源码,学会用 vue3 + ts 开发毫秒级渲染的倒计时组件,真是妙啊
在深入探讨学习的过程中,我们专注于分析了vant4组件库的源码,以此来掌握如何使用vue3和ts开发一个拥有毫秒级渲染能力的倒计时组件。通过本文,我们将带你了解从组件的实现原理到实际应用的关键步骤,让你在实际开发中能够灵活运用。
在开始学习之前,我们首先需要了解倒计时组件在电商类或移动端页面中的广泛应用。这使得深入理解组件的内部实现变得尤为重要。通过阅读组件的`README.md`文件和`CONTRIBUTING.md`,我们对项目的offer底层源码整体框架有了初步的了解。
为了实践学习,我们选择使用Node.js版本大于和pnpm,执行`pnpm dev`命令后,访问组件的演示页面`mand> [<arg>...]
-r 如果指定它,在命令完成后若需要则重新启动计算机。
<machine> 是目标计算机的名称。
<command> 是将要执行的命令(如下所示)。
<arg>... 是命令需要的一个或多个参数。
要获取关于某一特定命令的帮助,请键入:devcon.exe help <command>
classfilter 允许修改类别筛选程序。
classes 列出所有设备安装类别。
disable 禁用与指定的硬件或实例 ID 匹配的设备。
driverfiles 列出针对设备安装的驱动程序文件。
drivernodes 列出设备的所有驱动程序节点。
enable 启用与指定的硬件或实例 ID 匹配的设备。
find 查找与指定的硬件或实例 ID 匹配的设备。
findall 查找设备,vuereact源码剖析包括那些未显示的设备。
help 显示此信息。
hwids 列出设备的硬件 ID。
install 手动安装设备。
listclass 列出某一安装类别的所有设备。
reboot 重新启动本地计算机。
remove 删除与特定的硬件或实例 ID 匹配的设备。
rescan 扫描以发现新的硬件。
resources 列出设备的硬件资源。
restart 重新启动与特定的硬件或实例 ID 匹配的设备。
stack 列出预期的设备驱动程序堆栈。
status 列出设备的运行状态。
update 手动更新设备。
UpdateNI 手动更新设备,无用户提示
SetHwID 添加、删除和更改根枚举设备的硬件 ID 的顺序。
DevCon 命令示例
devcon -m:\\test find pci\*
列出计算机 test 上的所有已知 PCI 设备。(通过使用 -m,您可以指定一个目标计算机。您必须使用“进程间通信”(IPC) 访问此计算机。)
devcon -r install %WINDIR%\Inf\Netloop.inf *MSLOOP
安装一个新的 Microsoft 环回适配器实例。这将创建一个新的根枚举设备节点,使用此节点您可以安装“虚拟设备”,如环回适配器。如果需要重新启动计算机,此命令还将以安静模式重新启动计算机。
devcon classes
列出所有已知的安装类别。输出结果包含短的未本地化的名称(例如,“USB”)和描述性名称(例如,“通用串行总线控制器”)。
devcon classfilter upper !filter1 !filter2
删除这两个指定的筛选程序。
devcon classfilter lower !badfilter +goodfilter
用“goodfilter”替换“badfilter”。
devcon driverfiles =ports
列出与 ports 安装类别中的每一个设备关联的文件。
devcon disable *MSLOOP
禁用硬件 ID 以“MSLOOP”结尾(包括“*MSLOOP”)的所有设备。
devcon drivernodes @ROOT\PCI_HAL\PNP0A
列出设备“ROOT\PCI_HAL\PNP0A”的所有兼容驱动程序。这可以用来确定为什么选择原配的设备信息 (.inf) 文件而不选第三方 .inf 文件。
devcon enable '*MSLOOP
启用硬件 ID 为“*MSLOOP”的所有设备。单引号指示必须严格按字面解释硬件 ID(换句话说,星号 [“*”] 真的是 一个星号,而不是通配符)。
devcon find *
列出本地计算机上存在的所有设备的设备实例。
devcon find pci\*
列出本地计算机上所有已知的“外围组件互连”(PCI) 设备(如果一个设备的硬件 ID 以“PCI\”为前缀,此命令就认为该设备是 PCI 设备)。
devcon find =ports *pnp*
列出存在的作为 ports 安装类别的成员而且硬件 ID 中包含“PNP”的设备。
devcon find =ports @root\*
列出存在的作为 ports 安装类别的成员而且在枚举树的“root”分支中的设备(实例 ID 以“root\”为前缀)。请注意,有关实例 ID 的格式化方式,不应作任何编程假定。要确定根设备,可以检查设备状态位。此功能包括在 DevCon 中是为了帮助进行调试。
devcon findall =ports
列出 ports 类别的“不存在”的设备和存在的设备。这包括已经被删除的设备、从一个插槽移到另一个插槽的设备,以及在某些情况下由于 BIOS 改变而被不同地枚举的设备。
devcon listclass usb
列出命令中所列的每个类别(本例中是 USB 和 )的所有存在的设备。
devcon remove @usb\*
删除所有 USB 设备。被删除的设备列出时将显示其删除状态。
devcon rescan
重新扫描以发现新的“即插即用”设备。
devcon resources =ports
列出由 ports 安装类别中的所有设备使用的资源。
devcon restart =net @'ROOT\*MSLOOP\
重新启动环回适配器“ROOT\*MSLOOP\”。命令中的单引号指示必须按字面解释实例 ID。
devcon hwids=mouse
列出系统中鼠标类设备的所有硬件 ID。
devcon sethwid @ROOT\LEGACY_BEEP\ := beep
将硬件 ID beep 赋予旧式蜂鸣设备。
devcon stack =ports
列出设备预期的驱动程序堆栈。包括设备和类别高层/低层筛选程序,以及控制服务。
devcon status @pci\*
列出实例 ID 以“pci\”开头的每一个存在设备的状态。
devcon status @ACPI\PNP\1
列出特定设备实例的状态,在本例中是一个高级配置和电源界面 (ACPI) - 枚举的串行端口。
devcon status @root\rdp_mou\
列出 Microsoft 终端服务器或终端服务鼠标驱动程序的状态。
devcon status *PNP*
列出所有 COM 端口的状态。
devcon update mydev.inf *pnp
更新与硬件 ID *pnp 严格匹配的所有设备,让它们使用 Mydev.inf 中与硬件 ID *pnp 关联的最好的驱动程序。
注意:即使系统上已经存在更好的匹配项,这一更新也将强制所有设备使用 Mydev.inf 中的驱动程序。如果在获得签名之前,您想要在开发过程中安装驱动程序的新版本,则这是很有用的。此更新仅影响与指定的硬件 ID 匹配的设备,不会影响到其子设备。如果指定的 .inf 文件未经签名,则 Windows 可能会显示一个对话框,提示您确认是否应安装此驱动程序。如果需要重新启动计算机,将报告这一情况并且 DevCon 返回一个级别 1 错误。如果指定 -r,在需要重新启动计算机时就会自动重启。
注意
DevCon 将返回一个在脚本中使用的错误级别:“0”指示成功。
“1”指示需要重新启动。
“2”指示失败。
“3”指示语法错误。
如果您指定 -r 并且需要重新启动,则在处理完所有设备后,将在无任何警告信息的情况下重新启动。 如果您指定 -m:\\computer 并且命令对远程计算机不起作用,将报告一个错误。 为便于交互,DevCon 允许在实例 ID 中使用通配符。不要根据一台计算机或一种操作系统版本去推测有关另一台计算机或另一种操作系统版本的实例 ID 格式的任何信息。回到顶端
--------------------------------------------------------------------------------
这篇文章中的信息适用于:
Microsoft Windows Driver Development Kit (DDK) Microsoft Windows XP 驱动程序开发工具包 Microsoft Windows Server Driver Development Kit