微前端-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"

待续。。。

相关推荐
NiNg_1_23420 分钟前
Echarts连接数据库,实时绘制图表详解
前端·数据库·echarts
如若1231 小时前
对文件内的文件名生成目录,方便查阅
java·前端·python
滚雪球~2 小时前
npm error code ETIMEDOUT
前端·npm·node.js
沙漏无语2 小时前
npm : 无法加载文件 D:\Nodejs\node_global\npm.ps1,因为在此系统上禁止运行脚本
前端·npm·node.js
supermapsupport2 小时前
iClient3D for Cesium在Vue中快速实现场景卷帘
前端·vue.js·3d·cesium·supermap
brrdg_sefg2 小时前
WEB 漏洞 - 文件包含漏洞深度解析
前端·网络·安全
胡西风_foxww2 小时前
【es6复习笔记】rest参数(7)
前端·笔记·es6·参数·rest
m0_748254882 小时前
vue+elementui实现下拉表格多选+搜索+分页+回显+全选2.0
前端·vue.js·elementui
星就前端叭3 小时前
【开源】一款基于Vue3 + WebRTC + Node + SRS + FFmpeg搭建的直播间项目
前端·后端·开源·webrtc
m0_748234523 小时前
前端Vue3字体优化三部曲(webFont、font-spider、spa-font-spider-webpack-plugin)
前端·webpack·node.js