前端微前端:别让你的应用变成巨石应用

前端微前端:别让你的应用变成巨石应用

毒舌时刻

这应用做得跟巨石似的,想改个功能都得动全身。

各位前端同行,咱们今天聊聊前端微前端。别告诉我你还在维护一个巨大的单体应用,那感觉就像在没有分区的大房子里生活——能住,但乱得要命。

为什么你需要微前端

最近看到一个项目,代码量超过 100 万行,构建时间超过 10 分钟,团队协作困难。我就想问:你是在做应用还是在做代码仓库?

反面教材

// 反面教材:单体应用 // App.jsx import React from 'react'; import Header from './components/Header'; import Sidebar from './components/Sidebar'; import Dashboard from './components/Dashboard'; import Users from './components/Users'; import Products from './components/Products'; import Orders from './components/Orders'; import Settings from './components/Settings'; function App() { return ( <div> <Header /> <div> <Sidebar /> <main> <Dashboard /> <Users /> <Products /> <Orders /> <Settings /> </main> </div> </div> ); } export default App; 

毒舌点评:这代码,就像把所有东西都塞进一个大袋子里,管它有用没用。

正确姿势

1. Module Federation

// 正确姿势:Module Federation // 1. 安装依赖 // npm install webpack@5 // 2. 配置 webpack // webpack.config.js const { ModuleFederationPlugin } = require('webpack').container; module.exports = { plugins: [ new ModuleFederationPlugin({ name: 'host', remotes: { dashboard: 'dashboard@http://localhost:3001/remoteEntry.js', users: 'users@http://localhost:3002/remoteEntry.js', products: 'products@http://localhost:3003/remoteEntry.js', orders: 'orders@http://localhost:3004/remoteEntry.js', settings: 'settings@http://localhost:3005/remoteEntry.js', }, shared: { react: { singleton: true, requiredVersion: '^18.2.0', }, 'react-dom': { singleton: true, requiredVersion: '^18.2.0', }, }, }), ], }; // 3. 远程应用配置 // dashboard/webpack.config.js const { ModuleFederationPlugin } = require('webpack').container; module.exports = { plugins: [ new ModuleFederationPlugin({ name: 'dashboard', filename: 'remoteEntry.js', exposes: { './Dashboard': './src/Dashboard', }, shared: { react: { singleton: true, requiredVersion: '^18.2.0', }, 'react-dom': { singleton: true, requiredVersion: '^18.2.0', }, }, }), ], }; // 4. 主机应用 // App.jsx import React, { lazy, Suspense } from 'react'; import Header from './components/Header'; import Sidebar from './components/Sidebar'; const Dashboard = lazy(() => import('dashboard/Dashboard')); const Users = lazy(() => import('users/Users')); const Products = lazy(() => import('products/Products')); const Orders = lazy(() => import('orders/Orders')); const Settings = lazy(() => import('settings/Settings')); function App() { return ( <div> <Header /> <div> <Sidebar /> <main> <Suspense fallback={<div>加载中...</div>}> <Dashboard /> <Users /> <Products /> <Orders /> <Settings /> </Suspense> </main> </div> </div> ); } export default App; 

2. Single-SPA

// 正确姿势:Single-SPA // 1. 安装依赖 // npm install single-spa single-spa-react // 2. 注册应用 // src/registerApplications.js import { registerApplication, start } from 'single-spa'; registerApplication({ name: '@my-org/dashboard', app: () => import('@my-org/dashboard'), activeWhen: (location) => location.pathname.startsWith('/dashboard'), }); registerApplication({ name: '@my-org/users', app: () => import('@my-org/users'), activeWhen: (location) => location.pathname.startsWith('/users'), }); registerApplication({ name: '@my-org/products', app: () => import('@my-org/products'), activeWhen: (location) => location.pathname.startsWith('/products'), }); registerApplication({ name: '@my-org/orders', app: () => import('@my-org/orders'), activeWhen: (location) => location.pathname.startsWith('/orders'), }); registerApplication({ name: '@my-org/settings', app: () => import('@my-org/settings'), activeWhen: (location) => location.pathname.startsWith('/settings'), }); start(); // 3. 微应用配置 // packages/dashboard/src/index.js import React from 'react'; import ReactDOM from 'react-dom'; import singleSpaReact from 'single-spa-react'; import Dashboard from './Dashboard'; const lifecycles = singleSpaReact({ React, ReactDOM, rootComponent: Dashboard, errorBoundary(err, info, props) { return <div>应用出错了</div>; }, }); export const bootstrap = lifecycles.bootstrap; export const mount = lifecycles.mount; export const unmount = lifecycles.unmount; 

3. Qiankun

// 正确姿势:Qiankun // 1. 安装依赖 // npm install qiankun // 2. 注册应用 // src/main.js import { registerMicroApps, start } from 'qiankun'; registerMicroApps([ { name: 'dashboard', entry: '//localhost:3001', container: '#micro-app-container', activeRule: '/dashboard', }, { name: 'users', entry: '//localhost:3002', container: '#micro-app-container', activeRule: '/users', }, { name: 'products', entry: '//localhost:3003', container: '#micro-app-container', activeRule: '/products', }, { name: 'orders', entry: '//localhost:3004', container: '#micro-app-container', activeRule: '/orders', }, { name: 'settings', entry: '//localhost:3005', container: '#micro-app-container', activeRule: '/settings', }, ]); start(); // 3. 主应用 // src/App.jsx import React from 'react'; import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom'; function App() { return ( <Router> <div> <nav> <Link to="/dashboard">仪表盘</Link> <Link to="/users">用户</Link> <Link to="/products">产品</Link> <Link to="/orders">订单</Link> <Link to="/settings">设置</Link> </nav> <div></div> </div> </Router> ); } export default App; // 4. 微应用配置 // packages/dashboard/src/main.js import { createApp } from 'vue'; import App from './App.vue'; import router from './router'; let instance = null; function render(props) { const { container } = props; instance = createApp(App); instance.use(router); instance.mount(container ? container.querySelector('#app') : '#app'); } if (!window.__POWERED_BY_QIANKUN__) { render({}); } export async function bootstrap() { console.log('dashboard bootstraped'); } export async function mount(props) { console.log('dashboard mounted', props); render(props); } export async function unmount() { console.log('dashboard unmounted'); instance.unmount(); instance = null; } 

毒舌点评:这才叫前端微前端,将应用拆分成多个独立的微应用,团队协作更高效,构建时间更短,再也不用担心巨石应用的问题了。

Read more

【论文阅读12】Circle Loss:一统 Softmax 与 Triplet,从“线性”到“圆形”的优化视角

论文题目:《Circle Loss: A Unified Perspective of Pair Similarity Optimization》 目录 前言:两大门派的恩怨 派系一:基于分类的学习 (Classification-based) 派系二:基于度量的学习 (Metric Learning / Pairwise Learning) 为什么要提这个?(Circle Loss 的动机) 1. 万物归一 —— 统一视角 () 1.1 统一 Loss 公式 1.2 计算量的“降维打击” 2. 证明 Softmax 和 Triplet 都是“特例” 2.1 退化为 Softmax () 2.

【 AR眼镜】核心技术详解:硬件架构、核心算法、应用场景与发展趋势

【 AR眼镜】核心技术详解:硬件架构、核心算法、应用场景与发展趋势

文章目录 * 目录 * 引言 * 一、AI眼镜核心硬件架构 * 二、AI眼镜核心技术栈(软件+算法) * 2.1 环境感知技术(核心:计算机视觉) * 2.2 AI计算技术(核心:边缘智能) * 2.3 人机交互技术(核心:自然交互) * 三、AI眼镜软件生态与应用场景 * 3.1 软件生态架构 * 3.2 核心应用场景(行业+消费) * 四、AI眼镜关键技术挑战与解决方案 * 五、AI眼镜未来发展趋势 * 5.1 技术趋势 * 5.2 行业趋势 * 六、总结 目录 引言 若对您有帮助的话,请点赞收藏加关注哦,

开源机器人新体验:5步构建你的Open Duck Mini v2智能伙伴

开源机器人新体验:5步构建你的Open Duck Mini v2智能伙伴 【免费下载链接】Open_Duck_MiniMaking a mini version of the BDX droid. https://discord.gg/UtJZsgfQGe 项目地址: https://gitcode.com/gh_mirrors/op/Open_Duck_Mini 在机器人技术日益普及的今天,Open Duck Mini v2项目为初学者提供了一个完美的入门平台。这个42厘米高的迷你机器人不仅外形精致可爱,更拥有强大的运动能力和智能化控制系统,让每个人都能以不到400美元的成本拥有自己的机器人伙伴。 🚀 快速启动:从零到一的构建之旅 环境准备与项目获取 首先确保你的系统已安装Python环境,然后通过以下命令获取项目代码: git clone https://gitcode.com/gh_mirrors/

Meta Quest VR眼镜 开机无法自动重连WiFi的解决方法

Meta Quest VR眼镜 开机无法自动重连WiFi的解决方法

Meta Quest VR眼镜 开机无法自动重连WiFi的解决方法 关键词:Meta Quest 2 无法自动连接WiFi、Quest 3 WiFi受限、Quest 开机不自动重连、ADB 禁用网络检测、captive_portal_mode 设置、Quest 显示无互联网连接 最近在折腾 Meta Quest 2 / Quest 3 时,遇到一个非常典型的问题: 明明 WiFi 密码正确,信号也正常,但每次开机都不会自动重连,甚至显示“受限网络”或“无互联网连接”。 这个问题在国内网络环境下非常普遍,并不是设备损坏,而是系统机制导致。 本文从底层原理讲清楚,并给出稳定可用的解决方案。 一、问题根源分析 Meta Quest 系列基于 Android