微前端-qiankun(探索)

记录学习微前端

技术栈

  • lerna
  • qiankun
  • vue
  • tailwind

lerna

管理具有多个独立包的 JavaScript 项目的工具。

优点

  • 模块化:每个子应用可以作为一个单独的包,这样可以确保每个子应用的代码独立、可重用,并且可以独立开发和发布。
  • 代码共享:如果你的子应用之间有一些共享的组件或库,Lerna 可以帮助你创建一个公共的包,所有子应用都可以引用这个公共包。

初始化

安装

bash 复制代码
npm install lerna -g

初始化lerna

bash 复制代码
lerna init

创建packages文件 之后管理的包都放在此处

创建主应用

为了实现keepalive我这里还是使用vue2. 使用vuecli去创建vue2项目。

在需要渲染子应用的地方添加

vue 复制代码
 <div id="sub-container"></div>
  • sub-container:为应用渲染节点
vue 复制代码
load() {
      const apps = registerMicroApps([
        {
          name: "cust",
          entry: "//localhost:3002",
          container: "#sub-container",
          activeRule: "/cust-vue",
        },
        {
          name: "user",
          entry: "//localhost:3001",
          container: "#sub-container",
          activeRule: "/user-vue",
          props: {
            changeRouter: (path) => {
              console.log(path);
              this.$router.push(path);
            },
            menus: menus.menus,
            store,
          },
        },
      ]);
      console.log(apps);
      // 启动 qiankun
      start({
        sandbox: { strictStyleIsolation: true },
      });
    },
  • registerMicroApps、start来自qiankun库
  • name:子应用名称(唯一)
  • entry:子应用入口
  • container:渲染节点
  • activeRule:触发渲染子应用前缀
  • props:向子应用传递的属性

创建子应用

依然是vue2

修改webpack配置

js 复制代码
const { defineConfig } = require("@vue/cli-service");
const packageName = require("./package.json").name;
module.exports = defineConfig({
  transpileDependencies: true,
  devServer: {
    port: 3001,
    headers: {
      "Access-Control-Allow-Origin": "*",
    },
  },
  configureWebpack: {
    output: {
      library: `${packageName}-[name]`,
      libraryTarget: "umd",
      chunkLoadingGlobal: `webpackJsonp_${packageName}`,
    },
  },
});

在src下新建public-path.js注入全局变量

js 复制代码
if (window.__POWERED_BY_QIANKUN__) {
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

在main.js中加入生命周期代码

js 复制代码
let router = null;
let instance = null;
function render(props = {}) {
  const { container, store: mainStore, changeRouter, menus } = props;
  Vue.prototype.$changeRouter = changeRouter;
  if (mainStore) {
    store.replaceState({
      ...store.state,
      ...mainStore.state,
    });
    store.subscribe((mutation) => {
      mainStore.commit(mutation.type, mutation.payload);
    });
  }
  Vue.use(ElementUI);
  let routeList = [];
  if (menus) {
    routeList = routes.filter((route) => {
      return menus.some((menu) => {
        return menu.path === route.path;
      });
    });
  }

  router = new VueRouter({
    base: window.__POWERED_BY_QIANKUN__ ? "/user-vue/" : "/",
    mode: "history",
    store,
    routes: routeList,
  });

  if (!window.__CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE__) {
    // 第一次加载时,创建 Vue 实例

    instance = new Vue({
      router,
      store,
      render: (h) => h(App),
    }).$mount(container ? container.querySelector("#app") : "#app");
  } else {
    const cachedInstance = window.__CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE__;
    // 让当前路由在最初的 Vue 实例上可用
    router.apps.push(...cachedInstance.$router.apps);

    instance = new Vue({
      router,
      render: () => cachedInstance._vnode, // 从最初的 Vue 实例上获得 _vnode
    });

    // 缓存最初的 Vue 实例
    instance.cachedInstance = cachedInstance;

    router.onReady(() => {
      const { fullPath } = router.currentRoute;
      const { fullPath: oldFullPath } = cachedInstance.$router.currentRoute;
      // 当前路由的 fullPath 和上一次卸载时不一致,则切换至新路由
      if (fullPath !== oldFullPath) {
        cachedInstance.$router.replace(fullPath);
      }
    });

    instance.$mount(container ? container.querySelector("#app") : "#app");
  }
}

// 独立运行时
if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

export async function bootstrap() {
  console.log("[vue] vue app bootstraped");
}
export async function mount(props) {
  console.log("[vue] props from main framework", props);
  render(props);
}
export async function unmount() {
  console.log("[vue] vue app unmount");
  const cachedInstance = instance.cachedInstance || instance;
  window.__CACHE_INSTANCE_BY_QIAN_KUN_FOR_VUE__ = cachedInstance;
  const cachedNode = cachedInstance._vnode;
  cachedNode.data.keepAlive = true;
  cachedNode.data.hook.destroy(cachedNode);
  if (instance.cachedInstance) {
    instance.$destroy();
    instance = null;
  }
  router = null;
}

添加公用包css

使用lerna创建共用库

bash 复制代码
lerna create common-style

具体结构如下

安装tailwindcss并初始化创建配置文件

bash 复制代码
npm install -D tailwindcss
npx tailwindcss init

修改配置文件

js 复制代码
/** @type {import('tailwindcss').Config} */
module.exports = {
// content: ["./src/**/*.{html,js}"],
  content: ["../../packages/**/src/**/*.{vue,js,ts,jsx,tsx}"],
  theme: {
    extend: {},
  },
  plugins: [],
}
  • 这样我们在修改主应用和子应用代码的时候都能检测到修改重新输出新的css文件

在input.css中添加代码

css 复制代码
@tailwind base;
@tailwind components;
@tailwind utilities;
bash 复制代码
npx tailwindcss -i ./input.css -o ./output.css --watch

在common-style.js中导出output.css

javascript 复制代码
export * from "./output.css";

其他包引入公用库: 例如在主应用中引入

json 复制代码
//主应用package.json中添加依赖
 "dependencies": {
    "common-style":"0.0.0"//你的版本号
 }

执行安装命令

js 复制代码
yarn install

在需要使用的地方引入

js 复制代码
import "common-style"

待续。。。

相关推荐
软件技术NINI7 分钟前
html知识点框架
前端·html
深情废杨杨11 分钟前
前端vue-插值表达式和v-html的区别
前端·javascript·vue.js
GHUIJS11 分钟前
【vue3】vue3.3新特性真香
前端·javascript·vue.js
众生回避17 分钟前
鸿蒙ms参考
前端·javascript·vue.js
洛千陨17 分钟前
Vue + element-ui实现动态表单项以及动态校验规则
前端·vue.js
GHUIJS1 小时前
【vue3】vue3.5
前端·javascript·vue.js
&白帝&2 小时前
uniapp中使用picker-view选择时间
前端·uni-app
魔术师卡颂2 小时前
如何让“学源码”变得轻松、有意义
前端·面试·源码
谢尔登2 小时前
Babel
前端·react.js·node.js
ling1s2 小时前
C#基础(13)结构体
前端·c#