神秘的微前端(本文所介绍的是关于micro-app在项目中的实际应用)

神秘的微前端(本文所介绍的是关于micro-app在项目中的实际应用)

什么是微前端?使用微前端的好处?

首先,微前端的概念可以模糊的理解把大型且复杂的巨石应用划分成高内聚、低耦合的子应用去开发维护,然后在主应用中去渲染展示,从用户的角度看去就是一个整体的项目,但实际上从开发者角度而言,就是根据不同业务拆分成的各个子应用(子项目);
还有,使用微前端的好处就是不局限于技术、框架的限制,只要根据约定好的路由进行配置,就可以在主应用中加载不同子应用中具体的界面;其次,可以多个项目组同时进行开发,互不干扰;也可以很好的进行新老项目的迭代升级等等。

什么是micro-app?为什么选择它?

MicroApp借鉴了WebComponent的思想,通过CustomElement结合自定义的ShadowDom,将微前端封装成一个类WebComponent组件,从而实现微前端的组件化渲染。
并且由于自定义ShadowDom的隔离特性,micro-app不需要像single-spa一样要求子应用修改渲染逻辑并暴露出方法,也不需要修改webpack配置,其次就是接入成本低、兼容性高、性能强是我们选择MicroApp作为微前端框架使用的重要因素。

如何将已有的ruoyi项目利用micro-app拆分成微前端?

1.首先在项目根目录下,通过控制台下载安装micro-app:

npm install --save @micro-zoe/micro-app

2.在main.js主应用中引入

import microApp from '@micro-zoe/micro-app'
microApp.start()

3.在src目录下面创建config.js配置文件,具体内容如下:

// 开发环境地址
const config = {
//注:目前是将该项目改造成主应用,后文会提到子应用的相关配置以及注意事项
  child_management_demo: 'http://localhost:4011', //子应用1
  //如果还有其他子应用可以在这里继续添加
}

// 线上环境地址
if (process.env.NODE_ENV === 'production') {
  // 基座应用和子应用部署在同一个域名下,这里使用location.origin进行补全
  Object.keys(config).forEach((key) => {
    config[key] = window.location.origin
  })
}

export default config

下面主应用的router.js中添加此组件的静态菜单组件路由,当然也可以配置成动态的菜单

 {
    path: 'weiyizhi',
    component: Layout,
    children: [{
    //我这里的path所指定的是child-management-demo子应用中的具体的某个组件,所以你必须修改成你自己的
      path: '/child-management-demo/newCoreSystem/productCenter/SmsManagement/productMessageConfig',
      component: (resolve) => require(['@/views/page/child-management-demo.vue'], resolve),
      name: 'child-management-demo',
      meta: {
        title: '子应用-图标',
        icon: ''
      }
    }]
  },

还有就是注意修改router模式为history,否则访问子应用刷新界面时,路由会很丑

 export default new Router({
      mode: 'history', // 去掉url中的#
      base:'/management',
      scrollBehavior: () => ({
        y: 0
      }),
      routes: constantRoutes, dynamicRoutes
    })

在views文件夹@/views/page/位置添加一个组件,我这里为了学习就叫child-management-demo.vue(注意:这个组件是必须创建在主应用里面的,后续就是通过这个组件和所配置的子应用进行通信的)






最后就是修改主应用的vue.config.js的相关配置

//这里对publicPath、outputDir、以及devServer做了如下配置

module.exports = {
  // 部署生产环境和开发环境下的URL。
  // 默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上
  // 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。
  publicPath: process.env.NODE_ENV === "production" ? "/management/" : "/management/",
  // 在npm run build 或 yarn build 时 ,生成文件的目录名称(要和baseUrl的生产环境路径一致)(默认dist)
  outputDir: 'management',
  // 用于放置生成的静态资源 (js、css、img、fonts) 的;(项目打包之后,静态资源会放在这个文件夹下)
  assetsDir: 'static',
  // 是否开启eslint保存检测,有效值:ture | false | 'error'
  lintOnSave: process.env.NODE_ENV === 'development',
  // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
  productionSourceMap: false,
  // webpack-dev-server 相关配置
  devServer: {
    hot: false,
    host: '0.0.0.0',
    port: 3001,
    open: true,
    disableHostCheck: true,
    overlay: {
      warnings: false,
      errors: true,
    },
  },

主应用启动成功时,运行地址为:http://localhost:3001/management/
如上就是关于主应用的相关改造的地方

下面是将另一个ruoyi项目改造成为子应用,项目名为child-management-demo

1.在main.js中追加如下代码:

// 与基座进行数据交互
function handleMicroData () {
  // 是否是微前端环境
  if (window.__MICRO_APP_ENVIRONMENT__) {
    // 主动获取基座下发的数据
    console.log('child-management-demo getData:', window.microApp.getData())

    // 监听基座下发的数据变化
    window.microApp.addDataListener((data) => {
      console.log('child-management-demo addDataListener:', data)

      // 当基座下发path时进行跳转
      if (data.path && data.path !== router.currentRoute.path) {
        router.push(data.path)
      }
    })

    // 向基座发送数据
    setTimeout(() => {
      window.microApp.dispatch({ myname: 'child-management-demo' })
    }, 3000)
  }
}

// ----------分割线---默认模式------两种模式任选其一-----放开注释即可运行------- //
// const app = new Vue({
//   router,
//   render: h => h(App),
// }).$mount('#vue2-app')

// console.log('微应用child-management-demo渲染了')

// handleMicroData()

// // 监听卸载操作
// window.addEventListener('unmount', function () {
//   app.$destroy()
//   console.log('微应用child-management-demo卸载了')
// })

// ----------分割线---umd模式------两种模式任选其一-------------- //
let app = null
// 将渲染操作放入 mount 函数
function mount () {
  app = new Vue({
    router,
    store,
    render: h => h(App),
  }).$mount('#app')

  console.log('微应用child-management-demo渲染了')

  handleMicroData()
}

// 将卸载操作放入 unmount 函数
function unmount () {
  app.$destroy()
  app.$el.innerHTML = ''
  app = null
  console.log('微应用child-management-demo卸载了')
}

// 微前端环境下,注册mount和unmount方法
if (window.__MICRO_APP_ENVIRONMENT__) {
  window[`micro-app-${window.__MICRO_APP_NAME__}`] = { mount, unmount }
} else {
  // 非微前端环境直接渲染
  mount()
}

3.在src目录下面创建public-path.js文件,获取公共路径:

/* eslint-disable no-undef */
if (window.__MICRO_APP_ENVIRONMENT__) {
  __webpack_public_path__ = window.__MICRO_APP_PUBLIC_PATH__
}

4.下面子应用的router.js中,同样需要改为history模式

 export default new Router({
      mode: 'history', // 去掉url中的#
      base: window.__MICRO_APP_BASE_ROUTE__ || '/child-management-demo',
      scrollBehavior: () => ({
        y: 0
      }),
      routes: constantRoutes, dynamicRoutes
    })

5.最后就是修改子应用的vue.config.js的相关配置

module.exports = {
  // 部署生产环境和开发环境下的URL。
  // 默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上
  // 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。
  publicPath: '/child-management-demo/',
  // 在npm run build 或 yarn build 时 ,生成文件的目录名称(要和baseUrl的生产环境路径一致)(默认dist)
  outputDir: 'child-management-demo',
  // 用于放置生成的静态资源 (js、css、img、fonts) 的;(项目打包之后,静态资源会放在这个文件夹下)
  assetsDir: 'static',
  // 是否开启eslint保存检测,有效值:ture | false | 'error'
  lintOnSave: process.env.NODE_ENV === 'development',
  // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
  productionSourceMap: false,
  // webpack-dev-server 相关配置
  devServer: {
    hot: false,
    host: '0.0.0.0',
    port: 4011,
    disableHostCheck: true,
    open: false,
    overlay: {
      warnings: false,
      errors: true,
    },
    headers: {
      'Access-Control-Allow-Origin': '*',
    },
  }
  }

子应用启动成功时,运行地址为: http://localhost:4011/child-management-demo/
如上就是关于子应用的相关改造的地方

彩蛋:

关于路由简介:
1.访问主应用时:http://ip:port/主应用/具体组件路径
2.通过主应用调用子应用具体组件时:http://ip:port/主应用/子应用/子组件中具体组件路径
3.直接访问子应用时:http://ip:port/子应用/具体组件路径

如果菜单是后端返回的数据,同时需要前端写递归对数据进行处理时,那么子应用中store/modules/permission.js中,如下代码值得深思:

export function generaMenu(routes, data) {
  data.forEach(item => {
    //alert(JSON.stringify(item))
    const menu = {
      path: `${window.__MICRO_APP_ENVIRONMENT__ !== true ? item.path.replace("/child-management-demo","") : item.path}`,
      //三元表达式的嵌套
      component:
        (item.component === '' || item.component===null) && item.level === 1 && window.__MICRO_APP_ENVIRONMENT__ !== true ? Layout :
          ((item.component === '' || item.component===null) ? ParentView:
              (resolve) => require([`@/views${item.path = item.path.replace("/child-management-demo","")}`],resolve))
      ,
      //hidden: true,
      children: [],
      name: 'menu_' + item.menuId,
      meta: { title: item.menuName, id: item.menuId, icon:item.icon, roles: ['admin'] }
    }
    if (item.children) {
      generaMenu(menu.children, item.children)
    }
    // console.log("menu:",menu);
    routes.push(menu)
  })
}

以上就是想要将前端项目拆分成微前端架构的最基本操作与想法

你可能感兴趣的:(前端,前端)