2024年Web前端最全如何在微前端中加载 Vite 应用?,字节跳动面试是什么意思

前端资料汇总

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

我一直觉得技术面试不是考试,考前背背题,发给你一张考卷,答完交卷等通知。

首先,技术面试是一个 认识自己 的过程,知道自己和外面世界的差距。

更重要的是,技术面试是一个双向了解的过程,要让对方发现你的闪光点,同时也要 试图去找到对方的闪光点,因为他以后可能就是你的同事或者领导,所以,面试官问你有什么问题的时候,不要说没有了,要去试图了解他的工作内容、了解这个团队的氛围。
找工作无非就是看三点:和什么人、做什么事、给多少钱,要给这三者在自己的心里划分一个比例。
最后,祝愿大家在这并不友好的环境下都能找到自己心仪的归宿。

  由于无需打包的特性,服务器冷启动时间超快

借助 ES modules 的能力,模块化交给浏览器处理(虽然目前的阶段存在一个预编译的过程)。传统构建器需要打包依赖和源码,才能构建整个应用,并提供服务。

  项目大小不再成为限制项目热更新速度的因素

传统构建器在代码更改时,需要重新构建并载入页面,这样带来的的结果是:随着项目体积增长,构建耗时越长。基于 ES modules 的构建器只进行单文件编译,单文件更新,时间复杂度保持 O(1).

Vite 得益于原生 ES modules 的能力,大幅提升了开发时体验。相信未来,随着社区生态(CDN 服务、Deno)、ESM 相关标准(import-maps、import.meta)的逐步完善,以及越来越多的技术方案解决 ES modules 在浏览器端的相关难题(依赖瀑布,资源碎片化),前端会开启一个无构建的新篇章。

同时在微前端领域,脚本资源的打包规范向来是百花齐放(比如 singleSPA 默认支持 SystemJs 规范,icestark 默认支持 UMD 规范)。未来脚本资源的打包规范必定是趋于统一的 ES modules 规范。正是基于这两个原因,微前端支持 ES modules 应用的加载就成了用户强诉求。

微前端加载 Vite 应用

  加载 ES modules 微应用

Vite 会默认打包出符合标准的 ES modules 的脚本资源。ES modules 资源的加载方式如下:

然而,在 icestark 中需要依赖微应用导出 生命周期函数 来渲染微应用。使用 <script > 标签加载 ES modules 脚本的一个难题在于无法获取微应用导出的生命周期函数。基于这个考虑,实际实现中是通过 Dynamic Import 来加载脚本:

const { mount, unmount } = await import(url);

Dynamic Import 的浏览器兼容性如下:

774a22542d033bc01833ea23a3ee0a92.png

可以认为,支持 ES modules 的浏览器版本,对 Dynamic Import 的支持也非常良好。同时,为了兼容旧版浏览器,通过 new Function() 将其包裹:

const dynamicImport = new Function(‘url’, ‘return import(url)’);

const { mount, unmount } = await dynamicImport(url);

至此,除了能支持 IIFE / UMD 规范的微应用之外,icestark 支持了 ES modules 规范的应用加载,并通过 import 类型标识。icestark 整体加载流程图如下:

d9804538914fcec085610251c17ed023.png

  Vite 应用的改造

对于微应用而言,需要导出 生命周期函数,并选择合适的加载方式即可。

生命周期函数的接入非常简单,在 Vite 应用的入口文件(Vue 项目通常是 main.t|js,React 应用通常是 app.t|jsx)声明函数(以 Vue 应用为例):

import { createApp } from ‘vue’

  • import type { App as Root} from ‘vue’;

import App from ‘./App.vue’

  • import isInIcestark from ‘@ice/stark-app/lib/isInIcestark’;
  • createApp(App).mount(‘#app’);
  • let vue: Root | null = null;

  • if (!isInIcestark()) {

  • createApp(App).mount(‘#app’);

  • }

  • // 导出 mount 生命周期函数

  • export function mount({ container }: { container: Element}) {

  • vue = createApp(App);

  • vue.mount(container);

  • }

  • // 导出 unmount 生命周期函数

  • export function unmount() {

  • if (vue) {

  • vue.unmount();
    
  • }

}

然而,在实际构建过程中,我们发现声明的函数并没有在脚本资源中导出。这是个非常疑惑的点,让我们深入到 Vite 的源码,并在内置的 vite:build-html 找寻到一些蛛丝马迹:

if (isModule) {

inlineModuleIndex++

if (url && !isExcludedUrl(url)) {

//

// add it as an import

js += \nimport ${JSON.stringify(url)}

shouldRemove = true

} else if (node.children.length) {

//

js += \nimport "${id}?html-proxy&index=${inlineModuleIndex}.js"

shouldRemove = true

}

}

Vite 默认使用 index.html 作为入口,在解析 index.html 的过程中,会生成一个虚拟的入口文件,将脚本资源通过 import 注入进来,也就是最终的入口文件实际上类似于下面的代码:

import ‘./src/main.ts’;

import ‘polyfill’;

面对这个场景,我们想到了两种解决方案:

  • 借助 Vite Lib 模式,修改应用入口

// vite.config.ts

export default defineConfig({

build: {

lib: {

entry: ‘./src/main.ts’,

formats: [‘es’],

fileName: ‘index’

},

rollupOptions: {

preserveEntrySignatures: ‘exports-only’

}

},

})

这种方式有个明显的问题是:Vite 以 Lib 模式构建出的应用,其产物并不是一个完整的前端应用(缺少 index.html),无法满足独立运行的条件。

  • 通过插件修改 Vite 的这一默认行为

通过 vite-plugin-index-html 插件,结合 Vite 的解析能力,将入口修改为静态资源的入口。

import htmlPlugin from ‘vite-plugin-index-html’;

// vite.config.ts

总结

大厂面试问深度,小厂面试问广度,如果有同学想进大厂深造一定要有一个方向精通的惊艳到面试官,还要平时遇到问题后思考一下问题的本质,找方法解决是一个方面,看到问题本质是另一个方面。还有大家一定要有目标,我在很久之前就想着以后一定要去大厂,然后默默努力,每天看一些大佬们的文章,总是觉得只有再学深入一点才有机会,所以才有恒心一直学下去。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值