中台项目-微前端qiankun-umimax

学习视频🔊

基础: 黑马前端基于qiankun搭建微前端项目实战教程_哔哩哔哩_bilibili

路由、部署配置注意:qiankun+vite微前端上线注意事项,base公共路径设置_哔哩哔哩_bilibili

微前端

什么是微前端?

微前端是将前端应用分解成一系列更小、更易管理的独立部分的架构方案。类似于微服务,每个微应用可以由不同团队独立开发、测试、部署。

微前端的好处?

  1. 技术栈无关

主应用和子应用可以使用不同的技术栈

允许渐进式技术栈升级

降低全局技术升级的风险

  1. 独立开发部署

各团队可以独立开发、测试、部署

有哪些微前端方案?

下面介绍有哪些主流的微前端方案。

1、qiankun(蚂蚁金服,后续也采用这种)

优点:

  • 基于 single-spa 封装

  • 完善的沙箱机制

  • 开箱即用的 API

  • 中文社区活跃

适用场景:

  • 大型中台系统

  • 需要多团队协作的项目

2、single-spa

优点:

  • 最早的微前端框架

  • 灵活性高

  • 社区成熟

缺点:

  • 配置较复杂

  • 需要自己实现样式隔离

3、Module Federation(来自Webpack 5,中文:模块联邦,可以用来做远程组件)

优点:

  • Webpack 原生支持

  • 真正的运行时模块共享

  • 构建时优化

适用场景:

  • 新项目

  • 需要精细化控制模块共享

4、micro-app(京东)

优点:

  • 使用简单

  • 基于 Web Components

  • 性能好

适用场景:

  • 对性能要求高的项目

  • 喜欢简单配置的团队

5、Iframe

优点:

  • 具有天然的隔离属性,js沙箱、样式隔离等都很好。

缺点:

  • UI不同步,比如在iframe中添加蒙层弹框,只会在iframe中显示,不是全屏的。

  • 慢,每次进入会重新加载,多个iframe时浏览器容易卡死。

基础改造

🌈🌈🌈

基座改造🚩

一、Antd Pro基座改造(umi系)🤠

接下来对中台(antd Pro 创建的项目,作为基座)进行改造。

1、安装@umijs/plugin-qiankun

【umimax已内置】

pnpm i @umijs/plugin-qiankun

2、注册子应用

config/config.ts配置:

在config的qiankun.master.apps数组中 注册子应用

export default defineConfig({qiankun: {master: {apps: [{name: 'sub-umi',//子应用的名称entry: '//localhost:5175',//子应用的入口地址activeRule: '/qiankun/umi',//子应用的激活规则,指路由sandbox: {strictStyleIsolation: true,//严格样式隔离},},],},},//其他配置
})

3、配置访问子应用的路由

config/router.tsx :在基座的路由中添加能访问子应用的路由。

方式1:用microApp属性指定要渲染的子应用的name。(本次改造采用这种)

  {path: '/qiankun',name: 'qiankun',routes: [{path: '/qiankun/umi',name: 'sub-umi',microApp: 'sub-umi', //和注册时的name一致microAppProps: {// 子应用自动设置loading// autoSetLoading: true, //可以用autoSetLoading,需要子应用引入antdloader: (loading: boolean) => <Spin spinning={loading} />,},},],},

方式2:使用component属性指定组件,在组件中使用qiankun提供的 MicoApp组件

// 1、路由{path: '/app1',name: 'sub-app',element:<SubApp/>}// 2、 SubApp组件,用MicroApp组件占位,需要指定name(和注册时同名)
import React from 'react';
import { MicroApp } from '@umijs/max';
type Props = {};const MicroApp1 = (props: Props) => {return <MicroApp name="sub-app" />;
};export default MicroApp1;

二、非umi系基座改造

1、安装qiankun
pnpm i qiankun // 或者 yarn add qiankun
2、修改入口文件

在src/index.tsx中增加如下代码:从qiankun中引入注册和启动的函数,注册子应用并调用start启动。


import { start, registerMicroApps } from 'qiankun';// 1. 要加载的子应用列表
const apps = [{name: "sub-react", // 子应用的名称entry: '//localhost:8080', // 默认会加载这个路径下的html,解析里面的jsactiveRule: "/sub-react", // 匹配的路由container: "#sub-app" // 加载的容器},
]// 2. 注册子应用
registerMicroApps(apps, { //下面的配置对象可以不写beforeLoad: [async app => console.log('before load', app.name)],beforeMount: [async app => console.log('before mount', app.name)],afterMount: [async app => console.log('after mount', app.name)],
})start() // 3. 启动微服务
/*
// 配置qiankun启动参数
start({// prefetch: true, // 预加载  默认是true,即在主应用加载的时候,加载子应用sandbox: {//沙箱// experimentalStyleIsolation: true, // 实验性样式隔离,好像没用哦strictStyleIsolation: true, // 严格样式隔离},
})
*/

3、注册子应用路由,提供容器dom

router注册一下子应用的路由,element设置为null,在跳转到子应用的路由时,展示id为subApp的div。

//router/index.jsx
const router = [{path: "/",element: <Home />,},{path: "/app1/*",element: null,},
]//App.jsx
function App() {const element = useRoutes(router)return (<div className="main-layout"><nav><Link to="/">首页</Link><Link to="/app1">子应用1</Link></nav><div className="test-aa">123</div><div className="main-content"><Suspense fallback={<div>Loading...</div>}>{element}{/* 需要子应用的容器 */}<div id="subApp"></div></Suspense></div></div>)
}

一旦浏览器的 url 发生变化,便会自动触发 qiankun 的匹配逻辑。 所有 activeRule 规则匹配上的微应用就会被插入到指定的 container 中,同时依次调用微应用暴露出的生命周期钩子。

  • registerMicroApps(apps, lifeCycles?)

注册所有子应用,qiankun会根据activeRule去匹配对应的子应用并加载

  • start(options?)

启动 qiankun,可以进行预加载和沙箱设置,更多options : API 说明 - qiankun


至此基座基本就改造完成,接下来改造子应用。

子应用改造🤩

子应用改造主要需要注意:

  1. 用umd格式打包,当然,像umi,vite这些插件已经设置了。

一、umi子应用改造

中台项目的子应用是采用umi进行构建。

  1. 安装插件

在子应用目录安装@umijs/plugins插件,才能在umirc中用qiankun字段。

pnpm i @umijs/plugins
  1. 使用插件

.umirc.ts 中使用上面的插件,这样就在基座中通过子应用的地址来访问这个子应用了。


export default defineConfig({base: '/',   // 用qiankun插件后默认base为包名,所以这里重置一下qiankun: { //告诉umi这个项目需要用到qiankunslave: {},},plugins: ['@umijs/plugins/dist/qiankun', '@umijs/plugins/dist/model', '@umijs/plugins/dist/mf'], //plugins使用@umijs/plugins插件,功能分别是支持qiankun、允许useModel、模块联邦mf})

  1. 生命周期

如果需要在生命周期中做一些事情,可以在入口文件app.tsx中导出qiankun对象,在对象中的方法写代码,qiankun会执行这些生命周期函数。


export const qiankun = {async mount(props: any) {console.log(props)},async bootstrap() {console.log('umi app bootstraped');},async afterMount(props: any) {console.log('umi app afterMount', props);},
};

二、vue3+vite改造

创建子应用

创建子应用,选择vue3+vite

npm create vite@latest
改造子应用
  1. 安装vite-plugin-qiankun包,因为qiankun和vite有些问题,需要这个包解决。

pnpm i vite-plugin-qiankun
  1. 修改vite.config.js,使用上面的插件

import qiankun from 'vite-plugin-qiankun';defineConfig({base: '/sub-vue', // 和基座中配置的activeRule一致server: {port: 3002,cors: true,origin: 'http://localhost:3002'},plugins: [vue(),qiankun('sub-vue', { // 配置qiankun插件useDevMode: true})]
})
  1. 修改main.ts

我们需要提供三个必须的生命周期函数,即:bootstrap(只在第一次进入的时候执行)、mount(挂载)、onmount(卸载)

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper';let app: any;
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {createApp(App).mount('#app');
} else {renderWithQiankun({// 子应用挂载mount(props) {app = createApp(App);app.mount(props.container.querySelector('#app'));},// 只有子应用第一次加载会触发bootstrap() {console.log('vue app bootstrap');},// 更新update() {console.log('vue app update');},// 卸载unmount() {console.log('vue app unmount');app?.unmount();}});
}

三、create-react-app改造

1、改造入口文件

代码如下

  • 导出三个必须的生命周期函数,供qiankun使用。

  • 根据window.__POWERED_BY_QIANKUN__来决定render逻辑

let root: Root// 将render方法用函数包裹,供后续主应用与独立运行调用
function render(props: any) {const { container } = propsconst dom = container ? container.querySelector('#root') : document.getElementById('root')root = createRoot(dom)root.render(// 可以根据需要指定basename//<BrowserRouter basename={window.__POWERED_BY_QIANKUN__ ? "/sub-react" : ""}><BrowserRouter><App/></BrowserRouter>)
}// 判断是否在qiankun环境下,非qiankun环境下独立运行
if (!(window as any).__POWERED_BY_QIANKUN__) {render({});
}// 各个生命周期
// bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
export async function bootstrap() {console.log('react app bootstraped');
}// 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
export async function mount(props: any) {render(props);
}// 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
export async function unmount(props: any) {root.unmount();
}

2、新增public-path.js

动态设置 webpack publicPath,防止资源加载出错

if (window.__POWERED_BY_QIANKUN__) {// 动态设置 webpack publicPath,防止资源加载出错// eslint-disable-next-line no-undefwebpack_public_path = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}

3、修改webpack配置文件,用umd格式打包

这里用craco修改webpack的配置,所以应该先下载craco。在根目录下新增craco.config.js文件并新增如下配置

const { name } = require("./package");module.exports = {webpack: (webpackConfig) => {webpackConfig.output = {...webpackConfig.output,library: ${packageName}-[name],libraryTarget: "umd", //主要是这个配置,用umd打包这个项目chunkLoadingGlobal: webpackJsonp_${packageName},filename: "static/js/[name].umd.js",chunkFilename: "static/js/[name].umd.chunk.js",}return webpackConfig}
};

什么是umd?

umd格式_umd 库格式-CSDN博客


在基座能访问子应用,即说明配置成功。

说明

一、样式隔离

使用

子应用之间的样式隔离qiankun已经实现了,但是基座和子应用之间的样式隔离没有实现。

我们可以在基座注册子应用时 设置 strictStyleIsolation为true 这样设置主要是对直接设置class时进行隔离(className='test'),在项目中我们一般是 css module 和strictStyleIsolation 一起使用,子应用能加自己的前缀是更好的。

export default defineConfig({qiankun: {master: {apps: [{name: 'sub-umi',entry: '//localhost:5175',activeRule: '/qiankun/umi',sandbox: {strictStyleIsolation: true,//严格样式隔离},},],},},//其他配置
})

strictStyleIsolation原理⛔️

【基于shadow dom来做的样式隔离】

shadowDOM 的MDN地址如下:

使用影子 DOM - Web API | MDN

strictStyleIsolation 的原理是基于 Web Components 中的 Shadow DOM 技术。让我详细解释一下:

  1. 基本实现原理

function createShadowContainer(container, appName) {// 创建 Shadow DOMconst shadow = container.attachShadow({ mode: 'open' });// 子应用的所有内容都会被放入这个 Shadow DOM 中return shadow;
}

实际效果:

<!-- 普通 DOM 结构 -->
<div id="main"><div id="app1">#shadow-root (open)<!-- 子应用的所有内容都在 Shadow DOM 中 --><style>.title { color: red; }</style><div class="title">我是app1的标题</div></div>
</div>

  1. Shadow DOM 的特性

  • 独立的 DOM 树

  • 样式完全隔离

  • JavaScript 访问限制

  • 事件局部化

  1. 样式隔离效果

/* Shadow DOM 内部的样式 */
.title { color: red; }/* 外部的样式无法影响到 Shadow DOM 内部 */
#main .title { color: blue; } /* 这个样式不会影响 Shadow DOM 内的 .title */

主要优点:

  • 完全的样式隔离

  • 不需要额外的样式转换

  • 原生的隔离方案

主要缺点:

  • 一些第三方库可能无法正常工作

  • 弹窗类组件可能会被限制在 Shadow DOM 内

  • 浏览器兼容性问题

二、js沙箱

  1. 在基座中修改window会共享到各个子应用。

  2. 在子应用A修改window不会影响到子应用B。

qiankun中js沙箱的原理

qiankun中的js沙箱是对window进行隔离,主要解决全局变量冲突和全局状态相互影响的问题。

qiankun提供了三种沙箱模式:

1、在不支持proxy的浏览器,提供【快照沙箱】,在进入子应用之前

2、在支持proxy的浏览器,用proxy代理window,子应用修改代理后的window。单例就采用legacySandbox沙箱,多例就采用proxySandbox沙箱。

类似代码:

const proxy = new Proxy(window)
(function(window){//子应用的代码
})(proxy)

倔金沙箱: 

jhttps://juejin.cn/post/6920110573418086413#heading-12

三、剔除重复依赖

如果基座和子应用使用了项目的库,可以考虑子应用使用基座的包,从而减少重复加载

有两种方式:

  1. externals,基座用cdn引入包,子应用相同的cdn设置为ignore。(更推荐用externals

  2. 模块联邦,基座将重复包打包至remote.js,子应用不打包重复的包,而是在运行时请求基座的remote.js

externals

流程:

  • 基座:将所有公共依赖配置webpackexternals,并且在index.html使用外链引入这些公共依赖

  • 子应用:和主应用一样配置webpackexternals,并且在index.html使用外链引入这些公共依赖,注意,还需要给子应用的公共依赖的加上 ignore 属性(这是自定义的属性,非标准属性),qiankun在解析时如果发现igonre属性就会自动忽略

以lodahs为例:

基座:

修改config/config.ts文件,在externals中添加lodash,之后在headScripts数组中添加lodash的cdn地址。

// 修改config/config.js
export default defineConfig({/*** @name <head> 中额外的 script* @description 配置 <head> 中额外的 script*/headScripts: [// 解决首次加载时白屏的问题//{ src: '/scripts/loading.js', async: true },//lodash 的cdn{ src: 'https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js', async: false },],externals: {lodash: '_',//externals 指定lodash和他的全局变量名},
})

umi子应用:

子应用同样需要在自己的配置文件中添加cdn的lodash,并且需要添加ignore忽略lodash。

<!-- 注意:这里的公共依赖的版本,基座和子应用需要一致 -->


export default defineConfig({// 剔除重复包externals: {lodash: '_',},headScripts: [{src: 'https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js',async: false,ignore: true, // 子应用需要添加ignore,忽略lodash,使用基座挂载window上的lodash,这样只需要请求一次},],
})

四、公共组件

我们后续会用pnpm的workspace来做monorepo,这样在单仓下基座和子应用就能共享组件了,

但是这样还有一个问题,当公共组件变化,子应用就需要重新打包部署才能得到公共组件的变化,所以可以采用模块联邦的方式,让子应用使用基座的远程组件,这样就只需要对基座进行打包部署。

接下来先介绍monorepo改造基座,并举例在子应用中如何使用workspce的公共组件,最后介绍将公共组件转为远程组件使用。

1、monorepo改造

  1. 在基座的根目录新建pnpm-workspace.yaml 文件,文件内容:

意思是:

  • 会将这三个文件夹下的目录添加到workspace工作空间中,他们可以相互通过workspce访问到。

  • 其中app文件夹存放子应用的项目代码,src/expoese/components文件下存放各种公共组件的代码。

  • 在umi中要导出远程组件,需要将组件写到src/exposes文件夹下,umi自动处理exposes文件夹下的组件。

packages:- 'apps/*'- 'src/exposes/components/*'- 'src/exposes/*'
  1. 新建yaml中涉及的文件夹,将子应用放入apps文件夹中。

至此简单的monorepo改造完毕

2、添加公共组件

下面演示PureButton这个公共组件的创建和使用。

  1. 前置步骤

在src/exposes/components (看上面,这是一个workspace目录) 目录下执行npm init -y,我们会将components目录作为组件库的目录,后续的公共组件都写在这个目录下。其package.json类似步骤2(ps:也可以不这么做,也可以直接所有公共组件写在exposes下,只要组件是个npm包就行。)

{"name": "components","version": "1.0.1","description": "","main": "index.tsx","keywords": [],"author": "","license": "ISC"
}
  1. 在workspce中创建组件包

在src/exposes/components下新建一个PureButton 文件夹,在这个文件夹中执行npm init -y 生成package.json 文件,主要修改文件中以下字段:

  • name(我们可以用@loctek这个前缀,包名用横线分割,不要用驼峰。)

  • version

  • main(main是这个包的入口)

{"name": "@loctek/pure-button","version": "1.0.0","description": "","main": "index.tsx","types": "index.d.ts","keywords": [],"author": "","license": "ISC","private": true
}
  1. 书写组件代码

新建index.tsx,书写组件的代码

import styles from './style.module.less';
import type { Props } from './index.d';const PureButton = ({ btnStr = '' }: Props) => {return (<><div className={styles['btn-container']}>{btnStr}</div></>);
};export default PureButton;

  1. 测验组件是否生效

(这里单纯测试workspace是否生效,实际项目我们采用模块联邦的方式。)

在本地开发中,我们可以在apps的子应用中,在其package.json中添加上面的包,然后在子项目的目录执行pnpm i 或者 pnpm i @loctek/pure-button 命令,这样就会将@loctek/pure-button组件的软链接添加到子项目的node_modules中供子应用使用。

  "dependencies": {//其他包..."@loctek/pure-button": "workspace:*",},


3、利用模块联邦使用公共组件

官网地址:

Module Federation 插件

  1. 改造基座,导出远程组件

在config/config.ts中使用 mf 字段,这样最终会在打包文件中多出一个remote.js文件。用name起一个名字,remoteHash用于取消打包的hash,library指定打包的文件的模块

export default defineConfig({// 模块联邦mf: {name: 'master',//关闭remote.js的hashremoteHash: false,// qiankun时必须要,window是挂载到window上,默认是varlibrary: { type: 'window', name: 'master' },},//其他配置})
  1. 子应用注册远程组件

子应用的.umirc.ts中,1、添加mf插件、2、添加mf字段

mf中的remotes指定访问的远程组件地址和他的name,基座最终会被部署到80端口,所以我们的entry就是//localhost:80/remote.js',,remote.js就是会将基座中src/exposes下的文件打包进去。(ps:在本地开发阶段可以写基座项目启动的地址,比如 //localhost:8080/remote.js )

shared字段填这个子应用用到的远程包,因为我们会将所有的组件写入components,所以直接这样写好就行了。

  plugins: ['@umijs/plugins/dist/qiankun', '@umijs/plugins/dist/model', '@umijs/plugins/dist/mf'],//添加  @umijs/plugins/dist/mf'mf: {remotes: [{aliasName: 'masterAppXXX',//一个别名name: 'master', // 对应基座应用的 nameentry: '//localhost:80/remote.js', // 基座应用中导出的共享包的入口},],// 声明共享依赖shared: {'components': {singleton: true,//单例,整个应用只存在一个,防止一个库加载多个版本。eager: false,//控制共享模块的加载时机,默认为false:异步加载,实际用的时候加载;为true时指:同步加载,应用启动就加载,使用于本地开发的时候。requiredVersion: '^1.0.1',},},},
  1. 使用远程组件

随便在子应用中找个文件用远程的PureButton组件

//impoer MasterApp from '别名/包名'
import MasterApp from 'masterAppXXX/components';
//因为是默认导出,所以需要拿到默认导出的东西后解构
const { PureButton} = MasterApp;
export default function Foo(){return (<><PureButton btnStr="555" /></>)
}

五、应用之间的通信

官网地址:

微前端

useModel

基座

在基座中定义了一些model,并且在app.jsx这个入口文件中导出子应用中需要使用的model

入口文件:app.tsx中导出useQiankunStateForSlave函数。

// 子应用(需要子应用是umi项目)获取主应用的全局状态,需要在app.tsx导出useQiankunStateForSlave供umi使用
interface QiankunState {site: string;
}
export function useQiankunStateForSlave(): QiankunState {const { site } = useModel('site');return {site,//导出site供子应用使用};
}

子应用

在子应用中用useModel('@@qiankunStateFromMaster') 即可拿到导出的数据


import { useModel } from 'umi';export default function HomePage() {// 获取主应用的actionsconst model = useModel('@@qiankunStateFromMaster');console.log(model?.site)return <>...<>}

六、keep-alive

菜单切换时会重新加载子应用,我们能手动来加载子应用的显示隐藏来实现keep-alive的效果,umimax已经为我们提供好了【MicroAppWithMemoHistory】组件,直接用就行。

路由注册时使用这个组件,routes.ts:

export default [  ...['accountService', 'recordCenter'].map((base) => ({path: `/${base}/*`,component: './MicroAppWrapper',})),
]

组件代码:

import { useMemo } from 'react';
import { MicroAppWithMemoHistory } from '@umijs/max';const map = {'accountService': 'qijing','recordCenter': 'haitu',
};function MicroAppWrapper() {const info = useMemo(() => {const pathname = window.location.pathname;const base = pathname.split('/')[1];return {name: map[base],pathname,};}, []);return <MicroAppWithMemoHistory name={info.name} url={info.pathname} />;
}export default MicroAppWrapper;

 

nginx部署

官网地址:

入门教程 - qiankun

  • 可以将子应用和基座部署在同一个server,也可以部署在不同的server下。

一、部署在同一个server

部署在同一个server下,

  • 子应用的路由base需要和基座的activeRule保持一致。

  • 子应用的entery则是需要与其nginx路径一致

基座注册:

  qiankun: {master: {apps: [{name: 'sub-umi-1', //子应用的名称entry: '/sub/umi1/', //子应用的入口地址,nginx配置的目录activeRule: '/app-umi1-history', //子应用的激活规则,指路由sandbox: {strictStyleIsolation: true, //严格样式隔离},},],},},

子应用.umirc.ts

  base: '/app-umi2-history', //供基座访问的路由前缀,会和activeRule一样// 用qiankun插件后默认base为包名publicPath: '/sub/umi2/', //资源的前缀,会和nginx中存放的目录保持一次

二、部署在不同的server

部署在不同的server需要为子应用的server添加跨域

nginx.conf内容:

    # main主应用server {listen       80;server_name  localhost;#  CORS 配置add_header Access-Control-Allow-Origin '*' always;add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS' always;add_header Access-Control-Allow-Headers '*' always;add_header Access-Control-Allow-Credentials 'true' always;if ($request_method = 'OPTIONS') {return 204;}location / {root   html/main;index  index.html index.htm;try_files $uri $uri/ /index.html;}# API 代理配置location ^~ /auth {proxy_pass http://mall-center.dev.springbeetle.top;}location ^~ /perm {proxy_pass http://mall-center.dev.springbeetle.top;}# 处理子应用的代理# location ^~ /qiankun/react {#     proxy_pass http://localhost:5173;# }error_page   500 502 503 504  /50x.html;location = /50x.html {root   html;}}# umi子应用server {listen       5175;server_name  localhost;# # 添加5174全局 CORS 配置add_header Access-Control-Allow-Origin '*' always;add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS' always;add_header Access-Control-Allow-Headers '*' always;add_header Access-Control-Allow-Credentials 'true' always;if ($request_method = 'OPTIONS') {return 204;}location / {root   html/qiankun/umi;index  index.html index.htm;try_files $uri $uri/ /index.html;}# API 代理配置# location ^~ /auth {#      proxy_pass http://mall-center.dev.springbeetle.top;# }error_page   500 502 503 504  /50x.html;location = /50x.html {root   html;}}

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

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

相关文章

【Java学习笔记】代码块

代码块 介绍&#xff1a;代码块又称为初始化块&#xff0c;属于类中的成员&#xff08;即是类的一部分&#xff09;&#xff0c;类似于方法&#xff0c;将逻辑语句封装在方法体中&#xff0c;通过{}包围起来 与类方法的不同点 无方法名 无返回类型 无参数 只有方法体&#…

spring boot 2.7集成旧的springfox-boot-starter swagger oas 3.0

旧版本目前已经不维护推荐使用 springdoc-openapi-ui&#xff0c;这里为了演示使用旧的最新依赖 <dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId><version>3.0.0</version> </dep…

Linux按键驱动测试方式详细介绍

Linux按键驱动测试可采用以下分层方法&#xff1a; 基础事件检测 使用输入子系统调试工具&#xff1a; sudo apt install evtest # 安装事件测试工具 evtest # 选择对应设备编号触发按键后观察终端输出&#xff0c;正常情况应显示&#xff1a; Event:…

USB学习【13】STM32+USB接收数据过程详解

目录 1.官方的描述2.HAL的流程把接收到的数据从PMA拷贝到用户自己定义的空间中 3.处理接收到的数据4.最后再次开启准备接收工作 1.官方的描述 2.HAL的流程 以上的官方说法我们暂时按下不表。 如果接收到数据&#xff0c;会激活中断进入到USB_LP_CAN1_RX0_IRQHandler&#xff0…

上海内推 | 上海算法创新研究院-上海交大联合招收空间智能/具身智能算法实习生

最近这一两周不少公司已开启春招和实习招聘。 不同以往的是&#xff0c;当前职场环境已不再是那个双向奔赴时代了。求职者在变多&#xff0c;HC 在变少&#xff0c;岗位要求还更高了。 最近&#xff0c;我们又陆续整理了很多大厂的面试题&#xff0c;帮助一些球友解惑答疑&am…

C语言速成12之指针:程序如何在内存迷宫里找宝藏?

程序员Feri一名12年的程序员,做过开发带过团队创过业,擅长Java、鸿蒙、嵌入式、人工智能等开发,专注于程序员成长的那点儿事,希望在成长的路上有你相伴&#xff01;君志所向,一往无前&#xff01; 0. 前言&#xff1a;程序如何在内存迷宫里找宝藏&#xff1f; 想象内存是一个巨…

部署n8n

https://github.com/n8n-io/n8n docker volume create n8n_data docker run -it --rm --name n8n -p 5678:5678 -v n8n_data:/home/node/.n8n docker.n8n.io/n8nio/n8n Discover 2192 Automation Workflows from the n8ns Community

ABP VNext + Orleans:Actor 模型下的分布式状态管理最佳实践

ABP VNext Orleans&#xff1a;Actor 模型下的分布式状态管理最佳实践 &#x1f680; &#x1f4da; 目录 ABP VNext Orleans&#xff1a;Actor 模型下的分布式状态管理最佳实践 &#x1f680;一、引言&#xff1a;分布式系统的状态挑战 &#x1f4a1;二、架构图与技术栈 &am…

构建安全AI风险识别大模型:CoT、训练集与Agent vs. Fine-Tuning对比

构建安全AI风险识别大模型:CoT、训练集与Agent vs. Fine-Tuning对比 安全AI风险识别大模型旨在通过自然语言处理(NLP)技术,检测和分析潜在的安全威胁,如数据泄露、合规违规或恶意行为。本文从Chain-of-Thought (CoT)设计、训练集构建、以及Agent-based方法与**AI直接调优…

Baklib内容中台的主要构成是什么?

Baklib内容中台核心架构 Baklib作为一站式知识管理平台的核心载体&#xff0c;其架构设计围绕智能搜索引擎优化技术与多终端适配响应系统展开。通过模块化内容组件的灵活配置&#xff0c;企业可快速搭建知识库、FAQ页面及帮助中心等标准化场景&#xff0c;同时借助可视化数据看…

Ubuntu Desktop 24.04 常用软件安装步骤

文章目录 Ubuntu Desktop 24.04 常用软件安装步骤Snipaste F1快捷截图&#xff08;超方便 | 我6台电脑每台都用&#xff09;搜狗输入法快速浏览工具 | 空格键快速预览文件壁纸工具 | varietySSH 工具 | Termius 终端分屏工具 | TmuxCaffeine | 避免息屏小工具 一些设置将启动台…

详细使用@rollup/plugin-inject的方式

rollup/plugin-inject 是一个 Rollup 插件&#xff0c;它允许你在构建时自动注入模块中的变量引用&#xff0c;避免手动在每个文件中 import。Vite 使用的是 Rollup 构建底层&#xff0c;因此该插件在 Vite 项目中也适用。 一、使用场景 比如你希望在代码中不手动写 import { …

Day 0017:Web漏洞扫描(OpenVAS)解析

一、NVT脚本解析&#xff1a;漏洞检测的“DNA” 1. NVT脚本结构 每个NVT脚本都是一个Lua脚本&#xff0c;包含以下核心模块&#xff1a; lua -- 示例&#xff1a;检测Apache HTTPd 2.4.49路径穿越漏洞&#xff08;CVE-2021-41773&#xff09; script_id "1.3.6.1.4.1.…

【HarmonyOS Next之旅】DevEco Studio使用指南(二十六) -> 创建端云一体化开发工程

目录 1 -> 创建HarmonyOS应用工程 1.1 -> 新建工程 1.1.1 -> 前提条件 1.1.2 -> 选择模板 1.1.3 -> 配置工程信息 1.1.4 -> 关联云开发资源 1.2 -> 工程初始化配置 1.2.1 -> 自动开通云开发服务 1.3 -> 端云一体化开发工程目录结构 1.3.1…

Python 包管理工具 uv

Python 包管理工具 uv 是由 Astral 团队&#xff08;知名工具 Ruff 的开发者&#xff09;基于 Rust 开发的新一代工具&#xff0c;旨在通过高性能和一体化设计革新 Python 生态的依赖管理体验。以下是其核心特性、优势及使用指南的全面解析&#xff1a; 一、uv 的核心优势 极致…

何谓第二大脑?读书笔记

2025/05/11 发表想法 每个人都是矛盾结合体&#xff0c;既想学到新知识、新的能力&#xff0c;又想没办法专注的学习&#xff0c;既无法专注有渴望学习新技能&#xff0c;逐渐会产生焦虑、失眠等负面症状&#xff0c;这就是现实社会现照&#xff0c;那怎么办&#xff1f;我们能…

动态防御体系实战:AI如何重构DDoS攻防逻辑

1. 传统高防IP的静态瓶颈 传统高防IP依赖预定义规则库&#xff0c;面对SYN Flood、CC攻击等常见威胁时&#xff0c;常因规则更新滞后导致误封合法流量。例如&#xff0c;某电商平台遭遇HTTP慢速攻击时&#xff0c;静态阈值过滤无法区分正常用户与攻击者&#xff0c;导致订单接…

为什么在设置 model.eval() 之后,pytorch模型的性能会很差?为什么 dropout 影响性能?| 深度学习

在深度学习的世界里&#xff0c;有一个看似简单却让无数开发者困惑的现象&#xff1a; “为什么在训练时模型表现良好&#xff0c;但设置 model.eval() 后&#xff0c;模型的性能却显著下降&#xff1f;” 这是一个让人抓耳挠腮的问题&#xff0c;几乎每一个使用 PyTorch 的研究…

[爬虫知识] http协议

相关爬虫专栏&#xff1a;JS逆向爬虫实战 爬虫知识点合集 爬虫实战案例 引言&#xff1a;爬虫与HTTP的不解之缘 爬虫作用&#xff1a;模拟浏览器请求网页为何要懂HTTP&#xff1a;http是网络通信的基石&#xff0c;爬虫抓取数据就是通过HTTP协议进行的&#xff0c;了解http有…

《Spark/Flink/Doris离线实时数仓开发》目录

欢迎加入《Spark/Flink/Doris离线&实时数仓开发》付费专栏&#xff01;本专栏专为大数据工程师、数据分析师及准备大数据面试的求职者量身打造&#xff0c;聚焦Spark、Flink、Doris等核心技术&#xff0c;覆盖离线与实时数仓开发的全流程。无论你是想快速上手项目、提升技术…