(六)什么是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到client
 if (!hmrContext.modules.length) {
    if (file.endsWith('.html')) { // html文件不能被hmr
      config.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 updateModules
  if (needFullReload) { // html 文件更新 // 需要全量加载
    config.logger.info(colors.green(`page reload `) + colors.dim(file), {
      clear: !afterInvalidation,
      timestamp: true,
    })
    ws.send({
      type: 'full-reload', // 发给客户端
    })
    return
  }
  
  // 不需要全量加载就是hmr
  config.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 } = update
          path = 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 } = server
  const shortFile = getShortName(file, config.root)

  const isConfig = file === config.configFile
  const 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 handling
  const 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 js
      debugHmr(`[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: ModuleNode
      acceptedVia: ModuleNode
    }>()
    
    // 查找引用模块,判断是否需要重载页面,找不到引用者则会发起刷新。向上传递更新,直到遇到边界
    const hasDeadEnd = propagateUpdate(mod, timestamp, boundaries)
    if (hasDeadEnd) {
      needFullReload = true
      continue
    }

    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.Serverconst
  httpServer = 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 = 304
  return res.end()
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/159376.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

DevExpress中文教程 - 如何在macOS和Linux (CTP)上创建、修改报表(上)

DevExpress Reporting是.NET Framework下功能完善的报表平台&#xff0c;它附带了易于使用的Visual Studio报表设计器和丰富的报表控件集&#xff0c;包括数据透视表、图表&#xff0c;因此您可以构建无与伦比、信息清晰的报表。 DevExpress Reports — 跨平台报表组件&#x…

Pandas中loc和iloc函数(提取某几列或者行的数据)

loc函数&#xff1a;通过行索引&#xff08;列名、行名&#xff09; 中的具体值来取行数据&#xff08;如取"Index"为"A"的行&#xff09; iloc函数&#xff1a;通过行号&#xff08;数字&#xff09;来取行数据&#xff08;如取第二行的数据&#xff09;…

Vue 路由缓存 防止路由切换数据丢失

在切换路由的时候&#xff0c;如果写好了一丢数据在去切换路由在回到写好的数据的路由去将会丢失&#xff0c;这时可以使用路由缓存技术进行保存&#xff0c;这样两个界面来回换数据也不会丢失 在 < router-view >展示的内容都不会被销毁&#xff0c;路由来回切换数据也…

汇编-间接寻址(处理数组)

直接寻址很少用于数组处理&#xff0c;因为用常数偏移量来寻址多个数组元素时&#xff0c;直接寻址并不实用。取而代之的是使用寄存器作为指针(称为间接寻址(indirect addressing) ) 并控制该寄存器的值。如果一个操作数使用的是间接寻址&#xff0c; 就称之为间接操作数(indie…

Sql Server 2017主从配置之:事务日志传送

使用事务日志传送模式搭建Sql Server 2017主从同步&#xff0c;该模式有一定的延迟&#xff0c;是通过3个不同的定时任务&#xff0c;将主库的日志同步到从库进行恢复来实现数据库同步操作。 环境准备 两台服务器&#xff0c;配置都是8g2核&#xff0c;50g硬盘&#xff0c;操…

【ES6标准入门】JavaScript中的模块Module的加载实现:循环加载和Node加载,非常详细,建议收藏!!!

&#x1f601; 作者简介&#xff1a;一名大四的学生&#xff0c;致力学习前端开发技术 ⭐️个人主页&#xff1a;夜宵饽饽的主页 ❔ 系列专栏&#xff1a;JavaScript进阶指南 &#x1f450;学习格言&#xff1a;成功不是终点&#xff0c;失败也并非末日&#xff0c;最重要的是继…

Git企业开发级讲解(三)

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、版本回退1、内容2、演示 二、撤销修改1、情况⼀&#xff1a;对于⼯作区的代码&#xff0c…

基于STM32的无线传感器网络(WSN)通信方案设计与实现

无线传感器网络&#xff08;Wireless Sensor Network&#xff0c;简称WSN&#xff09;是由一组分布式的无线传感器节点组成的网络&#xff0c;用于监测和收集环境中的各类物理信息。本文将基于STM32微控制器&#xff0c;设计并实现一个简单的无线传感器网络通信方案&#xff0c…

Qkeras量化模型-直接搭建模型的量化感知训练

qkeras是谷歌的感知训练量化框架&#xff0c;具有一些功能&#xff1a; 1、支持导入keras模型到qkeras模型&#xff1b; 2、支持剪枝和量化&#xff0c;使用tensorflow lite一起配合&#xff0c;简直不要太好用&#xff1b; 3、支持指定量化函数&#xff0c;量化的bit数目、…

计算机毕业设计选题推荐-人才招聘微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

nacos客户端连接服务端报Client not connected, current status:STARTING

说明&#xff1a; nacos服务端版本&#xff1a;v2.1.2 nacos客户端版本&#xff1a;2.1.2 结果启动项目报错&#xff1a; Client not connected, current status:STARTING 解决&#xff1a; 降低客户端版本至 1.4.1 就Ok了 <dependency><groupId>com.alibaba.naco…

机器学习第5天:多项式回归与学习曲线

文章目录 多项式回归介绍 方法与代码 方法描述 分离多项式 学习曲线的作用 场景 学习曲线介绍 欠拟合曲线 示例 结论 过拟合曲线 示例 ​结论 多项式回归介绍 当数据不是线性时我们该如何处理呢&#xff0c;考虑如下数据 import matplotlib.pyplot as plt impo…

基于SSM的中小型企业财务管理设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

spring cloud openfeign 使用注意点

近期在做项目时给自己挖了一个坑&#xff0c;问题重现如下 使用的组件版本如下 spring boot 2.7.15&#xff0c;对应的 spring cloud 版本为 2021.0.5&#xff0c;其中 spring cloud 适配的 openfeign 版本是 3.1.5。 项目中使用的 feign 接口如下 public interface QueryApi…

使用express连接MySQL数据库编写基础的增、删、改、查、分页等接口

使用express连接MySQL数据库编写基础的增、删、改、查、分页接口 安装express-generator生成器 cnpm install -g express-generator通过生成器创建项目 express peifang-server切换至serverAPI目录 cd peifang-server下载所需依赖 cnpm install 运行项目 npm start访问项…

echarts 实现同一组legend控制两个饼图示例

实现同一组legend控制两个饼图示例&#xff1a; 该示例有如下几个特点&#xff1a; ①饼图不同值实现分割 ②实现tooltip自定义样式&#xff08;echarts 实现tooltip提示框样式自定义-CSDN博客&#xff09; ③自定义label内容 ④不同值颜色渐变 代码如下&#xff1a; this.o…

CentOS挂载:解锁文件系统的力量

目录 引言1 挂载简介2 挂载本地分区3 挂载网络共享文件系统4 使用CIFS挂载结论 引言 在CentOS&#xff08;一种基于Linux的操作系统&#xff09;上挂载文件系统是一项常见而重要的任务&#xff0c;无论是将新的磁盘驱动器添加到系统&#xff0c;还是挂载网络共享资源&#xff…

一个iOS tableView 滚动标题联动效果的实现

效果图 情景 tableview 是从屏幕顶部开始的&#xff0c;现在有导航栏&#xff0c;和栏目标题视图将tableView的顶部覆盖了 分析 我们为了达到滚动到某个分区选中标题的效果&#xff0c;就得知道 展示最顶部的cell或者区头在哪个分区范围内 所以我们必须首先获取顶部的位置 …

【bigo前端】egret中的对象池浅谈

本文首发于&#xff1a;https://github.com/bigo-frontend/blog/ 欢迎关注、转载。 egret是一款小游戏开发引擎&#xff0c;支持跨平台开发&#xff0c;之前使用这款引擎开发了一款捕鱼游戏&#xff0c;在这里简单聊下再egret中关于对象池的使用&#xff0c;虽然该引擎已经停止…

第六十二周周报

学习目标&#xff1a; 一、实验 二、论文 学习时间&#xff1a; 2023.11.11-2023.11.17 学习产出&#xff1a; 实验 1、CB模块实验效果出来了&#xff0c;加上去效果不太行&#xff0c;后续实验考虑是否将CB模块换到其他地方 2、CiFAR100实验已完成&#xff0c;效果比Vi…