亚洲国内愉拍自拍它具有权贵的优点: 相称通俗
发布日期:2022-09-23 05:21    点击次数:142
美妇欲仙欲死好涨噢噢亚洲国内愉拍自拍

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

什么是微前端

微前端是一种多个团队通过孤独发布功能的方式来共同构建当代化 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