Vue3 + Vite:我的 Qiankun 微前端主子应用实践指南

article/2025/6/15 23:38:57

前言

实践文章指南

  1. vue微前端qiankun框架学习到项目实战,基座登录动态菜单及权限控制>>>>
  2. 实战指南:Vue 2基座 + Vue 3 + Vite + TypeScript微前端架构实现动态菜单与登录共享>>>>
  3. 构建安全的Vue前后端分离架构:利用长Token与短Token实现单点登录(SSO)策略>>>>
  4. 从零开始发布你的第一个npm插件包并在多项目中使用>>>>
  5. 基于vue2基座接入vue3+vite+ts+arcoDesign进行vue3子应用改造详细步骤真实项目实战保姆教学附带nginx配置>>>>
  6. Vue封装组件发布到npm私服保姆级教程【环境版本区分】>>>>

先不多说,看效果!

在这里插入图片描述

前面基本上都是讲解了vue2主应用+vue3子应用配置的微前端集成方法,下面讲解如何使用vue3+vite基座+vue3+vite子应用配置微前端集成方法。基于vue2,qiankun官网还没出vue3的集成方法:vue3版本稳定后再补充。
在这里插入图片描述

环境准备

  • node版本:v18.18.2
  • npm 版本:9.8.1
  • vue版本:vue3+vite
  • qiankun版本:2.10.16
  • UI框架版本:element-plus2.9.1

注意,如果配置vue3子应用,需要安装node版本18以上版本,也是必须的版本支持。

开始配置主应用

第一步,安装qiankun框架

 npm i qiankun -S

第二步,主应用在main.ts同目录下创建qiankun.js

// src/qiankun.ts
import { registerMicroApps, start } from 'qiankun'
export function registerQiankunApps() {let msg = { system: 'fmas' }registerMicroApps([{name: 'son-vue3',entry: '//192.168.80.32:8081/',container: '#subapp-container',activeRule: '/systemSon',props: msg,sandbox: { strictStyleIsolation: true }}],{beforeLoad: [app => console.log('before load', app)],beforeMount: [app => console.log('before mount', app)],afterUnmount: [app => console.log('after unload', app)]})start()
}

第三步、配置主应用承载子应用模块的载体盒子

 Vue3 + Vite:我的 Qiankun 微前端主子应用实践指南

由于我一开始在main.js中调用qiankun.js方法的时候拿不到id“subapp-container”dom元素就会找不到为null值,我的解决办法为,在需要承载渲染的文件下的onMounted生命周期的时候注册子应用开启qiankun
 Vue3 + Vite:我的 Qiankun 微前端主子应用实践指南

<template><!-- 右侧主区域 --><div id="subapp-container"></div><el-main class="layout-main"><el-scrollbar><div class="layout-main-warp"><router-view v-slot="{ Component }"><transition name="fade-transform" mode="out-in"><keep-alive :include="cacheRouteNames"><component :is="Component"></component></keep-alive></transition></router-view></div></el-scrollbar></el-main>
</template><script setup lang="ts" name="LayoutMain">
import { useViewRoutesStore } from "../../stores/viewRoutes";
import { computed, onMounted } from "vue";
import { registerQiankunApps } from "../../qiankun.js";
const viewRoutesStore = useViewRoutesStore();
// 获取要缓存的路由组件name
const cacheRouteNames = computed(() => viewRoutesStore.cacheRouteNames);
// 在 LayoutMain 加载完毕并插入 DOM 后再注册子应用
onMounted(() => {registerQiankunApps();
});
</script><style scoped lang="scss">
:deep(.el-scrollbar__view) {/* 铺满高度 */height: 100%;
}
</style>

第四步、子应用安装qiankun相应插件

vite-plugin-qiankun 是专门为 Vite 开发环境设计的插件,用于集成 qiankun 微前端库。qiankun 是一个基于 Web Components 的微前端实现库,可以让你将一个大型应用拆分成多个独立的小块,每个小块可以在不同的环境下独立开发、测试和部署。

npm install vite-plugin-qiankun --save-dev

 Vue3 + Vite:我的 Qiankun 微前端主子应用实践指南

第五步、在 子应用 src 目录新增 public-path.js

if (window.__POWERED_BY_QIANKUN__) {__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

第六步、子应用入口文件 main.ts 修改

在子应用开发初期,我直接在router/index中硬编码了根据 Qiankun 环境设置的 historybase 路径。然而,这种方法缺乏灵活性,难以适应不同的部署场景或满足父级应用的需求变化。因此,我决定不再将路由的基础地址硬编码到子应用中,而是通过接收主应用传递的 baseName 参数来动态创建路由实例。这种方式确保了子应用能够在不同上下文中正确运行,尤其是在子应用可能需要部署在不同路径的情况下尤为重要。
上述配置不仅支持子应用在独立模式(非 Qiankun 环境)和嵌入 Qiankun 模式之间的无缝切换,还大幅提高了应用的复用性和灵活性。这样做的好处是,子应用无需修改代码即可适应多种环境,仅需在主应用中调整相应的配置参数,就能实现对子应用的灵活部署和集成。这种设计使得子应用更加健壮,能够更好地应对未来的变化和需求。
为此,我决定在子应用为qiankun模式也就是嵌入模式的时候,我重写了子应用的路由。

import './public-path'
/* eslint-disable no-underscore-dangle */
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper';import App from './App.vue'
import router from './router'
import {defaultRoutes,fullscreenRoutes
} from './router';
// 整合ElementPlus
import ElementPlus from 'element-plus';
// @ts-ignore 汉化
import zhCn from 'element-plus/dist/locale/zh-cn.mjs';
import "@/styles/index.scss"
// 图标
import { useElIcon } from '@/utils/setGlobal';
import '@/router/permission';
// 自定义全局指令
import { directive } from '@/directive';
import { createRouter, createWebHistory } from 'vue-router';console.log("我背加载了");
// 第五版本
let app: any;
// 判断是否在qiankun环境下
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {app = createApp(App)//注册自定义全局指令 app.use(createPinia())app.use(router)app.use(ElementPlus, { locale: zhCn });// 全局注册图标`ele-`开头(样式在index.scss中)useElIcon(app);directive(app);app.mount('#app')
} else {console.log('qiankun模式')renderWithQiankun({// qiankun的生命周期,挂载mount(props) {// 创建子应用独立的路由配置const subAppRouter = createRouter({history: createWebHistory(props.baseName), // 使用父应用传递的 baseName 创建 historyroutes: [{path: '/',redirect: 'login',},{path: '/login',name: 'login',component: () => import('@/views/login/index.vue'),meta: {requiresAuth: false,},},...defaultRoutes, ...fullscreenRoutes], // 这里可以添加你的子应用路由配置});app = createApp(App)//注册自定义全局指令 app.use(createPinia())app.use(subAppRouter);app.use(ElementPlus, { locale: zhCn });// 全局注册图标`ele-`开头(样式在index.scss中)useElIcon(app);directive(app);// 传递的值可以获取到了app.mount(props.container? props.container.querySelector("#app"): document.getElementById("app"));},// 应用加载bootstrap() {console.log("--bootstrap");return Promise.resolve();},// 修改update(props) {console.log("--update");return Promise.resolve();},// 销毁unmount() {console.log("--unmount");app?.unmount();return Promise.resolve();},});
}

第七步、子应用打包配置

其中base要与主应用中的activeRule参数保持一致。设置CORS响应头,用来告诉浏览器允许哪些域请求资源。当设置为 * 时,表示允许任何域访问资源。这意味着,如果服务器设置了这个头部,它允许来自任何域名的请求访问其资源(尽管实际应用中可能会根据需求进行更严格的限制)。保证在 vite-plugin-qiankun 或构建插件中,设置 origin 的效果是:明确告诉构建系统“子应用的资源来自哪个主机和端口”;避免默认拼接 window.location.origin(即主应用的端口)导致资源 404;特别在 ?import 或 动态资源拼接 时效果明显。

const path = require("path");
import { fileURLToPath, URL } from 'node:url'
import qiankun from 'vite-plugin-qiankun'
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueSetupExtend from 'vite-plugin-vue-setup-extend-plus'
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper';
// 向 defineConfig 传递对象改为传递方法,并返回配置对象
export default defineConfig(({ mode }) => {// mode:获取 --mode 指定的模式,process.cwd()项目根目录,下面 `env` 相当于 `import.meta.env`const env = loadEnv(mode, process.cwd())return {base: qiankunWindow.__POWERED_BY_QIANKUN__ ? '/hsk-admin/son-vue3/' : '/',// 开发服务器选项server: {headers: {'Access-Control-Allow-Origin': '*',},port: 8080, // //端口号, 如果端口号被占用,会自动提升1open: true, //启动服务时自动打开浏览器访问host: '0.0.0.0',origin: 'http://localhost:8081',proxy: {// 匹配以env.VITE_APP_BASE_API开头的请求,交给代理服务器转换到目标接口[env.VITE_APP_BASE_API]: {// 代理后的目标地址target: env.VITE_APP_SERVICE_URL,// 开启代理,是否允许跨域changeOrigin: true,// /dev-api/xxx => xxx, 将 env.VITE_APP_BASE_AP 替换为 '',也就是 /dev-api 会移除rewrite: (path) => path.replace(/^\/dev-api/, '')},}},build: {target: 'esnext',outDir: 'dist',assetsDir: 'static',sourcemap: true,rollupOptions: {output: {format: 'umd',name: 'son-vue3',entryFileNames: `static/js/[name].js`,assetFileNames: `static/[ext]/[name].[ext]`,globals: {vue: 'Vue',},},},},plugins: [vue(),qiankun(`son-vue3`, {  // 子应用的name值useDevMode: true}),vueSetupExtend(), // // 让 `<script setup name="xx">` 上 name 作为缓存组件名],resolve: {alias: {'@': fileURLToPath(new URL('./src', import.meta.url))}}}
})

第八步、子应用嵌入模式下配置

子应用嵌入情况下,不显示多余的内容,只显示需要现实的内容,比如右侧的内容模块,我配置在嵌入模式下,不会显示子应用的菜单,header栏,登录等dom元素。

<template><el-container class="layout-container"><layoutAside v-if="!qiankunWindow.__POWERED_BY_QIANKUN__"></layoutAside><!-- 右侧垂直 --><el-container direction="vertical"><layoutHeader v-if="!qiankunWindow.__POWERED_BY_QIANKUN__"></layoutHeader><layoutMain></layoutMain></el-container></el-container>
</template><script setup lang="ts" name="Layout">
// 同步导入
// import LayoutMain from './layoutMain/index.vue'
// import LayoutHeader from './layoutHeader/index.vue'
// import LayoutAside from './layoutAside/index.vue'
// 异步导入import {renderWithQiankun,qiankunWindow,
} from "vite-plugin-qiankun/dist/helper";
import { defineAsyncComponent } from "vue";const LayoutAside = defineAsyncComponent(() => import("./layoutAside/index.vue")
);
const layoutMain = defineAsyncComponent(() => import("./layoutMain/index.vue"));
const LayoutHeader = defineAsyncComponent(() => import("./layoutHeader/index.vue")
);
</script><style></style>

第九步、配置主应用路由

我设置了主应用的路由为createWebHistory模式,子应用路由应与其保持一致,主应用的base为hsk-admin/ ,主应用配置子应用路由为/son-vue3开头或者/son-vue2等类型开头,区分不同子应用的路由,如下:


export const defaultRoutes: any[] = [{path: '/son-vue3/systemSon',name: 'systemSon',meta: {title: 'son',icon: 'ele-Setting',},children: [{path: '/son-vue3/systemSon/menu',name: 'systemSonMenu',meta: {title: '菜单管理',icon: 'ele-Menu',}},]}
];
export const fullscreenRoutes: RouteRecordRaw[] = [];
// 创建路由实例
export const router = createRouter({// 参数获取的是 vite.config.ts 中的 base 属性值history: createWebHistory('hsk-admin/'),routes: [...defaultRoutes, ...fullscreenRoutes],
});
export default router;

经过上面配置,我主营用访问/son-vue3/systemSon/menu会被拼接为hsk-admin//son-vue3/systemSon/menu,这样,qiankun根据路由判断与 activeRule: '/hsk-admin/son-vue3',一致,则通过entry: '//192.168.80.32:8081/',的连接访问子应用,并渲染到主应用中idsubapp-containerDOM元素中,并通过props将主营用的base传递给子应用,防止子应用判断hsk-admin/son-vue3的时候出现404的报错。
 Vue3 + Vite:我的 Qiankun 微前端主子应用实践指南

第十步、配置子应用路由

如果子应用为嵌套模式下启动时候,使用主应用传递的 props.baseName 来设置子应用的路由 base,又的人会问我为什么这么做,实践出真理,我得出以下好处:

  1. 动态配置主应用挂在路径,保证其灵活配置,在微前端中,子应用的挂在路径不是固定的,比如又是挂载在/subapp1/有时候挂载在/hsk-admin/son-vue3,通过 props.baseName 由主应用传入,你的子应用可以灵活适应各种路径结构,不依赖硬编码的路径。
  2. 保证子应用内部路由正确运行,刷新,跳转都能正常不会出现空白页面,如果不设置,刷新/hsk/admin/son-vue3/menu会导致子应用匹配不到路由,页面报错或者空白。

唉~,还是通过表格更直观吧!

编号好处说明
1✅ 避免路由刷新后 404页面刷新(F5)后仍能正确加载当前路由页面,不报错。
2✅ 子应用路径不和主应用冲突子应用只处理 props.baseName 下的路径,避免与主应用路由冲突。
3✅ 动态挂载路径适配性强子应用不固定在某个路径下,可以根据主应用挂载路径动态调整,无需硬编码。
4✅ 解决静态资源路径错误问题Vue Router 的 base 决定 <img>、CSS、JS 等资源的正确路径前缀。
5✅ 支持主应用多实例挂载子应用同一子应用可以在不同路径下被主应用复用(如 /admin/son/user/son)。
6✅ 子应用路由跳转逻辑保持清晰子应用内部跳转不会跳出自己的路由范围,不影响主应用 URL。
7✅ 可配合 vite 的 base 设置,资源访问一致保证构建时 vite.config.ts 的 base 与 router 保持一致。
8✅ SSR、预渲染也能受益更好的 URL 管理,SSR 或静态部署时不会出现路径错误。
9✅ 有利于部署到任意路径无论部署在二级目录还是子域名,只需主应用传入正确 base 即可运行。
10✅ 更好的团队协作子应用团队只需专注于自身逻辑,主应用控制挂载路径,提高协作效率。

子应用路由配置要和主应用对应,如下:

import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router';
import type { RouteRecordRaw } from 'vue-router';
/**
* 因为 Vue-Router 提供的配置路由对象的 meta 属性有限,所以须要扩展 RouteMeta 接口。
* 路由对象 meta 属性说明:
* meta: {
* title: 菜单标题
* icon: 菜单图标
* linkTo: 外链地址(新窗口打开)
* cache: 是否缓存:true缓存,false不缓存,会将 name 值用于 <keep-alive>的includes上
* hidden: 是否在菜单中显示:true显示,false隐藏
* isBreadcrumb: 是否显示到面包屑:默认或true会显示,false不显示。
* }
*/
declare module 'vue-router' {interface RouteMeta {title?: string;icon?: string;linkTo?: string;cache?: boolean;hidden?: boolean;isBreadcrumb?: boolean;}
}
/**
* 动态路由:后端请求路由配置数据后,赋值给下面路由数组的顶级对象的children属性(即 布局 Layout 对象的
children属性)
* @returns 动态路由配置数组
*/
export const dynamicRoutes: RouteRecordRaw[] = [];
/**
* 默认路由配置,所有用户都可访问的路由,不管前端控制还是后端控制路由权限,都要将下面添加到路由表
* (后端路由控制:后端配置菜单数据中不需要下面的菜单项)
* @returns 默认路由配置数组
*/
export const defaultRoutes: RouteRecordRaw[] = [{path: '/son-vue3/systemSon',name: 'systemSon',redirect: '/son-vue3/systemSon/menu',meta: {title: 'son',icon: 'ele-Setting',},children: [{path: '/son-vue3/systemSon/menu',name: 'systemSonMenu',component: () => import('@/views/system/menu/index.vue'),meta: {title: '菜单管理',icon: 'ele-Menu',}},]},
];
/**
* 全屏显示路由,不作用到 layout 布局渲染出口。
* (后端路由控制:后端配置菜单数据中不需要下面的菜单项)
*/
export const fullscreenRoutes: RouteRecordRaw[] = [];
// 创建路由实例
export const router = createRouter({// 参数获取的是 vite.config.ts 中的 base 属性值history: createWebHistory(import.meta.env.BASE_URL),// 默认添加 401、404 路由配置,有 404 可防止控制台一直提示 No match found for location with path  'xxx'routes: [...defaultRoutes, ...fullscreenRoutes],
});
export default router;

第十一步、查看效果

 Vue3 + Vite:我的 Qiankun 微前端主子应用实践指南
 Vue3 + Vite:我的 Qiankun 微前端主子应用实践指南
经过上面的十个步骤,也就配置好了vue3+vite的微前端框架了,后续可以自己写一些动态菜单,sso单点登录,权限控制等逻辑即可。

后续实现指南

  1. vue微前端qiankun框架学习到项目实战,基座登录动态菜单及权限控制>>>>
  2. 实战指南:Vue 2基座 + Vue 3 + Vite + TypeScript微前端架构实现动态菜单与登录共享>>>>
  3. 构建安全的Vue前后端分离架构:利用长Token与短Token实现单点登录(SSO)策略>>>>
  4. 从零开始发布你的第一个npm插件包并在多项目中使用>>>>
  5. 基于vue2基座接入vue3+vite+ts+arcoDesign进行vue3子应用改造详细步骤真实项目实战保姆教学附带nginx配置>>>>
  6. Vue封装组件发布到npm私服保姆级教程【环境版本区分】>>>>

要源码私信我即可~


http://www.hkcw.cn/article/psOcGrGUod.shtml

相关文章

CloudFront 加速详解:AWS CDN 怎么用?

让全球访问更快速稳定&#xff0c;深入解读 AWS 的内容分发网络 在上一篇中&#xff0c;我们介绍了 Amazon S3 对象存储&#xff0c;它非常适合托管静态资源&#xff0c;比如图片、视频、网页等。但你可能遇到过这样的问题&#xff1a; “我把网站静态文件部署到了 S3&#xf…

嵌入式SDK技术EasyRTC音视频实时通话助力即时通信社交/教育等多场景创新应用

一、引言​ 在数字化时代&#xff0c;即时通信已成为人们生活和工作中不可或缺的部分。音视频功能作为即时通信的核心&#xff0c;能实现更加直观、高效的信息传递。EasyRTC作为一款强大的实时通信框架&#xff0c;具备诸多优势&#xff0c;为即时通信的音视频应用提供了优质解…

Rust 学习笔记:关于 Cargo 的练习题

Rust 学习笔记&#xff1a;关于 Cargo 的练习题 Rust 学习笔记&#xff1a;关于 Cargo 的练习题问题一问题二问题三问题四问题五问题六问题七 Rust 学习笔记&#xff1a;关于 Cargo 的练习题 参考视频&#xff1a; https://www.bilibili.com/video/BV1xjAaeAEUzhttps://www.b…

【时时三省】(C语言基础)数组作为函数参数

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 调用有参函数时&#xff0c;需要提供实参。例如sin ( x )&#xff0c;sqrt ( 2&#xff0c;0 )&#xff0c;max ( a&#xff0c;b )等。实参可以是常量、变量或表达式。数组元素的作用与变量…

基于Android的一周穿搭APP的设计与实现 _springboot+vue

开发语言&#xff1a;Java框架&#xff1a;springboot AndroidJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat12开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;Maven3.6 系统展示 APP登录 A…

【开源工具】超全Emoji工具箱开发实战:Python+PyQt5打造跨平台表情管理神器

&#x1f31f; 超全Emoji工具箱开发实战&#xff1a;PythonPyQt5打造跨平台表情管理神器 &#x1f308; 个人主页&#xff1a;创客白泽 - CSDN博客 &#x1f525; 系列专栏&#xff1a;&#x1f40d;《Python开源项目实战》 &#x1f4a1; 热爱不止于代码&#xff0c;热情源自每…

当 “欧洲版 Cursor” 遇上安全危机

在 AI 编程助手蓬勃发展的当下&#xff0c;安全问题正成为行业不容忽视的隐忧。近期&#xff0c;AI 编程助手公司 Replit 与号称 “欧洲版 Cursor” 的 Lovable 之间&#xff0c;因安全漏洞问题掀起了一场风波&#xff0c;引发了业界的广泛关注。​ Replit 的员工 Matt Palmer…

Agentic Workflow是什么?Agentic Workflow会成为下一个AI风口吗?

无论是想要学习人工智能当做主业营收&#xff0c;还是像我一样作为开发工程师但依然要运用这个颠覆开发的时代宠儿&#xff0c;都有必要了解、学习一下人工智能。 近期发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;入行门槛低&#x…

遥感影像建筑物变化检测

文章目录 效果1、环境安装2、项目下载3、数据集下载4、模型训练5、模型推理6、推理结果7、批量推理效果 1、环境安装 参考文章 搭建Pytorch的GPU环境超详细 win10安装3DGS环境(GPU)超详细 测试GPU环境可用 2、项目下载 https://gitcode.com/gh_mirrors/ch/change_detectio…

vue+cesium示例:地形开挖(附源码下载)

基于cesium和vue绘制多边形实现地形开挖效果&#xff0c;适合学习Cesium与前端框架结合开发3D可视化项目。 demo源码运行环境以及配置 运行环境&#xff1a;依赖Node安装环境&#xff0c;demo本地Node版本:推荐v18。 运行工具&#xff1a;vscode或者其他工具。 配置方式&#x…

Ubuntu 系统部署 MySQL 入门篇

一、安装 MySQL 1.1 更新软件包 在终端中执行以下命令&#xff0c;更新系统软件包列表&#xff0c;确保安装的是最新版本的软件&#xff1a; sudo apt update 1.2 安装 MySQL 执行以下命令安装 MySQL 服务端&#xff1a; sudo apt install mysql-server 在安装过程中&…

机器学习——主成分分析PCA

主成分分析—PCA 一、主成分分析简介1. PCA概述2. 降维 & PCA3. PCA的优缺点 二、PCA的数学基础1. 方差2. 协方差矩阵3. 特征值和特征向量 三、PCA的算法流程1. 标准化数据2. 计算协方差矩阵3.计算特征值和特征向量4. 选择主成分5. 数据投影 四、代码分析&#xff08;人脸识…

连接关键点:使用 ES|QL 联接实现更丰富的可观测性洞察

作者&#xff1a;来自 Elastic Luca Wintergerst ES|QL 的 LOOKUP JOIN 现已进入技术预览阶段&#xff0c;它允许你在查询时对日志、指标和追踪进行丰富处理&#xff0c;无需在摄取时进行非规范化。动态添加部署、基础设施或业务上下文&#xff0c;减少存储占用&#xff0c;加速…

专家:乌军“蛛网”行动影响深远 无人机开启新战局

乌克兰在大规模使用无人机发动特种攻击方面再次震惊了世界。6月1日,乌克兰国家安全局对俄罗斯境内多地的军用机场发动大规模无人机攻击,声称摧毁了“多达41架战略轰炸机”。俄罗斯方面承认乌军无人机成功袭击的事实,但表示损失没有那么大。西方媒体认为,乌方使用的这种无人…

西北华北华南大片区域高温来袭 多地将迎35℃以上高温

今明两天(6月3日至4日),我国降雨主要出现在云南和华南沿海、东北地区等地,局地还可能伴有强对流天气。随着高压脊东移,北方大部气温逐渐升高,华北、黄淮等地高温天气将发展增多,南方多地5日起也将加入高温行列。昨天,冷空气南下导致南方强降雨区域南压至华南和云南一带…

美拆解首艘核动力航母要花多少钱 5亿多美元启动拆解

美国《星条旗报》网站报道,世界上第一艘核动力航母“企业”号将在亚拉巴马州拆解。佛蒙特州的一家公司获得了超过5亿美元的资金,用于拆除停放在亚拉巴马州莫比尔市的这艘历史悠久的航母。五角大楼发布的采购公告显示,位于佛蒙特州弗农的北极星海事拆卸服务有限责任公司获得了…

基于AI的6个学术搜索工具推荐:功能特点与适用场景分析

和传统的学术搜索平台pubmed、google scholar相比&#xff0c;AI驱动的学术搜索引擎不再依赖简单的关键词匹配&#xff0c;而是通过理解用户意图和语境&#xff0c;实现更精准、更具上下文关联的检索结果&#xff0c;并能够将搜索内容总结为完整答案。 除了Perplexity和秘塔AI搜…

scrapy练习笔记

scrapy练习 文章目录 scrapy练习一、scrapy安装二、scrapy基本流程2.1 学习目标2.2 创建一个新项目2.3 创建Item2.4 解析 Response2.5 使用 Item2.6 开始爬取 三、爬取多页3.1 练习3.2 输出格式3.3 使用 Item Pipeline 暂时到这 一、scrapy安装 官网&#xff1a;https://www.s…

cmake学习1

基本起点 本笔记的主要参考文献是cmake文档&#xff0c;对文档的二次提炼和补充学习。 1. cmake_minimum_required() 任何项目的最顶层CMakeLists.txt都必须首先使用 cmake_minimum_required() 命令指定最低 CMake 版本。这建立策略设置并确保以下 CMake 函数以兼容的 CMake…