基于vue+single-spa实现微前端的小demo

最终实现的效果:
在这里插入图片描述

  1. vue cli

使用npm管理包自行安装基础环境啊
npm install -g @vue/cli 安装vue脚手架

  1. 创建主应用app1和子项目app2

vue create app1
vue create app2

  1. 改造主项目app1

npm i single-spa-vue -S

main.js改造

import Vue from 'vue'
import App from './App.vue'

import router from './router'
import { registerApplication, start } from 'single-spa'

Vue.config.productionTip = false

// 远程加载子应用
function createScript(url) {
  return new Promise((resolve, reject) => {
    const script = document.createElement('script')
    script.src = url
    script.onload = resolve
    script.onerror = reject
    const firstScript = document.getElementsByTagName('script')[0]
    firstScript.parentNode.insertBefore(script, firstScript)
  })
}
// 记载函数,返回一个 promise
function loadApp(url, globalVar) {
  // 支持远程加载子应用
  return async () => {
    await createScript(url + '/js/chunk-vendors.js')
    await createScript(url + '/js/app.js')
    // 这里的return很重要,需要从这个全局对象中拿到子应用暴露出来的生命周期函数
    return window[globalVar]
  }
}
// 子应用列表
const apps = [
  {
    name: 'app2',
    app: loadApp('http://localhost:8082', 'app2'),
    activeWhen: location => location.pathname.startsWith('/app2'),
    customProps: {}
  },
]
// 注册子应用
for (let i = apps.length - 1; i >= 0; i--) {
  registerApplication(apps[i])
}
 
new Vue({
  router,
  mounted() {
    // 启动
    start()
  },
  render: h => h(App)
}).$mount('#app')


vue.config.js

const package = require('./package.json')
module.exports = {
  // 告诉子应用在这个地址加载静态资源,否则会去基座应用的域名下加载
  publicPath: '//localhost:8081',
  // 开发服务器
  devServer: {
    port: 8081
  },
  configureWebpack: {
    // 导出umd格式的包,在全局对象上挂载属性package.name,基座应用需要通过这个全局对象获取一些信息,比如子应用导出的生命周期函数
    output: {
      // library的值在所有子应用中需要唯一
      library: package.name,
      libraryTarget: 'umd'
    }
  }
}

App.vue


<template>
  <div id="app">
    <div id="nav"><!-- 主应用 -->
      <router-link to="/home">app1</router-link> |
      <router-link to="/about">app1-about</router-link> |
      <router-link to="app2#x/home">app2</router-link> |
      <router-link to="app2#/about">app2-about</router-link>
      <button @click="test">测试</button>
    </div>
    <router-view/>
    <!-- 子应用容器 -->
    <div id = "microApp">
    </div>
  </div>
</template>
<script>

export default {
  methods: {
    test(){
      this.$router.push({path: "/app2/home"});
    }
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}
 
#nav {
  padding: 30px;
}
 
#nav a {
  font-weight: bold;
  color: #2c3e50;
}
 
#nav a.router-link-exact-active {
  color: #42b983;
}
</style>

src目录下新建views目录并新建about.vue和home.vue
about.vue


<!-- /views/About.vue -->
<template>
  <div class="about">
    <h1>app1 about page</h1>
  </div>
</template>

home.vue


<!-- /views/Home.vue -->
<template>
  <div class="home">
    <h1>app1 home page test</h1>
  </div>
</template>
 

src下新建router目录并新建index.js
index.js:

import VueRouter from 'vue-router'
import Vue from 'vue'
import Home from '@/views/home'
import About from '@/views/about'
Vue.use(VueRouter)
const routes = [
    { path: '/home', component: Home },
    { path: '/about', component: About },
    // { path: '*', component: Hello },
];
const router = new VueRouter({
    mode: 'history',
    // 通过环境变量来配置路由的 base url
    // base: process.env.VUE_APP_BASE_URL,
    routes
});
export default router

package.json:

  "scripts": {
    "serve": "vue-cli-service serve",
    "serve:micro": "vue-cli-service serve --mode micro",
    "build": "vue-cli-service build --mode buildMicro"
  },

app1项目改造完毕。

  1. 子应用app2应用改造
    main.js:
import Vue from 'vue'
import App from './App.vue'

import router from './router'
import singleSpaVue from 'single-spa-vue'
Vue.config.productionTip = false
/**
 * 子应用main.js
 */
const appOptions = {
  el: '#microApp',
  router,
  render: h => h(App)
}
// if(window.singleSpaNavigate) {
//   __webpack_public_path__="http://localhost:8082/"
// }
// 支持应用独立运行、部署,不依赖于基座应用
if (!window.singleSpaNavigate) {
  delete appOptions.el
  new Vue(appOptions).$mount('#app')
}
 
// 基于基座应用,导出生命周期函数
const vueLifecycle = singleSpaVue({
  Vue,
  appOptions
})
 
export function bootstrap () {
  console.log('app2 bootstrap')
  return vueLifecycle.bootstrap(() => {})
}
 
export function mount () {
  console.log('app2 mount')
  return vueLifecycle.mount(() => {})
}
 
export function unmount () {
  console.log('app2 unmount')
  return vueLifecycle.unmount(() => {})
}

vue.config.js

const package = require('./package.json')
module.exports = {
  // 告诉子应用在这个地址加载静态资源,否则会去基座应用的域名下加载
  publicPath: '//localhost:8082',
  // 开发服务器
  devServer: {
    port: 8082
  },
  configureWebpack: {
    // 导出umd格式的包,在全局对象上挂载属性package.name,基座应用需要通过这个全局对象获取一些信息,比如子应用导出的生命周期函数
    output: {
      // library的值在所有子应用中需要唯一
      library: package.name,
      libraryTarget: 'umd'
    }
  }
}

App.vue:

<template><!-- 子应用 -->
  <div id="vue">
    <!-- <div>"Welcome to Your Vue.js App"</div> -->
    <router-view />
  </div>
</template>

  1. src下新建views目录并新建about.vue和home.vue
    about.vue

<!-- /views/About.vue -->
<template>
  <div class="about">
    <h1>app2 about page</h1>
  </div>
</template>

home.vue


<!-- /views/Home.vue -->
<template>
  <div class="home">
    <h1>app2 home page</h1>
  </div>
</template>
 

src目录下新建router目录并新建index.js
index.js

import VueRouter from 'vue-router'
import Vue from 'vue'
import Home from '@/views/home'
import About from '@/views/about';
import Hello from '@/components/HelloWorld';
Vue.use(VueRouter)
/**
 * 子应用路由
 */
const routes = [
    { path: '/home', component: Home },
    { path: '/about', component: About },
    { path: '*', component: Hello }
    // {
    //     path: '/about',
    //     component: () => import(/* webpackChunkName: "about" */'../views/about.vue'),
    //     meta: {
    //       keepAlive: true
    //     }
    // }, 
];
const router = new VueRouter({
    // mode: 'history',
    // 通过环境变量来配置路由的 base url
    // base: process.env.VUE_APP_BASE_URL,
    routes
});
export default router

package.json下改造script属性值

  "scripts": {
    "serve": "vue-cli-service serve",
    "serve:micro": "vue-cli-service serve --mode micro",
    "build": "vue-cli-service build --mode buildMicro"
  },

app2改成完成!

开始运行

1、在app1目录执行cmd命令:

npm run serve

执行结果:

 DONE  Compiled successfully in 12673ms                                                                            下午5:05:14

  App running at:
  - Local:   http://localhost:8081/ 
  - Network: http://192.168.43.189:8081/

  Note that the development build is not optimized.
  To create a production build, run yarn build.


2、在app2目录执行cmd命令:

npm run serve

执行结果:

 DONE  Compiled successfully in 9440ms                                                                                   下午5:05:53

  App running at:
  - Local:   http://localhost:8082/
  - Network: http://192.168.43.189:8082/

  Note that the development build is not optimized.
  To create a production build, run yarn build.

打开浏览器输入

http://localhost:8081/
结束

运行完成后,你刷新非主页面路由会直接404,请看我的下一篇博客
页面刷新404问题


引入ElementUi继续改造demo:
vue+single-spa+ElementUi项目demo

出现了npm依赖没有安装的问题,根据提示自行安装

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值