基于 Vue 技术栈的微前端方案实践

2020/3/3 11:02:02

本文主要是介绍基于 Vue 技术栈的微前端方案实践,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

文章首发于我的博客 github.com/mcuking/blo…

项目地址:

preload-routes

async-routes

mobile-web-best-practice

前几天看到了 微前端在美团外卖的实践,感觉和笔者所在团队实践了一年多的微前端方案非常类似,只不过我们是基于 Vue 技术栈的,所以也想总结一篇文章分享给大家。因为笔者文笔不算太好,其中借用了一些美团文章的一些总结性的文字,还请见谅哦~

背景介绍

对于大型前端项目,比如公司内部管理系统(一般包括 OA、HR、CRM、会议预约等系统),如果将所有业务放在一个前端项目里,随着业务功能不断增加,就会导致如下这些问题:

  • 代码规模庞大,导致编译时间过长,开发、打包速度越来越慢

  • 项目文件越来越多,导致查找相关文件变得越来越困难

  • 某一个业务的小改动,导致整个项目的打包和部署

方案介绍

preload-routes 是目前笔者所在团队使用的微前端方案,会将整个前端项目拆解成主项目和子项目,其中两者作用如下:

  • 主项目:用于管理子项目的路由切换、注册子项目的路由和全局 Store 层、提供全局库和方法

  • 子项目:用于开发子业务线业务代码,一个子项目对应一个子业务线,并且包含两端(PC + Mobile)代码和复用层代码(即上面项目分层中的非视图层)

结合笔者之前的采用分层架构实现复用非视图代码的方式(感兴趣的话请参考笔者之前的文章 前端分层架构实践心得),完整的方案如下图所示:

如上图所示,将整个前端项目按照业务线拆分出多个子工程,每个子项目都是独立的仓库,只包含了单个业务线的代码,可以进行独立开发和部署,降低了项目维护的复杂度。

采用这套方案,使得我们的前端项目不仅保有了横向上(多个子项目)的扩展性,又拥有了纵向上(单个子项目)的复用性。那么这套方案具体是怎么实现的呢?下面就详细说明方案的实现机制。

实现机制

下面具体介绍下其中的实现机制:

1.子项目按照 vue-cli 3 的 library 模式进行打包,以便后续主项目引用

注:在 library 模式中,Vue 是外置的。这意味着包中不会有 Vue,即便你在代码中导入了 Vue。如果这个库会通过一个打包器使用,它将尝试通过打包器以依赖的方式加载 Vue;否则就会回退到一个全局的 Vue 变量。

2.在编译主项目的时候,通过 InsertScriptPlugin 插件将子项目的入口文件 main.js 以 script 标签形式插入到主项目的 html 中

注:务必将子项目的入口文件 main.js 对应的 script 标签放在主项目入口文件 app.js 的 script 标签之上,这是为了确保子项目的入口文件先于主项目的入口文件代码执行,接下来的步骤就会明白为什么这么做。

再注:本地开发环境下项目的入口文件编译后的 main.js 是保存在内存中的,所以磁盘上看不见,但是可以访问。

InsertScriptPlugin 核心代码如下:

compiler.hooks.compilation.tap('InsertScriptWebpackPlugin', (compilation) => {
  compilation.hooks.htmlWebpackPluginBeforeHtmlProcessing.tap(
    'InsertScriptWebpackPlugin',
    (htmlPluginData) => {
      const {
        assets: { js }
      } = htmlPluginData;
      // 将传入的 js 以 script 标签形式插入到 html 中
      // 注意:需要将子项目的入口文件 main.js 放在主项目入口文件 app.js 之前,因为需要子项目提前将自己的 route list 注册到全局上
      js.unshift(...self.files);
    }
  );
});
复制代码

3.主项目的 html 要访问子项目里的编译后的 js / css 等资源,需要进行代理转发

  • 如果是本地开发时,可以通过 webpack 提供的 proxy,例如:
const PROXY = {
  '/app-a/': {
    target: 'http://localhost:10241/'
  }
};
复制代码
  • 如果是线上部署时,可以通过 nginx 转发或者将打包后的主项目和子项目放在一个文件夹中

4.当浏览器解析 html 时,解析并执行子项目的 main.js,将子项目的 route list 注册到 Vue.__share__.routes 上,以便后续主项目将其合并到总的路由中

子项目 main.js 代码如下(为了尽量减少首次主项目页面渲染时加载的资源,子项目的入口文件建议只做路由注册)

import Vue from 'vue';
import routes from './routes';

const share = (Vue.__share__ = Vue.__share__ || {});
const routesPool = (share.routes = share.routes || {});

// 将子项目的 route list 挂载到 Vue.__share__.routes 上,以便后续主项目将其合并到总的路由中
routesPool[process.env.VUE_APP_NAME] = routes;
复制代码

5.继续向下解析,解析并执行主项目 main.js 时,从 Vue.__share__.routes 获取所有子项目的 route list,合并到总的路由表中,然后初始化一个 vue-router 实例,并传入到 new Vue 内

相关关键代码如下

// 从 Vue.__share__.routes 获取所有子项目的 route list,合并到总的
                   

这篇关于基于 Vue 技术栈的微前端方案实践的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程