likes
comments
collection
share

微前端(wujie)源码解析-3.wujie-vue

作者站长头像
站长
· 阅读数 3

wujie-vue2源码解析

前言

在前面的文章中,我们对无界的使用方式和整体架构进行了分析,wujie-vue2、wujie-vue3、wujie-react作为无界架构中的重要一环,起到了桥梁的作用,对不同框架做了适配,今天我们就来分析一下wujie-vue2的源码,看看它是如何实现的

整体架构

当我们使用无界时,通常是这样使用的:

<WujieVue width="100%" height="100%" name="vue2" :url="'//localhost:7200/'" :sync="true"></WujieVue>

wujie-vue2的整体架构如下图所示:

微前端(wujie)源码解析-3.wujie-vue

wujie-vue2依赖wujie,并且以插件的形式暴露出一个组件,这样我们在开发的时候就可以使用这个组件了

目录结构

我们先来看下wujie-vue2的目录结构

├── package.json
├── webpack.config.js
├── index.js

还有一些其他的文件,这里就不一一列举了,我们主要看下index.js文件,这个文件是wujie-vue2的入口文件。

核心代码

我们首先看下wujie-vue2的入口文件index.js的输入输出

import Vue from "vue";
import { bus, preloadApp, startApp as rawStartApp, destroyApp, setupApp } from "wujie";

const wujieVueOptions = {
 // code...
};

const WujieVue = Vue.extend(wujieVueOptions);

WujieVue.setupApp = setupApp;
WujieVue.preloadApp = preloadApp;
WujieVue.bus = bus;
WujieVue.destroyApp = destroyApp;
WujieVue.install = function (Vue) {
Vue.component("WujieVue", WujieVue);
};

export default WujieVue;

这段代码比较简单,我们来分析下:

  1. 首先我得依赖了vue以及wujie
  2. 定义了一个wujieVueOptions对象,这个对象是wujie-vue2的配置项,我们后面会详细分析
  3. 通过Vue.extend方法创建了一个WujieVue组件,这个组件就是我们在开发的时候使用的组件
  4. WujieVue组件上挂载了一些方法,包括注册组件、预加载、事件通信、销毁
  5. 最后挂载了一个install方法,这个方法我们比较熟悉,就是vue插件的安装方法,让后将WujieVue暴露出去

现在我们分析下wujie-vue2的配置项wujieVueOptions

const wujieVueOptions = {
    name: "WujieVue",
    props: {
       width: { type: String, default: "" },
       height: { type: String, default: "" },
       name: { type: String, default: "" },
       loading: { type: HTMLElement, default: undefined },
       url: { type: String, default: "" },
       sync: { type: Boolean, default: undefined },
       prefix: { type: Object, default: undefined },
       alive: { type: Boolean, default: undefined },
       props: { type: Object, default: undefined },
       attrs: {type: Object, default: undefined},
       replace: { type: Function, default: undefined },
       fetch: { type: Function, default: undefined },
       fiber: { type: Boolean, default: undefined },
       degrade: { type: Boolean, default: undefined },
       plugins: { type: Array, default: null },
       beforeLoad: { type: Function, default: null },
       beforeMount: { type: Function, default: null },
       afterMount: { type: Function, default: null },
       beforeUnmount: { type: Function, default: null },
       afterUnmount: { type: Function, default: null },
       activated: { type: Function, default: null },
       deactivated: { type: Function, default: null },
       loadError: { type: Function, default: null },
    },
    data() {
       return {
          startAppQueue: Promise.resolve(),
       };
    },
    mounted() {
       bus.$onAll(this.handleEmit);
       this.execStartApp();
       this.$watch(
          () => this.name + this.url,
          () => this.execStartApp()
       );
    },
    methods: {
       handleEmit(event, ...args) {
          this.$emit(event, ...args);
       },
       async startApp() {
          try {
             // $props 是vue 2.2版本才有的属性,所以这里直接全部写一遍
             await rawStartApp({
                name: this.name,
                url: this.url,
                el: this.$refs.wujie,
                loading: this.loading,
                alive: this.alive,
                fetch: this.fetch,
                props: this.props,
                attrs: this.attrs,
                replace: this.replace,
                sync: this.sync,
                prefix: this.prefix,
                fiber: this.fiber,
                degrade: this.degrade,
                plugins: this.plugins,
                beforeLoad: this.beforeLoad,
                beforeMount: this.beforeMount,
                afterMount: this.afterMount,
                beforeUnmount: this.beforeUnmount,
                afterUnmount: this.afterUnmount,
                activated: this.activated,
                deactivated: this.deactivated,
                loadError: this.loadError,
             });
          } catch (error) {
             console.log(error);
          }
       },
       execStartApp() {
          this.startAppQueue = this.startAppQueue.then(this.startApp);
       },
       destroy() {
          destroyApp(this.name);
       },
    },
    beforeDestroy() {
       bus.$offAll(this.handleEmit);
    },
    render(c) {
       return c("div", {
          style: {
             width: this.width,
             height: this.height,
          },
          ref: "wujie",
       });
    },
};

整体上看,这个配置项就是一个vue组件的配置项,我们在开发的时候,可以通过这些配置项来配置我们的组件

我们还是以上文提到的例子为入手点,从组件实际使用时的生命周期的角度来分析下:

  1. 当我们使用该组件的时候,props会传入一些参数,例如:width、height、name、url等等,这几个参数都比较好理解,width、height是组件的宽高,name是组件的名称,url是组件的地址
  2. render函数会渲染一个div,这个div的宽高就是我们传入的width和height,这个div的ref是wujie
  3. 当组件挂载到页面上的时候,会执行mounted钩子函数,这个钩子函数中,做了三件事情:
    1. 监听了bus的所有事件,当bus触发事件的时候,会执行handleEmit方法,这个方法会将bus的事件转化为vue的事件,然后触发vue的事件
    2. 执行了execStartApp方法,这个方法会执行startApp方法,继而会执行rawStartApp方法,这个方法是重点,我们以后会分析,这个函数最终会渲染一个子应用
    3. 添加watch监听了nameurl的变化,当nameurl发生变化的时候,会重新执行execStartApp方法,完成组件的重新渲染
  4. 当组件销毁的时候,会执行beforeDestroy钩子函数,钩子函数会移除bus的所有事件监听

总结

wujie-vue2的源码简单清晰,主要是对wujie的封装,最终暴露出一个我们大家都认识的组件,我们在开发的时候,只需要像正常使用一个组件一样使用它就可以了,非常方便。在wujie-vue3,wujie-react中,也是类似的封装逻辑,大家有兴趣可以自行了解。

下期预告:我们将开始分析wujie-core的源码,敬请期待。

今天的分享就到这里,感谢大家的阅读,如果大家喜欢我的文章,可以给个star,你们的支持是我写作的最大动力,谢谢大家!

如果觉得本文有帮助 记得点赞三连哦 十分感谢!

系列链接