让建站和SEO变得简单

让不懂建站的用户快速建站,让会建站的提高建站效率!

Vite 微前端实践,完毕一个组件化的决策
公司资讯

你的位置:迪士尼彩票 > 公司资讯 >

Vite 微前端实践,完毕一个组件化的决策

发布日期:2022-05-15 15:47    点击次数:74

本文转载自微信公众号「前端星辰」,作家旋律 。转载本文请相关前端星辰公众号。

什么是微前端

微前端是一种多个团队通过零丁发布功能的款式来共同构建当代化 web 期骗的时期技巧及法子政策。

微前端鉴戒了微办事的架构理念,将一个广博的前端期骗拆分为多个零丁活泼的袖珍期骗,每个期骗都不错零丁缔造、零丁驱动、零丁部署,再将这些袖珍期骗纠合为一个完整的期骗。微前端既不错将多个样式交融为一,又不错减少样式之间的耦合,培育样式彭胀性,比较一整块的前端仓库,微前端架构下的前端仓库倾向于更小更活泼。

特色 时期栈无关 主框架不落幕接入期骗的时期栈,子期骗可自主选拔时期栈 零丁缔造/部署 各个团队之间仓库零丁,单独部署,互不依赖 增量升级 当一个期骗广博之后,时期升级或重构卓越穷苦,而微应工具备渐进式升级的特色 零丁驱动时 微期骗之间驱动时互不依赖,有零丁的景色经管 培育落幕 期骗越广博,越难以珍贵,相助落幕越低下。微期骗不错很好拆分,培育落幕 面前可用的微前端决策

微前端的决策面前有以下几种类型:

基于 iframe 弥漫断绝的决策

行动前端缔造,咱们对 iframe 仍是很是老到了,在一个期骗中不错零丁驱动另一个期骗。它具有权臣的优点:

很是浅陋,无需任何阅兵 无缺断绝,JS、CSS 都是零丁的驱动环境 不落幕使用,页面上不错放多个 iframe 来组合业务

天然,瑕玷也很是杰出:

无法保持路由景色,刷新后路由景色就丢失 弥漫的断绝导致与子期骗的交互变得极其困难 iframe 中的弹窗无法打破其本人

通盘这个词期骗全量资源加载,加载太慢

这些权臣的瑕玷也催生了其他决策的产生。

基于 single-spa 路由劫持决策

single-spa 通过劫持路由的款式来做子期骗之间的切换,但接入款式需要交融自身的路由,有一定的局限性。

qiankun 孵化自蚂蚁金融科技基于微前端架构的云居品结伴接入平台。它对 single-spa 做了一层封装。主要贬责了 single-spa 的一些痛点和不及。通过 import-html-entry 包认知 HTML 赢得资源旅途,然后对资源进行认知、加载。

通过对推论环境的修改,它完毕了 JS 沙箱、形势断绝 等特色。

京东 micro-app 决策

京东 micro-app 并莫得因循 single-spa 的思绪,而是鉴戒了 WebComponent 的思惟,通过 CustomElement 迷惑自界说的 ShadowDom,将微前端封装成一个类 webComponents 组件,从而完毕微前端的组件化渲染。

在 Vite 上使用微前端

咱们从 咱们从 UmiJS 迁徙到了 Vite 之后,微前端也成为了大势所趋,那时也调研了好多决策。

为什么没用 qiankun

qiankun 是面前是社区主流微前端决策。它天然很完善、流行,但最大的问题即是不相持 Vite。它基于 import-html-entry 认知 HTML 来赢得资源,由于 qiankun 是通过 eval 来推论这些 js 的施行,而 Vite 中的 script 标签类型是 type="module",内部包含 import/export 等模块代码, 是以会报错:不允许在非 type="module" 的 script 内部使用 import。

退一步完毕,咱们摄取了 single-spa 的款式,并使用 systemjs 的款式进行了微前端加载决策,也踩了不少的坑。single-spa 莫得一个友好的教程来接入,文档天然多,但大多都在讲成见,那时让人合计有一种深邃的嗅觉。

自后看了它的源码发现,这都是些什么……内部大部分代码都是围绕路由劫持而伸开的,根柢莫得文档上那种魁岸上的嗅觉。而咱们又用不到它路由劫持的功能,那咱们为什么要用它?

从组件化的层面来说 single-spa 这种款式完毕得少量都不优雅。

它劫持了路由,与 react-router 和组件化的思维格不相入 接入款式一大堆零星的设立 单实例的决策,即团结时刻,唯有一个子期骗被展示

自后琢磨着 single-spa 的瑕玷,咱们不错我方完毕一个组件化的微前端决策。

怎样完毕一个浅陋、透明、组件化的决策

通过组件化思维完毕一个微期骗很是浅陋:子期骗导出一个法子,主期骗加载子期骗并调用该法子,并传入一个 Element 节点参数,子期骗得到该 Element 节点,将本人的组件 appendChild 到 Element 节点上。

类型商定

在此之前咱们需要商定一个主期骗与子期骗之间的一个交互款式。主要通过三个钩子来保证期骗的正确推论、更新、和卸载。

类型界说:

export interface AppConfig {   // 挂载   mount?: (props: unknown) => void;   // 更新   render?: (props: unknown) => ReactNode | void;   // 卸载   unmount?: () => void; } 

子期骗导出

通过类型的商定,咱们不错将子期骗导出:mount、render、unmount 为主要钩子。

React 子期骗完毕:

export default (container: HTMLElement) => {   let handleRender: (props: AppProps) => void;    // 包裹一个新的组件,用作更新处理   function Main(props: AppProps) {     const [state, setState] = React.useState(props);     // 将 setState 法子索要给 render 函数调用,保持父子期骗触发更新     handleRender = setState;     return <App {...state} />;   }    return {     mount(props: AppProps) {       ReactDOM.render(<Main {...props} />, container);     },     render(props: AppProps) {       handleRender?.(props);     },     unmount() {       ReactDOM.unmountComponentAtNode(container);     },   }; }; 

 

Vue 子期骗完毕:

import { createApp } from 'vue'; import App from './App.vue';  export default (container: HTMLElement) => {   // 创建   const app = createApp(App);   return {     mount() {       // 装载       app.mount(container);     },     unmount() {       // 卸载       app.unmount();     },   }; }; 
主期骗完毕

React 完毕

其中枢代码仅十余行,主要处理与子期骗交互 (为了易读性,袒护了无理处理代码):

export function MicroApp({ entry, ...props }: MicroAppProps) {   // 传递给子期骗的节点   const containerRef = useRef<HTMLDivElement>(null);   // 子期骗设立   const configRef = useRef<AppConfig>();    useLayoutEffect(() => {     import(/* @vite-ignore */ entry).then((res) => {       // 将 div 传给子期骗渲染       const config = res.default(containerRef.current);       // 调用子期骗的装载法子       config.mount?.(props);       configRef.current = config;     });     return () => {       // 调用子期骗的卸载法子       configRef.current?.unmount?.();       configRef.current = undefined;     };   }, [entry]);    return <div ref={containerRef}>{configRef.current?.render?.(props)}</div>; } 

完成,当今仍是完毕了主期骗与子期骗的装载、更新、卸载的操作。当今,它是一个组件,不错同期渲染出多个不同的子期骗,这点就比 single-spa 优雅好多。

entry 子期骗地址,天然真实情况会凭证 dev 和 prod 模式给出不同的地址:

<MicroApp className="micro-app" entry="//localhost:3002/src/main.tsx" /> 

Vue 完毕

<script setup lang="ts"> import { onMounted, onUnmounted, ref } from 'vue';  const { entry, ...props } = defineProps<{ entry: string }>(); const container = ref<HTMLDivElement | null>(null); const config = ref();  onMounted(() => {   const element = container.value;   import(/* @vite-ignore */ entry).then((res) => {     // 将 div 传给子期骗渲染     const config = res.default(element);     // 调用子期骗的装载法子     config.mount?.(props);     config.value = config;   }); });  onUnmounted(() => {   // 调用子期骗的卸载法子   config.value?.unmount?.(); }); </script>  <template>   <div ref="container"></div> </template>

怎样让子期骗也能零丁驱动

single-spa 等宽绰决策,都是将一个变量挂载到 window 上,通过判断该变量是否处于微前端环境,这么很不优雅。在 ESM 中,咱们不错通过 import.meta.url 传入参数来判断:

if (!import.meta.url.includes('microAppEnv')) {   ReactDOM.render(     <React.StrictMode>       <App />     </React.StrictMode>,     document.getElementById('root'),   ); } 

进口导入修改:

// 添加环境参数和面前时间幸免被缓存 import(/* @vite-ignore */ `${entry}?microAppEnv&t=${Date.now()}`); 

浏览器兼容性

IE 浏览器仍是徐徐退出咱们的视线,基于 Vite,咱们只需要相持 import 的特色浏览器就够了。天然,淌若研讨 IE 浏览器的话也不是不成以,很浅陋:将上头代码的 import 替换为 System.import 即 systemjs,亦然 single-spa 的所顾惜的用法。

浏览器 Chrome Edge Firefox Internet Explorer Safari import 61 16 60 No 10.1 Dynamic import 63 79 67 No 11.1 import.meta 64 79 62 No 11.1

模块公用

咱们的子组件必须要使用 mount 、unount 模式吗?谜底是不一定,淌若咱们的时期栈都是 React 的话。咱们的子期骗只导出一个 render 就够了。这么用的即是团结个 React 来渲染,平正是子期骗不错破钞父期骗的 Provider。但有个前提是两个期骗之间的 React 必须为团结个实例,不然就会报错。

咱们不错将 react、react-dom 、styled-componets 等常用模块提前打包成 ESM 模块,然后放到文献办事中使用。

调动 Vite 设立添加 alias:

defineConfig({   resolve: {     alias: {       react: '//localhost:8000/react@17.js',       'react-dom': '//localhost:8000/react-dom@17.js',     },   }, }); 

这么就能自地面使用团结份 React 代码了。还能抽离出主期骗和子期骗之间的公用模块,让期骗总体积更小。天然淌若没上 http2 的话,就需要研讨颗粒度的问题了。

在线 CDN 决策:https://esm.sh

还有个 importmap 决策,兼容性不太好,但改日是趋势:

<script type="importmap">   {     "imports": {       "react": "//localhost:8000/react@17.js"     }   } </script

 

父子通讯

组件式微期骗,不错传递参数而通讯,弥漫即是 React 组件通讯的模子。

资源旅途

import logo from './images/logo.svg';  <img src={logo} />; 

在 Vite 的 dev 模式中,子期骗内部静态资源一般会这么引入:

import logo from './images/logo.svg';  <img src={logo} />; 

图片的旅途:/basename/src/logo.svg,在主期骗浮现就会 404。因为该旅途仅仅存在于子期骗。咱们需要配合 URL 模块使用,这么旅途前边会带上 origin 前缀:

const logoURL = new URL(logo, import.meta.url);  <img src={logoURL.href} />; 

天然这么使用比较繁琐,咱们不错将其封装为一个 Vite 插件自动处理该场景。

路由同步

样式使用 react-router,那么它可能会存在路由不同步的问题,因为不是团结个 react-router 实例。即路由之间出现不联动的景象。

在 react-router 相持自界说 history 库,咱们不错创建:

import { createBrowserHistory } from 'history';  export const history = createBrowserHistory();  // 主期骗:路由进口 <HistoryRouter history={history}>{children}</HistoryRouter>;  // 主期骗:传递给子期骗 <Route   path="/child-app/*"   element={<MicroApp entry="//localhost:3002/src/main.tsx" history={history} />} />;  // 子期骗:路由进口 <HistoryRouter basename="/child-app" history={history}>   {children} </HistoryRouter>; 

最终子期骗使用团结份 history 模块。天然这不是独一的完毕,也不是优雅的款式,咱们不错将路由实例 navigate 传递给子期骗,这么也能完毕路由的交互。

留意:子期骗的 basename 必须与主期骗的 path 称呼保持一致。这里还需要修改 Vite 的设立 base 字段:

export default defineConfig({   base: '/child-app/',   server: {     port: 3002,   },   plugins: [react()], }); 

JS 沙箱

因为沙箱在 ESM 下不相持,因为无法动态改变推论环境中模块 window 对象,也无法注入新的全局对象。

一般 React、Vue 样式也很少修改全局变量,做好代码轨范检查才是最主要的。

CSS 形势断绝

自动 CSS 形势断绝是有代价的,一般咱们冷落子期骗使用不同的 CSS 前缀,再配合 CSS Modules 基本上能完毕需求。

打包部署

部署不错凭证子期骗的 base 甩掉在不同的目次,并将称呼对应。设立好 nginx 转发步伐就不错了。咱们不错将子期骗结伴路由前缀,便于 nginx 将主期骗诀别开并设立通用步伐。

比如将主期骗甩掉在 system 目次,子期骗甩掉在 app- 开始的目次:

location ~ ^/app-.*(\..+)$ {     root /usr/share/nginx/html; }  location / {     try_files $uri $uri/ /index.html;     root /usr/share/nginx/html/system;     index  index.html index.htm; } 

优点

1. 浅陋 中枢不及 100 行代码,无需过剩的文档

2. 活泼 通过商定的款式接入,也不错渐进增强

3. 透明 无任何劫持决策,更多逻辑透明性

4. 组件化 组件化的渲染及参数通讯

5. 基于 ESM 相持 Vite,面向改日

6. 向下兼容 可选 SystemJS 决策,兼容低版块浏览器

有示例吗

示例代码在 Github,感兴味的知己不错 clone 下来学习。由于咱们的时期栈是 React,是以这里示例的主期骗的完毕用的是 React 。

微前端组件(React):https://github.com/MinJieLiu/micro-app

微前端示例:https://github.com/MinJieLiu/micro-app-demo

结语

微前端的决策适合团队场景的最佳,打造一个团队能掌控的决策尤为遑急。

参考府上:

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/import.meta

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/import

 

 



友情链接:

Powered by 迪士尼彩票 @2013-2022 RSS地图 HTML地图

Copyright 365建站 © 2013-2021 365建站器 版权所有