微前端学习以及分享

news2025/1/16 13:53:11

微前端学习以及分享

注:本次分享demo的源码github地址:https://github.com/rondout/micro-frontend

什么是微前端

微前端的概念是由ThoughtWorks在2016年提出的,它借鉴了微服务的架构理念,核心在于将一个庞大的前端应用拆分成多个独立灵活的小型应用,每个应用都可以独立开发、独立运行、独立部署,再将这些小型应用融合为一个完整的应用,或者将原本运行已久、没有关联的几个应用融合为一个应用。微前端既可以将多个项目融合为一,又可以减少项目之间的耦合,提升项目扩展性,相比一整块的前端仓库,微前端架构下的前端仓库倾向于更小更灵活。

它主要解决了两个问题:

  • 随着项目迭代应用越来越庞大,难以维护。
  • 跨团队或跨部门协作开发项目导致效率低下的问题。

在这里插入图片描述

微前端架构有以下特点:

  • 技术栈无关
    主框架不限制接入应用的技术栈,微应用具备完全自主权

  • 独立开发、独立部署
    微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新

  • 增量升级

    在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略

  • 独立运行时
    每个微应用之间状态隔离,运行时状态不共享

现有的微前端解决方案

现在主流的微前端解决方案有以下几种:

  • qiankunqiankun 孵化自蚂蚁金融科技基于微前端架构的云产品统一接入平台,在经过一批线上应用的充分检验及打磨后,我们将其微前端内核抽取出来并开源,希望能同时帮助社区有类似需求的系统更方便的构建自己的微前端系统。

  • MicroApp:micro-app是由京东前端团队推出的一款微前端框架,它借鉴了WebComponent的思想,通过js沙箱样式隔离元素隔离路由隔离模拟实现了隔离特性,从而实现微前端的组件化渲染,旨在降低上手难度、提升工作效率。

    micro-app和技术栈无关,也不和业务绑定,可以用于任何前端框架。

  • Wujie:无界微前端方案基于 webcomponent 容器 + iframe 沙箱,能够完善的解决适配成本、样式隔离、运行性能、页面白屏、子应用通信、子应用保活、多应用激活、vite 框架支持、应用共享等用户的核心诉求。

目前而言,这三种方案都是不错的微前端解决方案,但是目前而言qiankun对vite的支持仍然不友好,qiankun本身是不支持vite构建的应用的,还需要使用社区的插件,而且我也有去做demo,然后觉得坑太多了,就选择了 MicroApp 方案来做微前端技术调研学习的方案。

学习目标

本次学习目标有以下几个:

  • 设计微前端架构

  • 实现基座应用和子应用之间的通信

  • 不同子应用之间的通信

  • 数据共享以及数据私有

  • 部署整个微前端架构以及有关应用

基座和子应用

微前端架构中很重要的一个概念就是基座和子应用,基座就是整个应用的基础,所有的子应用就是一个个单独的前端应用(工程)。在微前端架构中,子应用是可以单独开发然后适配基座的,最终整个应用运行后,子应用是挂载在基座上的。

我这边设计的整个微前端架构的目录结构如下:

  • assets
  • base-app
  • child-vue3-app
  • child-react-app
  • child-native-app
  • servers
  • package.json
  • tsconfig.json

这其中 base-appchild-vue3-appchild-react-appchild-native-app 都是一个单独的应用,可独立运行,也可和微前端架构一起整体运行。

package.json 中配置启动脚本:

 "install:main": "cd base-app && pnpm install",
 "install:vue": "cd child-vue3-app && pnpm install",
 "install:react": "cd child-react-app && pnpm install",
 "install:native": "cd child-native-app && pnpm install",
 "install:server": "cd servers && pnpm install",
 "dev:main": "cd base-app && pnpm dev",
 "dev:vue": "cd child-vue3-app && pnpm dev",
 "dev:react": "cd child-react-app && pnpm dev",
 "dev:native": "cd child-native-app && pnpm dev",
 "dev:server": "cd servers && pnpm dev",
 "install": "pnpm install:main && pnpm install:vue && pnpm install:react && pnpm install:native && pnpm install:server",
 "dev": "concurrently \"pnpm dev:main\" \"pnpm dev:vue\" \"pnpm dev:react\" \"pnpm dev:native\"",

我们在运行项目的时候可以先通过pnpm install命令安装所有的依赖(包括每一个子应用),然后通过pnpm dev命令启动项目。
这里大致解释一下 concurrently 这个工具,就是用来同时启动多个命令,比如我们这里启动了基座应用和子应用,因此我们可以通过pnpm dev命令同时启动基座应用和子应用。

搭建基座和子应用

我们可以用任意的技术栈来搭建基座应用。由于我们现在目前以及后续的技术栈是 vue3 + ts + vite 这套。因此这里我也就以该技术栈搭建基座。

子应用我们可以使用各种技术栈搭建不同的子应用来进行技术实践,我这边准备了一下几种技术栈:

  • vue3 + ts + vite
  • react + ts + vite
  • 原生native app (使用nodejs搭建静态资源服务器)

搭建基座应用和子应用的流程这里就无需多讲了。也不是本次分享的重点,因此这里略过。

我们设想的整个应用的结构如下:

在这里插入图片描述

我们把整个应用分为3个部分:

  • 头部 headers
  • 侧边 aside
  • 内容区域 Content

我们期望的是头部和侧边区域是基座应用,内容区域是子应用。比如我们的 demo 里面的,侧边栏有首页VUE APPREACT APP 以及 NATIVE APP,首页是基座应用,VUE APP 和 REACT APP 以及 NATIVE APP 是子应用。

初始化基座

首先是安装依赖:

npm i @micro-zoe/micro-app --save
// 或者
pnpm add @micro-zoe/micro-app
// 或者
yarn add @micro-zoe/micro-app

然后根据官方文档操作步骤,初始化基座的 MicroApp 有关的配置代码:

import microApp from "@micro-zoe/micro-app";

export function startMicro() {
    console.log("MicroApp start!");
    microApp.start();
}

这样子,我们的基座应用就初始化完成了,非常简单。

加载子应用

在基座应用中直接使用 <micro-app> 标签加载子应用:

<template>
  <!-- name:应用名称, url:应用地址 -->
  <micro-app name='my-app' url='http://localhost:3000/'></micro-app>
</template>

这里是以 vue 子应用为例,后续完整代码会有react版本的。这里注意nameurl只是这个标签的属性之二,该标签还支持多种属性,后续我们遇到一个说一个。比如我们在加载 vite 构建的子应用就需要加上iframe属性。因为目前vite构建的子应用目前只支持 iframe沙箱。

子应用TS配置

由于子应用无需安装 micro-app 依赖,并且子应用通过window对象实现微前端功能以及和基座应用之间的通信,因此我们最好是给子应用声明一下window对象上面的有关属性和方法:

env.d.ts:

/*
 * @Author: shufei.han
 * @Date: 2024-08-02 09:29:40
 * @LastEditors: shufei.han
 * @LastEditTime: 2024-08-29 12:05:55
 * @FilePath: \micro-frontend\child-vue3-app\env.d.ts
 * @Description: 
 */
/// <reference types="vite/client" />
import type { MicroMessageType } from '@/models/base.model';
import 'ant-design-vue/typings/global'

declare global {

    interface MicroMessage<T = any> {
        type: MicroMessageType;
        value?: T;
    }
    interface Window {
        microApp: {
            addDataListener:(dataListener: (data: MicroMessage) => any, autoTrigger?: boolean) => void;
            removeDataListener:(dataListener: (data: MicroMessage) => any, autoTrigger?: boolean) => void;
            removeGlobalDataListener:(dataListener: (data: MicroMessage) => any, autoTrigger?: boolean) => void;
            addGlobalDataListener:(dataListener: (data: MicroMessage) => any, autoTrigger?: boolean) => void;
            clearDataListener: () => void;
            getData: () => MicroMessage;
            dispatch: <T extends MicroMessage = MicroMessage, C extends Function>(data:T, cb?: C) => void;
            getGlobalData: () => MicroMessage;
            setGlobalData: <T extends MicroMessage = MicroMessage, C extends Function>(data:T, cb?: C) => void;
        };
        /** 应用名称 */
        __MICRO_APP_NAME__: string;
        /** 判断应用是否在微前端环境中 */
        __MICRO_APP_ENVIRONMENT__: boolean;
        /** 子应用的静态资源前缀 */
        __MICRO_APP_PUBLIC_PATH__: string;
        /** 子应用的基础路径 */
        __MICRO_APP_BASE_ROUTE__: string;
        /** 判断当前应用是否是主应用 */
        __MICRO_APP_BASE_APPLICATION__: string;
        /** 获取真实window(即主应用window) */
        rawWindow: Window;
        /** 获取真实document(即主应用document) */
        rawDocument: Document;
    }
}

export {}

子应用跨域配置

如果我们直接像上述配置一样直接接入子应用,由于浏览器的同源策略,如果子应用不支持跨域,则会报跨域错误。因此我们需要在子应用所在的 web server 进行跨域配置:

vite配置:

export default defineConfig({
  server: {
    headers: {
      'Access-Control-Allow-Origin': '*',
    }
  }
})

nodejs配置(我的 native app 是使用express搭建的静态资源服务,因此这里以该技术栈举例):

 private setCors() {
        this.instance.use((req, res, next) => {
            res.header("Access-Control-Allow-Origin", "*");
            res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With,Content-Type, Accept");
            next();
        });
    }

环境变量

Micro App提供了一下环境变量,用于我们开发过程中的某些环境有关的逻辑判断,这些环境变量大多都通过绑定为 window 对象的属性的方式来使用,因此如果我们使用Typescript开发子应用的话,就要进行这些全局变量的变量声明,我们在我们子应用中的声明文件(vite构建的应用的默认声明文件就是根目录下面的env.d.ts):

/// <reference types="vite/client" />

declare global {
    interface Window {
        /** 应用名称 */
        __MICRO_APP_NAME__: string;
        /** 判断应用是否在微前端环境中 */
        __MICRO_APP_ENVIRONMENT__: boolean;
        /** 子应用的静态资源前缀 */
        __MICRO_APP_PUBLIC_PATH__: string;
        /** 子应用的基础路径 */
        __MICRO_APP_BASE_ROUTE__: string;
        /** 判断当前应用是否是主应用 */
        __MICRO_APP_BASE_APPLICATION__: string;
        /** 获取真实window(即主应用window) */
        rawWindow: Window;
        /** 获取真实document(即主应用document) */
        rawDocument: Document;
    }
}

export {}

生命周期

micro-app通过CustomEvent定义生命周期,在组件渲染过程中会触发相应的生命周期事件。

生命周期列表
  1. created:
    <micro-app>标签初始化后,加载资源前触发。

  2. beforemount:
    加载资源完成后,开始渲染之前触发。

  3. mounted:
    子应用渲染结束后触发。

  4. unmount:
    子应用卸载时触发。

  5. error:
    子应用加载出错时触发,只有会导致渲染终止的错误才会触发此生命周期。

监听方式

Vue

<micro-app
  name='xx'
  url='xx'
  onCreated={() => console.log('micro-app元素被创建')}
  onBeforemount={() => console.log('即将渲染')}
  onMounted={() => console.log('已经渲染完成')}
  onUnmount={() => console.log('已经卸载')}
  onError={() => console.log('加载出错')}
/>

React:

因为React不支持自定义事件,所以我们需要引入一个polyfill

在标签所在的文件顶部 添加polyfill,注释也要复制。

/** @jsxRuntime classic */
/** @jsx jsxCustomEvent */
import jsxCustomEvent from '@micro-zoe/micro-app/polyfill/jsx-custom-event'

<micro-app
  name='xx'
  url='xx'
  onCreated={() => console.log('micro-app元素被创建')}
  onBeforemount={() => console.log('即将渲染')}
  onMounted={() => console.log('已经渲染完成')}
  onUnmount={() => console.log('已经卸载')}
  onError={() => console.log('加载出错')}
/>

数据通信

数据通信这一节的内容在微前端的领域非常重要,这关乎着各个子应用之间,以及子应用与主应用之间如何进行数据通信。

数据通信主要分为以下几种:

  • 主应用向子应用发送数据
  • 子应用向主应用发送数据
  • 各个子应用之间的互相通信
主应用向子应用发送数据

主应用给子应用传递数据有两种方式:

  1. 通过data属性发送数据
  2. 通过方法手动发送数据
通过data属性发送数据

data作为 micro-app 标签的属性,用该属性作为主应用向子应用传递数据的中介:

<micro-app :name="SubApps.REACT" @created="created" :data="data" keep-alive url="http://localhost:4003/" iframe  @datachange="handleChange"></micro-app>


const data = ref<MicroMessage>({
    type: MicroMessageType.TEXT_MSG,
    value: 'This is a initial TextMessage'
})
    
onMounted(() => {
	setInterval(() => {
    data.value.value = 'new value'
    }, 1000)    
})

这种方式类似于我们平时组件中的父子组件传参,区别就是,在子应用中需要通过getData方法区手动获取数据,该数据不是全响应式的(基座中绑定的数据如果发生变化会动态更改传给子应用的值,但是子应用需要通过getData方法手动获取更改后的值。)。

子应用:

const data = window.microApp.getData()
通过microApp.setData方法发送数据

除了通过data属性传递数据,还可以通过microApp.setData方法动态发送数据:

microApp.setData(name, message)

这两种方法是有区别的,第一种方法只能通过getData方法去手动的获取数据,数据更新时子应用是无感知的。而第二种方法类似于发布订阅机制,子应用可以随时监听到数据变化。

子应用通过注册事件监听数据

子应用可以使用window.microApp.addDataListener方法监听来自主应用的事件消息:

window.microApp.addDataListener((data: MicroMessage) => {
    handleMessage(data)
})

我们在收到消息后通过handleMessage方法来统一处理消息。

这里建议:虽然microApp只要求我们的消息格式是 Object 就可以,但是我这边建议,我们需要对消息进行一个统一的格式管理,方便维护。

比如我这边利用TS的特性,对基座和子应用之间的消息进行枚举,然后再定义接口来约束消息的格式,这样基座和子应用之间处理消息的时候根据消息类型来去做对应的处理逻辑:

// 对消息类型进行枚举
export enum MicroMessageType {
  CHANGE_THEME = 'change_theme',
  SET_COUNT = 'set_count',
  TEXT_MSG = 'text_msg',
}

// 约束来基座和子应用之间通信的消息格式
interface MicroMessage<T = any> {
    type: MicroMessageType;
    value?: T;
}

总的来说:通过data属性传递数据和通过发布订阅的模式(消息事件监听)传递数据是有区别的,在实际场景中根据自己需要选取使用。

在这里插入图片描述

子应用向主应用发送数据

在微前端种,不仅有主应用向子应用发送消息的场景,通常也可能会有子应用向主应用发送消息的场景,在
micro-app 中,子应用通过 window.microApp.dispatch方法发送数据,这里同样为了约束子应用向主应用发送的数据的格式,我们可以在声明文件中约定子应用推送消息的格式:

dispatch: <T extends MicroMessage = MicroMessage, C extends Function>(data:T, cb?: C) => void

然后我们就可以定义一个公用的方法用来向主应用发送数据:

export const sendMessageToBase = (message: MicroMessage) => {
    window.microApp.dispatch(message)
}

// 使用
const handleSend = () => {
    sendMessageToBase({
        type: MicroMessageType.TEXT_MSG,
        value: {value},
    });
};

在这里插入图片描述

全局数据通信

全局数据通信会向主应用和所有子应用发送数据,在跨应用通信的场景中适用。

主应用发送数据:

import microApp from "@micro-zoe/micro-app";

export const sendGlobalData = (message: MicroMessage) => {
    microApp.setGlobalData(message)
}

子应用发送数据:

// setGlobalData只接受对象作为参数
window.microApp.setGlobalData({type: '全局数据'})

主应用获取数据:

microApp.addGlobalDataListener((msg) => {
        Modal.info({title:`BaseApp收到全局数据`, content: JSON.stringify(msg), 									centered:true})
        mainStore.setGlobalMessages(msg)
}
                               
// 或者使用getGlobalData手动获取数据
const data = microApp.getGlobalData()

子应用获取数据:

window.microApp.addGlobalDataListener((data) => {
    handleGlobalMessage(data)
})

// 或者使用getGlobalData手动获取数据
const data = window.microApp.getGlobalData()

在这里插入图片描述

虚拟路由系统

MicroApp通过拦截浏览器路由事件以及自定义的locationhistory,实现了一套虚拟路由系统,子应用运行在这套虚拟路由系统中,和主应用的路由进行隔离,避免相互影响。建议全局配置路由模式:

microApp.start({
    'router-mode':'native'
});

MicroApp 提供一下这几种路由模式:

  • search模式
  • native模式
  • native-scope模式
  • pure模式
  • state模式

这里就不详细说明这些路由模式了,这里只说常用的两种:

search模式路由

search是默认模式,通常不需要特意设置,search模式下子应用的路由信息会作为query参数同步到浏览器地址上。这种模式最简单也不需要去做其他的配置。

但是这种模式的路由,在页面上看起来会很奇怪,不建议使用。

在这里插入图片描述

native模式路由

native模式是指放开路由隔离,子应用和主应用共同基于浏览器路由进行渲染,它拥有更加直观和友好的路由体验,但配置方式更加复杂。需要基于vue-routerbase配置或者react-routerbasename配置:

Vue3中的配置方法:

我们使用4.x版本的vue-router,在生成路由的方法createWebHashHistory中传入base配置:

history: createWebHashHistory(import.meta.env.VITE_BASE_URL)

这里需要注意这里配置的base需要和主应用中的该子应用的routerPath保持一致,因此推荐使用另一种方法来设置而非通过环境变量:

<micro-app :name="SubApps.VUE" @created="created" url="http://192.168.8.125:4002/"  baseroute="/vue/" iframe @datachange="handleChange"></micro-app>

// router/index.ts
const router = createRouter({
	..............
    history: createWebHashHistory(window.__MICRO_APP_BASE_ROUTE__ || '/'),
    ..............
}
React中使用

可以根据官方文档进行配置。

注意:根据子应用和主应用不同的路由模式,我们可能需要进行不同的配置或者不需要配置,具体查阅官方文档。

native模式的路由看着就比较清晰了:

在这里插入图片描述

总结

本次分享是对自己学习MicroApp的过程的一个分享,本次demo也只是一个非常基础的demo,微前端架构需要考虑的内容还很多,需要再实战中遇到问题再积累总结。
最后:本次demo的源码放在了https://github.com/rondout/micro-frontend,个人认为这个demo还是有一定的参考价值,不光是微前端,还包含了一些前端工程化的内容,大家有兴趣的话可以clone下来参考,后续如果有时间我也会继续维护和补充优化该demo。

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

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

相关文章

从MySQL到OceanBase离线数据迁移的实践

本文作者&#xff1a;玉璁&#xff0c;OceanBase 生态产品技术专家。工作十余年&#xff0c;一直在基础架构与中间件领域从事研发工作。现负责OceanBase离线导数产品工具的研发工作&#xff0c;致力于为 OceanBase 建设一套完善的生态工具体系。 背景介绍 在互联网与云数据库技…

LEAP 瞬移工具场景试点游戏关卡

你是否厌倦了在Unity编辑器中浪费时间浏览大型游戏关卡&#xff1f;不要看得比Leap更远&#xff01;这个功能强大的编辑器脚本允许您只需单击一下即可即时传输到场景中的任何位置。告别繁琐的手动导航&#xff0c;迎接闪电般快速的关卡设计。有了Leap&#xff0c;你就可以专注于…

Gin框架官方文档详解04:HTTP/2 推送,JSON相关

官方文档&#xff1a;https://gin-gonic.com/zh-cn/docs/ 注&#xff1a;强烈建议没用过Gin的读者先阅读第一节&#xff1a;第一个Gin应用。 目录 一、HTTP/2 推送二、JSONP三、PureJSON四、SecureJSON五、总结 一、HTTP/2 推送 首先&#xff0c;以“04HTTP2server推送”为根目…

linux 时区问题

一、修改系统时间和时区 查看当前下系统时间和时区 timedatectl设置系统时区 ​sudo timedatectl set-timezone <时区>​&#xff0c;例如&#xff1a; sudo timedatectl set-timezone Asia/Shanghai执行成功则没有输出。 更推荐使用 tzselect​ ​命令&#xff0c;…

通信工程学习:什么是VHDL超高速集成电路硬件描述语言

VHDL&#xff1a;超高速集成电路硬件描述语言 VHDL&#xff0c;全称为Very-High-Speed Integrated Circuit Hardware Description Language&#xff0c;即超高速集成电路硬件描述语言&#xff0c;是一种用于电路设计的高级语言。以下是关于VHDL的详细介绍&#xff1a; 一、起源…

ThingsBoard规则链节点:Split Array Msg节点详解

引言 拆分数组消息节点简介 用法 含义 应用场景 实际项目运用示例 智能仓储管理系统 智能电网监控系统 车联网平台 结论 引言 ThingsBoard是一个功能强大的物联网平台&#xff0c;它提供了设备管理、数据收集与处理以及实时监控等核心功能。其规则引擎允许用户定义复杂…

时序图分析(IIC通信为例)

一、时序图分析&#xff08;IIC通信为例&#xff09; 时序图-->编程 解析&#xff1a;时序概念&#xff1a;一般指可编程器件的编程方法&#xff0c;在单片机编程时&#xff0c;需要根据被控芯片的时序去写程序&#xff0c;把芯片上的时序用代码来实现&#xff0c;方可实…

数据结构4——栈

1. 栈的概念及结构 栈的概念&#xff1a; 栈是一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last In First Out&#xff09;的原则…

<Linux> 线程池

一、线程池 1. 池化技术 池化技术是一种在计算机科学中广泛应用的优化技术&#xff0c;它的核心思想是&#xff1a;预先创建并维护一组资源&#xff08;例如线程、连接、对象&#xff09;&#xff0c;供多个任务共享使用&#xff0c;以减少创建和销毁资源的开销&#xff0c;提…

贪吃蛇游戏(代码篇)

我们并不是为了满足别人的期待而活着。 前言 这是我自己做的第五个小项目---贪吃蛇游戏&#xff08;代码篇&#xff09;。后期我会继续制作其他小项目并开源至博客上。 上一小项目是贪吃蛇游戏&#xff08;必备知识篇&#xff09;&#xff0c;没看过的同学可以去看看&#xf…

使用Java API访问Apache Kafka

简介 Kafka是由Apache软件基金会开发的一个开源流处理平台,Kafka是一个功能强大且灵活的平台。 基本情况 软件名称:Apache Kafka 软件平台:跨平台 软件语言:Scala、Java 开发商:Apache软件基金会 软件授权:Apache License 2.0 最近更新时间:2024年7月23日 核心概念 -…

Ubuntu:用户不在sudoers文件中

1、问题 执行sudo xxx命令时&#xff0c;显示&#xff1a; user 不在sudoers文件中 需要查看系统版本进入恢复模式修复。 2、重启进入恢复模式 查看系统命令&#xff1a;uname -r 可能显示为&#xff1a;6.8.0-45-generic 重启Ubuntu系统&#xff0c;在开机时按ESC进入模…

oracle归档日志爆满问题处理

最近客户单位的oracle数据库出了问题&#xff0c;经常出现无法连接,报错提示 ORA-00257: archiver error, Connect internal only, until freed.&#xff0c;手动清除归档日志后可以恢复访问&#xff0c;但是过不了几天依旧会爆满&#xff0c;每日生成的归档日志很大。经过详细…

内部排序算法小结

练习目标&#xff1a; 1、实现 直接插入排序、冒泡排序、SHELL排序和快速排序&#xff1b; 2、随机生成100组数据&#xff0c;每组数据1000个元素。 注意&#xff1a;计时的单位是CPU的clock而非时间&#xff01;&#xff01;&#xff01; 【后续】 1、加入选择排序&#xff1b…

读书笔记《PPT演讲力》大树模型

作者把PPT演讲比作一棵大树&#xff0c;树的每一部分对应着PPT演讲的一个技巧。 根据这个大树模型&#xff0c;是否有联想到自己过往的演讲经历&#xff1f;演讲是否都达到了大树模型中说的效果&#xff1f;根据这个思维导图&#xff0c;结合自己的经历&#xff0c;试着总结3句…

云计算第四阶段-----CLOUND二周目 04-06

cloud 04 今日目标&#xff1a; 一、Pod 生命周期 图解&#xff1a; [rootmaster ~]# vim web1.yaml --- kind: Pod apiVersion: v1 metadata:name: web1 spec:initContainers: # 定义初始化任务- name: task1 # 如果初始化任务失败&#…

数字化与数智化,你知道它们的区别吗?

​其实老早就想说这个。中间一直在忙忙忙&#xff0c;还有处理自己的事情&#xff0c;导致拖更了。 最近听说一个物流大佬现在也转行做数字化厂家负责人&#xff0c;顺便给我讲解了这二者的区别&#xff0c;这里我就重新梳理了一下&#xff0c;加上了我自己的一些观点&#xf…

qt creator 开发环境的安装

1.找官网 官网地址&#xff1a;Installation | Qt Creator Documentation 点 Parent Directory 继续点 Parent Directory 点 archive/ 2.下载在线安装器 点 online_ainstallers 选择在线安装器版本 选择对应版本后进入下载列表&#xff0c;根据自己的系统选择下载。 下载后…

常用类(四)---String结构剖析

文章目录 1.String结构剖析2.String创建剖析3.String特性分析4.String方法总结5.StringBuffer和StringBuilder总结5.1stringbuffer对象的创建方法5.2string--stringbuffer之间的相互转换&#xff1a; 这个是我第二次学习string&#xff0c;听的是hsp老师的课程&#xff0c;我认…

IRP读写函数

驱动代码&#xff1a; #include <ntddk.h>#define DEVICE_NAME L"\\device\\MyDricer1" //设备对象名称 #define LINK_NAME L"\\dosdevices\\Goose" //符号链接名称VOID UnDirver(PDRIVER_OBJECT pDriverObj) {UNICODE_STRING uLinkName RTL_CONST…