1. 前端应用 如何做国际化?
关键词:国际化
前端应用实现国际化(i18n)主要是为了支持多语言环境,提高用户体验。这里有几种常用的方案:
-
使用国际化库:这是最常用的方法之一,可以通过引用第三方库来管理不同语言环境的资源文件。比如:
- React:可以使用
react-intl或react-i18next。 - Vue:可以使用
vue-i18n。 - Angular:可以使用
@ngx-translate/core。
这些库允许你将文本资源分开管理,并根据用户的语言偏好动态加载相应的资源。
- React:可以使用
-
浏览器 API:利用浏览器内置的国际化 API,如
Intl对象,来格式化日期、时间、货币等。 -
自建国际化框架:根据项目的具体需求,自定义国际化实现。这通常包括:
- 创建资源文件:为每种语言创建一个资源文件,用于存储翻译字符串。
- 语言选择功能:允许用户选择偏好的语言。
- 加载对应资源文件:根据用户的语言偏好,动态加载对应的资源文件并在界面上显示相应的文本。
-
服务端支持:有些情况下,前端应用可能需要服务端的支持来实现国际化,如动态提供不同语言的数据内容。
-
URL 路由:在 URL 中包含语言参数,来确定显示哪种语言的内容。例如,
/en/about显示英文版“关于”页面,而/zh/about显示中文版。 -
浏览器语言检测:通过检测浏览器的
navigator.language属性来自动选择最合适的语言版本。
在实际应用中,根据项目的大小、复杂度以及特定需求,可以选择一种或多种方案结合使用,以达到最佳的国际化效果。
2. flex:1 代表什么
关键词:flex 布局相关属性问题
在 CSS 的弹性盒模型(Flexbox)中,flex: 1表示子项(flex 子项)的伸缩性。
具体来说,flex: 1是flex-grow、flex-shrink和flex-basis三个属性的简写。其默认值等同于flex: 1 1 0%,分别代表以下含义:
flex-grow: 1:定义项目的放大比例为 1。这意味着当弹性容器有剩余空间时,该子项将按照比例伸展以填充剩余空间。如果存在多个flex-grow: 1的子项,它们将等分剩余空间。flex-shrink: 1:定义项目的缩小比例为 1。即如果空间不足,该项目将缩小。flex-basis: 0%:在分配多余空间之前,计算项目是否有多余空间,这里的0%表示不考虑项目本身的大小。
flex: 1经常用于自适应布局。例如,将父容器的display设置为flex,侧边栏大小固定后,将内容区设置为flex: 1,内容区则会自动放大占满剩余空间。
3. 请求失败会弹出一个 toast , 如何保证批量请求失败, 只弹出一个 toast
关键词:单例 toast
要确保批量请求失败时只弹出一个 toast,可以通过以下几种方式实现:
-
设置全局标志位:定义一个全局变量(如
isToastShown)来表示是否已经弹出过 toast。在请求失败的处理逻辑中,首先检查该标志位。如果尚未弹出 toast,则进行弹出操作,并设置标志位为true;如果标志位已经为true,则直接忽略后续的弹出操作。 -
使用防抖或节流函数:防抖(debounce)或节流(throttle)函数可以限制某个函数在一定时间内的执行次数。将弹出 toast 的操作封装在防抖或节流函数中,确保在短时间内的多个请求失败时,不会频繁弹出 toast。
-
集中处理错误:将所有请求的错误集中处理,而不是在每个请求的 catch 块中直接弹出 toast。例如,把所有请求的 Promise 添加到一个数组中,然后使用
Promise.all()或其他类似方法来统一处理这些 Promise 的结果。如果所有请求都失败了,再弹出一个 toast。 -
如果使用的是一些前端框架或库,它们可能提供了更方便的方式来处理这种情况。例如,在 Vue.js 中,可以使用 Vuex 来管理全局状态,实现类似的功能。具体的实现方式可能会因项目的架构和使用的技术而有所不同,但基本思路是相似的。
4. js 如何判空? 「空」包含了:空数组、空对象、空字符串、0、undefined、null、空 map、空 set , 都属于为空的数据
function isEmpty(value) {
// 空字符串
if (typeof value === "" && value.trim() === "") {
return true;
}
// 空数组
if (Array.isArray(value) && value.length === 0) {
return true;
}
// 空对象(不包括 `null`)
if (typeof value === "object" && value !== null && Object.keys(value).length === 0) {
return true;
}
// 数字 0
if (typeof value === "number" && value === 0) {
return true;
}
// `undefined`
if (typeof value === "undefined") {
return true;
}
// `null`
if (value === null) {
return true;
}
// 空 `Map`
if (value instanceof Map && value.size === 0) {
return true;
}
// 空 `Set`
if (value instanceof Set && value.size === 0) {
return true;
}
}
5. dom 里面, 如何判定 a 元素是否是 b 元素的子元素
关键词:dom.contains 方法
function isChildElement(a, b) {
return b.contains(a);
}
if (isChildElement(elementA, elementB)) {
console.log("元素 A 是元素 B 的子元素");
}
6. [微前端] 微前端架构一般是如何做 JavaScript 隔离
关键词:JS 隔离
在微前端架构中,JavaScript 隔离是核心之一,用以确保各个子应用间代码运行时不互相干扰、变量不冲突,以及能够安全地卸载应用。为了实现这一目标,主要采用以下几种方法:
1. 使用沙箱技术:
iframe:最直接的隔离方式是将子应用运行在iframe中。这种方式提供了良好的隔离性,因为iframe内部有自己独立的执行环境,包括 JavaScript 运行环境和 DOM 环境。但iframe的使用可能会导致性能问题,且父子通信复杂。- JavaScript Sandboxing:通过创建一个独立的 JavaScript 执行环境,比如使用 Web Workers,或者更高级的沙箱库(如 Google 的 Caja),以在主页环境隔离执行 JavaScript 代码。
2. 命名空间和模块化:
- 命名空间:通过命名空间(Namespace)封装每个子应用的代码,确保全局变量和函数不会与其他应用冲突。
- 模块化:利用 ES Modules 或 CommonJS 等模块化标准,使代码封装在模块中运行,通过 import/export 管理依赖,减少全局变量的使用,从而实现隔离。
3. 状态管理隔离:
- 虽然���要关注 JavaScript 代码的隔离,但在单页应用中,子应用间状态管理(如使用 Redux、Vuex 等状态管理库)也可能导致隔离问题。可以为每个子应用创建独立的状态树,只通过明确定义的接口来共享必要的状态信息。
4. 使用微前端框架或库:
- 模块联邦(Module Federation) :Webpack 的模块联邦功能允许不同的前端应用共享 JavaScript 模块,同时保持应用间的隔离。它可以动态地加载另一个应用导出的模块,而不需要将它们打包进单个文件里。
- 专门的微前端框架:如 Single-SPA、Qiankun 等,这些框架提供了一套完整的解决方案,用于管理微前端应用的加载、卸载以及相互隔离,部分内部采用了类似沙箱的技术实现隔离。
5. 服务端渲染(SSR)隔离:
- 通过服务端渲染各个微前端应用,再将渲染好的静态 HTML 集成到主应用中。这样,每个子应用的 JavaScript 在客户端激活(Hydration)之前是隔离的。SSR 可以减少初次加载时间,同时具备部分隔离性,尤其是在初次加载阶段。
实施 JavaScript 隔离时,需要根据具体项目需求、技术栈和团队的熟练度来选取合适的隔离策略,以确保子应用之间的高度独立性和可维护性。
7. [微前端] Qiankun 是如何做 JS 隔离的
关键词:JS 隔离
Qiankun 是一个基于 Single-SPA 的微前端实现库,它提供了比较完善的 JS 隔离能力,确保微前端应用间的独立运行,避免了全局变量污染、样式冲突等问题。Qiankun 实现 JS 隔离的主要机制包括:
1. JS 沙箱
Qiankun 使用 JS 沙箱技术为每个子应用创建一个独立的运行环境。沙箱有以下两种类型:
- 快照沙箱(Snapshot Sandbox) :在子应用启动时,快照并记录当前全局环境的状态,然后在子应用卸载时,恢复全局环境到启动前的状态。这种方式不会对全局对象进行真正的隔离,而是通过记录和恢复的方式避免全局环境被污染。
- Proxy 沙箱:通过
Proxy对象创建一个全新的全局对象代理,子应用的所有全局变量修改操作都将在这个代理对象上进行,从而不会影响到真实的全局对象。这种方式提供了更为彻底的隔离效果,是 Qiankun 中推荐的沙箱隔离方式。
2. 动态执行 JS 代码
Qiankun 通过动态执行 JS 代码的方式加载子应用,避免了脚本直接在全局环境下执行可能导致的变量污染。具体来说,它可以动态获取子应用的 JS 资源,然后在沙箱环境中运行这些代码,确保代码执行的全局变量不会泄露到主应用的全局环境中。
3. 生命周期隔离
Qiankun 给每个子应用定义了一套生命周期钩子,如 bootstrap、mount、unmount 等,确保在应用加载、激活和卸载的过程中正确管理和隔离资源。通过在 unmount 生命周期钩子中正确清理子应用创建的全局监听器、定时器等,进一步保证了不同子应用间的独立性和隔离性。
4. 样式隔离
虽然主要针对 JS 隔离,Qiankun 也提供了样式隔离机制,通过动态添加和移除样式标签,保证子应用样式的独立性,避免不同子应用间的样式冲突。
通过以上机制,Qiankun 能够有效实现微前端架构中子应用的 JS 隔离,加强了应用间的独立性和安全性,使得不同子应用可以无缝集成在一起,同时又能够保持各自的运行环境独立不受影响。
8. [微前端] 为何通常在 微前端 应用隔离, 不选择 iframe 方案
关键词:iframe 隔离方案弊端
在微前端架构中,虽然iframe能提供很好的应用隔离(包括 JavaScript 和 CSS 隔离),确保微前端应用之间不会相互干扰,但一般不把它作为首选方案,原因包括:
1. 性能开销
iframe会创建一个全新的浏览器上下文环境,每个iframe都有自己的文档对象模型(DOM)树、全局执行环境等。如果一个页面中嵌入了多个iframe,就会导致额外的内存和 CPU 资源消耗,特别是在性能有限的设备上更为显著。
2. 应用集成和交互问题
iframe自然隔离了父子页面的环境,这虽然提供了隔离,但同时也使得主应用与子应用之间的交云难度增加。虽然可以通过postMessage等 API 实现跨iframe通信,但这种方式相比于直接 JavaScript 调用来说,更为复杂,交互效率也较低。
3. UI 体验一致性
在iframe中运行的应用在视觉上可能与主应用难以实现无缝集成。iframe内外的样式、字体等一致性需要额外的处理。此外,iframe可能带来额外的滚动条,影响用户体验。
4. SEO 问题
如果微前端的某些内容是通过iframe呈现的,那么这部分内容对于搜索引擎是不可见的,这可能会对应用的 SEO 产生负面影响。
5. 安全问题
虽然iframe可以提供一定程度的隔离,但它也可能引入点击劫持等安全风险。此外,过多地使用iframe也可能增加网站被恶意脚本攻击的表面。
因此,虽然iframe是一种可行的应用隔离方法,它的这些局限性使得开发者在选择微前端技术方案时,往往会考虑其他提供更轻量级隔离、更好集成与交互体验的方案,如使用 JavaScript 沙箱、CSS 隔离技术、Web Components 等。这些方法虽然隔离性可能不如iframe彻底,但在整体的应用性能、用户体验和开发效率上通常会有更好的表现。
9. iframe 和 micro app对比
相同点(微前端优点)
-
接入项目与使用技术栈无关
-
各个项目相互独立,独立开发,独立后端
-
增量式升级(可以一点点增加新的系统进入)
iframe优点
- 自带的样式、环境隔离机制使得它具备天然的沙盒机制(相互隔离)。
- 嵌入子应用比较简单
iframe缺点:
-
iframe功能之间的跳转是无效的,刷新页面无法保存状态。
-
URL的记录完全无效,刷新会返回首页。
-
主应用劫持快捷键操作,事件冒泡不穿透到主文档树上。
-
模态弹窗的背景是无法覆盖到整个应用。
-
iframe应用加载失败,内容发生错误主应用无法感知,通信麻烦。
-
同一个主域下不同子域之间的跨域请求, 比如a.com和1.a.com 之间,1.a.com和2.a.com 之间。使用document.domian都指向主域,比如document.domain="a.com"
micro-app优点
-
micro-app 是借鉴了 WebComponent 的思想,将前端封装为一个webcomponent组件 使用shadowDom隔离,从而实现微前端的组件化渲染
-
它是目前接入微前端成本最低的框架,并且提供了JS沙箱、样式隔离、元素隔离、预加载、资源地址补全、插件系统、数据通信等一系列完善的功能
10. 如何清理源码里面没有被应用的代码, 主要是 JS、TS、CSS 代码
关键词:代码清理
1. 使用 ESLint
- 初始化 ESLint:如果你还没有使用 ESLint,可以通过
npx eslint --init命令来初始化配置。 - 配置规则:确保在
.eslintrc配置文件中启用了no-unused-vars规则,以识别未使用的变量和函数。
{
"rules": {
"no-unused-vars": "warn"
}
}
- 使用 ESLint 的 --fix 选项: 虽然 ESLint 主要用于识别问题,但它的 --fix 选项可以自动修复一些问题,包括删除未使用的变量等。不过,这种方式相对保守,无法删除大块的未使用代码
2. 使用 TypeScript 编译器选项
- 对于 TypeScript 项目,可以在
tsconfig.json文件中启用noUnusedLocals和noUnusedParameters选项,以识别未使用的本地变量和函数参数。
{
"compilerOptions": {
"noUnusedLocals": true,
"noUnusedParameters": true
}
}
3. 利用 Webpack 的 Tree Shaking
- 确保在生产模式下使用 Webpack,它自带 Tree Shaking 功能,可以去除死代码(未被使用的代码)。
- 使用 ES6 模块语法(即
import和export),因为 Tree Shaking 仅支持静态导入。
对于 CSS
1. 使用 PurgeCSS
- PurgeCSS分析你的内容和 CSS 文件,去除不匹配的选择器。非常适用于清楚在 HTML 或 JS 文件中未引用的 CSS 代码。
- 可以通过 Webpack、Gulp 或 PostCSS 等多种方式与 PurgeCSS 集成。
npm install purgecss
使用 PurgeCSS 时,配置你的内容文件路径(如 HTML 或 JSX 文件),它会扫描这些文件以确定哪些 CSS 选择器被使用:
// 一个基本的PurgeCSS配置例子
new PurgecssPlugin({
paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }),
});
使用 Codemods
Codemods 是 Facebook 提出的一种工具,允许你对代码库进行大规模的自动化重构。通过编写特定的脚本,你可以自定义删除或修改未被调用的代码的逻辑。例如,使用 jscodeshift 工具可以配合具体规则进行代码修改。
注意事项
- 测试:自动删除代码后,务必执行完整的测试套件,确认改动不会影响现有功能。
- 版本控制:在进行删除操作之前,确保代码已经提交到版本控制系统,以便必要时可以恢复。
- 逐步执行:尤其是在较大或复杂的项目中,建议分步骤、逐渐移除未使用的代码,每次删除后都进行测试和评估。
使用这些策略和工具可以帮助自动化清理未使用的代码,但是请注意,完全自动化的过程可能会有风险,依然需要人工审核和测试以确保代码的质量和应用的稳定性。
11. 一般是怎么做代码重构的
关键词:代码重构
在前端项目中进行代码重构,一般可以遵循以下步骤:
-
明确重构目标
- 确定需要解决的问题,例如提高代码的可读性、可维护性、性能,或者去除重复代码等。
-
代码分析
- 对现有代码进行全面的审查和理解,包括代码结构、逻辑流程、函数和模块之间的关系等。
- 可以使用工具如 ESLint 检查代码风格和潜在问题,使用性能分析工具如 Chrome DevTools 的 Performance 面板来检测性能瓶颈。
-
制定重构计划
- 根据分析结果,确定重构的步骤和顺序。
- 将大型的重构任务分解为较小的、可管理的子任务。
-
重写代码结构
- 对模块和组件进行合理的拆分和组织,使代码结构更加清晰。
- 例如,将功能相关的代码提取到单独的函数或模块中,提高代码的内聚性和复用性。
-
优化函数和方法
- 检查函数的长度和复杂性,对过长或过于复杂的函数进行分解。
- 去除不必要的参数传递和全局变量的使用。
-
处理数据结构
- 评估数据的存储和使用方式,选择更合适的数据结构(如从数组切换到对象,或者使用 Map、Set 等)来提高数据操作的效率。
-
优化性能
- 例如,减少不必要的计算、优化 DOM 操作、合理使用缓存等。
-
测试和验证
- 对重构后的代码进行全面的单元测试、集成测试和端到端测试,确保功能的正确性和稳定性。
-
代码审查
- 邀请团队成员对重构后的代码进行审查,获取反馈和建议,进一步优化代码。
-
文档更新
- 对重构后的代码功能、接口和使用方法进行文档更新,方便其他开发人员理解和使用。
以一个简单的前端项目为例,假设有一个处理用户数据展示的模块,最初的代码可能是所有功能都写在一个大型的函数中,并且数据存储在全局变量中。
重构时:
- 将数据处理、数据获取和数据展示的功能分别提取到不同的函数中。
- 将数据从全局变量改为使用模块内部的私有变量或通过参数传递。
- 对数据处理函数进行优化,去除重复的代码逻辑。
- 为新的函数和模块添加必要的注释和文档说明。
通过这样的重构过程,可以使前端项目的代码质量得到显著提升,为后续的开发和维护提供更好的基础。
12. 判断一个对象是否为空,包含了其原型链上是否有自定义数据或者方法。 该如何判定?
function isObjectEmpty(obj) {
// 首先获取对象自身的属性
const ownProperties = Object.getOwnPropertyNames(obj);
// 遍历自身属性
for (const property of ownProperties) {
const descriptor = Object.getOwnPropertyDescriptor(obj, property);
// 如果属性是数据属性并且有值,或者是方法(可调用函数),则对象不为空
if (
(descriptor.value && descriptor.value !== null && descriptor.value !== undefined) ||
typeof descriptor.value === "function"
) {
return false;
}
}
// 获取对象的原型
const prototype = Object.getPrototypeOf(obj);
// 如果有原型并且原型不是 `Object.prototype`(避免误判普通对象的默认方法)
while (prototype && prototype !== Object.prototype) {
const prototypeProperties = Object.getOwnPropertyNames(prototype);
// 遍历原型的属性
for (const property of prototypeProperties) {
const descriptor = Object.getOwnPropertyDescriptor(prototype, property);
// 如果原型上的属性是数据属性并且有值,或者是方法(可调用函数),则对象不为空
if (
(descriptor.value && descriptor.value !== null && descriptor.value !== undefined) ||
typeof descriptor.value === "function"
) {
return false;
}
}
// 继续沿着原型链向上查找
prototype = Object.getPrototypeOf(prototype);
}
// 如果以上检查都没有找到非空属性或方法,则对象为空
return true;
}
13. css 实现打字机效果
关键词:animation 帧动画、animation steps 属性
<!DOCTYPE html>
<html lang="en">
<head>
<style>
.typewriter {
width: 300px;
border-right: 4px solid black;
animation: typing 4s steps(30), blink 0.5s step-end infinite;
white-space: nowrap;
overflow: hidden;
}
@keyframes typing {
from {
width: 0;
}
to {
width: 300px;
}
}
@keyframes blink {
50% {
border-color: transparent;
}
}
</style>
</head>
<body>
<p class="typewriter">这是一个打字机效果的文本</p>
</body>
</html>
在上述代码中,.typewriter 类的元素用于实现打字机效果。
animation: typing 4s steps(30), blink 0.5s step-end infinite; 定义了两个动画:
typing动画用于模拟文字逐个出现的效果,从宽度为0逐渐增加到300px,steps(30)表示分 30 步完成动画,使文字出现有逐个显示的效果。blink动画用于模拟光标闪烁效果,每0.5s闪烁一次,在50%进度时,光标(通过右边框实现)变为透明来模拟闪烁。
14.vue项目打包部署流程
vue中npm run build对项目打包,打包后会新生成一个dist文件,直接打开是空白的,需要放到服务器上才能运行。
部署方式有:本地服务器部署、nginx服务器部署、云服务器部署三种方式。
本文只总结自己能看懂的,完整版作者在下面
作者:晴小篆
链接:juejin.cn/post/739041…