什么是微前端

简单来说就是
1、复用(嵌入)别人的项目页面,但是别人的项目运行在他自己的环境之上。
2、巨无霸应用拆分成一个个的小项目,这些小项目独立开发部署,又可以自由组合进行售卖

本文主要讲解如何在vue项目上使用阿里的qiankun插件进行微前端实践,对微前端的概念会在另外的文章中进行阐述,demo会附在文末。

首先我准备了三个项目、分别是主应用main-app、子应用b-childc-child。三个应用均是采用Vue技术栈,路由均是采用history模式

改造主应用main-app

1、首先需要安装qiankun框架 npm i qiankun -S

2、然后我们需要改造一下app.vue文件,我们要在app.vue中创建一个容器,负责把获取到的子应用加载到这里容器里面。

<template>
  <div id="app">
    <header>
      <router-link to="/about">主应用的page页面</router-link>
      <router-link to="/b-child">子应用B</router-link>
      <router-link to="/c-child">子应用C</router-link>
      <router-link to="/">回到主应用</router-link>
    </header>
    
    <router-view></router-view>
    <!-- id为appContainer就是放置子应用的容器 -->
     <div id="appContainer"></div>
  </div>
</template>

2、在src目录下创建qiankun.js文件,引用qiankun并且封装好

import {
  registerMicroApps,
  start
} from 'qiankun'

export const useQiankun = () => {
  const apps = [
    {
      name: 'b-child', // 必选,微应用的名称,微应用之间必须确保唯一
      entry: 'http://localhost:8022', // - 必选,微应用的入口
      container: '#appContainer', // -必选,微应用的容器节点的选择器或者 Element 实例
      activeRule: '/b-child', // - 必选,微应用的激活规则
      props: { // - 可选,主应用需要传递给微应用的数据。
        msg: '我是父应用传过来的值,传递给B应用'
      }
    },
    {
      name: 'c-child',
      entry: 'http://localhost:8033',
      container: '#appContainer',
      activeRule: '/c-child',
      props: {
        msg: '我是父应用传过来的值,传递给C应用'
      }
    }
  ]

  registerMicroApps(apps, {
    beforeLoad: [
      app => {
        console.log(`${app.name}的beforeLoad阶段`)
      }
    ],
    beforeMount: [
      app => {
        console.log(`${app.name}的beforeMount阶段`)
      }
    ],
    afterMount: [
      app => {
        console.log(`${app.name}的afterMount阶段`)
      }
    ],
    beforeUnmount: [
      app => {
        console.log(`${app.name}的beforeUnmount阶段`)
      }
    ],
    afterUnmount: [
      app => {
        console.log(`${app.name}的afterUnmount阶段`)
      }
    ]
  })

  start({ experimentalStyleIsolation: true, prefetch: 'all' })
}

具体的配置项说明可以参考qiankun的官方文档

3、在main.js中引用

import {
  useQiankun
} from './qiankun'

const vueApp = new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

vueApp.$nextTick(() => {
  useQiankun()
})

这里为了确保装载子应用的容器已创建,我们需要在new Vue()之后,等DOM加载好了在注册并启动qiankun

改造子应用b-child

1、在src目录下新增文件public-path.js,主要用于修改子项目的publicPath

/* eslint-disable camelcase */

if (window.__POWERED_BY_QIANKUN__) {
  // eslint-disable-next-line no-undef
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}

2、在main.js,引入public-path.js 并配合主项目导出single-spa需要的三个生命周期。注意:路由实例化需要在main.js里面完成,以便于路由的销毁,所以路由文件只需要导出路由配置即可(原模板导出的是路由实例)

import './public-path'
import Vue from 'vue'
import App from './App.vue'
import routes from './router'
import VueRouter from 'vue-router'
import store from './store'

Vue.config.productionTip = false

let router = null
let instance = null

function render ({ container, parentStore } = {}) {
  router = new VueRouter({
    base: window.__POWERED_BY_QIANKUN__ ? '/b-child' : process.env.BASE_URL,
    mode: 'history',
    routes
  })

  instance = new Vue({
    router,
    store,
    data () {
      return {
        parentStore
      }
    },
    render: h => h(App)
  }).$mount(container ? container.querySelector('#bChild') : '#bChild')
  // index.html 里面的 id 需要改成 appVueHash,否则子项目无法独立运行
}

if (!window.__POWERED_BY_QIANKUN__) {
  // 全局变量来判断环境
  render()
}

/**
 * bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
 * 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
 */
export async function bootstrap () {
  console.log('现在进入子应用b-child的bootstraped阶段')
}

/**
 * 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
 */
export async function mount (props) {
  if (props.parentStore) {
    await props.parentStore.dispatch('getResource', { name: 'B应用的资源' })
  }

  console.log('现在进入子应用b-child的mount周期', props)

  render(props)
}

/**
 * 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
 */
export async function unmount () {
  console.log('现在进入子应用b-child的unmount阶段')
  instance.$destroy()
  instance.$el.innerHTML = ''
  instance = null
  router = null
}

/**
 * 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
 */
export async function update (props) {
  console.log('update props', props)
}


这里有两点是需要注意的
1、修改public文件下的index.html 挂在应用的id,否则子项目无法独立运行
2、由于我们采用的是history模式,我们需要给路由设置base

 base: window.__POWERED_BY_QIANKUN__ ? '/b-child' : process.env.BASE_URL

3、修改打包配置文件vue.config.js ,主要是允许跨域、以及打包成umd格式

/*
 * @Author: Hzh
 * @Date: 2021-04-22 18:16:41
 * @LastEditTime: 2021-04-25 15:58:34
 * @LastEditors: Hzh
 * @Description:
 */
'use strict'
const path = require('path')
function resolve (dir) {
  return path.join(__dirname, dir)
}

const port = 8022 // 端口配置
const { name } = require('./package')

module.exports = {
  publicPath: process.env.NODE_ENV === 'development' ? '/' : '/b-child/',
  outputDir: 'dist',
  assetsDir: 'static',
  lintOnSave: process.env.NODE_ENV === 'development',
  productionSourceMap: false,
  devServer: {
    port: port,
    open: false, // 启动项目以后自动打开浏览器
    hot: true, 
    hotOnly: false,
    overlay: {
      warnings: false,
      errors: true
    },
    proxy: {
      '/auth': {
        target: 'www.baidu.com',
        changeOrigin: true,
        pathRewrite: {
          '^/auth': 'auth'
        }
      }
    },
    headers: {
      'Access-Control-Allow-Origin': '*'
    }
  },

  configureWebpack: {
    name: name,
    resolve: {
      alias: {
        '@': resolve('src')
      }
    },
   output: {
      // 把子应用打包成 umd 库格式
      library: `${sysName}`, // 微应用的包名,这里与主应用中注册的微应用名称一致
      libraryTarget: 'umd', // 将你的 library 暴露为所有的模块定义下都可运行的方式
      jsonpFunction: `webpackJsonp_${sysName}`// 按需加载相关,设置为 webpackJsonp_vue-projec 即可
    },
  }
}

到这一步子应用就改造完成了,c-child同理改造即可。

关于qiankun微前端的实战,如何解决主应用与子应用的通讯、子应用与子应用的通讯,子应用跳转子应用,如何实现子应用的keep-alive,本系列将持续更新说明

参考文章:从0实现一个single-spa的前端微服务

本示例的Demo码云地址:https://gitee.com/zhihua0123/qiankun-demo

在这里插入图片描述

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐