微前端x重构实践落地总结(上)

简介: 大家好,我是海怪。最近换到了新部门,在做智能平台相关的内容。我接到的第一个任务就是把以前前端的项目重构一次。 说是重构,不如说是重写一遍。因为原来的项目是 ant-design-vue + vue 全家桶,要切换成 ant-design + ant-design-pro + react 全家桶。 更让人头疼的是,产品经理并不会让我们有大把大把时间专门搞重构,我们要边重构边做需求。在这样的挑战下,我想到了微前端解决方案,下面就跟大家分享这次 微前端在重构上的落地实践吧。

image.png

前言


大家好,我是海怪。最近换到了新部门,在做智能平台相关的内容。我接到的第一个任务就是把以前前端的项目重构一次。


说是重构,不如说是重写一遍。因为原来的项目是 ant-design-vue + vue 全家桶,要切换成 ant-design + ant-design-pro + react 全家桶


更让人头疼的是,产品经理并不会让我们有大把大把时间专门搞重构,我们要边重构边做需求。在这样的挑战下,我想到了微前端解决方案,下面就跟大家分享这次 微前端在重构上的落地实践吧


这次实践我简化了一下,放在 Github 上,大家可以自行 clone 来玩玩。


技术栈


首先,来讲讲技术栈,老项目主要用了下面的技术:


  • 框架
  • Vue
  • vuex
  • vue-router
  • 样式
  • scss
  • UI
  • ant-design-vue
  • ant-design-pro for vue
  • 脚手架
  • vue-cli


image.png

新项目需要用到的技术有:


  • 框架
  • React
  • redux + redux-toolkit
  • react-router
  • 新式
  • less
  • UI
  • react-design-react
  • react-design-pro for react
  • 脚手架
  • 团队内部自创脚手架

image.png

可以看到两个项目除了业务之外,几乎没什么交集了。


微前端策略


image.png老项目作为主应用,通过 qiankun 去加载新项目(子应用)里的页面。


  • 当没有需求时,在新项目(子应用)重写页面,重写完了之后,在老项目(主应用)中加载新项目的页面,下掉老项目的页面
  • 当有需求时,也是在新项目(子应用)重写面面再做对应需求(向产品要多点时间),重写完了之后,在老项目(主应用)中加载新项目的页面


这样一来就可以避免 “我要一整个月都做重构” 的局面,而是可以做到一个页面一个页地慢慢迁移。最终等所有页面都在新项目写好之后,直接把老项目下掉,新项目就可以从幕后站出来了。相当于从重写的第一天开始,老项目就成替身了。

如果只看上面画的架构图,会觉得:啊,不就引入一个 qiankun 就完事了么?实际上还有很细节和问题需要注意的。


升级版架构


上图的架构有一个问题就是,当每次点击侧边栏的 MenuItem 时,都会加载一次微应用的子页面,也即:

image.png

微应用子页面之间的切换,其实就是在微应用里路由切换嘛,大可不需要通过重新加载一次微应用来做微应用子页面的切换。


所以,我想了一个办法:我在 旁边放了一个组件 Container。进入主应用后,这个组件先直接把微应用整个都加载了。

<a-layout>
  <!--  页面    -->
  <a-layout-content>
    <!--   子应用容器     -->
    <micro-app-container></micro-app-container>
    <!--   主应用路由     -->
    <router-view/>
  </a-layout-content>
</a-layout>
复制代码


当展示老页面时,把这个 Container 高度设为 0,要展示新页面时,再把 Container 高度自动撑开。

// micro-app-container
<template>
<div class="container" :style="{ height: visible ? '100%' : 0 }">
  <div id="micro-app-container"></div>
</div>
</template>
<script>
import { registerMicroApps, start } from 'qiankun'
export default {
  name: "Container",
  props: {
    visible: {
      type: Boolean,
      defaultValue: false,
    }
  },
  mounted() {
    registerMicroApps([
      {
        name: 'microReactApp',
        entry: '//localhost:3000',
        container: '#micro-app-container',
        activeRule: '/#/micro-react-app',
      },
    ])
    start()
  },
}
</script>
复制代码

这样一来,当进入老项目时,这个 Container 自动被 mounted 后就会地去加载子应用了。当在切换新页面时,本质上是在子应用里做路由切换,而不是从 A 应用切换到 B 应用了。

image.png


子应用的布局


由于新的项目(子应用)里的页面要供给老项目(主应用)来使用的,所以子应用也应该有两套布局:


第一套标准的管理后台布局,有 SiderHeader 还有 Content,另一套侧作为子应用时,只展示 Content 部分的布局。

// 单独运行时的布局
export const StandaloneLayout: FC = () => {
  return (
    <AntLayout className={styles.layout}>
      <Sider/>
      <AntLayout>
        <Header />
        <Content />
      </AntLayout>
    </AntLayout>
  )
}
// 作为子应用时的布局
export const MicroAppLayout = () => {
  return (
    <Content />
  )
}
复制代码


image.png

image.png


最后通过 window.__POWERED_BY_QIANKUN__ 就可以切换不同的布局了。

import { StandaloneLayout, MicroAppLayout } from "./components/Layout";
const Layout = window.__POWERED_BY_QIANKUN__ ? MicroAppLayout : StandaloneLayout;
function App() {
  return (
    <Layout/>
  );
}
复制代码


样式冲突


qiankun 是默认开启 JS 隔离(沙箱),关闭 CSS 样式隔离的。为什么这么做呢?因为 CSS 的隔离是不能无脑做去做的,下面来讲讲这方面的问题。


qiankun 一共提供了两种 CSS 隔离方法(沙箱):严格沙箱 以及 实验性沙箱


严格沙箱

开启代码:

start({
  sandbox: {
    strictStyleIsolation: true,
  }
})
复制代码

严格沙箱主要通过 ShadowDOM 来实现 CSS 样式隔离,效果是当子应用被挂在到 ShadowDOM 上,主子应用的样式 完完全全 地被隔离,无法互相影响。你说:这不是很好么?No No No。


这种沙箱的优点也成为了它自己的缺点:除了样式的硬隔离,DOM 元素也直接硬隔离了,导致子应用的一些 ModalPopoverDrawer 组件会因为找不到主应用的 body 而丢失,甚至跑到整个屏幕之外。


还记得我刚说主应用和子应用都用了 ant-design 么?ant-design 的 ModalPopoverDrawer 的实现方式就是要挂在到 document.body 上的,这么一隔离,它们一挂在整个元素起飞了。


实验性沙箱


开启代码:

start({
  sandbox: {
    experimentalStyleIsolation: true,
  }
})
复制代码

这种沙箱实现方式就是给子应用的样式加后缀标签,有点像 Vue 里的 scoped,通过名字来做样式 “软隔离”,比如像这样:


image.png


其实这种方式已经很好地做了样式隔离,但是主应用里经常有人喜欢写 !important 来覆盖 ant-design 的组件原样式:

.ant-xxx {
   color: white: !important;
}
复制代码

!importnant 的优先级是最高的,如果微应用也用了这个 .ant-xxx 类,就很容易被主应用的样式影响了。所以在加载微应用时,还需要处理 ant-design 之间的样式冲突问题。


ant-design 样式冲突

ant-design 提供了一个非常好的类名前缀功能:用 prefixCls 来做样式隔离,我自然也用上了:

// 自定义前缀
const prefixCls = 'cmsAnt';
// 设置 Modal、Message、Notification rootPrefixCls
ConfigProvider.config({
  prefixCls,
})
// 渲染
function render(props: any) {
  const { container, state, commit, dispatch } = props;
  const value = { state, commit, dispatch };
  const root = (
    <ConfigProvider prefixCls={prefixCls}>
      <HashRouter basename={basename}>
        <MicroAppContext.Provider value={value}>
          <App />
        </MicroAppContext.Provider>
      </HashRouter>
    </ConfigProvider>
  );
  ReactDOM.render(root, container
    ? container.querySelector('#root')
    : document.querySelector('#root'));
}
复制代码
@ant-prefix: cmsAnt; // 引入来改变全局变量值
复制代码

但是不知道为什么,在 less 文件中改了 ant-prefix 变量后,ant-design-pro 的样式还是老样子,有的组件样式改变了,有的没变化。


最后,我是通过 less-loadermodifyVars 在打包时来更新全局的 ant-prefix less 变量才搞定的:

var webpackConfig = {
  test: /.(less)$/,
  use: [
    ...
    {
      loader: 'less-loader',
      options: {
        lessOptions: {
          modifyVars: {
            'ant-prefix': 'cmsAnt'
          },
          sourceMap: true,
          javascriptEnabled: true,
        }
      }
    }
  ]
}
复制代码

具体 Issue 看 Issue: ant-design 改了 prefixCls 后 ant-design-pro 不生效

相关文章
|
21天前
|
资源调度 前端开发 测试技术
前端工程化实践:从零搭建现代化项目构建流程
【4月更文挑战第6天】本文介绍了前端工程化的概念和重要性,包括模块化、自动化、规范化和CI/CD。接着,讨论了选择合适的工具链,如包管理器、构建工具和测试框架。然后,详细阐述了如何从零开始搭建一个基于React的现代化项目构建流程,涉及初始化、代码规范、测试、CSS处理、代码分割和CI/CD配置。最后,提到了持续优化与迭代的方向,如性能优化、类型检查和微前端。通过这样的实践,开发者可以提升开发效率和代码质量,为项目长远发展奠定基础。
27 0
|
21天前
|
前端开发 数据可视化 JavaScript
探索前端可视化开发:低代码平台原理与实践
【4月更文挑战第7天】本文探讨了低代码平台在前端开发中的应用,介绍了其模型驱动、组件化和自动化部署的原理,强调了提升效率、降低技术门槛、灵活适应变更和保证一致性等优势。建议开发者明确适用场景,选择合适平台,并培养团队低代码技能,同时规划与现有技术栈的融合,实施持续优化治理。低代码平台正改变开发格局,为业务创新和数字化转型提供新途径。
46 0
|
1天前
|
前端开发 JavaScript 开发者
前端技术栈:探索现代Web开发的核心要素与代码实践
前端技术栈:探索现代Web开发的核心要素与代码实践
7 1
|
1天前
|
前端开发 JavaScript 开发工具
前端技术栈:构建现代Web应用的基石与实践
前端技术栈:构建现代Web应用的基石与实践
9 3
|
3天前
|
前端开发 JavaScript 开发者
【专栏】前端工程化实践与构建工具比较:Webpack、Rollup等
【4月更文挑战第27天】本文探讨了前端工程化的重要性,强调构建工具在其中的角色,如Webpack和Rollup。Webpack以其灵活性和插件系统适用于复杂SPA项目,建议开发者理解其核心概念并优化性能。Rollup则专注于JavaScript模块打包,生成更小、更快的代码,适合小型至中型项目和库创建,以其Tree-shaking技术减小代码体积。开发者应根据项目需求、团队技术和生态选择合适工具,掌握核心原理以提升开发效率和质量。
|
18天前
|
敏捷开发 前端开发 JavaScript
实践总结|前端架构设计的一点考究(上)
实践总结|前端架构设计的一点考究(上)
37 0
|
18天前
|
SQL 前端开发 JavaScript
实践总结|前端架构设计的一点考究(中)
实践总结|前端架构设计的一点考究(中)
34 0
|
18天前
|
设计模式 开发框架 前端开发
实践总结|前端架构设计的一点考究(下)
实践总结|前端架构设计的一点考究(下)
43 0
|
18天前
|
监控 前端开发 小程序
微信小程序全栈开发中的前端工程化实践
【4月更文挑战第12天】本文探讨了微信小程序全栈开发中的前端工程化实践,旨在提升开发效率和体验。重点包括代码规范与架构设计(模块化、组件化、MVC模式)、自动化构建与部署(使用Webpack、Git和CI工具)、前端框架与库(如Vue.js、React、Angular)以及性能优化策略(代码、资源优化和性能监控)。通过这些实践,开发者能更高效地掌握小程序开发,打造高质量应用。
|
21天前
|
JavaScript 前端开发 IDE
TypeScript在大型前端项目中的价值与实践策略
【4月更文挑战第7天】本文探讨了TypeScript在大型前端项目中的价值和实践策略。 TypeScript通过静态类型检查、代码提示、接口与泛型提高代码质量和开发效率。在大型项目中,可采用逐步迁移策略,制定类型规范,利用IDE特性,并维护类型定义文件。通过CI/CD和培训分享,团队能充分发挥TypeScript优势,提升项目可维护性、可扩展性和开发效率。
18 0