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

2024 你还不会微前端吗 (上) — 从巨石应用到微应用

前言

微前端系列分为 上/下 两篇,本文为 上篇 主要还是了解微前端的由来、概念、作用等,以及基于已有的微前端框架进行实践,并了解微前端的核心功能所在,而在 下篇 中主要就是通过自定义实现一个微前端框架来加深理解。

在这里插入图片描述

微前端是什么?

微前端 是一种类似于 微服务 的概念,因此要想更好的了解微前端,就必须先了解一下微服务。

微服务

微服务架构 是将一个庞大的业务系统按照业务模块拆分成 若干个独立的子系统,每个子系统都是一个独立的应用,它是一种将应用构建成一系列按 业务领域 划分模块的、小的自治服务的软件架构方式,倡导将 复杂的单体应用 拆分成 若干个功能单一、松偶合的服务,目的是降低开发难度、增强扩展性、便于敏捷开发,及持续集成与交付活动。

在这里插入图片描述

与微服务相对的另一个概念是传统的 单体式应用程序( Monolithic application ),单体式应用内部包含了 所有需要的服务,且各个服务功能模块具有 强耦合性(相互依赖),导致难以进行拆分和扩容。

在这里插入图片描述

简单来说,单体式应用程序 其实就是一台服务器处理了需要所有的功能,微服务 就是将功能按照业务模块划分成了不同的独立服务,各个微服务间通过 HTTP 协议进行通信,通过注册中心观测微服务状态。

微服务 概念主要存在于后端开发,但这个概念是不是和你听说过的 微前端 很像了。

微前端

随着大前端的快速发展 和 SPA 的大规模应用,也带来了新的问题,而这些问题都催化出了 微前端 的概念:

  • 项目功能不断增多、体积不断增大(巨石应用),导致打包时间成正比例增长,是否能保证更好的 项目扩展
  • 前端技术更新太快,一个项目历经一两年也许就需要进行项目升级,甚至是切换技术栈,但仍需要老项目的代码,是否能进行 新老版本的兼容
  • 团队技术栈不一,又需要保证同一项目的开发,是否能保证不同团队的 独立开发
  • SPA 项目的任何变动都需执行完整的打包、部署,是否能保证不同内容 独立部署

在这里插入图片描述

微前端 是一种类似于 微服务 的架构,是一种由独立交付的 多个前端应用 组成整体的架构风格,将前端应用分解成一些更小、更简单的能够 独立开发、测试、部署 的应用,而对外表现仍是 单个内聚的产品

微前端框架

微前端框架的核心

一个微前端框架至少要保证如下的核心功能:

  • 技术栈无关
    • 主框架不限制接入子应用的技术栈,微应用具备完全自主权
  • 独立开发、独立部署
    • 微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新
  • 增量升级
    • 在面对各种复杂场景时,通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略
  • 独立运行时
    • 每个微应用之间状态隔离,运行时状态不共享

single-spa

single-spa 是一个将多个单页面应用聚合为一个整体应用的 JavaScript 微前端框架。

核心原理

基座 (主) 应用 中注册所有 App 的路由,single-spa 保存各子应用的路由映射关系,充当微前端控制器 Controler,当对应的 URL 变换时,除了匹配 基座应用 本身的路由外,还会匹配 子应用 路由并加载渲染子应用。

子应用会经过如下过程

  • 下载 (loaded)
  • 初始化 (initialized)
  • 挂载 (mounted)
  • 卸载 (unmounted)

single-spa 还会通过 生命周期 为这些过程提供对应的 钩子函数

qiankun

qiankun 是一个基于 single-spa微前端 实现库,目的是提供更简单、无痛的构建一个生产可用微前端架构系统。

包括在 single-spa 文档中也有推荐使用 qiankun

在这里插入图片描述

single-spa 实践

创建子应用

Vue3 子应用

为了快速的创建应用,这里通过 vue create vue3-micro-app 来快速创建技术栈为 Vue3子应用

以下在子应用中的处理方式可用 single-spa-vue 来简化

页面效果如下

在这里插入图片描述

子应用入口文件

为了子应用既可以独立运行,也可以在基座应用中运行,需要在子应用入口文件进行一些修改,具体如下:

  • 将原本的初始化内容封装在自定义的 render 函数中,目的是可以在不同的环境执行初始化操作
    • 若当前在基座应用中进行渲染,则其页面内容对应的挂载容器需要指定为基座容器中对应的 DOM 节点
    • window.singleVue3 不存在时意味着是子应用独立运行,此时直接按照原本的初始化方式进行即可,即直接调用 render() 函数
  • 子应用必须导出 bootstrap、mount、unmount 等生命周期函数,且其返回值类型要为 fullfilled 状态的 Promise,否则后续操作不会执行
  • 定义 instance 变量存储实例对象,方便在当前子应用在基座应用中被切换时可以执行真正的卸载子应用
// main.tsimport { createApp } from 'vue'
import type { App as AppType } from 'vue'
import App from './App.vue'
import router from './router'let instance: AppTypefunction render(container?: string) {instance = createApp(App)instance.use(router).mount(container || '#micro-vue-app')
}// 当 window.singleVue3 不存在时,意味着是子应用单独运行
if (!window.singleVue3) {render();
}// 子应用必须导出 以下生命周期 bootstrap、mount、unmount
export const bootstrap = () => {return Promise.resolve()
};
export const mount = (props: any) => {render(props.container);return Promise.resolve()
};
export const unmount = () => {instance.unmount();return Promise.resolve()
};

为什么要将 x.mount('#app') 换成 x.mount('#micro-vue-app') ?

如果你明白 子应用基座应用 中的渲染方式就不难理解了,因为当前这个子应用的挂载容器的 id="app" 而基座应用中的默认挂载容器也是 id="app",这显然会导致冲突,初始化渲染时会渲染基座应用本身,但是当你切换到 vue3 的子应用时,就会发现当前子应用的内容整个覆盖了基座应用的内容,因为此时子应用在进行挂挂载的时候,会把已经渲染 基座应用 的容器再一次作为 子应用 的容器进行渲染,于是内容就会被完全替换成子应用的内容。

基座应用被子应用替换效果如下:

在这里插入图片描述

路由配置

路由模式为 hash 模式,默认路由配置:

import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
import HomeView from '../views/HomeView.vue'const routes: Array<RouteRecordRaw> = [{path: '/',name: 'home',component: HomeView},{path: '/about',name: 'about',component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')}
]const router = createRouter({history: createWebHashHistory(),routes
})export default router

打包配置

vue.config.js 下必须将打包后的输出格式指定为 umd 格式:

module.exports = {configureWebpack: {output: {library: 'singleVue3',libraryTarget: 'umd',globalObject: 'window',},devServer: {port: 5000,},},
}

React 子应用

类似的,这里通过 npx create-react-app react-micro-app 来快速创建技术栈为 React子应用

以下在子应用中的处理方式可用 single-spa-react 来简化

页面效果如下

在这里插入图片描述

子应用入口文件

此部分核心内容和上述的 vue3 子应用一致,不在额外说明,入口文件代码如下:

// index.jsimport React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import App from './App'let root = nullfunction render(props = {}) {const container = document.getElementById(props.container ? props.container.slice(1) : 'root',)if(!container) returnroot = ReactDOM.createRoot(container)root.render(<React.StrictMode><App {...props} /></React.StrictMode>,)
}// 当 window.singleReact 不存在时,意味着是子应用单独运行
if (!window.singleReact) {render()
}// 子应用必须导出 以下生命周期 bootstrap、mount、unmount
export const bootstrap = () => {return Promise.resolve()
}
export const mount = (props) => {render(props)return Promise.resolve()
}
export const unmount = () => {root.unmount()return Promise.resolve()
}

路由配置

路由模式为 hash 模式,自定义路由配置:

<Router><Switch><Route path="/" exact><Home /></Route><Route path="/about" exact><About /></Route></Switch>
</Router>

打包配置

对于 React 应用来讲,要么是一开始就是自定义 webpack 相关配置,要么是基于 crate-react-app 内置的 webpack 配置进行修改,由于上面是通过 crate-react-app 的方式创建的项目,因此可以基于其内置的配置文件进行修改:

不想通过 eject 的方式去修改配置文件,可通过 react-app-rewired 进行重写

  • 执行 npm run eject 将内置的 webpack 配置像暴露出来,会生成一个 scripts 目录 和 config 目录
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P9e0Qk2S-1666075878612)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c0e23630cafb4dd5ad5faaf4aa0eaa9f~tplv-k3u1fbpfcp-watermark.image?)]

  • 这里只需要关注 config 目录即可,因为 webpack.config.js 在该目录下
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gwo8SRWR-1666075878612)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e0869f8bf4f14f18b078523157be673c~tplv-k3u1fbpfcp-watermark.image?)]

  • 为其中的 output 配置添加上如下两行配置项即可(需重新启动)

     output: {library: 'singleReact',libraryTarget: 'umd',globalObject: 'window',....}
    

创建基座应用

基座应用的技术栈这里选择 Vue2,同样可以通过 vue create vue2-main-app 的方式创建对应的基座应用:

在这里插入图片描述

路由配置

为了让 基座应用 的路由看起来更美观,这里选择 history 模式(也可选 hash 模式),接着配置具体路由:

  • 基座应用的路由
    • 配置对应的路由路径
    • 指定对应组件作为路由渲染视图,如 HomeView 组件
  • 微应用的路由
    • 只需要设定对应的路由路径,不需要指定对应的具体组件
import Vue from 'vue'
import VueRouter, { RouteConfig } from 'vue-router'
import HomeView from '../views/HomeView.vue'Vue.use(VueRouter)const routes: Array<RouteConfig> = [{path: '/',name: 'home',component: HomeView},{path: '/vue3-micro-app',name: 'about',},{path: '/react-micro-app',name: 'about',}
]const router = new VueRouter({mode: 'history',base: process.env.BASE_URL,routes
})export default router

子应用挂载容器

基座应用中的 <router-view /> 是为了渲染基座应用自身的路由视图,而子应用是不能通过 <router-view /> 来渲染,因此,我们需要在基座应用中设定一个 Dom 节点专门用于渲染子应用的视图,这里就将子应用的内容挂载在 <div id="micro-content"></div> 的节点中:

<!-- 主内容 -->
<main class="content"><!-- 基座应用路由视图渲染 --><router-view /><!-- 子应用挂载容器 --><div id="micro-content"></div>
</main>

菜单配置

菜单配置实际就是指定路由的跳转,具体包含内容如下:

  • Home 菜单渲染的视图内容是 基座应用 中对应的 HomeView 组件
  • Vue3-micro-app 菜单渲染视图是名为 vue3-micro-app子应用
  • Home 菜单渲染视图是基名为 react-micro-app子应用

基座应用注册子应用

基座应用 中尚未注册 子应用 时的页面效果如下:
在这里插入图片描述

为了切换菜单路由时,对应的子应用能够被正确的渲染在基座应用中,需要我们在基座应用中注册子应用:

  • 通过 pnpm install single-spa -S 安装 single-spa
  • 通过 single-spa 中提供的 registerApplication()start() 函数完成注册和启动,该逻辑可抽离到 registerApplication.ts
    // registerApplication.tsimport { registerApplication, start } from 'single-spa';// 子应用
    export const applications = [{name: 'singleVue3',async activeWhen() {await loadScript('http://localhost:5000/js/chunk-vendors.js');await loadScript('http://localhost:5000/js/app.js');return window.singleVue3},app(location: Location) {return location.pathname.startsWith('/vue3-micro-app')},customProps: {container: '#micro-content'}
    },
    {name: 'singleReact',async activeWhen() {await loadScript('http://localhost:3000/static/js/main.js');return window.singleReact},app(location: Location) {return location.pathname.startsWith('/react-micro-app')},customProps: {container: '#micro-content'}
    }]// 加载子应用 script
    export const loadScript = async (url: string) => {await new Promise((resolve, reject) => {const script = document.createElement('script');script.src = url;script.onload = resolve;script.onerror = reject;document.head.appendChild(script)});
    }// 注册子应用
    export const registerApps = (apps: any[] = applications) => {apps.forEach(({name,activeWhen,app,customProps = {}}) => registerApplication(name, activeWhen, app, customProps));start();
    }
    
  • main.ts 中导入并执行 registerApplication() 即可

效果预览

源码地址

其中包含了 子应用 在基座应用中 特定位置的渲染,以及 子应用 自身路由的切换时的效果,可以看出子应用路由和主应用路由互不影响。

在这里插入图片描述

qiankun 实践

有了前面 single-spa 的基础,通过 qiankun 来实现微前端更加简单了,因为 qiankun 本身就是基于 single-spa 实现的微前端架构系统,目的是提供更简单、简洁的方式接入。

下面我们还是使用上述的三个项目来通过 qiankun 的形式来实现微前端。

配置基座应用

vue2-main-app 的入口文件 registerApplication.ts 中使用 qiankun 进行简单配置即可,相比于上面 single-spa 的方式来说更简单:

// registerApplication.tsimport { registerMicroApps, start } from 'qiankun';// 默认子应用
export const applications = [{name: 'singleVue3', // app name registeredentry: 'http://localhost:5000',container: '#micro-content',activeRule: '/vue3-micro-app',},{name: 'singleReact', // app name registeredentry: 'http://localhost:3000',container: '#micro-content',activeRule: '/react-micro-app',},
]// 注册子应用
export const registerApps = (apps: any[] = applications) => {registerMicroApps(applications);start();
}

配置子应用

子应用 部分该导出的生命周期还是要导出,值得注意的是生命周期中的 container 已经是对应基座应用中的 真实 DOM 节点,而不是 CSS 选择器,因此只需要进行简单的修改即可,具体如下所示:

vue3-micro-app 子应用

// src/main.tsimport { createApp } from 'vue'
import type { App as AppType } from 'vue'
import App from './App.vue'
import router from './router'let instance: AppTypefunction render(container?: string) {instance = createApp(App)// 这里的 container 已经是对应基座应用中的真实 DOM 节点,而不是 CSS 选择器instance.use(router).mount(container || '#micro-vue-app')
}// 当 window.singleVue3 不存在时,意味着是子应用单独运行
if (!window.singleVue3) {render();
}// 子应用必须导出 以下生命周期 bootstrap、mount、unmount
export const bootstrap = () => {return Promise.resolve()
};
export const mount = (props: any) => {render(props.container);return Promise.resolve()
};
export const unmount = () => {instance.unmount();return Promise.resolve()
};

react-micro-app 子应用

// src/index.jsimport React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import App from './App'let root = nullfunction render(props = {}) {// 这里的 container 已经是对应基座应用中的真实 DOM 节点,而不是 CSS 选择器const container = props.container || document.getElementById('root')if(!container) returnroot = ReactDOM.createRoot(container)root.render(<React.StrictMode><App {...props} /></React.StrictMode>,)
}// 当 window.singleReact 不存在时,意味着是子应用单独运行
if (!window.singleReact) {render()
}// 子应用必须导出 以下生命周期 bootstrap、mount、unmount
export const bootstrap = () => {return Promise.resolve()
}
export const mount = (props) => {render(props)return Promise.resolve()
}
export const unmount = () => {root.unmount()return Promise.resolve()
}

子应用配置 CORS

前面说过基座应用是需要将子应用的入口文件加载到当前应用下来执行的,这个过程第一步就是请求对应的入口文件,由于浏览器 同源策略 的限制,我们必须要在子应用中配置当前子应用的资源是允许被跨域请求的。

子应用没有配置 CORS 发生跨域

在这里插入图片描述

vue3-micro-app 配置 CORS

vue.config.js 中配置 devServer 既可,其中的 devServer 可以配置所有符合 webpack-dev-server 的选项:

module.exports = {publicPath: '//localhost:5000',configureWebpack: {output: {library: 'singleVue3',libraryTarget: 'umd',globalObject: 'window',},devServer: {port: 5000,headers: {'Access-Control-Allow-Origin': '*',},},},
}

react-micro-app 配置 CORS

因为之前是通过 npm run eject 的方式暴露出来和 webpack 相关的配置,在查看对应的 config\webpackDevServer.config.js 配置发现其内部已经默认做了 CORS 配置

在这里插入图片描述

最后

经过以上的实践,下面简单地对微前端框架核心内容进行自己的理解:

  • 技术栈无关
    • 任何一个子应用不论使用什么技术栈,最终都会被编译为 JavaScript 代码,因此真正在执行时无论基座应用还是子应用都已经是同一种语言形式
  • 独立开发、独立部署
    • 子应用本质上就是普遍使用的 spa 单页面应用,因此当然可以拥有独立代码仓库进行关联,可独立发布运行,也可作为子应用运行,只需要做好不同环境的兼容即可
  • 增量升级
    • 子应用能够独立开发部署,自然支持自身应用的功能的独立扩展,又或者是接入新的子应用
  • 独立运行时
    • 保证多个子应用在基座应用中运行时,自身的状态不受其他子应用的影响

本篇文章就这里就结束了,下一篇文章再去聊聊微前端的实现原理,以及通过自己实现一个微前端的方式加深理解。

希望本篇文章能对你有所帮助!!!

在这里插入图片描述

参考

  • 微前端架构的几种技术选型

  • 2022年你必须要会的微前端

  • 什么叫做微服务?它和传统的项目之间有什么区别?

  • 什么是微服务架构?

相关文章:

2024 你还不会微前端吗 (上) — 从巨石应用到微应用

前言 微前端系列分为 上/下 两篇&#xff0c;本文为 上篇 主要还是了解微前端的由来、概念、作用等&#xff0c;以及基于已有的微前端框架进行实践&#xff0c;并了解微前端的核心功能所在&#xff0c;而在 下篇 中主要就是通过自定义实现一个微前端框架来加深理解。 微前端是…...

WPF+MVVM案例实战(三)- 动态数字卡片效果实现

1、创建项目 打开 VS2022 &#xff0c;新建项目 Wpf_Examples&#xff0c;创建各层级文件夹&#xff0c;安装 CommunityToolkit.Mvvm 和 Microsoft.Extensions.DependencyInjectio NuGet包,完成MVVM框架搭建。搭建完成后项目层次如下图所示&#xff1a; 这里如何实现 MVVM 框…...

#网络安全#渗透测试# 渗透测试应用

网络安全渗透测试是一种重要的安全评估方法&#xff0c;用于发现和评估网络系统中的安全漏洞。在进行渗透测试时&#xff0c;需要注意以下几个关键点&#xff1a; 法律和道德考量 获得授权&#xff1a;在进行渗透测试之前&#xff0c;必须获得目标系统的正式授权。未经授权的测…...

MicroServer Gen8再玩 OCP万兆光口+IT直通之二

这个接上一篇&#xff0c;来个简单测试。 一、测试环境 PC端&#xff1a;Win10&#xff0c;网卡&#xff1a;万兆光纤&#xff08;做都做了&#xff0c;都给接上&#xff09;&#xff0c;硬盘使用N年的三星SSD 840 交换机&#xff1a;磊科GS10&#xff0c;带两个万兆口 Gen…...

【JAVA面试题】Java和C++主要区别有哪些?各有哪些优缺点?

文章目录 强烈推荐前言区别&#xff1a;1. 语法和编程风格2.内存管理3.平台独立性4.性能5.指针和引用6.多线程7.使用场景 Java 的优缺点优点&#xff1a;缺点&#xff1a; C 的优缺点优点&#xff1a;缺点&#xff1a; 总结专栏集锦 强烈推荐 前些天发现了一个巨牛的人工智能学…...

保姆级教程!!教你通过【Pycharm远程】连接服务器运行项目代码

小罗碎碎念 这篇文章主要解决一个问题——我有服务器&#xff0c;但是不知道怎么拿来写代码&#xff0c;跑深度学习项目。确实&#xff0c;玩深度学习的成本比较高&#xff0c;无论是前期的学习成本&#xff0c;还是你需要具备的硬件成本&#xff0c;都是拦路虎。小罗没有办法…...

JMeter详细介绍和相关概念

JMeter是一款开源的、强大的、用于进行性能测试和功能测试的Java应用程序。 本篇承接上一篇 JMeter快速入门示例 &#xff0c; 对该篇中出现的相关概念进行详细介绍。 JMeter测试计划 测试计划名称和注释&#xff1a;整个测试脚本保存的名称&#xff0c;以及对该测试计划的注…...

如何使用Git

简介 一.git简介 Git是一个分布式版本控制工具,通常用来对软件开发过程中的源代码文件进行管理.通过Git仓库来存储和管理这些文件,Git仓库分两种: 本地仓库:开发人员自己电脑上的Git仓库远程仓库:远程服务器上的Git仓库 commit:提交,将本地文件和版本信息保存到本地仓库 p…...

Redis 哨兵 问题

前言 相关系列 《Redis & 目录》&#xff08;持续更新&#xff09;《Redis & 哨兵 & 源码》&#xff08;学习过程/多有漏误/仅作参考/不再更新&#xff09;《Redis & 哨兵 & 总结》&#xff08;学习总结/最新最准/持续更新&#xff09;《Redis & 哨兵…...

安卓基础001

前言 也是好久没有更新博客了,最近实习也是需要学习一些知识哈哈哈哈哈哈为了更好的发展嘛,咱们从客户端开始,过程可能有点像写前端,不喜勿喷,希望在学习的过程中也可以给大家带来一些简单得帮助吧....... tips:这里跳过安卓studio安装,大家可自行寻找教程 写的不详细,只是为了…...

shodan2:绕过shodan高级会员限制+metasploit批量验证漏洞

shodan2 shodanmetasploit批量验证漏洞 shodan的这个指令语法是特别多的&#xff0c;那么我不可能说一个个全部讲完&#xff0c;因为有的参数可能你一辈子都用不上&#xff0c;主要就是把一些红队最常用的参数给你讲完&#xff0c;今天我们看看怎么去查一个cve-2019-0708的一…...

【JAVA毕业设计】基于Vue和SpringBoot的母婴商城系统

本文项目编号 T 030 &#xff0c;文末自助获取源码 \color{red}{T030&#xff0c;文末自助获取源码} T030&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…...

探索Python安全字符串处理的奥秘:MarkupSafe库揭秘

文章目录 探索Python安全字符串处理的奥秘&#xff1a;MarkupSafe库揭秘第一部分&#xff1a;背景介绍第二部分&#xff1a;MarkupSafe是什么&#xff1f;第三部分&#xff1a;如何安装MarkupSafe&#xff1f;第四部分&#xff1a;MarkupSafe的简单使用方法1. 使用escape函数2.…...

Xcode真机运行正常,打包报错

1.问题&#xff1a; 老项目Xcode真机运行没问题&#xff0c;但但打包的时候却报了以下错误&#xff1a; some files could not be transferred (code 23) at /AppleInternal/Library/BuildRoots/4ff29661-3588-11ef-9513-e2437461156c/Library/Caches/com.apple.xbs/Sources/r…...

Android Audio基础——音频混音线程介绍(十)

MixerThread 是 Android 音频输出的核心部分,主要负责将多个音频流混合成一个输出流,通常用于处理多个音频源(如音乐播放器、语音通话、系统提示音等)的混音操作,混音后的音频数据会被发送到音频硬件(如扬声器或耳机)进行最终输出。大多数 Android 的音频都需要经过 Mix…...

【Excel】函数各类公式总结

在 Excel 中&#xff0c;有许多常用的公式和函数用于各种类型的计算&#xff0c;包括基本的数学运算、统计运算、逻辑判断、查找与引用、文本处理&#xff0c;以及复数计算。下面列出了一些常用的 Excel 函数&#xff1a; 1、数学与三角函数 SUM求和函数&#xff0c;计算一组…...

【入门篇】2.9 系统滴答定时器 SysTick

目录 一,SysTick 系统滴答定时器 二,SysTick寄存器 2.1 SysTick 控制和状态寄存器(CTRL) 2.2 SysTick 重装载数值寄存器(LOAD) 2.3. SysTick 当前值寄存器(VAL) 2.4 SysTick 校准值寄存器(CALIB) 三,使用SysTick定时器 四,用法示例 一,SysTick 系统滴答定时…...

BiRefNet:颠覆图像分割,AI黑科技再升级

BiRefNet&#xff1a;颠覆图像分割&#xff0c;AI黑科技再升级 BiRefNet 是一款超强的图像分割 AI 模型&#xff0c;精准度惊人✨&#xff0c;适用于医疗、农业、工业等多个领域&#x1f30d;&#xff0c;让图像处理变得简单高效&#xff01;快来体验这款黑科技吧&#xff01;…...

编写一个简单的Iinput_dev框架

往期内容 本专栏往期内容&#xff1a; input子系统的框架和重要数据结构详解-CSDN博客input device和input handler的注册以及匹配过程解析-CSDN博客input device和input handler的注册以及匹配过程解析-CSDN博客 I2C子系统专栏&#xff1a; 专栏地址&#xff1a;IIC子系统_憧憬…...

ctfshow的sql注入解题思路171-211

ctfshow-SQL注入 web171&#xff1a;爆库名->爆表名->爆字段名->爆字段值 -1 union select 1,database() ,3 -- //返回数据库名 -1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema库名 -- //获取数据库里的表名 -…...

深入理解C语言中的静态库与动态库 —— 原理与实践

引言 在 C 语言编程中&#xff0c;库是预编译的代码集合&#xff0c;用于实现特定功能&#xff0c;以供其他程序使用。库可以分为静态库和动态库两种主要类型。静态库在编译阶段被链接到目标程序中&#xff0c;而动态库则是在运行时被加载。本文旨在深入探讨这两种库的工作原理…...

本地缓存库分析(一):golang-lru

文章目录 本地缓存概览golang-lru标准lrulru的操作PutGet 2q&#xff1a;冷热分离lruPutGet expirable_lru&#xff1a;支持过期时间的lruPutGet过期 总结 本地缓存概览 在业务中&#xff0c;一般会将极高频访问的数据缓存到本地。以减少网络IO的开销&#xff0c;下游服务的压…...

qt配置https请求

qt应用版本 windows 32位 先说下心理路程&#xff0c;你能遇到的我都遇到了&#xff0c;你能想到的我都想到了&#xff0c;怎么解决看这一篇就够了&#xff0c;从上午12点到晚上12点几乎没离开电脑&#xff08;除了吃饭&#xff09;&#xff0c;对于openssl这种用的时候无感&am…...

C语言进阶——文件操作

一、文件的基本知识 1.1什么是文件 在程序设计中&#xff0c;一般谈的文件有两种&#xff1a;程序文件、数据文件。 程序文件&#xff1a;包括源程序文件&#xff08;后缀为.c&#xff09;&#xff0c;目标文件&#xff08;windows环境后缀为.obj&#xff09;&#xff0c;可执…...

MYSQL-查看用户权限语法(二十一)

13.7.5.21 SHOW GRANTS 语句 SHOW GRANTS [FOR user]此语句以GRANT语句的形式显示分配给MySQL用户帐户的权限&#xff0c;必须执行GRANT语句才能复制权限分配。 注意 要显示MySQL帐户的非特权信息&#xff0c;请使用SHOW CREATE USER语句。 参见第 13.7.5.12 节“ SHOW CREA…...

在MySQL中存储IP地址的最佳实践

文章目录 一、IP地址的格式二、存储IP地址的数据类型选择1. VARCHAR优点缺点 2. INT 或 BIGINT优点缺点示例 3. VARBINARY优点缺点示例 三、最佳实践建议1. 选择合适的数据类型2. 索引优化3. 数据验证4. 安全性考虑 四、Java支持五、结论 在现代网络应用中&#xff0c;IP地址是…...

Vite打包配置

Vite打包配置 1.项目启动自动打开网页 {"scripts": {"dev": "vite --open"} }2.base配置打包公共路径 配置base选项的作用主要是指定项目在开发或生产环境中的公共基础路径。这个配置项对于确保资源能够正确加载尤为关键&#xff0c;尤其是在…...

node集成redis (教学)

文章目录 前言一、安装redis二、可视化界面测试连接1.vscode安装插件 三、node代码编写1.先安装两个库&#xff08;redis和ioredis&#xff09;2.测试连接 &#xff08;前提是你的redis服务器要启动起来&#xff09; 总结 前言 在Node.js中集成ioredis是一个常见的做法&#x…...

江协科技STM32学习- P22 实验-ADC单通道/ADC多通道

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…...

RL学习笔记-马尔可夫过程

参考资料&#xff1a;蘑菇书、周博磊老师课程 在强化学习中&#xff0c;智能体与环境交互是通过马尔可夫决策过程来表示的&#xff0c;因此马尔可夫决策过程是强化学习的基本框架。 马尔可夫性质 指一个随机过程在给定现在状态及所有过去状态情况下&#xff0c;其未来状态的条件…...

昆山苏州网站建设/免费发布产品信息的网站

网易考拉&#xff08;以下简称考拉&#xff09;是网易旗下以跨境业务为主的综合型电商&#xff0c;自2015年1月9日上线公测后&#xff0c;业务保持了高速增长&#xff0c;这背后离不开其技术团队的支撑。微服务化是电商IT架构演化的必然趋势&#xff0c;网易考拉的服务架构演进…...

婚庆网站模板免费下载/电子商务网店运营推广

Python中多态的作用 让具有不同功能的函数可以使用相同的函数名&#xff0c;这样就可以用一个函数名调用不同内容(功能)的函数。 Python中多态的特点 1、只关心对象的实例方法是否同名&#xff0c;不关心对象所属的类型&#xff1b; 2、对象所属的类之间&#xff0c;继承关系…...

企业网站设计合同/浙江网站建设营销

目录 快速入门 1.基础标签 练习&#xff1a;公司简介案例 2.图片、音频、视频标签 3.超链接标签 4.列表标签 5.表格标签 练习&#xff1a;课程表 6.布局标签 7.表单标签 基本使用 表单项 快速入门 <html><head><title>html 快速入门</title&g…...

广宗企业做网站/做个小程序需要花多少钱

1 下载安装包1.1 压缩包[外链图片转存失败(img-oesO8K09-1566652568838)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw "点击并拖拽以移动")]1.2 安装包使用安装包安装则无需后续步骤[外链图片转存失败(img-Y3x59iO4-15666525…...

百度网站怎么做视频播放器/首页图片点击率如何提高

1、Viasfora &#xff08;关键字跟括号其他的高亮清晰分类&#xff09; 2.ClaudiaIDE &#xff08;VS代码背景替换也可以换自己想要的图片&#xff09; 3.VSCommands for Visual Studio 2013 代码显示增强&#xff1a;它可以在尾部显示方法或过程名字 4.Indent Guides &#x…...

源码做网站教程/百度推广优化技巧

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼最近学习CUDA C的编程&#xff0c;在并行运行一个简单的解调算法的时候&#xff0c;统计时间后发现运行速度越来越慢(但还是运算结果正确的)&#xff0c;后来简化到只运行其中一个核函数的时候&#xff0c;就算复杂度下降了&#x…...