Vite: 插件开发

概述

  • 说到自定义的能力,肯定很容易想到 插件机制 ,利用一个个插件来扩展构建工具自身的能力
  • 虽然 Vite 的插件机制是基于 Rollup 来设计的,但实际上 Vite 的插件机制也包含了自己独有的一部分,与Rollup 的各个插件 Hook 并非完全兼容
  • 因此我们重点关注 Vite 独有的部分以及和 Rollup 所区别的部分,而对于 Vite 和 Rollup 中相同的 Hook (如 resolveId 、load 、 transform )只是稍微提及,就不再展开赘述

一个简单的插件示例

  • Vite 插件与 Rollup 插件结构类似,为一个 name 和各种插件 Hook 的对象
    {
    	 // 插件名称
    	 name: 'vite-plugin-xxx',
    	 load(code) {
    	 	// 钩子逻辑
    	 },
    }
    
  • 如果插件是一个 npm 包,在 package.json 中的包命名也推荐以 vite-plugin 开头
  • 一般情况下因为要考虑到外部传参,我们不会直接写一个对象,而是实现一个返回插件对象的 工厂函数,如下代码所示
    // myPlugin.js
    export function myVitePlugin(options) {
    	 console.log(options)
    	 return {
    		 name: 'vite-plugin-xxx',
    		 load(id) {
    		 	// 在钩子逻辑中可以通过闭包访问外部的 options 传参
    		 }
    	 }
    }
    
    // 使用方式
    // vite.config.ts
    import { myVitePlugin } from './myVitePlugin';
    export default {
     plugins: [myVitePlugin({ /* 给插件传参 */ })]
    }
    

插件 Hook


1 )通用 Hook

  • 在双引擎架构这一节中介绍过,Vite 开发阶段会模拟 Rollup 的行为
  • 其中 Vite 会调用一系列与 Rollup 兼容的钩子,这个钩子主要分为三个阶段:
    • 服务器启动阶段: options 和 buildStart 钩子会在服务启动时被调用
    • 请求响应阶段: 当浏览器发起请求时,Vite 内部依次调用 resolveId 、 load 和 transform 钩子
    • 服务器关闭阶段: Vite 会依次执行 buildEnd 和 closeBundle 钩子
  • 除了以上钩子,其他 Rollup 插件钩子(如 moduleParsed 、 renderChunk )均不会在Vite 开发阶段调用, 而生产环境下,由于 Vite 直接使用 Rollup,Vite 插件中所有 Rollup 的插件钩子都会生效

2 ) 独有 Hook

  • Vite 中特有的一些 Hook,这些 Hook 只会在 Vite 内部调用,而放到Rollup 中会被直接忽略

2.1 给配置再加点料: config

  • Vite 在读取完配置文件(即 vite.config.ts )之后,会拿到用户导出的配置对象,然后
    执行 config 钩子
  • 在这个钩子里面,你可以对配置文件导出的对象进行自定义的操作,如下代码所示:
    // 返回部分配置(推荐)
    const editConfigPlugin = () => ({
    	 name: 'vite-plugin-modify-config',
    	 config: () => ({
    		 alias: {
    		 	react: require.resolve('react')
    		 }
    	 })
    })
    
  • 官方推荐的姿势是在 config 钩子中返回一个配置对象,这个配置对象会和 Vite 已有的配
    置进行深度的合并
  • 不过你也可以通过钩子的入参拿到 config 对象进行自定义的修改,如下代码所示:
    const mutateConfigPlugin = () => ({
    	 name: 'mutate-config',
    	 // command 为 `serve`(开发环境) 或者 `build`(生产环境)
    	 config(config, { command }) {
    		 // 生产环境中修改 root 参数
    		 if (command === 'build') {
    		 	config.root = __dirname;
    		 }
    	 }
    })
    
  • 在一些比较深层的对象配置中,这种直接修改配置的方式会显得比较麻烦, 如 optimizeDeps.esbuildOptions.plugins ,需要写很多的样板代码,类似下面这样:
    // 防止出现 undefined 的情况
    config.optimizeDeps = config.optimizeDeps || {}
    config.optimizeDeps.esbuildOptions = config.optimizeDeps.esbuildOptions || {}
    config.optimizeDeps.esbuildOptions.plugins = config.optimizeDeps.esbuildOptions.plugins || []
    
  • 因此这种情况下,建议直接返回一个配置对象,这样会方便很多:
    config() {
     return {
    	 optimizeDeps: {
    		 esbuildOptions: {
    		 	plugins: []
    		 }
    	 }
     }
    }
    

2.2 记录最终配置: configResolved

  • Vite 在解析完配置之后会调用 configResolved 钩子,这个钩子一般用来记录最终的配置
    信息,而不建议再修改配置,用法如下所示:
const exmaplePlugin = () => {
	 let config
	 return {
		 name: 'read-config',
		 configResolved(resolvedConfig) {
			 // 记录最终配置
			 config = resolvedConfig
		 },
		 // 在其他钩子中可以访问到配置
		 transform(code, id) {
		 	console.log(config)
		 }
	 }
}

2.3 获取 Dev Server 实例: configureServer

  • 这个钩子仅在开发阶段会被调用,用于扩展 Vite 的 Dev Server,一般用于增加自定义 server 中间件,如下代码所示
const myPlugin = () => ({
	 name: 'configure-server',
	 configureServer(server) {
		 // 姿势 1: 在 Vite 内置中间件之前执行
		 server.middlewares.use((req, res, next) => {
			 // 自定义请求处理逻辑
		 })
		 // 姿势 2: 在 Vite 内置中间件之后执行
		 return () => {
			 server.middlewares.use((req, res, next) => {
			 // 自定义请求处理逻辑
			 })
		 }
	 }
})

2.4 转换 HTML 内容: transformIndexHtml

  • 这个钩子用来灵活控制 HTML 的内容,你可以拿到原始的 html 内容后进行任意的转换
    const htmlPlugin = () => {
      return {
        name: 'html-transform',
        transformIndexHtml(html) {
          return html.replace(
            /<title>(.*?)</title > /,
            `<title>换了个标题</title>`
          )
        }
      }
    }
    // 也可以返回如下的对象结构,一般用于添加某些标签
    const htmlPlugin = () => {
      return {
        name: 'html-transform',
        transformIndexHtml(html) {
          return {
            html,
            // 注入标签
            tags: [{
              // 放到 body 末尾,可取值还有`head`|`head-prepend`|`body-prepend`,顾名思义
              injectTo: 'body',
              // 标签属性定义
              attrs: {
                type: 'module',
                src: './index.ts'
              },
              // 标签名
              tag: 'script',
            }, ],
          }
        }
      }
    }
    

2.5 热更新处理: handleHotUpdate

  • 这个钩子会在 Vite 服务端处理热更新时被调用,你可以在这个钩子中拿到热更新相关的
    上下文信息,进行热更模块的过滤,或者进行自定义的热更处理。
  • 下面是一个简单的例子:
    const handleHmrPlugin = () => {
      return {
        async handleHotUpdate(ctx) {
          // 需要热更的文件
          console.log(ctx.file)
          // 需要热更的模块,如一个 Vue 单文件会涉及多个模块
          console.log(ctx.modules)
          // 时间戳
          console.log(ctx.timestamp)
          // Vite Dev Server 实例
          console.log(ctx.server)
          // 读取最新的文件内容
          console.log(await read())
          // 自行处理 HMR 事件
          ctx.server.ws.send({
            type: 'custom',
            event: 'special-update',
            data: {
              a: 1
            }
          })
          return []
        }
      }
    }
    // 前端代码中加入
    if (import.meta.hot) {
      import.meta.hot.on('special-update', (data) => {
        // 执行自定义更新
        // { a: 1 }
        console.log(data)
        window.location.reload();
      })
    }
    

2.6 总结一下

  • 以上就是 Vite 独有的五个钩子,我们来重新梳理一下:
    • config : 用来进一步修改配置
    • configResolved : 用来记录最终的配置信息
    • configureServer : 用来获取 Vite Dev Server 实例,添加中间件
    • transformIndexHtml : 用来转换 HTML 的内容
    • handleHotUpdate : 用来进行热更新模块的过滤,或者进行自定义的热更新处理

3 )插件 Hook 执行顺序

  • Vite 的通用钩子和独有钩子,估计你现在脑子里面一点乱: 这么多的钩子,到底谁先执行、谁后执行?

  • 来复盘一下上述的两类钩子,在 Vite 的脚手架工程中新建 test-hooks-plugin.ts :

    // test-hooks-plugin.ts
    // 注: 请求响应阶段的钩子
    // 如 resolveId, load, transform, transformIndexHtml在下文介绍
    // 以下为服务启动和关闭的钩子
    export default function testHookPlugin() {
      return {
        name: 'test-hooks-plugin',
        // Vite 独有钩子
        config(config) {
          console.log('config');
        },
        // Vite 独有钩子
        configResolved(resolvedCofnig) {
          console.log('configResolved');
        },
        // 通用钩子
        options(opts) {
          console.log('options');
          return opts;
        },
        // Vite 独有钩子
        configureServer(server) {
          console.log('configureServer');
          setTimeout(() => {
            // 手动退出进程
            process.kill(process.pid, 'SIGTERM');
          }, 3000)
        },
        // 通用钩子
        buildStart() {
          console.log('buildStart');
        },
        // 通用钩子
        buildEnd() {
          console.log('buildEnd');
        },
        // 通用钩子
        closeBundle() {
          console.log('closeBundle');
        }
      }
    
  • 将插件加入到 Vite 配置文件中,然后启动,你可以观察到各个 Hook 的执行顺序:

  • 由此我们可以梳理出 Vite 插件的执行顺序:

  • 服务启动阶段: config 、 configResolved 、 options 、 configureServer 、buildStart

  • 请求响应阶段: 如果是 html 文件,仅执行 transformIndexHtml 钩子;对于非 HTML
    文件,则依次执行 resolveId 、 load 和 transform 钩子

  • 热更新阶段: 执行 handleHotUpdate 钩子

  • 服务关闭阶段: 依次执行 buildEnd 和 closeBundle 钩子

插件应用位置

  • 梳理完 Vite 的各个钩子函数之后,接下来让我们来了解一下 Vite 插件的应用情景和应用顺序

  • 默认情况下 Vite 插件同时被用于开发环境和生产环境,你可以通过 apply 属性来决定应用场景:

    {
    	 // 'serve' 表示仅用于开发环境,'build'表示仅用于生产环境
    	 apply: 'serve'
    }
    
  • apply 参数还可以配置成一个函数,进行更灵活的控制

    apply(config, { command }) {
    	 // 只用于非 SSR 情况下的生产环境构建
    	 return command === 'build' && !config.build.ssr
    }
    
  • 同时,你也可以通过 enforce 属性来指定插件的执行顺序:

    {
    	 // 默认为`normal`,可取值还有`pre`和`post`
    	 enforce: 'pre'
    }
    
  • Vite 中插件的执行顺序如下图所示

  • Vite 会依次执行如下的插件:

    • Alias (路径别名)相关的插件
    • ⭐️ 带有 enforce: ‘pre’ 的用户插件
    • Vite 核心插件
    • ⭐️ 没有 enforce 值的用户插件,也叫 普通插件
    • Vite 生产环境构建用的插件
    • ⭐️ 带有 enforce: ‘post’ 的用户插件
    • Vite 后置构建插件(如压缩插件)

插件开发实战


1 ) 虚拟模块加载插件

  • 什么是虚拟模块呢?

  • 作为构建工具,一般需要处理两种形式的模块,一种存在于真实的磁盘文件系统中,另一
    种并不在磁盘而在内存当中,也就是 虚拟模块 。

  • 通过虚拟模块,我们既可以把自己手写的一些代码字符串作为单独的模块内容,又可以将内存中某些经过计算得出的变量作为模块内容进行加载,非常灵活和方便。

  • 接下来让我们通过一些具体的例子来实操一下,首先通过脚手架命令初始化一个 react + ts 项目

    • $ npm init vite
  • 然后通过 pnpm i 安装依赖,接着新建 plugins 目录,开始插件的开发:

    // plugins/virtual-module.ts
    import { Plugin } from 'vite';
    // 虚拟模块名称
    const virtualFibModuleId = 'virtual:fib';
    // Vite 中约定对于虚拟模块,解析后的路径需要加上`\0`前缀
    const resolvedFibVirtualModuleId = '\0' + virtualFibModuleId;
    export default function virtualFibModulePlugin(): Plugin {
      let config: ResolvedConfig | null = null;
      return {
        name: 'vite-plugin-virtual-module',
        resolveId(id) {
          if (id === virtualFibModuleId) {
            return resolvedFibVirtualModuleId;
          }
        },
        load(id) {
          // 加载虚拟模块
          if (id === resolvedFibVirtualModuleId) {
            return 'export default function fib(n) { return n <= 1 ? n : fib(n - 1) + fib(n - 2);
          }
        }
      }
    }
    
  • 接着我们在项目中来使用这个插件:

    // vite.config.ts
    import virtual from './plugins/virtual-module.ts'
    // 配置插件
    {
     	plugins: [react(), virtual()]
    }
    
  • 然后在 main.tsx 中加入如下的代码:

    import fib from 'virtual:fib';
    alert(`结果: ${fib(10)}`)
    
  • 这里我们使用了 virtual:fib 这个虚拟模块,虽然这个模块不存在真实的文件系统中,但你打开浏览器后可以发现这个模块导出的函数是可以正常执行的: 弹出 55

  • 接着我们来尝试一下如何通过虚拟模块来读取内存中的变量,在 virtual-module.ts 中增加如下代码

    import { Plugin, ResolvedConfig } from 'vite';
    const virtualFibModuleId = 'virtual:fib';
    const resolvedFibVirtualModuleId = '\0' + virtualFibModuleId;
    
    + const virtualEnvModuleId = 'virtual:env';
    + const resolvedEnvVirtualModuleId = '\0' + virtualEnvModuleId;
    export default function virtualFibModulePlugin(): Plugin {
    + let config: ResolvedConfig | null = null;
     return {
     	name: 'vite-plugin-virtual-fib-module',
    +     configResolved(c: ResolvedConfig) {
    +        config = c;
    +     },
        resolveId(id) {
    	   if (id === virtualFibModuleId) { 
    	 	  return resolvedFibVirtualModuleId;
    	   }
    +      if (id === virtualEnvModuleId) { 
    +        return resolvedEnvVirtualModuleId;
    +      }
        },
        load(id) {
    	   if (id === resolvedFibVirtualModuleId) {
    	 	  return 'export default function fib(n) { return n <= 1 ? n : fib(n - 1) + fib(n - 2);
    	   }
    +    if (id === resolvedEnvVirtualModuleId) {
    +      return `export default ${JSON.stringify(config!.env)}`;
    +    }
      }
     }
    }
    
  • 在新增的这些代码中,我们注册了一个新的虚拟模块 virtual:env ,紧接着我们去项目去使用:

    // main.tsx
    import env from 'virtual:env';
    console.log(env)
    
  • virtual:env 一般情况下会有类型问题,我们需要增加一个类型声明文件来声明这个模块:

    // types/shim.d.ts
    declare module 'virtual:*' {
     export default any;
    }
    
  • 这样就解决了类型报错的问题。接着你可以去浏览器观察一下输出的情况:

  • Vite 环境变量能正确地在浏览器中打印出来,说明在内存中计算出来的 virtual:env 模
    块的确被成功地加载了

  • 从中你可以看到,虚拟模块的内容完全能够被动态计算出来,因此它的灵活性和可定制程度非常高,实用性也很强,在 Vite 内部的插件被深度地使用,社区当中也有不少知名的插件(如 vite-plugin-windicss 、 vite-plugin-svg-icons 等)也使用了虚拟模块的技术

2 ) Svg 组件形式加载

  • 在一般的项目开发过程中,我们有时候希望能将 svg 当做一个组件来引入,这样我们可
    以很方便地修改 svg 的各种属性,相比于 img 标签的引入方式也更加优雅。

  • 但 Vite 本身并不支持将 svg 转换为组件的代码,需要我们通过插件来实现。接下来我们就来写一个 Vite 插件,实现在 React 项目能够通过组件方式来使用 svg 资源。首先安装一下需要的依赖:

    • $ pnpm i resolve @svgr/core -D
  • 接着在 plugins 目录新建 svgr.ts :

    import { Plugin } from 'vite';
    import * as fs from 'fs';
    import * as resolve from 'resolve';
    interface SvgrOptions {
    	  // svg 资源模块默认导出,url 或者组件
    	  defaultExport: 'url' | 'component';
    }
    export default function viteSvgrPlugin(options: SvgrOptions) {
      const { defaultExport = 'url' } = options;
      return {
        name: 'vite-plugin-svgr',
        async transform(code, id) {
          // 转换逻辑: svg -> React 组件
        }
      }
    }
    
  • 让我们先来梳理一下开发需求,用户通过传入 defaultExport 可以控制 svg 资源的默认导出

  • 当 defaultExport 为 component ,默认当做组件来使用,即:

    import Logo from './Logo.svg'
    // 在组件中直接使用
    <Logo />
    
  • 当 defaultExports 为 url ,默认当做 url 使用,如果需要用作组件,可以通过 具名
    导入 的方式来支持:

    import logoUrl, { ReactComponent as Logo } from './logo.svg';
    // url 使用
    <img src={logoUrl} />
    // 组件方式使用
    <Logo />
    
  • 明确了需求之后,接下来让我们来整理一下插件开发的整体思路

  • 主要逻辑, 在 transform 钩子中完成,流程如下

    • 根据 id 入参过滤出 svg 资源;
    • 读取 svg 文件内容;
    • 利用 @svgr/core 将 svg 转换为 React 组件代码;
    • 处理默认导出为 url 的情况;
    • 将组件的 jsx 代码转译为浏览器可运行的代码
  • 下面是插件的完整的代码

import { Plugin } from 'vite';
import * as fs from 'fs';
import * as resolve from 'resolve';
interface SvgrOptions {
  defaultExport: 'url' | 'component';
}
export default function viteSvgrPlugin(options: SvgrOptions): Plugin {
  const {
    defaultExport = 'component'
  } = options;
  return {
    name: 'vite-plugin-svgr',
    async transform(code, id) {
      // 1. 根据 id 入参过滤出 svg 资源;
      if (!id.endsWith('.svg')) {
        return code;
      }
      const svgrTransform = require('@svgr/core')
        .transform;
      // 解析 esbuild 的路径,后续转译 jsx 会用到,我们这里直接拿 vite 中的 esbuild 即可
      const esbuildPackagePath = resolve.sync('esbuild', {
          basedir: require.resolve('vite')
        }
        const esbuild = require(esbuildPackagePath);
        // 2. 读取 svg 文件内容;
        const svg = await fs.promises.readFile(id, 'utf8');
        // 3. 利用 `@svgr/core` 将 svg 转换为 React 组件代码
        const svgrResult = await svgrTransform(
          svg, {}, {
            componentName: 'ReactComponent'
          }
        );
        // 4. 处理默认导出为 url 的情况
        let componentCode = svgrResult;
        if (defaultExport === 'url') {
          // 加上 Vite 默认的 `export default 资源路径`
          componentCode += code;
          componentCode = svgrResult.replace('export default ReactComponent', 'export { ReactComponent }');
        }
        // 5. 利用 esbuild,将组件中的 jsx 代码转译为浏览器可运行的代码;
        const result = await esbuild.transform(componentCode, {
          loader: 'jsx',
        });
        return {
          code: result.code,
          map: null // TODO
        };
      },
   };
}
  • 接下来让我们在项目中使用这个插件:

    // vite.config.ts
    import svgr from './plugins/svgr';
    // 返回的配置
    {
     plugins: [
     // 省略其它插件
     svgr()
     ]
    }
    
  • 接着我们在项目中用组件的方式引入 svg:

    // App.tsx
    import Logo from './logo.svg'
    function App() {
     return (
    	 <>
    	 	<Logo />
    	 </>
     )
    }
    export default App;
    
  • 打开浏览器,可以看到组件已经正常显示

  • 调试技巧

    • 另外,在开发调试插件的过程,我推荐大家在本地装上 vite-plugin-inspect 插件
    • 并在Vite 中使用它
      // vite.config.ts
      import inspect from 'vite-plugin-inspect';
      // 返回的配置
      {
      	 plugins: [
      		 // 省略其它插件
      		 inspect()
      	 ]
      }
      
  • 这样当你再次启动项目时,会发现多出一个调试地址:

  • 你可以通过这个地址来查看项目中各个模块的编译结果

  • 点击特定的文件后,可以看到这个模块经过各个插件处理后的中间结果

  • 通过这个面板,我们可以很清楚地看到相应模块经过插件处理后变成了什么样子,让插件
    的调试更加方便

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

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

相关文章

2024年6月京东睡眠呼吸暂停和低通气事件检测赛题-baseline

赛题地址&#xff1a;DC竞赛-大数据竞赛平台 (datacastle.cn) 一、数据集介绍 train_x训练数据集特征描述&#xff0c;其样本分布不均匀&#xff0c;0样本29808&#xff0c;1样本3221&#xff0c;2样本4520&#xff0c;共计37549条样本 第一维度&#xff1a;60 位受试样本数总…

数据恢复篇:如何在Android上恢复删除的短信

如果您不小心删除了Android设备上的短信并想要检索它们&#xff0c;则可以尝试以下方法&#xff1a; 如何在Android上恢复删除的短信 检查您的备份&#xff1a; 如果您之前备份了Android设备&#xff0c;则可以从备份中恢复已删除的短信。检查您设备的内部存储空间或 Google 云…

CentOS停止维护,如何应对?

一、事件背景 2020年12月08日&#xff0c;CentOS官方宣布了停止维护CentOS Linux的计划&#xff0c;并推出了CentOS Stream项目。 更多信息&#xff0c;请参见CentOS官方公告。 版本变化说明CentOS 9不再支持新的软件和补丁更新CentOS 82021年12月31日停止维护服务CentOS 720…

goLang小案例-获取从控制台输入的信息

goLang小案例-获取从控制台输入的信息 1. 案例代码展示 package mainimport ("bufio""fmt""log""os" )var pl fmt.Printlnfunc main() {//控制台输出欢迎提示pl("Hello Go")fmt.Print("what is your name? ")…

20240627优雅草新产品取得原始软件著作权授权

https://doc.youyacao.com/22/2153 20240627优雅草新产品取得原始软件著作权授权 介绍 历程消息&#xff1a;优雅草2024年新产品最新取得原始著作权两份&#xff0c;2款产品将在近期完成为商业授权产品在蜻蜓松鼠官网售卖&#xff0c;本两款产品是智慧园区能源监测管理系统解…

Harris角点检测原理及其在python-opencv的调用

文章目录 原理测试 原理 Harris 角点检测的基本思路如下&#xff1a;考虑一个局部的区域&#xff0c;将其作为一个窗口四处移动&#xff0c;若窗口灰度发生了较大的变化&#xff0c;那么&#xff0c;就认为窗口内存在角点&#xff0c;否则窗口内就不存在角点。 对于图像 I ( …

HBase与Hive数据交互

一、hbase数据导入hive hive通过建立外部表和普通表加载hbase表数据到hive表中。 两种方式加载hbase中的表到hive中&#xff0c;一是hive创建外部表关联hbase表数据&#xff0c;是hive创建普通表将hbase的数据加载到本地。 1.创建外部表 hbase中创建test表&#xff0c;且插入…

没有思考过 Embedding,不足以谈 AI

在当今的人工智能&#xff08;AI&#xff09;领域&#xff0c;Embedding 是一个不可或缺的概念。如果你没有深入理解过 Embedding&#xff0c;那么就无法真正掌握 AI 的精髓。接下来&#xff0c;我们将深入探讨 Embedding 的基本概念。 1. Embedding的基本概念 1.1 什么是 Emb…

ET实现游戏中邮件系统逻辑思路(服务端)

ET是一个游戏框架&#xff0c;用的编程语言是C#&#xff0c;游戏引擎是Unity&#xff0c;框架作者&#xff1a;熊猫 ET社区 在游戏中我们通常都会看到有邮件系统&#xff0c;邮件系统的作用有给玩家通知、发放奖励等 下面小编使用ET框架带大家看一下邮件系统的一种实现方…

远程过程调用RPC实现原理

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f4a5;&#x1f4a5;个人主页&#xff1a;奋斗的小羊 &#x1f4a5;&#x1f4a5;所属专栏&#xff1a;C语言 &#x1f680;本系列文章为个人学习…

深度挖掘数据资产,洞察业务先机:利用先进的数据分析技术,精准把握市场趋势,洞悉客户需求,为业务决策提供有力支持,实现持续增长与创新

在当今日益激烈的商业竞争环境中&#xff0c;企业想要实现持续增长与创新&#xff0c;必须深入挖掘和有效运用自身的数据资产。数据不仅是企业运营过程中的副产品&#xff0c;更是洞察市场趋势、理解客户需求、优化业务决策的重要资源。本文将探讨如何通过利用先进的数据分析技…

多行业预约门店服务小程序源码系统 支持多门店预约 带完整的安装代码包以及搭建教程

系统概述 该系统基于先进的云计算和大数据技术&#xff0c;采用模块化设计&#xff0c;具有高度的可扩展性和可定制性。无论是餐饮、美容美发、健身房还是其他服务行业&#xff0c;都可以通过该系统轻松实现多门店预约功能。同时&#xff0c;我们还提供了丰富的接口和插件&…

stylelint 配置

1.vscode 安装插件Stylelint 2.项目安装插件 pnpm i stylelint stylelint-config-standard stylelint-config-recommended-scss stylelint-config-recommended-vue postcss postcss-html postcss-scss stylelint-config-recess-order stylelint-config-html -D 依赖 说明 备…

如何判断一个Repo是否是Private还是Internal?

Github的Repository分为三种类型&#xff0c;主要是用于决定谁可以访问、查看和克隆该仓库。GitHub 提供了几种不同的可见性选项&#xff0c;包括 Private、Public 和 Internal。 Private 只有仓库的拥有者和被明确邀请为协作者&#xff08;Collaborator&#xff09;的用户才能…

VMware虚拟机移植保姆级教程

文章目录 前言:一、打包与备份二、VMware移植1. 文件介绍2. 移植过程总结:前言: 前几日对电脑做了一个大的更新升级,不仅将硬件进行了升级,还对电脑的软件进行了升级也就是我从Win10今家庭版升级到Win11专业版啦,之前没有升级是因为数据量很多,怕升级后找不到自己需要的…

Windows和Linux C++判断磁盘空间是否充足

基本是由百度Ai写代码生成的&#xff0c;记录一下。实现此功能需要调用系统的API函数。 对于Windows&#xff0c;可调用函数GetDiskFreeSpaceEx&#xff0c;使用该函数需要包含头文件windows.h。该函数的原型&#xff1a; 它的四个参数&#xff1a; lpDirectoryName&#xff0…

基于SpringBoot养老院管理系统设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f;感兴趣的可以先收藏起来&#xff0c;还…

基于改进天鹰优化算法(IAO)优化RBF神经网络数据回归预测 (IAO-RBF)的数据回归预测(多输入多输出)

改进天鹰优化算法(IAO)见&#xff1a;【智能优化算法】改进的AO算法(IAO)-CSDN博客 代码原理 基于改进天鹰优化算法&#xff08;IAO&#xff09;优化RBF神经网络数据回归预测&#xff08;IAO-RBF&#xff09;的多输入多输出&#xff08;MIMO&#xff09;数据回归预测&#xf…

CVPR24已开源:刷新监督学习SOTA,无监督多目标跟踪时代来临!

论文标题&#xff1a; Matching Anything by Segmenting Anything 论文作者&#xff1a; Siyuan Li, Lei Ke, Martin Danelljan, Luigi Piccinelli, Mattia Segu, Luc Van Gool, Fisher Yu 导读&#xff1a; 在计算机视觉的征途中&#xff0c;多目标跟踪&#xff08;MOT&…

Centos安装redis(附:图形化管理工具)

第一步&#xff1a;下载redis wget http://download.redis.io/releases/redis-6.2.7.tar.gz 第二步&#xff1a;解压 tar zxvf redis-6.2.7.tar.gz 第三步&#xff1a;安装依赖环境 yum -y install gcc-c第四步&#xff1a;安装依赖环境 make install第五步&#xff1a;修…