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

简介: 2022 你还不会微前端吗 (上) — 从巨石应用到微应用

路由配置

路由模式为 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 目录

image.png

  • 这里只需要关注 config 目录即可,因为 webpack.config.js 在该目录下
  • image.png
  • 为其中的 output 配置添加上如下两行配置项即可(需重新启动)
output: {
    library: 'singleReact',
    libraryTarget: 'umd',
    globalObject: 'window',
    ....
 }
复制代码

创建基座应用

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

image.png

路由配置

为了让 基座应用 的路由看起来更美观,这里选择 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子应用

基座应用注册子应用

基座应用 中尚未注册 子应用 时的页面效果如下:

image.png

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

  • 通过 pnpm install single-spa -S 安装 single-spa
  • 通过 single-spa 中提供的 registerApplication()start() 函数完成注册和启动,该逻辑可抽离到 registerApplication.ts
// registerApplication.ts
import { 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.ts
import { registerMicroApps, start } from 'qiankun';
// 默认子应用
export const applications = [
    {
        name: 'singleVue3', // app name registered
        entry: 'http://localhost:5000',
        container: '#micro-content',
        activeRule: '/vue3-micro-app',
      },
      {
        name: 'singleReact', // app name registered
        entry: '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.ts
import { createApp } from 'vue'
import type { App as AppType } from 'vue'
import App from './App.vue'
import router from './router'
let instance: AppType
function 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.js
import React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import App from './App'
let root = null
function render(props = {}) {
  // 这里的 container 已经是对应基座应用中的真实 DOM 节点,而不是 CSS 选择器
  const container = props.container || document.getElementById('root')
  if(!container) return
  root = 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 发生跨域

image.png

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 配置

image.png

最后

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

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

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

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


目录
相关文章
|
15天前
|
前端开发 JavaScript 开发者
前端开发中的异步编程:Promise 和 Async/Await 的比较与应用
在现代前端开发中,异步编程是不可或缺的技术。本文将深入探讨Promise和Async/Await这两种主流的异步编程方式,分析它们的优劣势及在实际项目中的应用场景。通过比较它们的语法、可读性和错误处理机制,帮助开发者更好地选择和理解如何在项目中高效地利用这些技术。
|
1月前
|
缓存 前端开发 jenkins
Serverless 应用引擎产品使用合集之前端的项目部署在镜像里时,页面总是自动刷新,是什么导致的
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
|
15天前
|
开发框架 前端开发 JavaScript
现代前端开发中的跨平台解决方案比较与应用
在现代软件开发中,跨平台解决方案成为了开发者们的热门话题。本文将探讨几种主流的跨平台开发框架及其特点,重点比较它们在前端开发中的应用场景和优劣势。通过对比分析,读者可以更好地理解如何选择适合自己项目需求的跨平台解决方案。
|
25天前
|
人工智能 前端开发 搜索推荐
人工智能(AI)在前端设计中的创新应用
人工智能(AI)在前端设计中的创新应用
41 4
|
1月前
|
前端开发 持续交付 开发工具
详细介绍Git的基本原理、在前端开发中的应用以及如何使用Git来优化团队协作
【6月更文挑战第14天】Git是前端开发中的必备工具,它通过分布式版本控制管理代码历史,支持分支、合并和冲突解决,促进团队协作。在前端开发中,Git用于代码追踪、版本控制、代码审查和持续集成部署,优化团队协作。制定分支策略、编写清晰提交信息、定期合并清理分支以及使用Git钩子和自动化工具能进一步提升效率。理解并善用Git,能有效提升前端项目的质量和开发效率。
35 3
|
7天前
|
人工智能 Rust 前端开发
未来趋势下的前端开发:WebAssembly 的兴起与应用
随着互联网应用的复杂化和性能需求的提升,前端开发正迎来一个新的变革时代。本文探讨了WebAssembly(Wasm)作为一种新兴技术的崛起,以及它在前端开发中的潜力和应用前景。
|
1月前
|
前端开发 算法 JavaScript
优化算法在前端性能提升中的应用
随着互联网应用的日益复杂,前端性能优化成为开发者关注的焦点。本文探讨了优化算法在前端性能提升中的重要作用,包括对JavaScript代码的优化、资源加载的算法选择以及页面渲染的优化策略。通过合理应用优化算法,可以有效提升前端应用的性能和用户体验。
|
1月前
|
存储 前端开发 API
大型前端应用如何做系统融合?
【6月更文挑战第8天】在数字化时代,大型前端应用需与多系统融合以增强功能和体验。融合的关键步骤包括:清晰规划和设计,深入理解各系统,统一数据模型,设计稳定接口,确保安全性与稳定性,以及团队协作。通过这些方法,实现系统间的无缝衔接,提升服务质量和应用价值。示例代码展示了前端应用与外部系统数据交互。在不断变化的技术环境中,持续创新融合策略至关重要。
36 3
|
1月前
|
前端开发 JavaScript 云计算
未来趋势下的前端开发:探索WebAssembly的应用
在移动端和云计算时代的背景下,前端开发正逐渐走向更高效、更强大的WebAssembly技术。本文将深入探讨WebAssembly在前端开发中的应用和未来发展趋势,带您领略前端技术的新风貌。
|
27天前
|
人工智能 搜索推荐 前端开发
人工智能(AI)在前端设计中应用
人工智能(AI)在前端设计中应用
93 0