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

使用Webpack构建微前端应用

英文社区对 Webpack Module Federation 的响应非常热烈,甚至被誉为“A game-changer in JavaScript architecture”,相对而言国内对此热度并不高,这一方面是因为 MF 强依赖于 Webpack5,升级成本有点高;另一方面是国内已经有一些成熟微前端框架,例如 qiankun。不过我个人觉得 MF 有不少实用性强,非常值得学习、使用的特性,包括:

  • 应用可按需导出若干模块,这些模块最终会被单独打成模块包,功能上有点像 NPM 模块;

  • 应用可在运行时基于 HTTP(S) 协议动态加载其它应用暴露的模块,且用法与动态加载普通 NPM 模块一样简单;

  • 与其它微前端方案不同,MF 的应用之间关系平等,没有主应用/子应用之分,每个应用都能导出/导入任意模块;

  • 等等。

而对于微前端的解释,大家可以查这篇文章。

简单示例

Module Federation 的基本逻辑是一端导出模块,另一端导入、使用模块,实现上两端都依赖于 Webpack 5 内置的 ModuleFederationPlugin 插件:

  1. 对于模块生成方,需要使用 ModuleFederationPlugin 插件的 expose 参数声明需要导出的模块列表;

  2. 对于模块使用方,需要使用 ModuleFederationPlugin 插件的 remotes 参数声明需要从哪些地方导入远程模块。

接下来,我们按这个流程一步步搭建一个简单的 Webpack Module Federation 示例,基本框架:

//MF-basic
├─ app-1
│  ├─ dist
│  │  ├─ ...
│  ├─ package.json
│  ├─ src
│  │  ├─ main.js
│  │  ├─ foo.js
│  │  └─ utils.js
│  └─ webpack.config.js
├─ app-2
│  ├─ dist
│  │  ├─ ...
│  ├─ package.json
│  ├─ src
│  │  ├─ bootstrap.js
│  │  └─ main.js
│  ├─ webpack.config.js
├─ lerna.json
└─ package.json

提示:为简化依赖管理,示例引入 lerna 实现 Monorepo 策略,不过这与文章主题无关,这里不做过多介绍。

其中,app-1app-2 是两个独立应用,分别有一套独立的 Webpack 构建配置,类似于微前端场景下的“微应用”概念。在本示例中,app-1 负责导出模块 —— 类似于子应用;app-2 负责使用这些模块 —— 类似于主应用。

我们先看看模块导出方 —— 也就是 app-1 的构建配置:

const path = require("path");
const { ModuleFederationPlugin } = require("webpack").container;module.exports = {mode: "development",devtool: false,entry: path.resolve(__dirname, "./src/main.js"),output: {path: path.resolve(__dirname, "./dist"),// 必须指定产物的完整路径,否则使用方无法正确加载产物资源publicPath: `http://localhost:8081/dist/`,},plugins: [new ModuleFederationPlugin({// MF 应用名称name: "app1",// MF 模块入口,可以理解为该应用的资源清单filename: `remoteEntry.js`,// 定义应用导出哪些模块exposes: {"./utils": "./src/utils","./foo": "./src/foo",},}),],// MF 应用资源提供方必须以 http(s) 形式提供服务// 所以这里需要使用 devServer 提供 http(s) server 能力devServer: {port: 8081,hot: true,},
};

作用模块导出方,app-1 的配置逻辑可以总结为:

  1. 需要使用 ModuleFederationPluginexposes 项声明哪些模块需要被导出;使用 filename 项定义入口文件名称;

  2. 需要使用 devServer 启动开发服务器能力。

使用 ModuleFederationPlugin 插件后,Webpack 会将 exposes 声明的模块分别编译为独立产物,并将产物清单、MF 运行时等代码打包进 filename 定义的应用入口文件(Remote Entry File)中。例如 app-1 经过 Webpack 编译后,将生成如下产物:

//MF-basic
├─ app-1
│  ├─ dist
│  │  ├─ main.js
│  │  ├─ remoteEntry.js
│  │  ├─ src_foo_js.js
│  │  └─ src_utils_js.js
│  ├─ src
│  │  ├─ ...
  • main.js 为整个应用的编译结果,此处可忽略;

  • src_utils_js.jssrc_foo_js.js 分别为 exposes 声明的模块的编译产物;

  • remoteEntry.jsModuleFederationPlugin 插件生成的应用入口文件,包含模块清单、MF 运行时代码。

拓展1

devServer的配置项

devServer 是 Webpack 开发工具中的一个选项,主要用于提供一个快速的本地开发服务器,帮助开发人员在开发过程中更方便地构建和测试应用。使用 devServer 可以提高开发效率,提供热更新功能,并简化静态资源的提供。以下是 devServer 的一些关键特性和配置选项:

关键特性

  1. 热模块替换 (Hot Module Replacement, HMR):

    • 支持在运行时替换、添加或删除模块,而无需完全刷新页面。这使得开发者可以看到改动的即时效果,提高开发效率。
  2. 自动刷新页面:

    • 当文件发生变化时,devServer 可以自动刷新浏览器,确保开发者总是能看到最新的代码效果。
  3. 自定义路由:

    • 可以配置 devServer 的路由,例如重定向请求到特定页面,适用于单页应用(SPA)。
  4. Proxy:

    • 支持 API 代理,允许将开发环境中的请求代理到其他服务器,这在需要与后端 API 交互时非常有用。
  5. 错误页面:

    • 可以定制错误页面,方便开发者在出现问题时进行调试。

基本配置

Webpack 配置文件中,通常在 module.exports 对象中添加 devServer 配置。以下是一个基本的配置示例:

const path = require('path');module.exports = {// 其他配置...devServer: {contentBase: path.join(__dirname, 'dist'), // 静态文件的目录compress: true, // 启用 gzip 压缩port: 9000, // 监听端口hot: true, // 启用热模块替换open: true, // 自动打开浏览器proxy: {'/api': 'http://localhost:5000' // 代理 API 请求},historyApiFallback: true, // 处理所有 404 请求,重定向到 index.html},
};
  • contentBase: 指定静态文件的根目录,默认是 public 目录。
  • compress: 布尔值,启用 gzip 压缩。
  • port: 指定开发服务器的端口。
  • hot: 启用热模块替换。
  • open: 在服务器启动后自动打开浏览器。
  • proxy: 配置代理,将请求代理到其他服务器,适用于跨域请求。
  • historyApiFallback: 启用后将所有 404 响应重定向到 index.html,适合单页应用。

接下来继续看看模块导入方 —— 也就是 app-2 的配置方法:

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { ModuleFederationPlugin } = require("webpack").container;module.exports = {mode: "development",devtool: false,entry: path.resolve(__dirname, "./src/main.js"),output: {path: path.resolve(__dirname, "./dist"),},plugins: [// 模块使用方也依然使用 ModuleFederationPlugin 插件搭建 MF 环境new ModuleFederationPlugin({// 使用 remotes 属性声明远程模块列表remotes: {// 地址需要指向导出方生成的应用入口文件RemoteApp: "app1@http://localhost:8081/dist/remoteEntry.js",},}),new HtmlWebpackPlugin(),],devServer: {port: 8082,hot: true,open: true,},
};

作用远程模块使用方app-2 需要使用 ModuleFederationPlugin 声明远程模块的 HTTP(S) 地址与模块名称(示例中的 RemoteApp),之后在 app-2 中就可以使用模块名称异步导入 app-1 暴露出来的模块,例如:

// app-2/src/main.js
(async () => {const { sayHello } = await import("RemoteApp/utils");sayHello();
})();

其中:

  • remoteEntry.jsapp-1 构建的应用入口文件;

  • src_utils_js.js 则是 import("RemoteApp/utils") 语句导入的远程模块。

总结一下,MF 中的模块导出/导入方都依赖于 ModuleFederationPlugin 插件,其中导出方需要使用插件的 exposes 项声明导出哪些模块,使用 filename 指定生成的入口文件;导入方需要使用 remotes 声明远程模块地址,之后在代码中使用异步导入语法 import("module") 引入模块。

这种模块远程加载、运行的能力,搭配适当的 DevOps 手段,已经足以满足微前端的独立部署、独立维护、开发隔离的要求,在此基础上 MF 还提供了一套简单的依赖共享功能,用于解决多应用间基础库管理问题。

拓展2

ModuleFederationPlugin 插件

ModuleFederationPlugin 是 Webpack 5 引入的一个强大功能,旨在实现微前端架构(Micro Frontends)。它允许多个独立构建的应用程序(或模块)在运行时动态共享和加载彼此的代码,从而实现更灵活的代码复用和跨团队协作。

核心概念

  1. 微前端:将一个大型应用拆分成多个小型、独立的应用,每个应用可以独立开发、部署和维护。

  2. 共享模块:通过 ModuleFederationPlugin,不同的应用可以共享某些公共依赖,从而减少重复代码和整体包的大小。

  3. 动态加载:应用可以在运行时加载其他应用的模块,而无需在构建时将它们打包在一起。

使用方法

1. 配置主应用

首先,我们需要在主应用(Host)中配置 ModuleFederationPlugin。以下是一个简单的配置示例:

// webpack.config.js (主应用)
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const path = require('path');module.exports = {mode: 'development',entry: './src/index.js',output: {filename: 'main.js',path: path.resolve(__dirname, 'dist'),publicPath: 'auto', // 适应于动态加载},plugins: [new ModuleFederationPlugin({name: 'app1', // 主应用的名称filename: 'remoteEntry.js', // 远程入口文件exposes: {// 指定要暴露的模块'./Component': './src/Component', },shared: {// 共享模块react: { singleton: true, eager: true },'react-dom': { singleton: true, eager: true },},}),],
};

在这个示例中,配置了一个名为 app1 的主应用,暴露了一个名为 Component 的模块,并且共享了 reactreact-dom

2. 配置远程应用

接下来,我们需要配置一个远程应用(Remote),它将从主应用中加载模块。

// webpack.config.js (远程应用)
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
const path = require('path');module.exports = {mode: 'development',entry: './src/index.js',output: {filename: 'remoteEntry.js',path: path.resolve(__dirname, 'dist'),publicPath: 'auto',},plugins: [new ModuleFederationPlugin({name: 'app2', // 远程应用的名称filename: 'remoteEntry.js',remotes: {app1: 'app1@http://localhost:3001/remoteEntry.js', // 指定主应用的远程入口},shared: {react: { singleton: true, eager: true },'react-dom': { singleton: true, eager: true },},}),],
};

在这个示例中,远程应用 app2 可以从主应用 app1 中加载暴露的模块。确保两个应用都在不同的端口运行(例如,app1 在 3001 端口,app2 在 3002 端口),然后你可以在 app2 中加载和使用 app1 中的模块。

3. 加载远程模块

在远程应用中,我们可以使用动态导入来加载主应用暴露的模块。

// src/index.js (远程应用)
import('app1/Component').then((Component) => {// 使用加载的组件const App = Component.default;// 渲染组件
});

依赖共享

上例应用相互独立,各自管理、打包基础依赖包,但实际项目中应用之间通常存在一部分公共依赖 —— 例如 Vue、React、Lodash 等,如果简单沿用上例这种分开打包的方式势必会出现依赖被重复打包,造成产物冗余的问题,为此 ModuleFederationPlugin 提供了 shared 配置用于声明该应用可被共享的依赖模块。

例如,改造上例模块导出方 app-1 ,添加 shared 配置:

module.exports = {// ...plugins: [new ModuleFederationPlugin({name: "app1",filename: `remoteEntry.js`,exposes: {"./utils": "./src/utils","./foo": "./src/foo",}, // 可被共享的依赖模块
+     shared: ['lodash']}),],// ...
};

接下来,还需要修改模块导入方 app-2,添加相同的 shared 配置:

module.exports = {// ...plugins: [// 模块使用方也依然使用 ModuleFederationPlugin 插件搭建 MF 环境new ModuleFederationPlugin({// 使用 remotes 属性声明远程模块列表remotes: {// 地址需要指向导出方生成的应用入口文件RemoteApp: "app1@http://localhost:8081/dist/remoteEntry.js",},
+     shared: ['lodash']}),new HtmlWebpackPlugin(),],// ...
};

之后,运行页面可以看到最终只加载了一次 lodash 产物,而改动前则需要分别从导入/导出方各加载一次 lodash

添加 shared 后改动前

注意,这里要求两个应用使用 版本号完全相同 的依赖才能被复用,假设上例应用 app-1 用了 lodash@4.17.0 ,而 app-2 用的是 lodash@4.17.1,Webpack 还是会同时加载两份 lodash 代码,我们可以通过 shared.[lib].requiredVersion 配置项显式声明应用需要的依赖库版本来解决这个问题:

module.exports = {// ...plugins: [new ModuleFederationPlugin({// ...// 共享依赖及版本要求声明
+     shared: {
+       lodash: {
+         requiredVersion: "^4.17.0",
+       },
+     },}),],// ...
};

上例 requiredVersion: "^4.17.0" 表示该应用支持共享版本大于等于 4.17.0 小于等于 4.18.0 的 lodash,其它应用所使用的 lodash 版本号只要在这一范围内即可复用。requiredVersion 支持 Semantic Versioning 2.0 标准,这意味着我们可以复用 package.json 中声明版本依赖的方法。

requiredVersion 的作用在于限制依赖版本的上下限,实用性极高。除此之外,我们还可以通过 shared.[lib].shareScope 属性更精细地控制依赖的共享范围,例如:

module.exports = {// ...plugins: [new ModuleFederationPlugin({// ...// 共享依赖及版本要求声明
+     shared: {
+       lodash: {
+         // 任意字符串
+         shareScope: 'foo'
+       },
+     },}),],// ...
};

在这种配置下,其它应用所共享的 lodash 库必须同样声明为 foo 空间才能复用。shareScope 在多团队协作时能够切分出多个资源共享空间,降低依赖冲突的概率。

以下为我总结后的app1和app2webpack.config.js配置项

app1:

const path = require('path');
const { ModuleFederationPlugin } = require('webpack').container;
const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {mode: 'development',devtool: false,entry: {main: path.resolve(__dirname, './src/main.js'),},output: {path: path.resolve(__dirname, './dist'),publicPath: 'http://localhost:8081/dist/',filename: '[name].js', // 初始文件名模板chunkFilename: 'src_[name]_js.js', // 动态分块文件名模板},module: {rules: [{test: /\.(js|jsx)$/,exclude: /node_modules/,use: {loader: 'babel-loader',options: {presets: ['@babel/preset-env', '@babel/preset-react'],plugins: ['@babel/plugin-transform-runtime','@babel/plugin-syntax-dynamic-import']}}}],},plugins: [new ModuleFederationPlugin({name: 'app1',filename: 'remoteEntry.js',exposes: {'./utils': './src/utils','./foo': './src/foo',},shared: {lodash: {singleton: true, // 确保只有一个实例requiredVersion: '^4.17.21', // 指定所需的版本范围import: 'lodash', // 指定导入的模块路径strictVersion: false, // 是否严格匹配版本},},}),new HtmlWebpackPlugin(),],optimization: {splitChunks: {chunks: 'all',cacheGroups: {foo: {test: /[\\/]src[\\/]foo\.js$/,name: 'foo',chunks: 'all',},utils: {test: /[\\/]src[\\/]utils\.js$/,name: 'utils',chunks: 'all',},},},},devServer: {port: 8081,hot: true,},
};

app2:

const path = require('path');
const { ModuleFederationPlugin } = require('webpack').container;
const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {mode: 'development',devtool: false,entry: {main: path.resolve(__dirname, './src/main.js'),},output: {path: path.resolve(__dirname, './dist'),publicPath: 'http://localhost:8082/dist/', // 确保与 app2 的启动端口一致filename: '[name].js',chunkFilename: 'src_[name]_js.js',},module: {rules: [{test: /\.(js|jsx)$/,exclude: /node_modules/,use: {loader: 'babel-loader',options: {presets: ['@babel/preset-env', '@babel/preset-react'],plugins: ['@babel/plugin-transform-runtime','@babel/plugin-syntax-dynamic-import']}}}],},plugins: [new ModuleFederationPlugin({name: 'app2',filename: 'remoteEntry.js',remotes: {app1: 'app1@http://localhost:8081/dist/remoteEntry.js', // 指向 app1 的 remoteEntry.js},shared: {lodash: {singleton: true,requiredVersion: '^4.17.21',import: 'lodash',strictVersion: false,},}, // 如果需要共享库,可以在这里配置}),new HtmlWebpackPlugin(),],optimization: {splitChunks: {chunks: 'all',cacheGroups: {// 可以根据需要添加缓存组},},},devServer: {port: 8082,hot: true,open: true,},
};

拓展3

shared 配置

常见配置选项

  • singleton:

    • 类型boolean
    • 说明: 如果设置为 true,Webpack 会确保只会有一个实例被加载,适用于像 React 这样的库,它们通常应该只存在一个实例。
  • eager:

    • 类型boolean
    • 说明: 如果设置为 true,Webpack 会在应用加载时立即加载这个共享模块,而不是在第一次使用时异步加载。这对于某些共享库(如 React)可能是有益的。
  • requiredVersion:

    • 类型string
    • 说明: 用于指定所需的版本范围。Webpack 会根据这个版本来判断是否可以共享该模块。如果不兼容,则会引发警告或错误。
  • strictVersion:

    • 类型boolean
    • 说明: 如果设置为 true,则要求共享模块的版本必须完全匹配 requiredVersion 指定的版本。这样可以避免因版本不匹配导致的问题。
  • import:

    • 类型string
    • 说明: 可以用于指定在其他应用中使用时共享模块的导入路径。例如,你可以使用此配置来导入不同的模块路径。
  • shareScope:

    • 类型string
    • 说明: 用于指定共享模块的作用域,通常在微前端架构中使用。

完整的微前端应用

Module Federation 是一种非常新的技术,社区资料还比较少,接下来我们来编写一个完整的微前端应用,帮助你更好理解 MF 的功能与用法。微前端架构通常包含一个作为容器的主应用及若干负责渲染具体页面的子应用,分别对标到下面示例的 packages/hostpackages/order 应用:

//MF-micro-fe
├─ packages
│  ├─ host
│  │  ├─ public
│  │  │  └─ index.html
│  │  ├─ src
│  │  │  ├─ App.js
│  │  │  ├─ HomePage.js
│  │  │  ├─ Navigation.js
│  │  │  ├─ bootstrap.js
│  │  │  ├─ index.js
│  │  │  └─ routes.js
│  │  ├─ package.json
│  │  └─ webpack.config.js
│  └─ order
│     ├─ src
│     │  ├─ OrderDetail.js
│     │  ├─ OrderList.js
│     │  ├─ main.js
│     │  └─ routes.js
│     ├─ package.json
│     └─ webpack.config.js
├─ lerna.json
└─ package.json

示例代码:MF-micro-fe,可以辅助阅读

先看看 order 对应的 MF 配置:

module.exports = {// ...plugins: [new ModuleFederationPlugin({name: "order",filename: "remoteEntry.js",// 导入路由配置exposes: {"./routes": "./src/routes",},}),],
};

注意,order 应用实际导出的是路由配置文件 routes.js。而 host 则通过 MF 插件导入并消费 order 应用的组件,对应配置:

module.exports = {// ...plugins: [// 模块使用方也依然使用 ModuleFederationPlugin 插件搭建 MF 环境new ModuleFederationPlugin({// 使用 remotes 属性声明远程模块列表remotes: {// 地址需要指向导出方生成的应用入口文件RemoteOrder: "order@http://localhost:8081/dist/remoteEntry.js",},})],// ...
};

注意,order 应用实际导出的是路由配置文件 routes.js。而 host 则通过 MF 插件导入并消费 order 应用的组件,对应配置:

module.exports = {// ...plugins: [// 模块使用方也依然使用 ModuleFederationPlugin 插件搭建 MF 环境new ModuleFederationPlugin({// 使用 remotes 属性声明远程模块列表remotes: {// 地址需要指向导出方生成的应用入口文件RemoteOrder: "order@http://localhost:8081/dist/remoteEntry.js",},})],// ...
};

之后,在 host 应用中引入 order 的路由配置并应用到页面中:

import localRoutes from "./routes";
// 引入远程 order 模块
import orderRoutes from "RemoteOrder/routes";const routes = [...localRoutes, ...orderRoutes];const App = () => (<React.StrictMode><HashRouter><h1>Micro Frontend Example</h1><Navigation /><Routes>{routes.map((route) => (<Routekey={route.path}path={route.path}element={<React.Suspense fallback={<>...</>}><route.component /></React.Suspense>}exact={route.exact}/>))}</Routes></HashRouter></React.StrictMode>
);export default App;

通过这种方式,一是可以将业务代码分解为更细粒度的应用形态;二是应用可以各自管理路由逻辑,降低应用间耦合性。最终能降低系统组件间耦合度,更有利于多团队协作。除此之外,MF 技术还有非常大想象空间,国外有大神专门整理了一系列实用 MF 示例:Module Federation Examples,感兴趣的读者务必仔细阅读这些示例代码。

拓展4

什么是沙箱技术

沙箱”是一种用于安全和隔离的技术,通常用于运行不可信或潜在危险的代码而不影响主系统或其他应用程序的环境。在软件开发、网络安全和虚拟化等领域,沙箱的概念被广泛应用。以下是沙箱的一些关键特征和应用场景:

关键特征

  1. 隔离性:

    • 沙箱环境与主系统或其他应用程序是隔离的。运行在沙箱中的代码无法直接访问主系统的资源,确保主系统不会受到不良代码的影响。
  2. 安全性:

    • 沙箱可以限制代码的权限和行为,防止其执行恶意操作,如访问敏感数据、修改系统文件等。
  3. 可控性:

    • 在沙箱中运行的代码可以被监控和控制,可以轻松地对其进行调试和分析。
  4. 资源限制:

    • 沙箱可以限制代码的资源使用,例如 CPU、内存和网络访问,防止代码过度消耗系统资源。

应用场景

  1. Web 浏览器:

    • 现代浏览器使用沙箱技术来隔离网页和插件,防止恶意网页对用户系统的攻击。例如,浏览器的“安全沙箱”可以防止 JavaScript 访问本地文件系统。
  2. 虚拟机和容器:

    • 通过虚拟机(如 VMware、VirtualBox)和容器(如 Docker),可以创建沙箱环境来运行应用程序。每个虚拟机或容器都是一个独立的执行环境,提供了隔离和安全性。
  3. 移动应用:

    • 移动操作系统(如 Android 和 iOS)使用沙箱来限制应用程序的访问权限,确保一个应用程序无法影响其他应用程序或系统。
  4. API 测试:

    • 在开发和测试 API 时,可以使用沙箱环境来模拟生产环境,以安全地测试代码而不影响实际数据。
  5. 恶意软件分析:

    • 安全研究人员使用沙箱环境分析和研究恶意软件的行为,帮助识别威胁而不危及主系统的安全。

在微前端中的应用

在微前端架构中,沙箱可以用于隔离不同微前端应用之间的代码和状态,确保它们不会相互影响。虽然 Module Federation 本身没有内置的沙箱功能,但可以借助其他技术(如 iframe 或 Web Workers)来实现沙箱效果,增强微前端应用的安全性。

Module Federation 实现的微前端架构并未提供沙箱能力,可能会造成一些问题,如全局命名冲突、CSS 风格冲突、恶意代码注入、CSS 风格冲突等。而qiankun是拥有js沙箱技术的,感兴趣的可以前往官网学习。

相关文章:

使用Webpack构建微前端应用

英文社区对 Webpack Module Federation 的响应非常热烈&#xff0c;甚至被誉为“A game-changer in JavaScript architecture”&#xff0c;相对而言国内对此热度并不高&#xff0c;这一方面是因为 MF 强依赖于 Webpack5&#xff0c;升级成本有点高&#xff1b;另一方面是国内已…...

Apache RocketMQ 5.1.3安装部署文档

官方文档不好使&#xff0c;可以说是一坨… 关键词&#xff1a;Apache RocketMQ 5.0 JDK 17 废话少说&#xff0c;开整。 1.版本 官网地址&#xff0c;版本如下。 https://rocketmq.apache.org/download2.配置文件 2.1namesrv端口 在ROCKETMQ_HOME/conf下 新增namesrv.pro…...

CMS(Concurrent Mark Sweep)垃圾回收器的具体流程

引言 CMS&#xff08;Concurrent Mark Sweep&#xff09;收集器是Java虚拟机中的一款并发收集器&#xff0c;其设计目标是最小化停顿时间&#xff0c;非常适合于对响应时间敏感的应用。与传统的串行或并行收集器不同&#xff0c;CMS能够尽可能地让垃圾收集线程与用户线程同时运…...

【Linux】Socket编程-UDP构建自己的C++服务器

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Linux 目录 一&#xff1a;&#x1f525; UDP 网络编程 &#x1f98b; 接口讲解&#x1f98b; V1 版本 - echo server&#x1f98b; V2 版本 - DictServer&#x1f98b; V3 版本 - 简单聊天室 二&a…...

磁盘结构、访问时间、调度算法

目录 一、什么是磁盘&#xff1f; 二、磁盘分类 1、从磁头分 2、通过盘面分 三、一次磁盘读/写的时间 四、磁盘调度算法 1、先来先到服务算法FCFS 2、最短寻找时间优先SSTF 3、扫描算法&#xff08;SCAN&#xff09; 4、LOOk算法 5、循环扫描算法&#xff08;C-SCAN…...

详解归并排序

归并排序 归并排序的基本概念归并排序的详细步骤1. 分解阶段2. 合并阶段3. 归并排序的递归流程 时间复杂度分析空间复杂度分析算法步骤2-路归并排序代码分析代码讲解1. 合并两个子数组的函数 merge()2. 归并排序函数 mergeSort()3. 打印数组的函数 printArray()4. 主函数 main(…...

45.在 Vue 3 中使用 OpenLayers 鼠标点击播放视频

引言 在 Web 开发中&#xff0c;地图可视化和互动功能是越来越重要的应用场景。OpenLayers 是一个强大的开源 JavaScript 库&#xff0c;用于显示和处理地图数据&#xff0c;支持多种地图服务和交互功能。在这个教程中&#xff0c;我们将介绍如何在 Vue 3 中集成 OpenLayers&a…...

《大话Java+playWright》系列教程初级篇-初识

后续代码会整理开源-大家期待吧&#xff01;&#xff01;&#xff01; 首先讲下为啥不用python&#xff0c;因为不想下载各种安装插件&#xff0c;太麻烦了&#xff0c;好多不兼容。 所以选择了java。 先来讲下什么是playwright&#xff0c;playwright是微软开源自动化测试工…...

05.HTTPS的实现原理-HTTPS的握手流程(TLS1.2)

05.HTTPS的实现原理-HTTPS的握手流程&#xff08;TLS1.2&#xff09; 简介1. TLS握手过程概述2. TLS握手过程细化3. 主密钥&#xff08;对称密钥&#xff09;生成过程4. 密码规范变更 简介 主要讲述了混合加密流程完成后&#xff0c;客户端和服务器如何共同获得相同的对称密钥…...

提示词工程

一、六何分析法快速写出准确的提示词 英文单词中文解释提问时的思考示例Why何故问题的背景&#xff0c;包括为什么做及目标&#xff08;做成什么样&#xff09;最近我们要与某品牌合作推广冲牙器&#xff0c;对方需要我们策划一场营销活动What何事具体是什么事写一个营销策划方…...

基于python网络爬虫的搜索引擎设计

一、毕业设计&#xff08;论文&#xff09;题目&#xff1a;基于网络爬虫的搜索引擎设计 - 基于网络爬虫的搜索引擎设计1 二、毕业设计&#xff08;论文&#xff09;工作自 2022-09-01 起至 2022-10-28 止 三、毕业设计&#xff08;论文&#xff09;内容要求&#xff1a; 主…...

ip-协议

文章目录 1. 网络层2. ip协议2.1 ip协议格式2.2 网段划分基本概念网段划分的两种方式为什么要网段划分&#xff1f;特殊的IP地址IP地址数量不足 2.3 私有IP与公网IP2.4 路由 3. IP的分片与组装为什么要分片与组装&#xff1f;如何分片&#xff1f;如何组装&#xff1f; 1. 网络…...

Git(11)之log显示支持中文

Git(11)之log显示支持中文 Author&#xff1a;Once Day Date&#xff1a;2024年12月21日 漫漫长路有人对你微笑过嘛… 参考文档&#xff1a;GIT使用log命令显示中文乱码_gitlab的log在matlab里显示中文乱码-CSDN博客 全系列文章可查看专栏: Git使用记录_Once_day的博客-CSD…...

oneflow深度学习框架使用问题总结(Windows/Linux)

目录 1.简述 2.在Windows下使用Oneflow深度学习框架&#xff08;错误记录&#xff0c;谨慎&#xff0c;官方不支持&#xff0c;需要WSL&#xff09; 2.1安装Anaconda 2.1创建虚拟环境 2.2安装Pytorch 2.3安装Pycharm 2.4 安装Oneflow 3.在Linux下使用Oneflow深度学习框…...

论文研读:AnimateDiff—通过微调SD,用图片生成动画

1.概述 AnimateDiff 设计了3个模块来微调通用的文生图Stable Diffusion预训练模型, 以较低的消耗实现图片到动画生成。 论文名&#xff1a;AnimateDiff: Animate Your Personalized Text-to-Image Diffusion Models without Specific Tuning 三大模块&#xff1a; 视频域适应…...

SQLAlchemy示例(连接数据库插入表数据)

背景需求 连接数据库&#xff0c;插入表中一些数据。 其用户是新建用户&#xff0c;所以只能插入&#xff0c;不能更新。 再次输入数据则使用更新数据语法&#xff0c;这个没调试。 #! /usr/bin/env python # -*- coding: utf-8 -*-from sqlalchemy import create_engine, …...

Springboot3国际化

国际化实现步骤 Spring Boot 3 提供了强大的国际化支持&#xff0c;使得应用程序可以根据用户的语言和区域偏好适配不同的语言和地区需求。 添加国际化资源文件&#xff1a; 国际化资源文件通常放在 src/main/resources 目录下&#xff0c;并按照不同的语言和地区命名&#xf…...

阿尔萨斯(JVisualVM)JVM监控工具

文章目录 前言阿尔萨斯(JVisualVM)JVM监控工具1. 阿尔萨斯的功能2. JVisualVM启动3. 使用 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff…...

框架专题:反射

1. 什么是反射&#xff1f; 简单来说&#xff0c;反射是一种程序自省的能力&#xff0c;即在程序运行时动态地获取其结构信息或操作其行为。这包括类、方法、属性等元信息。反射的核心在于让代码变得更加动态化&#xff0c;从而突破静态语言的限制。 以Java为例&#xff0c;反…...

【Go】context标准库

文章目录 1. 概述1.1 什么是 Context1.2 设计原理1.3 使用场景源码分析核心:Context接口4个实现6个方法TODO 和 BackgroundWithCancelcancelpropagateCancel 绑定父对象WithTimeout 和 WithDeadlineWithValue总结参考1. 概述 基于版本: go1.22.3/src/context/context.go 1.1…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版​分享

平时用 iPhone 的时候&#xff0c;难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵&#xff0c;或者买了二手 iPhone 却被原来的 iCloud 账号锁住&#xff0c;这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式&#xff0c;避免服务器受到各种恶意攻击和网络威胁&#xff0c;那么&#xff0c;服务器硬防通常都会应用在哪些场景当中呢&#xff1f; 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法&#xff0c;用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理&#xff0c;能够自动确定一个阈值&#xff0c;将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

(二)原型模式

原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

Neo4j 集群管理:原理、技术与最佳实践深度解析

Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战

“&#x1f916;手搓TuyaAI语音指令 &#x1f60d;秒变表情包大师&#xff0c;让萌系Otto机器人&#x1f525;玩出智能新花样&#xff01;开整&#xff01;” &#x1f916; Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制&#xff08;TuyaAI…...

.Net Framework 4/C# 关键字(非常用,持续更新...)

一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...