前端资料汇总
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
我一直觉得技术面试不是考试,考前背背题,发给你一张考卷,答完交卷等通知。
首先,技术面试是一个 认识自己 的过程,知道自己和外面世界的差距。
更重要的是,技术面试是一个双向了解的过程,要让对方发现你的闪光点,同时也要 试图去找到对方的闪光点,因为他以后可能就是你的同事或者领导,所以,面试官问你有什么问题的时候,不要说没有了,要去试图了解他的工作内容、了解这个团队的氛围。
找工作无非就是看三点:和什么人、做什么事、给多少钱,要给这三者在自己的心里划分一个比例。
最后,祝愿大家在这并不友好的环境下都能找到自己心仪的归宿。
▐ 由于无需打包的特性,服务器冷启动时间超快
借助 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 的浏览器兼容性如下:
可以认为,支持 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 整体加载流程图如下:
▐ 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
总结
大厂面试问深度,小厂面试问广度,如果有同学想进大厂深造一定要有一个方向精通的惊艳到面试官,还要平时遇到问题后思考一下问题的本质,找方法解决是一个方面,看到问题本质是另一个方面。还有大家一定要有目标,我在很久之前就想着以后一定要去大厂,然后默默努力,每天看一些大佬们的文章,总是觉得只有再学深入一点才有机会,所以才有恒心一直学下去。