关于微前端这里做一个简单的介绍:
"微前端构架"是一种使用微服务模式构建前端应用的方式,微前端中的理念是将一组组服务拆分成相互解耦的模块,然后通过一个统一的父模块进行整体的调度。同时在微前端的架构中,我们可以同时使用React, Vue, Angular,甚至是原生的Js,Jquery开发的应用都可以通过微前端进行调度。
关于微前端的框架:
微前端的框架现在比较成熟的是 “Single-spa” 和 “QianKun”, 而"QianKun"是基于Single-spa实现的,所以使用"QianKun"可能会更加的简单些。


  • 为了便于大家理解微前端,这里放上三张微前端架构的核心流程图:
    核心实现
    在这里插入图片描述
    整体调度
    在这里插入图片描述
    工作状态
    在这里插入图片描述

进入正题: Single-spa框架如何在Vue项目中落地,完成调度:
  1. 开始前需要先使用Vue-cli脚手架搭建多个Vue项目(创建的项目需要包含Vue-Router的路由机制)
  2. 将一个Vue的项目作为整体调度的父项目,用来完成整个服务的调度
  3. 配置父项目中的路由:(注意: 父项目中的路由配置中不用配置Component,因为它只是完成一个整体的调度,没有具体的组件,只需要配置path和name属性即可。同时我们这里设置了路由模式为: history)
const routes = [{
    path: "/vue",
    name: "vue"
}]

const router = new VueRouter({
    mode: "history",
    routes
})
  1. 在父项目的App文件(或者其他的.vue文件)中创建一个节点用于子项目的挂载:
template>
  <div id="app">
    <div class="header">this is parent headers</div>
    <div id="single-spa">
      <div id="vue"></div>  //子项目将会被挂载到这里
    </div>
  </div>
</template>
  1. 在父项目中注册子项目: 在src目录下创建一个single.config.js的配置文件,同时再main.js文件中引入执行,在single.config.js文件中写入如下代码:
import * as singleSpa from "single-spa"

/**
 * 加载子项目的js文件
 * @param {string} url 加载js的url路径 
 */
const runScript = (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)
    })
}

// 使用singleSpa.registerApplication()进行服务的注册 
// 参数1: 注册服务的名称
// 参数2: 加载子项目中的文件(就是使用webpack打包好后的文件)
// 参数3: 调起该项目的加载路由
singleSpa.registerApplication(
    'singleChild',
    async () => {
        await runScript("http://127.0.0.1:3000/js/chunk-vendors.js")
        await runScript("http://127.0.0.1:3000/js/app.js");
        return window.singleVue;
    },
    location => location.pathname.startsWith("/vue")
)
//启动服务
singleSpa.start()
  1. 配置子项目:
  • 子项目中的路由只需要按照正常的项目配置即可
  • 安装single-sap-vue进行vue项目的微前端配置:将Vue实例的所有的配置参数抽离为一个对象交由single-spa-vue处理,同时需要导出生命周期: bootstrap,mount,unmount方法
//这里均是在main.js文件中进行的,main.js文件中的其他的内容这里省略, 大家可以具体进行代码拆分
import singleSpaVue from "single-spa-vue"
const options = {   // vue的配置参数
  el: "#vue",
  render: h => h(App),
  router
}
const vueLifeCycles = singleSpaVue({
  Vue,
  appOptions: options
})
export const bootstrap = vueLifeCycles.bootstrap;
export const mount = vueLifeCycles.mount;
export const unmount = vueLifeCycles.unmount;
export default vueLifeCycles;
  • 更改子项目的webpack配置,使其导出的是一个库文件(Library),同时需要更改publicPath的配置,使得子项目切换时不会出现文件引入路径的错误:
module.exports = {
	// 指定publicPath保证请求资源路径正确
    publicPath: "//localhost:3000/",
    // css在所有环境下,都不单独打包为文件。这样是为了保证最小引入(只引入js)
    css: {
        extract: false
    },
    configureWebpack: {
        devtool: 'none', // 不打包sourcemap
        output: {     // 重点: 将其导出为library库文件
            library: "singleVue", // 导出名称
            libraryTarget: "window", //挂载目标
        }
    },
    devServer: {
        contentBase: './',
        compress: true,
    }
}

如此,一个最简单的Single-spa便搭建完成,这里提一下自动化引入子项目文件:

  • 在引入子项目中的js文件的时候都需要依次去调用多次的runScript()方法去引入文件,所以这里我们处理一下完成自动引入所有的文件:
  1. 在子项目中安装一个叫做stats-webpack-plugin的插件,并在webpack中进行配置。(这个插件的作用是能够在webpack打包的时候记录打包的文件,publicPath等等的信息,所以我们可以在父项目中通过子组件的这个项目其获知子项目中打包出了哪些文件)
    配置:
plugins: [
          new StatsPlugin("stats.json", {
              chunkModules: false,
              entryPoints:true,
              source: false,
              chunks:false,
              modules: false,
              assets: false,
              children: false,
              exclude: [/node_modules/]
          })
      ]
  • 经过这个插件处理后打包出的项目中便会有一个stats.json的文件记录了打包信息,在父项目中去拿取子项目的这个文件完成所有js文件的自动引入:
/**
* 通过url去拿去子项目的stats.json文件获取子项目中的打包静态文件并进行加载,
* 参数是拿取stats.json的url, 和需要加载stats.json中记录的那一份内容
*/
const getManifest = (url, bundle) => new Promise(async (resolve) => {
    const {data} = await axios.get(url);
    const {entrypoints, publicPath} = data;
    const assets = entrypoints[bundle].assets;
    for(let i = 0; i < assets.length; i++){
        await runScript(publicPath + assets[i]).then(() => {
            if(i === assets.length - 1){
                resolve()
            }
        })
    }
})
  • 此时的registerApplication()方法需要改为以下的形式:
singleSpa.registerApplication(
    'singleChild',
    async () => {
        let vueChild = null;
        await getManifest("http://localhost:3000/stats.json", "app").then(() => {
            vueChild = window.singleChild
        })
        return vueChild;
    },
    location => location.pathname.startsWith("/vue") 
)

这样就完成了一个自动化加载子项目中所有打包的静态文件


关于CSS的样式隔离,大家可以使用postcss-selector-namespace,它会在CSS样式文件中分别加上一个前缀帮助我们完成样式的隔离


关于项目间的状态通信,可以在父项目中定义一个公共的store,将它挂载的window上供子项目共用,当然这只是一种方式,Single-spa文档上也提供了一些方式,感兴趣大家可以去看看。


微前端是最近比较火热的前端方面的变革,所以只要你js足够的好,在微前端你就有无限的可能…

Logo

前往低代码交流专区

更多推荐