使用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沙箱技术的,感兴趣的可以前往官网学习。

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

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

相关文章

SQLite本地数据库的简介和适用场景——集成SpringBoot的图文说明

前言&#xff1a;现在项目普遍使用的数据库都是MySQL&#xff0c;而有些项目实际上使用SQLite既足矣。在一些特定的项目中&#xff0c;要比MySQL更适用。 这一篇文章简单的介绍一下SQLite&#xff0c;对比MySQL的优缺点、以及适用的项目类型和集成SpringBoot。 1. SQLite 简介 …

游戏引擎学习第62天

回顾 我们目前正在开发一把虚拟剑&#xff0c;目的是让角色可以用这把剑进行攻击。最初的工作中&#xff0c;我们使用了一个摇滚位图作为虚拟剑的模型&#xff0c;并且实现了一个基本的功能&#xff1a;角色可以丢下剑。但这个功能并没有达到预期的效果&#xff0c;因为我们想…

spring专题笔记(六):bean的自动装配(自动化注入)-根据名字进行自动装配、根据类型进行自动装配。代码演示,通俗易懂。

目录 一、根据名字进行自动装配--byName 二、根据类型进行自动装配 byType 本文章主要是介绍spring的自动装配机制&#xff0c; 用代码演示spring如何根据名字进行自动装配、如何根据类型进行自动装配。代码演示&#xff0c;通俗易懂。 一、根据名字进行自动装配--byName Us…

petalinux 中 cmake 需要用到的环境变量

自定义修改部分环境变量, 实测可用

深入解析MySQL索引结构:从数组到B+树的演变与优化

前言&#xff1a; 在数据库查询中&#xff0c;索引是一种关键的性能优化工具。然而&#xff0c;索引的失效可能导致查询效率大幅下降。为了更好地理解索引的工作原理及规避其失效&#xff0c;深入了解索引结构的演变过程尤为重要。 MySQL 的索引数据结构从简单到复杂&#xff0…

43. Three.js案例-绘制100个立方体

43. Three.js案例-绘制100个立方体 实现效果 知识点 WebGLRenderer&#xff08;WebGL渲染器&#xff09; WebGLRenderer是Three.js中最常用的渲染器之一&#xff0c;用于将3D场景渲染到网页上。 构造器 WebGLRenderer(parameters : Object) 参数类型描述parametersObject…

Python vs PHP:哪种语言更适合网页抓取

本文将比较 Python 和 PHP&#xff0c;以帮助读者确定哪种语言更适合他们的需求。文章将探讨两种语言的优点和缺点&#xff0c;并根据读者的经验水平分析哪种语言可能更容易上手。接下来&#xff0c;文章将深入探讨哪种语言在抓取网页数据方面更胜一筹。 简而言之&#xff0c;…

python+reportlab创建PDF文件

目录 字体导入 画布写入 创建画布对象 写入文本内容 写入图片内容 新增页 画线 表格 保存 模板写入 创建模板对象 段落及样式 表格及样式 画框 图片 页眉页脚 添加图形 构建pdf文件 reportlab库支持创建包含文本、图像、图形和表格的复杂PDF文档。 安装&…

打造两轮差速机器人fishbot:从零开始构建移动机器人

大家好&#xff0c;我是梦笔生花&#xff0c;我们一起来动手创建一个两轮差速的移动机器人fishbot。 机器人除了雷达之外&#xff0c;还需要IMU加速度传感器以及可以驱动的轮子&#xff0c;我们曾介绍过机器人学部分&#xff0c;曾对两差速模型进行过介绍&#xff0c;所以我们…

Python函数(十二):函数的创建和调用、参数传递、返回值

前言&#xff1a;在编程的世界里&#xff0c;函数是一种基本的构建块&#xff0c;它允许我们将代码封装成可重复使用的单元。在Python中&#xff0c;函数的使用尤为重要&#xff0c;因为它不仅有助于代码的模块化&#xff0c;还提高了代码的可读性和可维护性。本章节&#xff0…

UE5 崩溃问题汇总!!!

Using bundled DotNet SDK version: 6.0.302 ERROR: UnrealBuildTool.dll not found in "..\..\Engine\Binaries\DotNET\UnrealBuildTool\UnrealBuildTool.dll" 在你遇到这种极奇崩溃的BUG &#xff0c;难以解决的时候。 尝试了N种方法&#xff0c;都不行的解决方法。…

nginx Rewrite 相关功能

一、Nginx Rewrite 概述 定义 Nginx 的 Rewrite 模块允许对请求的 URI 进行重写操作。它可以基于一定的规则修改请求的 URL 路径&#xff0c;然后将请求定向到新的 URL 地址&#xff0c;这在很多场景下都非常有用&#xff0c;比如实现 URL 美化、网站重构后的 URL 跳转等。主要…

GA-Kmeans-Transformer时序聚类+状态识别组合模型

创新研究亮点&#xff01;GA-Kmeans-Transformer时序聚类状态识别组合模型 目录 创新研究亮点&#xff01;GA-Kmeans-Transformer时序聚类状态识别组合模型效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.创新研究亮点&#xff01;GA-Kmeans-Transformer时序聚类状态识…

最新的强大的文生视频模型Pyramid Flow 论文阅读及复现

《PYRAMIDAL FLOW MATCHING FOR EFFICIENT VIDEO GENERATIVE MODELING》 论文地址&#xff1a;2410.05954https://arxiv.org/pdf/2410.05954 项目地址&#xff1a; jy0205/Pyramid-Flow&#xff1a; 用于高效视频生成建模的金字塔流匹配代码https://github.com/jy0205/Pyram…

阻塞队列BlockingQueue实战及其原理分析

1. 阻塞队列介绍 1.1 队列 是限定在一端进行插入&#xff0c;另一端进行删除的特殊线性表。先进先出(FIFO)线性表。允许出队的一端称为队头&#xff0c;允许入队的一端称为队尾。 数据结构演示网站&#xff1a;https://www.cs.usfca.edu/~galles/visualization/Algorithms.ht…

hadoop搭建

前言 一般企业中不会使用master slave01 slave02来命名 vmware创建虚拟机 打开vmware软件&#xff0c;新建虚拟机 典型 稍后安装系统 选择centos7 虚拟机名称和安放位置自行选择&#xff08;最小化安装消耗空间较少&#xff09; 默认磁盘大小即可 自定义硬件 选择centos7的i…

测试 - 1 ( 9000 字详解 )

一&#xff1a; 测试入门 测试是指运用特定的方法、手段或工具&#xff0c;对某一对象进行验证、检查或评估&#xff0c;判断其是否符合预期标准或目标。例如&#xff0c;修理好一盏灯后通过按开关测试其是否正常工作&#xff1b;通过一次数学测验评估学生对代数知识的掌握程度…

【MATLAB第110期】#保姆级教学 | 基于MATLAB的PAWN全局敏感性分析方法(无目标函数)含特征变量置信区间分析

【MATLAB第110期】#保姆级教学 | 基于MATLAB的PAWN全局敏感性分析方法&#xff08;无目标函数&#xff09;含特征变量置信区间分析 一、介绍 PAWN&#xff08;Probabilistic Analysis With Numerical Uncertainties&#xff09;是一种基于密度的全局敏感性分析&#xff08;Gl…

DX12 快速教程(2) —— 渲染天蓝色窗口

快速导航 新建项目 "002-DrawSkyblueWindow"DirectX 12 入门1. COM 技术&#xff1a;DirectX 的中流砥柱什么是 COM 技术COM 智能指针 2.创建 D3D12 调试层设备&#xff1a;CreateDebugDevice什么是调试层如何创建并使用调试层 3.创建 D3D12 设备&#xff1a;CreateD…

【合作原创】使用Termux搭建可以使用的生产力环境(八)

前言 在上一篇【合作原创】使用Termux搭建可以使用的生产力环境&#xff08;七&#xff09;-CSDN博客中我们讲到了安装百度网盘、VS Code还有java&#xff0c;这篇我打算讲一下最后的编程&#xff0c;还有输入法相关问题解决。众所周知我的本职工作是Java程序猿&#xff0c;因…