微前端之IceStark介绍和基础使用

简介: 微前端之IceStark介绍和基础使用

1. 介绍


阿里飞冰团队于 2019 年 6 月发布的微前端框架,内部服务于淘宝、飞猪、钉钉、阿里云等大型项目。


IceStark 支持以下特性:


  1. 框架无关(也是所有微前端框架都会解决的问题)


  1. 快速迁移:支持从 url,html 文本,entry应用入口文件等方式直接接入


  1. 完善的 js 沙箱,使微应用接入更安全


  1. 性能优秀(大家都优秀)


  1. 支持 ES Module


  1. 支持微模块(Single SPA 也有)


IceStark 本身并不是基于 Single SPA 进行二次开发,而是自身内部实现了一套微应用加载逻辑,但是在浏览器历史记录处理(前进与回退等事件需要渲染对应子应用)也借鉴了部分 Single SPA 的逻辑;并提供了与 SingleSPA 类似的配置 API,兼容 Single SPA 微应用。


2. 主应用


IceStark 的官方文档中,对主应用职责进行了说明:


  1. 负责整体系统 Layout 布局


  1. 注册和配置微应用


  1. 避免过多样式代码


  1. 减少公共 API 暴露,避免应用耦合


IceStark 主应用构建与 qiankun 比较相似,都通过 registerMicroApps 来注册微应用,通过 start 开启路由劫持来加载/卸载微应用。


应该是因为官方内部使用 React 更多一点,所以对 React 的应用注册提供了 AppRouterAppRoute 来替换原有的 ReactRouter,直接注册微应用。


因为笔者对 React 不是很熟悉,大家可以看 官方文档 - React 主应用接入


首先,引入 IceStark


npm install @ice/stark --save


然后,创建 micro-apps.ts


import { registerMicroApps, start } from '@ice/stark';
import NProgress from 'nprogress'
import "nprogress/nprogress.css";
const getDivDom:HTMLElement = (id: string) => document.getElementById(id)
// 子应用
const microApps = [
  {
    name: 'sub-app-one',
    entry: '//localhost:3001',
    activeRule: '/sub-one'
  }
]
// 生命周期处理
const lifeCycles = {
  beforeMount: (app: any) => {
    console.log("before mount app.name====>>>>>", app.name)
  },
  afterMount: (app: any) => {
    console.log("after mount app.name====>>>>>", app.name)
  },
  afterUnmount: (app: any) => {
    console.log("after unmount app.name====>>>>>", app.name)
  }
}
// 子应用处理
const normalizeMicroApp = (apps = []) => {
  return apps.map((app: any) => ({
    container: getDivDom('#sub-container'),
    ...app
  }))
}
const register = () => registerMicroApps(normalizeMicroApp(microApps), lifeCycles)
export default {
    register,
    start
}


这里有以下几点需要注意:


  1. IceStark 子应用生命周期与 qiankun 等不同,没有 beforeLoad,增加了 beforeUpdateafterUpdate


  1. 子应用挂载节点配置 container 需要是一个 HTMLElement 节点,而不是字符串


  1. 子应用配置没有 loader 选项,如果要配置加载过度动画,需要配置到 start 方法。


// 这里对上面的 start 方法进行一下扩展
const startLoading = () => NProgress.start()
const endLoading = () => NProgress.done()
const startApp = (props: StartConfiguration) => {
  start({
    onAppEnter: startLoading,
    onFinishLoading: endLoading,
    onError: endLoading,
    ...props
  })
}


3. 子应用


微应用接入与 qiankunSingle SPA 基本一致,都需要子应用打包成 umd 格式,在入口处导出声明周期函数,并且都需要给子应用配置基准路由,避免无法匹配。


IceStark 微应用不需要 bootstrap 初始化周期


3.1 Webpack 应用


使用 Vue CLICRA(create react app) 之类的脚手架创建的应用都算是 webpack 应用,这里还是以 Vue 作为例子。


首先,依然是修改 main.ts 入口文件


import Vue from 'vue';
import VueRouter from 'vue-router';
import isInIcestark from '@ice/stark-app/lib/isInIcestark';
import setLibraryName from '@ice/stark-app/lib/setLibraryName';
import App from './App.vue';
import routes from './router';
let router = null;
let instance = null;
function render(props = {}) {
  const { container } = props;
  router = new VueRouter({
    base: window.__POWERED_BY_QIANKUN__ ? '/app-vue/' : '/',
    mode: 'history',
    routes,
  });
  instance = new Vue({
    router,
    store,
    render: (h) => h(App),
  }).$mount(container || '#app');
}
// 注意:`setLibraryName` 的入参需要与 webpack 工程配置的 output.library 保持一致
setLibraryName('microApp');
export function mount(props) {
  render(props)
}
export function unmount() {
  instance && instance.$destroy();
  instance.$el.innerHTML = '';
  instance = null;
  router = null;
}
if (!isInIcestark()) {
  render()
}


这里的 VueRouter 路由实例可以拆分出来单独实例化。IceStark 提供了 getBasename 方法用来查询 setLibraryName 时定义的微应用基准路由。


import Vue from 'vue';
import Router from 'vue-router';
import getBasename from '@ice/stark-app/lib/getBasename';
Vue.use(Router);
export default new Router({
  mode: 'history',
  base: getBasename(),
  routes: [
    // ...
  ],
});


最后,也是修改 webpack 配置


module.exports = {
  output: {
    // 设置模块导出规范为 umd
    libraryTarget: 'umd',
    // 与上文 setLibraryName 设置的名称一致
    library: 'microApp',
  }
}


3.2 Vite 子应用


Vite 子应用的应用实例化和路由配置与上面基本一致,这里不再赘述


根据官方文档,这里接入 Vite 子应用有两种方式:


1. 使用 Vite Lib 模式,指定入口为 main.ts


cimport { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
  plugins: [vue()],
  build: {
    lib: {
      entry: './src/main.ts',
      formats: ['es'],
      fileName: 'index'
    },
    rollupOptions: {
      preserveEntrySignatures: 'exports-only',
    }
  },
})


但是这种方式会造成子应用无法单独运行(也可以改造成按照环境变量来设置不同的 lib 配置)


**2. 使用官方插件  vite-plugin-index-html **


import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import htmlPlugin from 'vite-plugin-index-html';
export default defineConfig({
  plugins: [
    vue(),
   htmlPlugin({
     input: './src/main.ts', // 指定确定的入口文件
     preserveEntrySignatures: "exports-only", // 确保入口文件导出生命周期函数
   })
  ],
})


🚀 因为 Vite 应用启动时采用的是原生 ES Module,所以主应用在注册时需要声明脚本加载方式:


registerMicroApps([
  {
    name: 'seller',
    activePath: '/seller',
    // ...
    loadScriptMode: 'import', // 指定加载 ES modules 类型微应用
  }
])


4. 样式隔离


🚀🚀 样式隔离通常指子应用间的样式隔离,主应用如果没有过分区分的话,有可能会对子应用样式造成影响。


这里的隔离方式推荐以下几种:


  1. CSS Module:主应用与子应用都采用该方式,就可以避免主应用样式影响


  1. 主应用与微应用避免重复使用样式重置插件,建议提取到主应用即可


  1. 对于不同的应用间有不同UI库或者主题配置,建议修改类名前缀


不论是 qiankun 还是 IceStark,这类微前端框架通常都能做到 JS 的沙箱隔离,但是对于样式隔离的问题,大家采用的方案一般都是 Shadow DOM,但是 Shadow DOM 并不能解决 Dialog 这类直接插入到 body 节点下的组件样式,并且兼容性也待商榷。所以,采用 CSS Module 并约定一个良好的应用样式规范,才是避免样式冲突最好的方法。


目录
相关文章
|
编解码 前端开发 数据处理
前端基础向--从项目入手封装公共组件
前端基础向--从项目入手封装公共组件
303 0
|
Web App开发 存储 JavaScript
开启前端全栈之路—— node 基础
能够和后端程序员更加紧密的配合。 网站业务逻辑前置,学习前端技术需要后端技术支撑(Ajax)。 扩展知识视野,能够站在更高的角度审视整个项目。
142 0
开启前端全栈之路—— node 基础
|
域名解析 网络协议 前端开发
[前端必知 ]HTTP or TCP/IP 基础
[前端必知 ]HTTP or TCP/IP 基础
124 0
|
域名解析 缓存 网络协议
前端基础的几个概念
《基础系列》
138 0
|
JSON 前端开发 JavaScript
Web前端学习:jQuery基础 · 小终结【异步处理AJAX】
Web前端学习:jQuery基础 · 小终结【异步处理AJAX】
123 0
Web前端学习:jQuery基础 · 小终结【异步处理AJAX】
|
前端开发 JavaScript
Web前端学习:jQuery基础--3【jquery操作样式类名、添加元素、jQuery-CSS()方法】
Web前端学习:jQuery基础--3【jquery操作样式类名、添加元素、jQuery-CSS()方法】
174 0
Web前端学习:jQuery基础--3【jquery操作样式类名、添加元素、jQuery-CSS()方法】
|
缓存 前端开发 数据可视化
前端基础向--空表格处理与分页调整,优化用户体验
前端基础向--空表格处理与分页调整,优化用户体验
192 0
|
前端开发 算法 数据处理
前端基础向~从项目出手封装工具函数
前端基础向~从项目出手封装工具函数
164 0
|
前端开发 安全 JavaScript
前端基础向~封装Axios优化页面Loading效果
前端基础向~封装Axios优化页面Loading效果
370 0
|
JavaScript
Vue 2 阅读理解(四)之 Parse 函数定义
Vue 2 阅读理解(四)之 Parse 函数定义
218 0