子应用在接入微前端项目时需要进行必要微前端改造,那问题来了,为什么要做微前端改造?这里面其实是有一个前提条件,即在做微前端项目时如果要将一个项目当成子应用接入微前端中,首先需要做的是主应用应能获取所有子应用的生命周期(包括它的一些方法),这样在主应用里就可以控制子应用的加载和卸载。同时,也需要我们在微前端框架里获取子应用的所有结构(包括依赖文件等等这些内容),所以说子应用需要做一些改造才能完成微前端的接入。

一、Vue2 项目改造

1.1 创建 vue.config.js 文件

在 Vue2 项目中创建 vue.config.js 文件,完成以下配置项设置。

  1. const path = require('path');
  2. const { name } = require('./package'); // 获取当前项目的名称
  3. function resolve(dir) {
  4. return path.join(__dirname, dir);
  5. }
  6. const port = 9004;
  7. module.exports = {
  8. outputDir: 'dist', // 打包目录
  9. assetsDir: 'static', // 打包的静态资源目录
  10. filenameHashing: true, // 打包出来的文件带有hash信息
  11. publicPath: 'http://localhost:9004', // 公共路径
  12. // 本地服务
  13. devServer: {
  14. contentBase: path.join(__dirname, 'dist'), // 告诉服务器从哪里提供内容
  15. hot: true, // 是否热更新
  16. disableHostCheck: true,
  17. port,
  18. headers: {
  19. /**
  20. * 配置本地服务的跨域,允许所有的资源可以被访问
  21. *
  22. * 注:为什么要配置跨域呢?
  23. *
  24. * 在写微前端框架的时候,其实我们需要在主应用里或者框架里去获取当前本地服务里的内容。
  25. * 如果在获取的时候不去给它设置跨域,会出现资源的拦截,导致无法获取子应用中的内容,因
  26. * 此需设置成允许跨域
  27. */
  28. 'Access-Control-Allow-Origin': '*',
  29. },
  30. },
  31. // 自定义webpack配置,替换 vue-cli-service 里面的 webpack 的一些配置
  32. configureWebpack: {
  33. resolve: {
  34. alias: {
  35. '@': resolve('src'),
  36. },
  37. },
  38. /**
  39. * 在进行微前端项目改造时,必须配置output下方选项
  40. */
  41. output: {
  42. /**
  43. * 把子应用打包成 umd 库格式(支持commonJS,即支持浏览器环境、node环境)
  44. */
  45. libraryTarget: 'umd',
  46. filename: 'vue2.js', // 文件打包出来的名字
  47. /**
  48. * 可以使我们在当前全局环境下获取我们当前打包的内容,即通过 window.vue2 可
  49. * 以获取子应用打包的内容,在微前端框架里会用到这一信息(value值对应子应用名称)
  50. */
  51. library: 'vue2',
  52. jsonpFunction: `webpackJsonp_${name}`,
  53. },
  54. },
  55. };

注:vue.config.js 该文件能生效是有前提条件的,即该项目是通过 vue-cli 创建的,并基于vue-cli-service 进行的项目启动。

1.2 修改 main.js

为了实现 vue2 项目能接入微前端,需对文件 main.js 进行如下改造。

原文件

  1. import Vue from 'vue'
  2. import App from './App.vue'
  3. import router from './router'
  4. Vue.config.productionTip = false
  5. new Vue({
  6. router,
  7. render: h => h(App),
  8. }).$mount('#app')

修改后

  1. import Vue from 'vue';
  2. import App from './App.vue';
  3. import router from './router';
  4. Vue.config.productionTip = false;
  5. /**
  6. * 创建 render 函数。
  7. *
  8. * 注:可以在微前端框架里进行引用,也可以通过window.vue2来获取到对应内容
  9. */
  10. let instance = null;
  11. const render = () => {
  12. instance = new Vue({
  13. router,
  14. render: (h) => h(App),
  15. }).$mount('#app');
  16. };
  17. render();
  18. /**
  19. * 在微前端环境下,通常不会让子应用自行触发render函数,而是通过微前端的生命周期
  20. * 来触发对应的render函数。因此需要加一个限制,即判断当前是否处于微前端环境下
  21. */
  22. // 如果不在微前端环境下,则直接执行 render 函数
  23. if (!window.__MICRO_WEB__) {
  24. render();
  25. }
  26. /**
  27. * 如果在微前端环境下,则暴露一组生命周期,生命周期何时执行可以在微前端框架里进行控制。
  28. * + 生命周期: 开始
  29. * + 生命周期:渲染成功
  30. * + 生命周期:卸载
  31. */
  32. export const boostrap = () => {
  33. console.log('开始加载');
  34. };
  35. export const mount = () => {
  36. render();
  37. };
  38. export const unmount = () => {
  39. console.log('卸载', instance);
  40. };

改造后的效果如下,在调试窗口可以看到全局变量 window.vue2 里面暴露了一组生命周期函数。
WX20210716-054601.png

二、Vue3 项目改造

2.1 创建 vue.config.js 文件

在 Vue3 项目中创建 vue.config.js 文件,完成以下配置项设置(注:基本与Vue2项目配置一样,详见Vue2项目改造)。

  1. const path = require('path');
  2. const { name } = require('./package');
  3. function resolve(dir) {
  4. return path.join(__dirname, dir);
  5. }
  6. const port = 9005;
  7. module.exports = {
  8. outputDir: 'dist',
  9. assetsDir: 'static',
  10. filenameHashing: true,
  11. publicPath: 'http://localhost:9005',
  12. devServer: {
  13. contentBase: path.join(__dirname, 'dist'),
  14. hot: true,
  15. disableHostCheck: true,
  16. port,
  17. headers: {
  18. 'Access-Control-Allow-Origin': '*',
  19. },
  20. },
  21. // 自定义webpack配置
  22. configureWebpack: {
  23. resolve: {
  24. alias: {
  25. '@': resolve('src'),
  26. },
  27. },
  28. output: {
  29. // 把子应用打包成 umd 库格式
  30. libraryTarget: 'umd',
  31. filename: 'vue3.js',
  32. library: 'vue3',
  33. jsonpFunction: `webpackJsonp_${name}`,
  34. },
  35. },
  36. };

注:vue.config.js 该文件能生效是有前提条件的,即该项目是通过 vue-cli 创建的,并基于vue-cli-service 进行的项目启动。

2.2 修改 main.js

为了实现 vue3 项目能接入微前端,需对文件 main.js 进行如下改造。

原文件

  1. import { createApp } from 'vue';
  2. import App from './App.vue';
  3. import router from './router';
  4. createApp(App).use(router).mount('#app')

修改后

  1. import { createApp } from 'vue';
  2. import App from './App.vue';
  3. import router from './router';
  4. let instance = null;
  5. function render() {
  6. instance = createApp(App); // 在 vue3 中,createApp 返回 vue 实例
  7. instance.use(router).mount('#app');
  8. }
  9. if (!window.__MICRO_WEB__) {
  10. render();
  11. }
  12. export async function bootstrap() {
  13. console.log('vue3.0 app bootstrap');
  14. }
  15. export async function mount(app) {
  16. render();
  17. }
  18. export async function unmount() {
  19. console.log('unmount', instance);
  20. }

注:针对 Vue2、Vue3 等Vue项目,接入微前端方式基本一样。

三、React15 项目改造

在 React15 项目中,此次项目的启动是通过 webpack-dev-server 进行启动的。

  1. "scripts": {
  2. "start": "cross-env NODE_ENV=development webpack-dev-server --mode production"
  3. },

因此要完成 React15 项目的微前端接入,需修改 webpack 相关配置。

3.1 修改 webpack.config.js 文件

  1. const path = require('path');
  2. const HtmlWebpackPlugin = require('html-webpack-plugin');
  3. const MiniCssExtractPlugin = require('mini-css-extract-plugin');
  4. module.exports = {
  5. entry: {
  6. path: ['./index.js'],
  7. },
  8. /**
  9. * webpack 整体打包的时候会打包成 (function(){ ....代码内容....})() 这样一个立即执行的方法,
  10. * 在这个方法里有我们所有的代码内容。当配置了library之后,webpack 在打包的时候就会配置成如下格式:
  11. * var react15 = (function(){})()
  12. * 变量 react15 是存放在全局的,通过 react15 这个全局变量就可以拿到各个生命周期方法
  13. */
  14. output: {
  15. path: path.resolve(__dirname, 'dist'),
  16. /**
  17. * 把子应用打包成 umd 库格式(支持commonJS,即浏览器环境、node环境都可以进行引用)
  18. */
  19. libraryTarget: 'umd',
  20. filename: 'react15.js', // 文件打包出来的名字
  21. /**
  22. * 可以使我们在当前全局环境下获取我们当前打包的内容,即通过 window.react15
  23. * 以获取子应用打包的内容,在微前端框架里会用到这一信息(value值对应子应用名称)
  24. */
  25. library: 'react15',
  26. // 会把 AMD 模块命名为 UMD 构建
  27. umdNamedDefine: true,
  28. publicPath: 'http://localhost:9002/',
  29. },
  30. module: {
  31. rules: [
  32. {
  33. test: /\.js(|x)$/,
  34. use: {
  35. loader: 'babel-loader',
  36. options: {
  37. presets: ['@babel/preset-env', '@babel/preset-react'],
  38. },
  39. },
  40. },
  41. {
  42. test: /\.(c|sc)ss$/,
  43. use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
  44. },
  45. {
  46. test: /\.(png|svg|jpg|gif)$/,
  47. use: {
  48. loader: 'url-loader',
  49. },
  50. },
  51. ],
  52. },
  53. optimization: {
  54. splitChunks: false,
  55. minimize: false,
  56. },
  57. plugins: [
  58. new HtmlWebpackPlugin({
  59. template: './public/index.html',
  60. }),
  61. new MiniCssExtractPlugin({
  62. filename: '[name].css',
  63. }),
  64. ],
  65. devServer: {
  66. contentBase: path.join(__dirname, 'dist'), // 告诉服务器从哪里提供内容
  67. hot: true, // 是否热更新
  68. compress: true,
  69. port: 9002,
  70. headers: {
  71. /**
  72. * 配置本地服务的跨域,允许所有的资源可以被访问
  73. *
  74. * 注:为什么要配置跨域呢?
  75. *
  76. * 在写微前端框架的时候,其实我们需要在主应用里或者框架里去获取当前本地服务里的内容。
  77. * 如果在获取的时候不去给它设置跨域,会出现资源的拦截,导致无法获取子应用中的内容,因
  78. * 此需设置成允许跨域
  79. */
  80. 'Access-Control-Allow-Origin': '*',
  81. },
  82. historyApiFallback: true,
  83. },
  84. };

主体配置与vue.config.js文件基本差不多。

3.2 修改入口文件index.js

为了实现 react15 项目能接入微前端,需对文件 index.js 进行如下改造。

原文件

  1. import React from 'react'
  2. import ReactDOM from 'react-dom'
  3. import BasicMap from './src/router/index.jsx';
  4. import "./index.scss"
  5. ReactDOM.render(<BasicMap />, document.getElementById('app-react'));

修改后

  1. import React from 'react';
  2. import ReactDOM from 'react-dom';
  3. import BasicMap from './src/router/index.jsx';
  4. import './index.scss';
  5. /**
  6. * 创建render函数
  7. *
  8. * 注:由于ReactDOM.render返回的并不是一个对象内容,故在unmount生命周期
  9. * 中无法像Vue那样清除对象内容。后续可以将根元素节点上的内容置为空。
  10. */
  11. const render = () => {
  12. ReactDOM.render(<BasicMap />, document.getElementById('app-react'));
  13. };
  14. // 判断当前环境是否在微前端环境,不在微前端环境则直接执行
  15. if (!window.__MICRO_WEB__) {
  16. render();
  17. }
  18. /**
  19. * 在微前端环境,则暴露一组生命周期
  20. */
  21. export function bootstrap() {
  22. console.log('react bootstrap');
  23. }
  24. export function mount(app) {
  25. console.log('react mount');
  26. render();
  27. }
  28. export function unmount(ctx) {
  29. console.log('react unmout');
  30. }

四、React16 项目改造

在 React16 项目中,此次项目的启动是通过 webpack-dev-server 进行启动的。

  1. "scripts": {
  2. "start": "cross-env NODE_ENV=development webpack-dev-server --mode production"
  3. },

因此要完成 React16 项目的微前端接入,需修改 webpack 相关配置。

在 React16 项目中,webpack的配置以及入口文件 index.js 的修改基本与 React15 的保持一致(除打包应用名称不同),故此处不在复述。