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 单页面应用,因此当然可以拥有独立代码仓库进行关联,可独立发布运行,也可作为子应用运行,只需要做好不同环境的兼容即可
  • 增量升级
  • 子应用能够独立开发部署,自然支持自身应用的功能的独立扩展,又或者是接入新的子应用
  • 独立运行时
  • 保证多个子应用在基座应用中运行时,自身的状态不受其他子应用的影响

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

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


目录
相关文章
|
1月前
|
移动开发 缓存 前端开发
深入理解前端路由:原理、实现与应用
本书《深入理解前端路由:原理、实现与应用》全面解析了前端路由的核心概念、工作原理及其实现方法,结合实际案例探讨了其在现代Web应用中的广泛应用,适合前端开发者和相关技术人员阅读。
|
2月前
|
前端开发 项目管理
Gitflow分支策略及其在前端工程化中的应用
Gitflow 分支策略也并非适用于所有项目。对于一些小型或简单的前端项目,可能会显得过于复杂。在实际应用中,需要根据项目的具体情况和团队的需求进行适当调整和优化。
|
2月前
|
自然语言处理 前端开发 JavaScript
深入理解前端中的 “this” 指针:从基础概念到复杂应用
本文全面解析前端开发中“this”指针的运用,从基本概念入手,逐步探讨其在不同场景下的表现与应用技巧,帮助开发者深入理解并灵活掌握“this”的使用。
|
2月前
|
存储 前端开发 JavaScript
前端中对象的深度应用与最佳实践
前端对象应用涉及在网页开发中使用JavaScript等技术创建和操作对象,以实现动态交互效果。通过定义属性和方法,对象可以封装数据和功能,提升代码的组织性和复用性,是现代Web开发的核心技术之一。
|
2月前
|
前端开发
结合具体案例分析Gitflow分支策略在大型前端项目中的应用优势
通过这个具体案例可以看出,Gitflow 分支策略在大型前端项目中能够提供有条不紊的开发环境,保障项目的稳定性和持续发展。
|
2月前
|
缓存 JavaScript 前端开发
JavaScript 与 DOM 交互的基础及进阶技巧,涵盖 DOM 获取、修改、创建、删除元素的方法,事件处理,性能优化及与其他前端技术的结合,助你构建动态交互的网页应用
本文深入讲解了 JavaScript 与 DOM 交互的基础及进阶技巧,涵盖 DOM 获取、修改、创建、删除元素的方法,事件处理,性能优化及与其他前端技术的结合,助你构建动态交互的网页应用。
53 5
|
2月前
|
前端开发 开发者
本文将深入探讨 BEM 的概念、原理以及其在前端开发中的应用
BEM(Block-Element-Modifier)是一种前端开发中的命名规范和架构方法,旨在提高代码的可维护性和复用性。通过将界面拆分为独立的模块,BEM 提供了一套清晰的命名规则,增强了代码的结构化和模块化设计,促进了团队协作。本文深入探讨了 BEM 的概念、原理及其在前端开发中的应用,分析了其优势与局限性,为开发者提供了宝贵的参考。
57 8
|
2月前
|
JavaScript 前端开发 测试技术
构建高效可维护的前端应用
构建高效可维护的前端应用
|
2月前
|
编解码 监控 JavaScript
打造高效前端应用
打造高效前端应用
37 1
|
2月前
|
前端开发 JavaScript 开发者
使用React和Redux构建高效的前端应用
使用React和Redux构建高效的前端应用
42 1