前端模块化-理解Tapable与Webpack中的Hooks

news2024/11/13 15:06:26

前言

Webpack 中的核心架构是基于 Tapable 实现的,Tapable 是一个类似于 Node.js 的 EventEmitter 的库,专门用于实现发布-订阅模式。Webpack 中的核心组件 Compiler、Compilation、Module、Chunk、ChunkGroup、Dependency、Template 都是通过 Tapable 实现的。

Tappable 主要负责管理 Hooks,Hooks 是一系列具有特定生命周期的事件。通过 Tappable,Webpack 中的不同部分可以创建和触发 Hooks,而插件可以监听这些 Hooks,在适当的时机执行自定义的逻辑。这种设计模式使得插件开发更加灵活,能够介入 Webpack 构建流程的不同阶段。

Tappable 可以被视为 Webpack 插件系统的基石。它提供了一种机制,使得插件可以注册自己的逻辑,而这些逻辑可以被集中执行,而不需要硬编码到 Webpack 的核心逻辑中。这种松耦合的设计让插件开发者更容易理解和维护自己的代码,也让整个插件系统更容易扩展。

本节对应的 demo 可以在这里找到。

Tapable 中的核心概念

Hook

在 Tappable 中,Hook 是一个核心类,代表一个事件(或者说是一个钩子)。每个 Hook 实例都可以被订阅,订阅者可以在事件触发时执行自己的逻辑。Hook 的主要职责是管理订阅者和触发事件。

const { Hook } = require("tapable");

const myHook = new Hook(["arg1", "arg2"]);

myHook.tap("Plugin1", (arg1, arg2) => {
  console.log("Plugin1:", arg1, arg2);
});

myHook.tap("Plugin2", (arg1, arg2) => {
  console.log("Plugin2:", arg1, arg2);
});

myHook.call(42, "hello");

HookCodeFactory

HookCodeFactory 是一个工厂类,用于生成 Hook 的触发函数。每个 Hook 都有一个对应的 HookCodeFactory,HookCodeFactory 会根据 Hook 的类型和订阅者的类型生成不同的触发函数。

const { Hook, HookCodeFactory } = require("tapable");

class MyHook extends Hook {
  constructor(args) {
    super(args);
    this.compile = this.compileFactory();
  }

  compileFactory() {
    return HookCodeFactory((args) => {
      return args.map((arg) => `console.log('${arg}:', ${arg});`).join("");
    });
  }
}

const myHook = new MyHook(["arg1", "arg2"]);

const code = myHook.compile({
  tap: (tapInfo) => {
    return `console.log('Tapped by ${tapInfo.name}');`;
  },
  type: "sync",
});

console.log(code);

在上述示例中,MyHook 继承自 Hook,并通过 HookCodeFactory 生成了用于触发事件的代码。compileFactory 方法返回一个函数,该函数接受一个参数 args,并返回一个字符串,其中包含了触发 Hook 事件时执行的代码。这样的设计使得 Hook 类型可以通过不同的 HookCodeFactory 来实现不同的触发逻辑。

Hook 的类型与用途

AsyncParallelBailHook

/**
 * 类似于 AsyncParallelHook,但如果任何插件的回调函数返回除 undefined 之外的值,执行将停止,并在返回该值的情况下调用最终回调。
 * 当插件的结果可以决定是否应执行后续插件时很有用。
 * 支持异步插件注册和执行,包括回调函数和返回 Promise 的函数。
 */

class AsyncParallelBailHook {
  constructor(args) {
    this.args = args;
    this.taps = [];
  }

  tap(name, callback) {
    this.taps.push({ type: "sync", callback, pluginName: name });
  }

  tapPromise(pluginName, callback) {
    this.taps.push({ type: "promise", callback, pluginName });
  }

  tapAsync(pluginName, callback) {
    this.taps.push({ type: "async", callback, pluginName });
  }

  callAsync(...args) {
    const finalCallback = args.pop();
    let count = 0;

    const done = (err, result) => {
      count++;
      if (err || result || count === this.taps.length) {
        finalCallback(err, result);
      }
    };

    for (const tap of this.taps) {
      const callback = tap.callback;
      if (tap.type === "sync") {
        done(null, callback(...args));
      } else if (tap.type === "promise") {
        Promise.resolve(callback(...args)).then(
          (result) => done(null, result),
          done
        );
      } else if (tap.type === "async") {
        callback(...args, done);
      }
    }
  }

  promise(...args) {
    return new Promise((resolve, reject) => {
      this.callAsync(...args, (err, result) => {
        if (err) {
          reject(err);
        } else {
          resolve(result);
        }
      });
    });
  }
}

// Demo
const asyncParallelBailHook = new AsyncParallelBailHook(["arg1", "arg2"]);

asyncParallelBailHook.tap("Plugin1", (arg1, arg2) => {
  console.log("Plugin1:", arg1, arg2);
  return "Result from Plugin1";
});

asyncParallelBailHook.tapAsync("Plugin2", (arg1, arg2, callback) => {
  console.log("Plugin2:", arg1, arg2);
  setTimeout(() => callback("Result from Plugin2"), 500);
});

asyncParallelBailHook.tapPromise("Plugin3", (arg1, arg2) => {
  console.log("Plugin3:", arg1, arg2);
  return new Promise((resolve) =>
    setTimeout(() => resolve("Result from Plugin3"), 1000)
  );
});

asyncParallelBailHook.callAsync("Hello", "World", (result) => {
  console.log("Final Result:", result);
});

asyncParallelBailHook.promise("Hello", "World").then((result) => {
  console.log("Promise Result:", result);
});

SyncHook

/**
 * 允许按照特定的顺序执行多个插件(回调函数)。如果任何插件返回一个值,它不会停止执行。
 * 适用于需要同步执行插件并且执行顺序很重要的场景。
 */
class SyncHook {
  constructor(args) {
    this.args = args;
    this.taps = [];
  }

  tap(name, callback) {
    this.taps.push({ name, callback });
  }

  call(...args) {
    for (const tap of this.taps) {
      tap.callback(...args);
    }
  }
}

// Demo
const syncHook = new SyncHook(["arg1", "arg2"]);
syncHook.tap("Plugin1", (arg1, arg2) => {
  console.log("Plugin1:", arg1, arg2);
});

syncHook.tap("Plugin2", (arg1, arg2) => {
  console.log("Plugin2:", arg1, arg2);
});

syncHook.call("Hello", "World");

SyncBailHook

/**
 * 类似于 SyncHook,但如果任何插件返回除 undefined 之外的值,执行将停止,并返回该值。
 * 当插件的结果可以决定是否应执行后续插件时很有用。
 */
class SyncBailHook {
  constructor(args) {
    this.args = args;
    this.taps = [];
  }
  tap(name, callback) {
    this.taps.push({ name, callback });
  }
  call(...args) {
    for (const tap of this.taps) {
      // 只要监听函数中有一个函数的返回值不为 null,则跳过剩下所有的逻辑
      const result = tap.callback(...args);
      if (result) {
        break;
      }
    }
  }
}

// Usage
const syncBailHook = new SyncBailHook(["arg1", "arg2"]);
syncBailHook.tap("Plugin1", (arg1, arg2) => {
  console.log("Plugin1:", arg1, arg2);
});

syncBailHook.tap("Plugin2", (arg1, arg2) => {
  console.log("Plugin2:", arg1, arg2);
  return "Result from Plugin2";
});

const result = syncBailHook.call("Hello", "World");
console.log("Final Result:", result);

SyncWaterfallHook

/**
 *  类似于 SyncHook,但每个插件的结果作为参数传递给顺序中的下一个插件。
 *  在一个插件的结果需要传递给顺序中的下一个插件以形成瀑布效果时很有用。
 */
class SyncWaterfallHook {
  constructor(args) {
    this.args = args;
    this.taps = [];
  }

  tap(name, callback) {
    this.taps.push({ name, callback });
  }

  call(...args) {
    let result = args;
    for (const tap of this.taps) {
      // 上一个监听函数的返回值可以传给下一个监听函数
      result = [tap.callback(...result)];
    }
    return result[0];
  }
}

// Usage
const syncWaterfallHook = new SyncWaterfallHook(["arg1", "arg2"]);
syncWaterfallHook.tap("Plugin1", (arg1, arg2) => {
  console.log("Plugin1:", arg1, arg2);
  return "ModifiedArg1";
});

syncWaterfallHook.tap("Plugin2", (arg1, arg2) => {
  console.log("Plugin2:", arg1, arg2);
  return "FinalResult";
});

const result = syncWaterfallHook.call("Hello", "World");
console.log("Final Result:", result);

SyncLoopHook

/**
 * 允许插件重复执行,直到插件返回 undefined 为止。它会在插件返回 undefined 之前一直循环调用插件。
 * 适用于需要多次执行插件直到满足某个条件的场景。
 */

class SyncLoopHook {
  constructor(args) {
    this.args = args;
    this.taps = [];
  }

  tap(name, callback) {
    this.taps.push({ name, callback });
  }

  call(...args) {
    for (const tap of this.taps) {
      let iterationResult;
      do {
        iterationResult = tap.callback(...args);
      } while (iterationResult !== undefined);
    }
  }
}

// Demo
const syncLoopHook = new SyncLoopHook(["arg1", "arg2"]);
let count = 0;

syncLoopHook.tap("Plugin1", (arg1, arg2) => {
  console.log("Plugin1:", arg1, arg2);
  count++;
  if (count < 3) {
    return true; // 继续循环
  }
});

syncLoopHook.tap("Plugin2", (arg1, arg2) => {
  console.log("Plugin2:", arg1, arg2);
});

syncLoopHook.call("Hello", "World");

AsyncParallelHook

/**
 * AsyncParallelHook 是一个并行的异步钩子,允许多个插件并行执行,最后在所有插件执行完成后调用回调函数。
 * 适用于并行执行异步插件的场景。
 */
class AsyncParallelHook {
  constructor(args) {
    this.args = args;
    this.taps = [];
  }

  tapAsync(name, callback) {
    this.taps.push({ name, callback });
  }

  callAsync(...args) {
    const finalCallback = args.pop();
    let count = this.taps.length;
    const done = () => {
      count--; // 有点类似Promise.all的实现
      if (count === 0) {
        finalCallback();
      }
    };

    for (const tap of this.taps) {
      tap.callback(...args, done);
    }
  }
}

// Demo
const asyncParallelHook = new AsyncParallelHook(["arg1", "arg2"]);
asyncParallelHook.tapAsync("Plugin1", (arg1, arg2, callback) => {
  console.log("Plugin1:", arg1, arg2);
  setTimeout(callback, 1000);
});

asyncParallelHook.tapAsync("Plugin2", (arg1, arg2, callback) => {
  console.log("Plugin2:", arg1, arg2);
  setTimeout(callback, 500);
});

asyncParallelHook.callAsync("Hello", "World", () => {
  console.log("All plugins executed.");
});

AsyncParallelBailHook

/**
 * 类似于 AsyncParallelHook,但如果任何插件的回调函数返回除 undefined 之外的值,执行将停止,并在返回该值的情况下调用最终回调。
 * 当插件的结果可以决定是否应执行后续插件时很有用。
 * 支持异步插件注册和执行,包括回调函数和返回 Promise 的函数。
 */

class AsyncParallelBailHook {
  constructor(args) {
    this.args = args;
    this.taps = [];
  }

  tap(name, callback) {
    this.taps.push({ type: "sync", callback, pluginName: name });
  }

  tapPromise(pluginName, callback) {
    this.taps.push({ type: "promise", callback, pluginName });
  }

  tapAsync(pluginName, callback) {
    this.taps.push({ type: "async", callback, pluginName });
  }

  callAsync(...args) {
    const finalCallback = args.pop();
    let count = 0;

    const done = (err, result) => {
      count++;
      if (err || result || count === this.taps.length) {
        finalCallback(err, result);
      }
    };

    for (const tap of this.taps) {
      const callback = tap.callback;
      if (tap.type === "sync") {
        done(null, callback(...args));
      } else if (tap.type === "promise") {
        Promise.resolve(callback(...args)).then(
          (result) => done(null, result),
          done
        );
      } else if (tap.type === "async") {
        callback(...args, done);
      }
    }
  }

  promise(...args) {
    return new Promise((resolve, reject) => {
      this.callAsync(...args, (err, result) => {
        if (err) {
          reject(err);
        } else {
          resolve(result);
        }
      });
    });
  }
}

// Demo
const asyncParallelBailHook = new AsyncParallelBailHook(["arg1", "arg2"]);

asyncParallelBailHook.tap("Plugin1", (arg1, arg2) => {
  console.log("Plugin1:", arg1, arg2);
  return "Result from Plugin1";
});

asyncParallelBailHook.tapAsync("Plugin2", (arg1, arg2, callback) => {
  console.log("Plugin2:", arg1, arg2);
  setTimeout(() => callback("Result from Plugin2"), 500);
});

asyncParallelBailHook.tapPromise("Plugin3", (arg1, arg2) => {
  console.log("Plugin3:", arg1, arg2);
  return new Promise((resolve) =>
    setTimeout(() => resolve("Result from Plugin3"), 1000)
  );
});

asyncParallelBailHook.callAsync("Hello", "World", (result) => {
  console.log("Final Result:", result);
});

asyncParallelBailHook.promise("Hello", "World").then((result) => {
  console.log("Promise Result:", result);
});

AsyncSeriesBailHook

/**
 * 类似于 AsyncSeriesHook,但如果任何插件的回调函数返回除 undefined 之外的值,执行将停止,并在返回该值的情况下调用最终回调。
 * 当插件的结果可以决定是否应执行后续插件时很有用。
 */

class AsyncSeriesBailHook {
  constructor(args) {
    this.args = args;
    this.taps = [];
  }

  tap(name, callback) {
    this.taps.push({ type: "sync", callback, pluginName: name });
  }

  tapPromise(pluginName, callback) {
    this.taps.push({ type: "promise", callback, pluginName });
  }

  tapAsync(pluginName, callback) {
    this.taps.push({ type: "async", callback, pluginName });
  }

  async callAsync(...args) {
    const finalCallback = args.pop();
    let currentIndex = 0;

    const next = async (err, result) => {
      if (err || result !== undefined) {
        return finalCallback(err, result);
      }

      if (currentIndex < this.taps.length) {
        const currentTap = this.taps[currentIndex++];
        const callback = currentTap.callback;

        try {
          if (currentTap.type === "sync") {
            await next(null, await callback(...args));
          } else if (currentTap.type === "promise") {
            await next(null, await Promise.resolve(callback(...args)));
          } else if (currentTap.type === "async") {
            callback(...args, (err, result) => next(err, result));
          }
        } catch (error) {
          next(error);
        }
      } else {
        finalCallback();
      }
    };

    next(null, undefined);
  }

  async promise(...args) {
    return new Promise((resolve, reject) => {
      this.callAsync(...args, (err, result) => {
        if (err) {
          reject(err);
        } else {
          resolve(result);
        }
      });
    });
  }
}

// Demo
const asyncSeriesBailHook = new AsyncSeriesBailHook(["arg1", "arg2"]);

asyncSeriesBailHook.tap("Plugin1", async (arg1, arg2) => {
  console.log("Plugin1:", arg1, arg2);
  return "Result from Plugin1";
});

asyncSeriesBailHook.tapAsync("Plugin2", async (arg1, arg2, callback) => {
  console.log("Plugin2:", arg1, arg2);
  setTimeout(() => callback("Result from Plugin2"), 500);
});

asyncSeriesBailHook.tapPromise("Plugin3", async (arg1, arg2) => {
  console.log("Plugin3:", arg1, arg2);
  return new Promise((resolve) =>
    setTimeout(() => resolve("Result from Plugin3"), 1000)
  );
});

asyncSeriesBailHook.callAsync("Hello", "World", (result) => {
  console.log("Final Result:", result);
});

asyncSeriesBailHook.promise("Hello", "World").then((result) => {
  console.log("Promise Result:", result);
});

AsyncSeriesWaterfallHook

/**
 * 类似于 AsyncSeriesHook,但每个插件的结果作为参数传递给顺序中的下一个插件。
 * 在一个插件的结果需要传递给顺序中的下一个插件以形成瀑布效果时很有用。
 */

class AsyncSeriesWaterfallHook {
  constructor(args) {
    this.args = args;
    this.taps = [];
  }

  tap(name, callback) {
    this.taps.push({ type: "sync", callback, pluginName: name });
  }

  tapPromise(pluginName, callback) {
    this.taps.push({ type: "promise", callback, pluginName });
  }

  tapAsync(pluginName, callback) {
    this.taps.push({ type: "async", callback, pluginName });
  }

  async callAsync(...args) {
    const finalCallback = args.pop();
    let result = args.shift();
    let currentIndex = 0;

    const next = async (err, value) => {
      if (err) {
        return finalCallback(err);
      }

      if (currentIndex < this.taps.length) {
        const currentTap = this.taps[currentIndex++];
        const callback = currentTap.callback;

        try {
          if (currentTap.type === "sync") {
            result = callback(value, ...args);
            next(null, result);
          } else if (currentTap.type === "promise") {
            result = await callback(value, ...args);
            next(null, result);
          } else if (currentTap.type === "async") {
            callback(value, ...args, (err, newValue) => next(err, newValue));
          }
        } catch (error) {
          next(error);
        }
      } else {
        finalCallback(null, result);
      }
    };

    next(null, undefined);
  }

  async promise(...args) {
    return new Promise((resolve, reject) => {
      this.callAsync(...args, (err, result) => {
        if (err) {
          reject(err);
        } else {
          resolve(result);
        }
      });
    });
  }
}

// Demo
const asyncSeriesWaterfallHook = new AsyncSeriesWaterfallHook(["arg1", "arg2"]);

asyncSeriesWaterfallHook.tap("Plugin1", (value, arg1, arg2) => {
  console.log("Plugin1:", value, arg1, arg2);
  return "Result from Plugin1";
});

asyncSeriesWaterfallHook.tapAsync(
  "Plugin2",
  async (value, arg1, arg2, callback) => {
    console.log("Plugin2:", value, arg1, arg2, callback);
    setTimeout(() => callback(null, "Result from Plugin2"), 500);
  }
);

asyncSeriesWaterfallHook.tapPromise("Plugin3", async (value, arg1, arg2) => {
  console.log("Plugin3:", value, arg1, arg2);
  return new Promise((resolve) =>
    setTimeout(() => resolve("Result from Plugin3"), 1000)
  );
});

asyncSeriesWaterfallHook.callAsync(
  "Initial Value",
  "Hello",
  "World",
  (result) => {
    console.log("Final Result:", result);
  }
);

asyncSeriesWaterfallHook
  .promise("Initial Value", "Hello", "World")
  .then((result) => {
    console.log("Promise Result:", result);
  });

参考

  • tabpable
  • 强大的异步流程控制库 Async.js

本文首发于个人 Github前端开发笔记,由于笔者能力有限,文章难免有疏漏之处,欢迎指正

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Fiddler安装与使用

下载Fiddler 访问Fiddler官方网站&#xff0c;下载适用于您操作系统的最新版本Fiddler。目前&#xff0c;Fiddler支持Windows、macOS和Linux平台。 Web Debugging Proxy and Troubleshooting Tools|Fiddler (telerik.com) 安装Fiddler&#xff0c;以Windows为例 Windows用户…

gitlab给用户添加项目权限

1.进入管理员界面 2.进入群组 3.添加用户

【RISC-V设计-04】- RISC-V处理器设计K0A之架构

【RISC-V设计-04】- RISC-V处理器设计K0A之架构 文章目录 【RISC-V设计-04】- RISC-V处理器设计K0A之架构1. 简介2. 主要特点3. 结构框图4. 指令列表5. CSR指令集6. 中断返回指令7. 总结 1. 简介 在前几篇文章中&#xff0c;介绍了RISC-V处理器的结构和指令集&#xff0c;从本…

Animate软件基本概念:视频及音频

视频和音频是ANimate软件中比较重要的素材类型。 FlashASer&#xff1a;AdobeAnimate2021软件零基础入门教程https://zhuanlan.zhihu.com/p/633230084 FlashASer&#xff1a;实用的各种Adobe Animate软件教程https://zhuanlan.zhihu.com/p/675680471 FlashASer&#xff1a;A…

DSP如何进行竞价

下面根据DSP的系统构成还拆解讲解里面的各个模块&#xff0c;这一节将竞价系统&#xff0c;也就是竞价流程 0、负载均衡 增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。 1、ADX发起竞价请求 上面会携带User ID等用户信息和广告信息一大堆信息。 2、解析竞价…

fastadmin 表单添加默认搜索条件

项目场景&#xff1a;员工列表&#xff0c;查看员工邀约客户明细&#xff0c;在 dialog 窗口中的 table怎么获取当前员工的数据呢&#xff1f;看似简单的需求&#xff0c;实际操作起来还是有点考究的&#xff0c;记录一下实现步骤。 页面1&#xff1a;员工列表 页面2&#xff…

sql_day14(获取各门店的面积)

描述&#xff1a;获取各门店的面积 获取各门店的面积 门店面积信息可以从分店面积明细表中获取。 先取实际经营面积(8)&#xff0c; 如果取不到&#xff08;实际经营面积为空&#xff09;再取经营面积(7)。 如果取不到&#xff08;经营面积为空&#xff09;再取合同面积(1)。…

AI大模型赋能开发者|海云安创始人谢朝海受邀在ISC.AI 2024大会就“大模型在软件开发安全领域的应用”主题发表演讲

近日&#xff0c;ISC.AI 2024 第十二届互联网安全大会在北京国家会议中心盛大开幕。作为全球规格最高、规模最大、影响力最深远的安全峰会之一&#xff0c;本次大会以“打造安全大模型 引领安全行业革命”为主题&#xff0c;聚焦安全与AI两大领域&#xff0c;吸引了众多行业领袖…

您知道Jmeter中Redirect Automatically 和 Follow Redirects的使用场景吗?

相信很多使用过jmeter的同学都没有关注过请求中的Redirect Automatically 和 Follow Redirects选项&#xff0c;如下图&#xff1a; 在 JMeter 中&#xff0c;Redirect Automatically 和 Follow Redirects 是与 HTTP 请求重定向相关的两个选项&#xff0c;它们之间是有很大区别…

Ubuntu小键盘消失,并且安装好搜狗输入法后无法打出中文的问题

Ubuntu右上角的键盘图标不见了_ubuntu虚拟机键盘选项消失了-CSDN博客解决Ubuntu18.04安装好搜狗输入法后无法打出中文的问题_ubuntu18.04 搜狗输入法无法输入中文-CSDN博客 sudo apt install libqt5qml5 libqt5quick5 libqt5quickwidgets5 qml-module-qtquick2sudo apt instal…

小智常见报表-自由报表

概述 自由报表&#xff1a;具有自由设计、修改、完善的能力的报表。 应用场景 如下图所示&#xff0c;简单展示数据 示例说明 数据准备 在数据面板中添加数据集&#xff0c;可选择Json数据集和API服务数据集。Json数据集输入如下图所示&#xff1a; [{"姓名"…

Keytool:Uniapp 云打包需要生成证书的操作笔记

文章目录 背景操作步骤概述安装 Java 并检测版本生成证书 xxx.keystore问题&#xff1a;报错&#xff0c;没有权限使用证书 背景 我用 uniapp 想要用云打包&#xff0c;但是需要本机生成一个证书 操作步骤概述 安装 Java在终端输入 /usr/libexec/java_home -V 之后&#xff…

2024华为数通HCIP-datacom最新题库(H12-831变题更新⑨)

请注意&#xff0c;华为HCIP-Datacom考试831已变题 请注意&#xff0c;华为HCIP-Datacom考试831已变题 请注意&#xff0c;华为HCIP-Datacom考试831已变题 近期打算考HCIP的朋友注意了&#xff0c;如果你准备去考试&#xff0c;还是用的之前的题库&#xff0c;切记暂缓。 如…

【Python学习笔记】序列化

【Python学习笔记】序列化 文章目录 【Python学习笔记】序列化1.python使用pickle序列化数据1.1. 环境准备1.2. 序列化datetime对象1.3. 序列化DataFrame对象1.3.1 json1.3.2 pickle 1.4 序列化list列表 2. flaskweb接口传输序列化数据2.1. bytes形式传输2.1.1. datetime对象2.…

k8s使用kustomize来部署应用

k8s使用kustomize来部署应用 本文主要是讲述kustomzie的基本用法。首先&#xff0c;我们说一下部署文件的目录结构。 ./ ├── base │ ├── deployment.yaml │ ├── kustomization.yaml │ └── service.yaml └── overlays└── dev├── kustomization.…

SpringBoot 集成 Sharding-JDBC 实现读写分离、分库分表

文章目录 一、Sharding-JDBC的应用场景二、SpringBoot 集成 Sharding-JDBC2.1、前期准备2.2、导入pom.xml依赖包2.3、结构代码实现2.3.1、MybatisPlusConfig&#xff08;分页插件&#xff09;2.3.2、TOrder&#xff08;订单对象&#xff09;2.3.3、TOrderMapper&#xff08;订单…

【论文阅读】Fourier Spectrum Discrepancies in Deep Network Generated Images

文章目录 Learning Self-Consistency for Deepfake Detection背景关键方法傅立叶谱分析图像转换分类实验讨论总结Learning Self-Consistency for Deepfake Detection 会议:NeurIPS 2020 作者: 背景 深度生成模型(GAN、VAE等)能生成与真图无法区分的逼真图像 关键 对…

批发行业进销存-库存预警 源码CyberWinApp-SAAS 本地化及未来之窗行业应用跨平台架构

一交互代码 未来之窗_人工智能_VOS通用(数据,库存预警,1080,500);"> 二、通用代码 function 未来之窗_人工智能_VOS通用(网址,标题,宽度,高度){var 机器人默认page网址;var 未来之窗app_通用ID"未来之窗防重复";var title标题;var 机器人宽度宽度;var 机器…

Tensorflow预训练模型转PyTorch

深度学习领域是计算机科学中变化最快的领域之一。大约 5 年前&#xff0c;当我开始研究这个主题时&#xff0c;TensorFlow 被认为是主导框架。如今&#xff0c;大多数研究人员已经转向 PyTorch。 NSDT工具推荐&#xff1a; Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/…

Opencv学习-窗口交互

交互操作能够增加用户对程序流程的控制&#xff0c;使程序可以根据用户需求实现不同的处理结果。有时某一个参数需要反复尝试不同的数值&#xff0c;这时交互操作可以实现在程序运行过程中改变参数数值的作用&#xff0c;避免重复运行程序&#xff0c;节省时间&#xff0c;同时…