vue 子应用

本节我们将详细介绍 vue 框架的应用作为子应用的接入步骤。v2 demov3 demo

vue 子应用接入步骤

1. bridge 依赖安装

TIP
  1. 请注意,桥接函数的安装不是必须的,你可以自定义导出函数。
  2. 我们提供桥接函数是为了进一步降低用户接入成本并降低用户出错概率,桥接函数中将会内置一些默认行为,可以避免由于接入不规范导致的错误,所以这也是我们推荐的接入方式。
  3. 我们分别为 vue 2、3 应用提供不同的 bridge 包,目的是为了更好的类型提示及精简参数。
vue2 应用
vue3 应用
npm install @garfish/bridge-vue-v2 --save

2. 入口文件处导出 provider 函数

更多 bridge 函数参数介绍请参考 这里

vue2 导出

使用 @garfish/bridge-vue-v2 导出
自定义导出
import Vue from 'vue';
import VueRouter from 'vue-router';
import store from './store';
import App from './App.vue';
import Home from './components/Home.vue';
import { vueBridge } from '@garfish/bridge-vue-v2';

Vue.use(VueRouter);
Vue.config.productionTip = false;

function newRouter(basename) {
  const router = new VueRouter({
    mode: 'history',
    base: basename,
    routes: [
      { path: '/home', component: Home },
    ],
  });
  return router;
}

export const provider = vueBridge({
  // 根组件
  rootComponent: App,
  // 可选,注册 vue-router或状态管理对象
  appOptions: ({ basename, dom, appName, props, appInfo }) => {
  // pass the options to Vue Constructor. check https://vuejs.bootcss.com/api/#%E9%80%89%E9%A1%B9-%E6%95%B0%E6%8D%AE
    return {
      el: '#app',
      router: newRouter(basename),
      store,
    };
  },
});

vue3 导出

使用 @garfish/bridge-vue-v3 导出
自定义导出
import { h, createApp } from 'vue';
  import { createRouter, createWebHistory } from 'vue-router';
  import { stateSymbol, createState } from './store.js';
  import App from './App.vue';
  import Home from './components/Home.vue';
  import { vueBridge } from '@garfish/bridge-vue-v3';

  const routes = [
    { path: '/home', component: Home },
  ];

  function newRouter(basename) {
    const router = createRouter({
      history: createWebHistory(basename),
      routes,
    });
    return router;
  }

  export const provider = vueBridge({
    rootComponent: App,
    // 可选,注册 vue-router或状态管理对象
    handleInstance: (vueInstance, { basename, dom, appName, props, appInfo}) => {
      vueInstance.use(newRouter(basename));
      vueInstance.provide(stateSymbol, createState());
    },
  });

3. 根组件设置路由的 basename

TIP
  1. 为什么要设置 basename?请参考 issue
  2. 我们强烈建议使用从主应用传递过来的 basename 作为子应用的 basename,而非主、子应用约定式,避免 basename 后期变更未同步带来的问题。
  3. 目前主应用仅支持 history 模式的子应用路由,why
vue2
vue3
import Vue from 'vue';
import VueRouter from 'vue-router';
import store from './store';
import App from './App.vue';
import Home from './components/Home.vue';
import { vueBridge } from '@garfish/bridge-vue-v2';

Vue.use(VueRouter);
Vue.config.productionTip = false;

function newRouter(basename) {
  const router = new VueRouter({
    mode: 'history',
    base: basename,
    routes: [
      { path: '/home', component: Home },
    ],
  });
  return router;
}

4. 更改 webpack 配置

// webpack.config.js
const webpack = require('webpack');
const isDevelopment = process.env.NODE_ENV !== 'production';

module.exports = {
  output: {
    // 开发环境设置 true 将会导致热更新失效
    clean: isDevelopment ? false : true,
    filename: '[name].[contenthash].js',
    chunkFilename: '[name].[contenthash].js',
    // 需要配置成 umd 规范
    libraryTarget: 'umd',
    // 修改不规范的代码格式,避免逃逸沙箱
    globalObject: 'window',
    // webpack5 使用 chunkLoadingGlobal 代替,或不填保证 package.json name 唯一即可
    jsonpFunction: 'garfish-demo-react',
    // 保证子应用的资源路径变为绝对路径
    publicPath: 'http://localhost:8080',
  },
  plugin: [
    // 保证错误堆栈信息及 sourcemap 行列信息正确
    new webpack.BannerPlugin({
      banner: 'Micro front-end',
    }),
  ],
  devServer: {
    // 保证在开发模式下应用端口不一样
    port: '8000',
    headers: {
      // 保证子应用的资源支持跨域,在上线后需要保证子应用的资源在主应用的环境中加载不会存在跨域问题(**也需要限制范围注意安全问题**)
      'Access-Control-Allow-Origin': '*',
    },
  },
};

【重要】注意:

  1. libraryTarget 需要配置成 umd 规范;
  2. globalObject 需要设置为 'window',以避免由于不规范的代码格式导致的逃逸沙箱;
  3. 如果你的 webpack 为 v4 版本,需要设置 jsonpFunction 并保证该值唯一(否则可能出现 webpack chunk 互相影响的可能)。若为 webpack5 将会直接使用 package.json name 作为唯一值,请确保应用间的 name 各不相同;
  4. publicPath 设置为子应用资源的绝对地址,避免由于子应用的相对资源导致资源变为了主应用上的相对资源。这是因为主、子应用处于同一个文档流中,相对路径是相对于主应用而言的
  5. 'Access-Control-Allow-Origin': '*' 允许开发环境跨域,保证子应用的资源支持跨域。另外也需要保证在上线后子应用的资源在主应用的环境中加载不会存在跨域问题(也需要限制范围注意安全问题);

5. 增加子应用独立运行兼容逻辑

TIP

last but not least, 别忘了添加子应用独立运行逻辑,这能够让你的子应用脱离主应用独立运行,便于后续开发和部署。

vue2
vue3
// src/main.js
import Vue from 'vue';
import VueRouter from 'vue-router';

// 这能够让子应用独立运行起来,以保证后续子应用能脱离主应用独立运行,方便调试、开发
if (!window.__GARFISH__) {
    const router = new VueRouter({
      mode: 'history',
      base: '/',
      routes: [
        { path: '/home', component: Home },
      ],
    });
    new Vue({
      store,
      router,
      render: (h) => h(App),
    }).$mount('#app');
}