当前位置: 首页 > news >正文

(六)什么是Vite——热更新时vite、webpack做了什么

 vite分享ppt,感兴趣的可以下载:

​​​​​​​Vite分享、原理介绍ppt

什么是vite系列目录:

(一)什么是Vite——vite介绍与使用-CSDN博客

(二)什么是Vite——Vite 和 Webpack 区别(冷启动)-CSDN博客

(三)什么是Vite——Vite 主体流程(运行npm run dev后发生了什么?)-CSDN博客

(四)什么是Vite——冷启动时vite做了什么(源码、middlewares)-CSDN博客

(五)什么是Vite——冷启动时vite做了什么(依赖、预构建)-CSDN博客

(六)什么是Vite——热更新时vite、webpack做了什么-CSDN博客

(七)什么是Vite——vite优劣势、命令-CSDN博客

热更新时 webpack 做了什么:

打包工具实现热更新的思路都大同小异:主要是通过WebSocket,创建浏览器和服务器的通信,监听文件的改变,当文件被修改时,服务端发送消息通知客户端修改相应的代码,客户端对应不同的文件进行不同的操作的更新。

webpack的热更新就是,当我们对代码做修改并保存后,webpack会对修改的代码块进行重新打包,并将新的模块发送至浏览器端,浏览器用新的模块代替旧的模块,从而实现了在不刷新浏览器的前提下更新页面。相比起直接刷新页面的方案,HMR的优点是可以保存应用的状态。当然,随着项目体积的增长,热更新的速度也会随之下降。

其中,使用webpack冷启动项目的流程是1 -> 2 -> A -> B,热更新的流程是1 -> 2 -> 3 -> 4 -> 5。热更新的大致流程如下:

  • 编辑文件并保存后,webpack就会调用Webpack-complier对文件进行编译;
  • 编译完后传输给HMR Server,HMR得知某个模块发生变化后,就会通知HMR Runtime;
  • HMR Runtime就会加载要更新的模块,从而让浏览器实现更新并不刷新的效果。

热更新时 vite 做了什么:

Webpack: 重新编译,请求变更后模块的代码,客户端重新加载。

Vite: 请求变更的模块,再重新加载。

Vite 通过 chokidar 来监听文件系统的变更,只用对发生变更的模块重新加载, 只需要精确的使相关模块与其临近的 HMR边界连接失效即可,这样HMR 更新速度就不会因为应用体积的增加而变慢而 Webpack 还要经历一次打包构建。所以 HMR 场景下,Vite 表现也要好于 Webpack。

Vite的HMR在构建过程中有以下优势

Vite的HMR使得前端开发者在开发阶段能够更加高效地进行模块修改,快速查看结果并保持应用程序的状态,极大地提升了开发体验和开发效率。

Vite中主要依赖以下几个步骤来实现HMR的功能:Vite介绍和原理解析

1、在重写模块地址的时候,记录模块依赖链 importMaps 。这样在后续更新的时候,可以知道哪些文件需要被热更新。

 代码中可以使用 import.meta.hot 接口来标记"HMR Boundary"。

2、接着,当文件更新的时候,会沿着之前记录下 模块依赖链 imoprtMaps 链式结构找到对应的"HMR Boundary", 再从此处重新加载对应更新的模块。

3、如果没有遇到对应的boundary, 则整个应用重新刷新。

热更新的实现:

热更新主要与项目编写的源码有关。前面提到,对于源码,vite使用原生esm方式去处理,在浏览器请求源码文件时,对文件进行处理后返回转换后的源码。vite对于热更新的实现,大致可以分为以下步骤:

  • 服务端基于 watcher 监听文件改动,根据类型判断更新方式,并编译资源
  • 客户端通过 WebSocket 监听到一些更新的消息类型
  • 客户端收到资源信息,根据消息类型执行热更新逻辑

1、创建一个websocket服务端: HMR机制的实践与原理

vite执行 createWebSocketServer 函数,创建webSocket服务端,并监听 change 等事件。

const { createServer } = await import('./server');
const server = await createServer({root,base: options.base,mode: options.mode,configFile: options.config,logLevel: options.logLevel,clearScreen: options.clearScreen,optimizeDeps: { force: options.force },server: cleanOptions(options),
})
...
const ws = createWebSocketServer(httpServer, config, httpsOptions)
...
const watcher = chokidar.watch(// config file dependencies might be outside of root[path.resolve(root), ...config.configFileDependencies],resolvedWatchOptions,
)watcher.on('change', async (file) => {file = normalizePath(file)...// 热更新调用await onHMRUpdate(file, false)
})watcher.on('add', onFileAddUnlink)
watcher.on('unlink', onFileAddUnlink)
...

2、创建一个 client 来接收 webSocket 服务端 的信息

const clientConfig = defineConfig({...output: {file: path.resolve(__dirname, 'dist/client', 'client.mjs'),sourcemap: true,sourcemapPathTransform(relativeSourcePath) {return path.basename(relativeSourcePath)},sourcemapIgnoreList() {return true},},
})

vite会创建一个 client.mjs 文件,合并 UserConfig 配置,通过 transformIndexHtml 钩子函数,在转换 index.html 的时候,把生成 client 的代码注入到 index.html 中,这样在浏览器端访问 index.html 就会加载 client 生成代码,创建 client 客户端与 webSocket 服务端建立 connect 链接,以便于接受 webScoket 服务器信息。

3、服务端监听文件变化,给 client 发送 message ,通知客户端。

同时服务端调用 onHMRUpdate 函数,该函数会根据此次修改文件的类型,通知客户端是要刷新还是重新加载文件。

const onHMRUpdate = async (file: string, configOnly: boolean) => {if (serverConfig.hmr !== false) {try {// 执行热更新// 服务端调用handleHMRUpdate函数,该函数会根据此次修改文件的类型,通知客户端是要刷新还是重新加载文件。await handleHMRUpdate(file, server, configOnly)} catch (err) {ws.send({type: 'error',err: prepareError(err),})}}
}// 创建hmr上下文const hmrContext: HmrContext = {file,timestamp,modules: mods ? [...mods] : [],read: () => readModifiedFile(file), // 异步读取文件server,}// 根据文件类型来选择本地更新还是hmr,把消息send到clientif (!hmrContext.modules.length) {if (file.endsWith('.html')) { // html文件不能被hmrconfig.logger.info(colors.green(`page reload `) + colors.dim(shortFile), {clear: true,timestamp: true,})ws.send({type: 'full-reload',  // 全量加载path: config.server.middlewareMode? '*': '/' + normalizePath(path.relative(config.root, file)),})} else {...}return}  // --------  // function updateModulesif (needFullReload) { // html 文件更新 // 需要全量加载config.logger.info(colors.green(`page reload `) + colors.dim(file), {clear: !afterInvalidation,timestamp: true,})ws.send({type: 'full-reload', // 发给客户端})return}// 不需要全量加载就是hmrconfig.logger.info(colors.green(`hmr update `) +colors.dim([...new Set(updates.map((u) => u.path))].join(', ')),{ clear: !afterInvalidation, timestamp: true },)ws.send({type: 'update',updates,})

这段代码阐述的意思就是:

  • html文件不参与热更新,只能全量加载。
  • 浏览器客户端接收 'full-reload' , 表示启动本地刷新,直接刷新通过 http 请求,加载全部资源,这里做了协商缓存。(vite对于node_modules 的文件做了强缓存,而对我们编写的源码做了协商缓存。)
  • 浏览器客户端接收 'update', 表示启动 hmr,浏览器只需要去按需加载对应的模块就可以了。

使用方法如下:

import foo from './foo.js'
foo()
if (import.meta.hot) {import.meta.hot.accept('./foo.js', (newFoo) => {newFoo.foo()})
}

下面将以具体代码进行介绍其原理。

客户端逻辑:https://github.com/vitejs/vite/blob/main/packages/vite/src/node/plugins/importAnalysis.ts#L399
// record for HMR import chain analysis
// make sure to normalize away base
importedUrls.add(url.replace(base, '/'))

浏览器文件是几时被注入的?在 importAnalysis 插件中:

if (hasHMR && !ssr) {debugHmr(`${isSelfAccepting? `[self-accepts]`: acceptedUrls.size? `[accepts-deps]`: `[detected api usage]`} ${prettyImporter}`)// 在用户业务代码中注入Vite客户端代码str().prepend(`import { createHotContext as __vite__createHotContext } from "${clientPublicPath}";` +`import.meta.hot = __vite__createHotContext(${JSON.stringify(importerModule.url)});`)
}

https://github.com/vitejs/vite/blob/main/packages/vite/src/client/client.ts#L70

case 'update':notifyListeners('vite:beforeUpdate', payload)// 发生错误的时候,重新加载整个页面if (isFirstUpdate && hasErrorOverlay()) {window.location.reload()return} else {clearErrorOverlay()isFirstUpdate = false}payload.updates.forEach((update) => {if (update.type === 'js-update') {// js更新逻辑, 会进入一个缓存队列,批量更新,从而保证更新顺序queueUpdate(fetchUpdate(update))} else {// css更新逻辑, 检测到更新的时候,直接替换对应模块的链接,重新发起请求let { path, timestamp } = updatepath = path.replace(/\?.*/, '')const el = ([].slice.call(document.querySelectorAll(`link`)) as HTMLLinkElement[]).find((e) => e.href.includes(path))if (el) {const newPath = `${path}${path.includes('?') ? '&' : '?'}t=${timestamp}`el.href = new URL(newPath, el.href).href}console.log(`[vite] css hot updated: ${path}`)}})break
break
服务端处理HMR模块更新逻辑: https://github.com/vitejs/vite/blob/main/packages/vite/src/node/server/hmr.ts#L42
export async function handleHMRUpdate(file: string,server: ViteDevServer
): Promise<any> {const { ws, config, moduleGraph } = serverconst shortFile = getShortName(file, config.root)const isConfig = file === config.configFileconst isConfigDependency = config.configFileDependencies.some((name) => file === path.resolve(name))const isEnv = config.inlineConfig.envFile !== false && file.endsWith('.env')if (isConfig || isConfigDependency || isEnv) {// 如果配置文件或者环境文件发生修改时,会触发服务重启,才能让配置生效。// auto restart server 配置&环境文件修改则自动重启服务await restartServer(server)return}// (dev only) the client itself cannot be hot updated.if (file.startsWith(normalizedClientDir)) {ws.send({type: 'full-reload',path: '*'})return}const mods = moduleGraph.getModulesByFile(file)// check if any plugin wants to perform custom HMR handlingconst timestamp = Date.now()const hmrContext: HmrContext = {file,timestamp,modules: mods ? [...mods] : [],read: () => readModifiedFile(file),server}// modules 是热更新时需要执行的各个插件// Vite 会把模块的依赖关系组合成 moduleGraph,它的结构类似树形,热更新中判断哪些文件需要更新也会依赖 moduleGraph   for (const plugin of config.plugins) {if (plugin.handleHotUpdate) {const filteredModules = await plugin.handleHotUpdate(hmrContext)if (filteredModules) {hmrContext.modules = filteredModules}}}if (!hmrContext.modules.length) {// html file cannot be hot updated// html 文件更新时,将会触发页面的重新加载。if (file.endsWith('.html')) {[config.logger.info](http://config.logger.info/)(chalk.green(`page reload `) + chalk.dim(shortFile), {clear: true,timestamp: true})ws.send({type: 'full-reload',path: config.server.middlewareMode? '*': '/' + normalizePath(path.relative(config.root, file))})} else {// loaded but not in the module graph, probably not jsdebugHmr(`[no modules matched] ${chalk.dim(shortFile)}`)}return}updateModules(shortFile, hmrContext.modules, timestamp, server)
}// Vue 等文件更新时,都会进入 updateModules 方法,正常情况下只会触发 update,实现热更新,热替换;
function updateModules(file: string,modules: ModuleNode[],timestamp: number,{ config, ws }: ViteDevServer
) {const updates: Update[] = []const invalidatedModules = new Set<ModuleNode>()let needFullReload = false// 遍历插件数组,关联下面的片段for (const mod of modules) {invalidate(mod, timestamp, invalidatedModules)if (needFullReload) {continue}const boundaries = new Set<{boundary: ModuleNodeacceptedVia: ModuleNode}>()// 查找引用模块,判断是否需要重载页面,找不到引用者则会发起刷新。向上传递更新,直到遇到边界const hasDeadEnd = propagateUpdate(mod, timestamp, boundaries)if (hasDeadEnd) {needFullReload = truecontinue}updates.push(...[...boundaries].map(({ boundary, acceptedVia }) => ({type: `${boundary.type}-update` as Update['type'],timestamp,path: boundary.url,acceptedPath: acceptedVia.url})))}if (needFullReload) {// 重刷页面} else {// 向ws客户端发送更新事件, Websocket 监听模块更新, 并且做对应的处理。ws.send({type: 'update',updates})}
}

在 createServer 的时候,通过 WebSocket 创建浏览器和服务器通信,使用 chokidar 监听文件的改变,当模块内容修改是,发送消息通知客户端,只对发生变更的模块重新加载。

export async function createServer( inlineConfig: InlineConfig = {} ): Promise<ViteDevServer> {// 生成所有配置项,包括vite.config.js、命令行参数等const config = await resolveConfig(inlineConfig, 'serve', 'development')// 初始化connect中间件const middlewares = connect() as Connect.ServerconsthttpServer = middlewareMode ? null : await resolveHttpServer(serverConfig, middlewares, httpsOptions)const ws = createWebSocketServer(httpServer, config, httpsOptions)// 初始化文件监听const watcher = chokidar.watch(path.resolve(root), {ignored: ['**/node_modules/**', '**/.git/**', ...(Array.isArray(ignored) ? ignored : [ignored])],ignoreInitial: true, ignorePermissionErrors: true, disableGlobbing: true, ...watchOptions}) as FSWatcher// 生成模块依赖关系,快速定位模块,进行热更新const moduleGraph: ModuleGraph = new ModuleGraph((url, ssr) => container.resolveId(url, undefined, { ssr }))// 监听修改文件内容watcher.on('change', async (file) => {file = normalizePath(file)if (file.endsWith('/package.json')) {return invalidatePackageDjianata(packageCache, file)}// invalidate module graph cache on file changemoduleGraph.onFileChange(file)if (serverConfig.hmr !== false) {try {// 执行热更新await handleHMRUpdate(file, server)} catch (err) { ws.send({ type: 'error', err: prepareError(err) }) }}})// 主要中间件,请求文件转换,返回给浏览器可以识别的js文件middlewares.use(transformMiddleware(server))...return server
}

优化策略

由于vite打包是让浏览器一个个模块去加载的,因此,就很容易存在http请求的瀑布流问题(浏览器并发一次最多6个请求)。此次,vite内部为了解决这个问题,主要采取了3个方案。

  1. 预打包,确保每个依赖只对应一个请求/文件。比如lodash。此处可以参考 https://github.com/vitejs/vite/blob/main/packages/vite/src/node/optimizer/esbuildDepPlugin.ts#L73
  2. 代码分割code split。可以借助 rollup 内置的 manualChunks 来实现。
  3. Etag 304 状态码,让浏览器在重复加载的时候直接使用浏览器缓存。

https://github.com/vitejs/vite/blob/main/packages/vite/src/node/server/middlewares/transform.ts#L155

// check if we can return 304 early
const ifNoneMatch = req.headers['if-none-match']
if (ifNoneMatch &&(await moduleGraph.getModuleByUrl(url))?.transformResult?.etag ===ifNoneMatch
) {isDebug && debugCache(`[304] ${prettifyUrl(url, root)}`)res.statusCode = 304return res.end()
}

 与 webpack 的热更新对比起来,两者都是建立 socket 联系,但是两者不同的是,前者是通过 bundle.js 的 hash 来请求变更的模块,进行热替换。后者是根据自身维护 HmrModule ,通过文件类型以及服务端对文件的监听给客户端发送不同的 message,让浏览器做出对应的行为操作。

相关文章:

(六)什么是Vite——热更新时vite、webpack做了什么

vite分享ppt&#xff0c;感兴趣的可以下载&#xff1a; ​​​​​​​Vite分享、原理介绍ppt 什么是vite系列目录&#xff1a; &#xff08;一&#xff09;什么是Vite——vite介绍与使用-CSDN博客 &#xff08;二&#xff09;什么是Vite——Vite 和 Webpack 区别&#xff0…...

贝加莱MQTT功能

贝加莱实现MQTT Client端的功能库和例程 导入库和例程&#xff0c;AS Logical View中分别通过Add Object—Library&#xff0c;Add—Program插入MQTT库和例程。 将例程Sample放置于CPU循环周期中 定义证书存放路径&#xff0c;在AS Physical View 中&#xff0c;右击PLC—Con…...

基于JavaWeb+SSM+购物系统微信小程序的设计和实现

基于JavaWebSSM购物系统微信小程序的设计和实现 源码获取入口前言主要技术系统设计功能截图Lun文目录订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 前言 第一章 绪 论 1.1选题背景 互联网是人类的基本需求&#xff0c;特别是在现代社会&#xff0c;…...

为什么需要Code Review?

1. Code Review 是什么&#xff1f; 代码审查&#xff08;Code Review&#xff09;是软件开发过程中对代码进行系统性检查和评审的一项活动。它是指团队成员之间相互检查彼此编写的代码&#xff0c;以确保代码质量、可读性和符合编码标准等。 2. Code Review 的必要性 ● 提…...

【计算机网络笔记】ICMP(互联网控制报文协议)

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…...

Git教程1:生成和提交SSH公钥到远程仓库

要生成 Git 的公钥并将其提交到远程仓库&#xff0c;你可以按照以下步骤进行操作&#xff1a; 打开命令行终端&#xff0c;并确保已经安装了 Git。在终端中输入以下命令来生成 SSH 密钥对&#xff1a;ssh-keygen -t rsa -b 4096 -C "your_emailexample.com"这将生成…...

贝茄莱BR AS实时数据采集功能

实时数据采集功能在PLC系统调试过程中&#xff0c;有助于调试人员对变量变化进行监测&#xff0c;通过波形对比&#xff0c;反应不同变量间的相互作用。该测试目的在于验证贝加莱系统组态软件的实时数据采集功能。 贝加莱系统组态软件提供Trace功能&#xff0c;连接PLC&#x…...

Git的基本操作以及原理介绍

文章目录 基本操作创建git仓库配置name和email .git目录的结构git add & git commit.git目录结构的变化 git追踪管理的数据git的版本回退回退的原理回退的三种情况 版本库中文件的删除git分支管理分支的删除合并分支时的冲突分支的合并模式分支策略git stash不要在master分…...

2023安全与软工顶会/刊中区块链智能合约相关论文

2023安全与软工顶会/刊中区块链智能合约相关论文 前言软工顶会ISSTAFSEASEICSE 软工顶刊TOSEMTSE 安全顶会S&PUSENIX SecurityCCSNDSS 前言 主要整理了2023年四大安全顶会、四大软工顶会和两个软工顶刊中&#xff0c;有关区块链智能合约的相关论文。 搜索方式是&#xff1…...

word文档转换为ppt文件,怎么做?

大家是否会遇到需要将word文档转换为ppt文件的情况&#xff1f;除了反反复复粘贴复制以外&#xff0c;还有其他方法可以转换文件格式&#xff0c;今天给大家分享word转换ppt方法。 首先我们先将word文件打开大纲模式 然后我们将文中的大标题设置为1级标题&#xff0c;副标题设…...

机器视觉选型-什么时候用远心镜头

物体厚 当被检测物体厚度较大&#xff0c;需要检测不止一个平面时&#xff0c;典型应用如食品盒&#xff0c;饮料瓶等。 物体位置变化 当被测物体的摆放位置不确定&#xff0c;可能跟镜头成一定角度时。 物体上下跳动 当被测物体在被检测过程中上下跳动&#xff0c;如生产线上下…...

quartz笔记

Quartz-CSDN博客 上面是Quartz的一些基本知识,如果对quartz的基本API不是很了解的话,建议先看下上面的 和Linux Crontab对比 1.执行粒度: Linux Crontab是进程级 quart是线程级 2.跨平台性: Crontab只能在Linxu运行 quart是java实现,可以跨平台 3.调度集上 Crontab的…...

ER 图是什么

文章目录 前言什么是 ER图ER 图实例简化的 ER 图总结 前言 产品经理在梳理产业业务逻辑的过程中&#xff0c;非常重要的一项工作就是梳理各个业务对象之间的关系。如果涉及对象很对的时候&#xff0c;没有工具支持的话很难处理清楚。今天我们就来介绍一个梳理业务对象关系的工…...

PLC电力载波通讯,一种新的IoT通讯技术

前言: PLC-IoT 是 PLC 技术应用在物联场景的创新实践,有效解决电力线路信号干扰、衰减问题,支持 IP 化通信能力,使能终端设备智能化,构建智慧边缘联接。PLC让传统IoT有了更多的连接可能: 电力线通信技术适用的场景包括电力配用电网络、城市智慧路灯、交通路口信号灯、园…...

Elasticsearch:通过摄取管道加上嵌套向量对大型文档进行分块轻松地实现段落搜索

作者&#xff1a;VECTOR SEARCH 向量搜索是一种基于含义而不是精确或不精确的 token 匹配技术来搜索数据的强大方法。 然而&#xff0c;强大的向量搜索的文本嵌入模型只能按几个句子的顺序处理短文本段落&#xff0c;而不是可以处理任意大量文本的基于 BM25 的技术。 现在&…...

OpenCV图像纹理

LBP描述 LBP&#xff08;Local Binary Pattern&#xff0c;局部二值模式&#xff09;是一种用来描述图像局部纹理特征的算子&#xff1b;它具有旋转不变性和灰度不变性等显著的优点。它是首先由T. Ojala, M.Pietikinen, 和D. Harwood 在1994年提出&#xff0c;用于纹理特征提取…...

自媒体写手提问常用的ChatGPT通用提示词模板

如何撰写一篇具有吸引力和可读性的自媒体文章&#xff1f; 如何确定自媒体文章的主题和受众群体&#xff1f; 如何为自媒体文章取一个引人入胜的标题&#xff1f; 如何让自媒体文章的开头更加吸引人&#xff1f; 如何为自媒体文章构建一个清晰、逻辑严谨的框架&#xff1f;…...

分类预测 | Matlab实现PSO-LSTM-Attention粒子群算法优化长短期记忆神经网络融合注意力机制多特征分类预测

分类预测 | Matlab实现PSO-LSTM-Attention粒子群算法优化长短期记忆神经网络融合注意力机制多特征分类预测 目录 分类预测 | Matlab实现PSO-LSTM-Attention粒子群算法优化长短期记忆神经网络融合注意力机制多特征分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1…...

3GPP TS38.201 NR; Physical layer; General description (Release 18)

TS38.201是介绍性的标准&#xff0c;简单介绍了RAN的信道组成和PHY层承担的功能&#xff0c;下图是PHY层相关标准的关系。 文章目录 结构信道类型调制方式PHY层支持的过程物理层测量其他标准TS 38.202: Physical layer services provided by the physical layerTS 38.211: Ph…...

【GitLab】-HTTP 500 curl 22 The requested URL returned error: 500~SSH解决

写在前面 本文主要介绍通过SSH的方式拉取GitLab代码。 目录 写在前面一、场景描述二、具体步骤1.环境说明2.生成秘钥3.GitLab添加秘钥4.验证SSH方式4.更改原有HTTP方式为SSH 三、参考资料写在后面系列文章 一、场景描述 之前笔者是通过 HTTP Personal access token 的方式拉取…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

conda相比python好处

Conda 作为 Python 的环境和包管理工具&#xff0c;相比原生 Python 生态&#xff08;如 pip 虚拟环境&#xff09;有许多独特优势&#xff0c;尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处&#xff1a; 一、一站式环境管理&#xff1a…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件

今天呢&#xff0c;博主的学习进度也是步入了Java Mybatis 框架&#xff0c;目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学&#xff0c;希望能对大家有所帮助&#xff0c;也特别欢迎大家指点不足之处&#xff0c;小生很乐意接受正确的建议&…...

Nginx server_name 配置说明

Nginx 是一个高性能的反向代理和负载均衡服务器&#xff0c;其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机&#xff08;Virtual Host&#xff09;。 1. 简介 Nginx 使用 server_name 指令来确定…...

Device Mapper 机制

Device Mapper 机制详解 Device Mapper&#xff08;简称 DM&#xff09;是 Linux 内核中的一套通用块设备映射框架&#xff0c;为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程&#xff0c;并配以详细的…...

iview框架主题色的应用

1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题&#xff0c;无需引入&#xff0c;直接可…...

go 里面的指针

指针 在 Go 中&#xff0c;指针&#xff08;pointer&#xff09;是一个变量的内存地址&#xff0c;就像 C 语言那样&#xff1a; a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10&#xff0c;通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...

Ubuntu系统复制(U盘-电脑硬盘)

所需环境 电脑自带硬盘&#xff1a;1块 (1T) U盘1&#xff1a;Ubuntu系统引导盘&#xff08;用于“U盘2”复制到“电脑自带硬盘”&#xff09; U盘2&#xff1a;Ubuntu系统盘&#xff08;1T&#xff0c;用于被复制&#xff09; &#xff01;&#xff01;&#xff01;建议“电脑…...

python读取SQLite表个并生成pdf文件

代码用于创建含50列的SQLite数据库并插入500行随机浮点数据&#xff0c;随后读取数据&#xff0c;通过ReportLab生成横向PDF表格&#xff0c;包含格式化&#xff08;两位小数&#xff09;及表头、网格线等美观样式。 # 导入所需库 import sqlite3 # 用于操作…...