2024.07.12 更新前端面试问题总结(17 道题)

2024.05.26 - 2024.07.12 更新前端面试问题总结(17 道题)

获取更多面试相关问题可以访问

github 地址: github.com/pro-collect...

gitee 地址: gitee.com/yanleweb/in...

目录:

  • 中级开发者相关问题【共计 9 道题】

    • 779.[React] 循环渲染中 为什么推荐不用 index 做 key【热度: 320】【web 框架】【出题公司: TOP100 互联网】
    • 784.前端应用 如何做国际化?【热度: 199】【web 应用场景】【出题公司: 美团】
    • 787.flex:1 代表什么【热度: 400】【CSS】【出题公司: 百度】
    • 788.请求失败会弹出一个 toast , 如何保证批量请求失败, 只弹出一个 toast【热度: 420】【web 应用场景】【出题公司: PDD】
    • 789.css 实现翻牌效果【热度: 116】【CSS】【出题公司: 快手】
    • 790.js 如何判空? 「空」包含了:空数组、空对象、空字符串、0、undefined、null、空 map、空 set , 都属于为空的数据【热度: 640】【JavaScript】【出题公司: PDD】
    • 792.css 实现打字机效果【热度: 96】【CSS】【出题公司: TOP100 互联网】
    • 793.dom 里面, 如何判定 a 元素是否是 b 元素的子元素【热度: 400】【web 应用场景】【出题公司: TOP100 互联网】
    • 794.前端如何实现折叠面板效果?【热度: 113】【web 应用场景】【出题公司: TOP100 互联网】
  • 高级开发者相关问题【共计 8 道题】

    • 778.[React] 如何避免使用 context 的时候, 引起整个挂载节点树的重新渲染【热度: 420】【web 框架】【出题公司: TOP100 互联网】
    • 780.[微前端] 微前端架构一般是如何做 JavaScript 隔离【热度: 127】【工程化】【出题公司: 阿里巴巴】
    • 781.[微前端] Qiankun 是如何做 JS 隔离的【热度: 228】【工程化】【出题公司: 阿里巴巴】
    • 782.[微前端] 为何通常在 微前端 应用隔离, 不选择 iframe 方案【热度: 280】【工程化】【出题公司: 阿里巴巴】
    • 783.应用如何做应用灰度发布【热度: 247】【工程化】【出题公司: 腾讯】
    • 785.如何清理源码里面没有被应用的代码, 主要是 JS、TS、CSS 代码【热度: 329】【web 应用场景】【出题公司: 腾讯】
    • 786.一般是怎么做代码重构的【热度: 191】【web 应用场景】【出题公司: PDD】
    • 791.判断一个对象是否为空,包含了其原型链上是否有自定义数据或者方法。 该如何判定?【热度: 546】【JavaScript】【出题公司: PDD】

中级开发者相关问题【共计 9 道题】

779.[React] 循环渲染中 为什么推荐不用 index 做 key【热度: 320】【web 框架】【出题公司: TOP100 互联网】

关键词:React 循环渲染问题

在 React 的循环渲染中,不推荐使用数组的index(索引)作为元素的key,主要基于以下几点理由:

  1. 列表项顺序的变化 :如果列表项的顺序会发生变化(如排序、增加、删除操作),使用index作为key可能会导致性能问题和组件状态的错误。这是因为 React 依赖key来判断哪些元素是新元素、哪些被移除,以及哪些元素的位置发生了变化。当使用index作为key时,即使数据项的内容改变了,key仍然保持不变,导致 React 无法正确识别和优化渲染。

  2. 性能问题 :当列表项发生变动时,如果使用index作为key,React 可能会做更多的 DOM 操作来更新视图,因为它无法准确地通过key识别哪些元素是新的,哪些元素被移动了位置。这可能导致不必要的重渲染和性能下降。

  3. 组件状态的问题 :对于使用 state 的组件,如果列表项的顺序改变,使用index作为key可能会导致状态错乱。例如,当你删除一个列表项时,后面的项会移上来,它们的index改变了,如果它们有独立的状态,这时会由于index作为key使得状态与视图匹配错误。

因此,推荐的做法是使用唯一的、稳定的标识符(如数据库中的 id 或者具有唯一性的 hash 值等)作为key,这样无论数据如何变化,每个元素的key都是稳定的,可以帮助 React 更准确、更高效地进行 DOM 的比对和更新。

784.前端应用 如何做国际化?【热度: 199】【web 应用场景】【出题公司: 美团】

关键词:国际化

前端应用实现国际化(i18n)主要是为了支持多语言环境,提高用户体验。这里有几种常用的方案:

  1. 使用国际化库:这是最常用的方法之一,可以通过引用第三方库来管理不同语言环境的资源文件。比如:

    • React :可以使用react-intlreact-i18next
    • Vue :可以使用vue-i18n
    • Angular :可以使用@ngx-translate/core

    这些库允许你将文本资源分开管理,并根据用户的语言偏好动态加载相应的资源。

  2. 浏览器 API :利用浏览器内置的国际化 API,如Intl对象,来格式化日期、时间、货币等。

  3. 自建国际化框架:根据项目的具体需求,自定义国际化实现。这通常包括:

    • 创建资源文件:为每种语言创建一个资源文件,用于存储翻译字符串。
    • 语言选择功能:允许用户选择偏好的语言。
    • 加载对应资源文件:根据用户的语言偏好,动态加载对应的资源文件并在界面上显示相应的文本。
  4. 服务端支持:有些情况下,前端应用可能需要服务端的支持来实现国际化,如动态提供不同语言的数据内容。

  5. URL 路由 :在 URL 中包含语言参数,来确定显示哪种语言的内容。例如,/en/about 显示英文版"关于"页面,而 /zh/about 显示中文版。

  6. 浏览器语言检测 :通过检测浏览器的navigator.language属性来自动选择最合适的语言版本。

在实际应用中,根据项目的大小、复杂度以及特定需求,可以选择一种或多种方案结合使用,以达到最佳的国际化效果。

787.flex:1 代表什么【热度: 400】【CSS】【出题公司: 百度】

关键词:flex 布局相关属性问题

在 CSS 的弹性盒模型(Flexbox)中,flex: 1表示子项(flex 子项)的伸缩性。

具体来说,flex: 1flex-growflex-shrinkflex-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,内容区则会自动放大占满剩余空间。

以下是一个简单的示例代码,展示了flex: 1的效果:

html 复制代码
<!DOCTYPE html>
<html>
  <head>
    <style>
      .container {
        display: flex;
        width: 300px;
        height: 200px;
        background-color: lightblue;
      }

      .item1 {
        background-color: lightcoral;
        flex: 1;
      }

      .item2 {
        background-color: lightgreen;
        flex: 1;
      }
    </style>
  </head>

  <body>
    <div class="container">
      <div class="item1">item1</div>
      <div class="item2">item2</div>
    </div>
  </body>
</html>

在上述代码中,.container是一个 flex 容器,它包含两个子元素.item1.item2,并且都将flex属性设置为1。当调整.container的宽度时,.item1.item2会等比例地增大或缩小,以占满剩余空间。

788.请求失败会弹出一个 toast , 如何保证批量请求失败, 只弹出一个 toast【热度: 420】【web 应用场景】【出题公司: PDD】

关键词:单例 toast

要确保批量请求失败时只弹出一个 toast,可以通过以下几种方式实现:

  1. 设置全局标志位:定义一个全局变量(如 isToastShown)来表示是否已经弹出过 toast。在请求失败的处理逻辑中,首先检查该标志位。如果尚未弹出 toast,则进行弹出操作,并设置标志位为 true;如果标志位已经为 true,则直接忽略后续的弹出操作。
  2. 使用防抖或节流函数:防抖(debounce)或节流(throttle)函数可以限制某个函数在一定时间内的执行次数。将弹出 toast 的操作封装在防抖或节流函数中,确保在短时间内的多个请求失败时,不会频繁弹出 toast。
  3. 集中处理错误:将所有请求的错误集中处理,而不是在每个请求的 catch 块中直接弹出 toast。例如,把所有请求的 Promise 添加到一个数组中,然后使用 Promise.all() 或其他类似方法来统一处理这些 Promise 的结果。如果所有请求都失败了,再弹出一个 toast。

以下是使用全局标志位和集中处理错误的示例代码:

javascript 复制代码
let isToastShown = false; // 全局标志位

function makeRequests() {
  const requests = [fetchPost(), fetchComments()]; // 多个请求的 Promise

  Promise.all(requests)
  .then(() => {
      // 所有请求成功的处理逻辑
    })
  .catch(errors => {
      if (!isToastShown) { // 检查标志位
        notify(errors[0]); // 弹出 toast
        isToastShown = true; // 设置标志位为 true
      }
    });
}

function fetchJSON(url, input) {
  return fetch(url, input)
  .then(res => {
      if (res.ok) {
        return res.json();
      }
      const err = new HttpError(res);
      if (!isToastShown) { // 检查标志位
        notify(err); // 弹出 toast
        is toastShown = true; // 设置标志位为 true
      }
      throw err;
    });
}

在上述代码中,定义了一个全局变量 isToastShown 来标记是否已经弹出过 toast。在 fetchJSON 函数中,当请求失败时,如果 isToastShownfalse,则弹出 toast 并设置其为 true。在 makeRequests 函数中,使用 Promise.all 来处理多个请求。如果所有请求都失败(即 errors 数组有内容),并且 isToastShownfalse,则弹出 toast 并设置标志位。

这样,无论有多少个请求失败,都只会弹出一个 toast。当有新的批量请求时,记得在请求开始前将 isToastShown 重置为 false

另外,如果使用的是一些前端框架或库,它们可能提供了更方便的方式来处理这种情况。例如,在 Vue.js 中,可以使用 Vuex 来管理全局状态,实现类似的功能。具体的实现方式可能会因项目的架构和使用的技术而有所不同,但基本思路是相似的。

789.css 实现翻牌效果【热度: 116】【CSS】【出题公司: 快手】

关键词:css 动效应用

主要是考察几个属性的使用

  • transform: rotateY 用于 Y 轴旋转
  • transition 用于过度动画

还有一个要点:

  • 翻转卡牌的时候,正面在上, 要将背面隐藏; 背面在上, 要将正面隐藏;

效果如下:

实现比较简单, 直接贴代码

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <style>
      .card {
        display: flex;
      }

      .flip-card {
        float: left;
        position: relative;
        height: 36vmin;
        width: calc(40vmin / 1.4);
        background-color: white;
        padding: 20px;
        border-radius: calc(40vmin / 20);
        box-shadow: 0 calc(40vmin / 40) calc(40vmin / 10) 0 rgba(0, 0, 0, 0.6);
        overflow: hidden;
        transition: transform 200ms linear, box-shadow 200ms linear, background-color 200ms linear;
        transform: rotateY(0deg);
      }

      .label:hover .flip-card {
        transform: rotateY(180deg);
        background-color: black;
        transition: transform 200ms linear, box-shadow 200ms linear, background-color 200ms linear;
      }

      .label:hover .flip-front {
        opacity: 0;
        display: none;
        transition: transform 200ms linear, box-shadow 200ms linear, background-color 200ms linear;
      }

      .label:hover .flip-end {
        opacity: 1;
        display: block;
        transform: rotateY(180deg);
        color: white;
        font-size: 20px;
        transition: transform 200ms linear, box-shadow 200ms linear, background-color 200ms linear;
      }

      .flip-front {
        width: 100%;
        height: 100%;
        opacity: 1;
        cursor: pointer;
      }

      .flip-end {
        width: 100%;
        height: 100%;
        opacity: 0;
        display: none;
        cursor: pointer;
      }

      .label {
        background-color: white;
        border-radius: calc(40vmin / 20);
      }
    </style>
  </head>
  <body>
    <div class="card">
      <div class="label">
        <div class="flip-card">
          <div class="flip-front">我是正面</div>
          <div class="flip-end">
            在上述代码中,我们创建了一个带有 card 类的容器,内部有一个 card-inner 元素,它包含了 card-front(正面)和
            card-back(背面)两个元素。 当鼠标悬停在 card 元素上时,通过 :hover 选择器将 card-inner 元素绕 Y 轴旋转 180
            度,实现翻牌效果。
          </div>
        </div>
      </div>
    </div>
  </body>
</html>

790.js 如何判空? 「空」包含了:空数组、空对象、空字符串、0、undefined、null、空 map、空 set , 都属于为空的数据【热度: 640】【JavaScript】【出题公司: PDD】

关键词:判断

以下是一个 JavaScript 方法,用于校验您提到的各种"为空"的场景:

javascript 复制代码
function isEmpty(value) {
  // 空字符串
  if (typeof value === "string" && 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;
  }

  return false;
}

您可以使用这个方法来检测各种值是否为空,例如:

javascript 复制代码
const emptyStr = "";
const emptyArr = [];
const emptyObj = {};
const zero = 0;
const undef = undefined;
const nullVal = null;
const emptyMap = new Map();
const emptySet = new Set();

console.log(isEmpty(emptyStr));
console.log(isEmpty(emptyArr));
console.log(isEmpty(emptyObj));
console.log(isEmpty(zero));
console.log(isEmpty(undef));
console.log(isEmpty(nullVal));
console.log(isEmpty(emptyMap));
console.log(isEmpty(emptySet));

792.css 实现打字机效果【热度: 96】【CSS】【出题公司: TOP100 互联网】

关键词:animation 帧动画、animation steps 属性

主要是对 css 动画的一个实际应用考察

以下是一个使用 CSS 实现简单打字机效果的示例代码:

html 复制代码
<!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 逐渐增加到 300pxsteps(30) 表示分 30 步完成动画,使文字出现有逐个显示的效果。

  • blink 动画用于模拟光标闪烁效果,每 0.5s 闪烁一次,在 50% 进度时,光标(通过右边框实现)变为透明来模拟闪烁。

793.dom 里面, 如何判定 a 元素是否是 b 元素的子元素【热度: 400】【web 应用场景】【出题公司: TOP100 互联网】

关键词:dom.contains 方法

在 DOM(文档对象模型)中,要判断元素 a 是否是元素 b 的子元素,您可以使用以下的 JavaScript 代码:

javascript 复制代码
function isChildElement(a, b) {
  return b.contains(a);
}

可以这样使用上述函数:

javascript 复制代码
const elementA = document.getElementById("elementA");
const elementB = document.getElementById("elementB");

if (isChildElement(elementA, elementB)) {
  console.log("元素 A 是元素 B 的子元素");
} else {
  console.log("元素 A 不是元素 B 的子元素");
}

794.前端如何实现折叠面板效果?【热度: 113】【web 应用场景】【出题公司: TOP100 互联网】

关键词:transition 过度动画

以下是在您给出的折叠面板示例基础上添加动画效果的版本:

HTML 结构(与您之前给出的相同)

html 复制代码
<div class="accordion">
  <div class="accordion-item">
    <div class="accordion-header">标题 1</div>
    <div class="accordion-content">
      <p>这是内容 1</p>
    </div>
  </div>
  <div class="accordion-item">
    <div class="accordion-header">标题 2</div>
    <div class="accordion-content">
      <p>这是内容 2</p>
    </div>
  </div>
  <!-- 可以添加更多的折叠项 -->
</div>

CSS 样式

css 复制代码
.accordion {
  width: 300px;
}

.accordion-item {
  border: 1px solid #ccc;
  margin-bottom: 5px;
}

.accordion-header {
  background-color: #f1f1f1;
  padding: 10px;
  cursor: pointer;
}

.accordion-content {
  padding: 10px;
  overflow: hidden;
  max-height: 0;
  transition: max-height 0.3s ease;
}

.accordion-item.open.accordion-content {
  max-height: 500px; /* 您可以根据内容的实际高度进行调整 */
}

JavaScript 功能

javascript 复制代码
const accordionHeaders = document.querySelectorAll(".accordion-header");

accordionHeaders.forEach((header) => {
  header.addEventListener("click", () => {
    const item = header.parentElement;
    item.classList.toggle("open");
  });
});

这样,当点击折叠面板的标题时,内容会以平滑的动画效果展开或收起。您可以根据实际需求调整动画的时长和内容展开的最大高度等参数。

高级开发者相关问题【共计 8 道题】

778.[React] 如何避免使用 context 的时候, 引起整个挂载节点树的重新渲染【热度: 420】【web 框架】【出题公司: TOP100 互联网】

关键词:React Context 渲染问题

要避免在 React 开发中使用 context 时引起整个挂载节点树的重新渲染,可以采取以下方法:

  1. React Context 数据分割:把提供 context value 的部分提取到单独的组件中,并且仅在该组件中修改 context value。这样,当 context value 变化时,只有真正使用该 context 的消费组件会重新渲染,而非所有挂载节点都会重新渲染。

假设我们有一个应用,需要管理主题颜色和用户信息两个不同的数据。

首先,创建两个 Context:

jsx 复制代码
import React from "eact";

// 创建主题颜色 Context
const ThemeContext = React.createContext({ theme: "light" });

// 创建用户信息 Context
const UserContext = React.createContext({ user: null });

在顶层组件中,提供这两个 Context 的 Provider,并设置相应的值:

jsx 复制代码
class App extends React.Component {
  state = {
    theme: "dark",
    user: { name: "John Doe", age: 25 },
  };

  render() {
    return (
      <ThemeContext.Provider value={this.state.theme}>
        <UserContext.Provider value={this.state.user}>
          <Toolbar />
        </UserContext.Provider>
      </ThemeContext.Provider>
    );
  }
}

然后,在需要使用主题颜色的组件中,可以通过以下方式获取:

jsx 复制代码
class ThemedButton extends React.Component {
  static contextType = ThemeContext;

  render() {
    const theme = this.context;
    return <Button theme={theme} />;
  }
}

在需要使用用户信息的组件中,同样方式获取:

jsx 复制代码
class UserProfile extends React.Component {
  static contextType = UserContext;

  render() {
    const user = this.context;
    return (
      <div>
        <p>用户名:{user.name}</p>
        <p>年龄:{user.age}</p>
      </div>
    );
  }
}

在上述例子中,我们将主题颜色和用户信息分割到不同的 Context 中。ThemeContext 用于传递主题相关的数据,UserContext 用于传递用户相关的数据。这样,不同的组件可以根据自己的需求订阅相应的 Context,获取所需的数据,而不会相互干扰。每个组件只需要关注自己所使用的 Context,提高了代码的可读性和可维护性。同时,当某个 Context 的数据发生变化时,只有订阅了该 Context 的组件才会重新渲染,避免了不必要的重新渲染。

  1. 对消费组件使用 React.memo() 进行包裹:React.memo 可以对函数组件进行浅比较,如果组件的 props 没有变化,就不会触发重新渲染。通过将消费 context 的组件用 React.memo() 包裹,可以避免不必要的重新渲染。

例如,假设有一个 ContextProvider 组件提供 context value,以及一个使用该 context 的子组件 ConsumerComponent,优化后的代码可能如下所示:

jsx 复制代码
const ContextProvider = ({ children }) => {
  // 管理 context value 的状态
  const [value, setValue] = useState(/* 初始值 */);

  return <MyContext.Provider value={value}>{children}</MyContext.Provider>;
};

const ConsumerComponent = React.memo(({ contextValue }) => {
  // 仅根据 context value 进行渲染或处理逻辑
  return <div>{/* 使用 context value 的相关逻辑 */}</div>;
});

在上述示例中,ContextProvider 负责管理 context value 的状态变化,而 ConsumerComponent 是使用 context 的消费组件,并通过 React.memo() 进行了包裹。这样,当 value 发生变化时,只有 ConsumerComponent 会根据浅比较来决定是否重新渲染,而不是整个挂载节点树都重新渲染。

通过以上方式,可以减少使用 context 时不必要的重新渲染,提高应用的性能。但具体的优化策略还需要根据项目的实际情况进行选择和调整。同时,还需注意避免在 context 中传递过于复杂或频繁变化的数据,以减少不必要的渲染次数。

780.[微前端] 微前端架构一般是如何做 JavaScript 隔离【热度: 127】【工程化】【出题公司: 阿里巴巴】

关键词: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 隔离时,需要根据具体项目需求、技术栈和团队的熟练度来选取合适的隔离策略,以确保子应用之间的高度独立性和可维护性。

781.[微前端] Qiankun 是如何做 JS 隔离的【热度: 228】【工程化】【出题公司: 阿里巴巴】

关键词:JS 隔离

Qiankun 是一个基于 Single-SPA 的微前端实现库,它提供了比较完善的 JS 隔离能力,确保微前端应用间的独立运行,避免了全局变量污染、样式冲突等问题。Qiankun 实现 JS 隔离的主要机制包括:

1. JS 沙箱

Qiankun 使用 JS 沙箱技术为每个子应用创建一个独立的运行环境。沙箱有以下两种类型:

  • 快照沙箱(Snapshot Sandbox):在子应用启动时,快照并记录当前全局环境的状态,然后在子应用卸载时,恢复全局环境到启动前的状态。这种方式不会对全局对象进行真正的隔离,而是通过记录和恢复的方式避免全局环境被污染。

  • Proxy 沙箱 :通过 Proxy 对象创建一个全新的全局对象代理,子应用的所有全局变量修改操作都将在这个代理对象上进行,从而不会影响到真实的全局对象。这种方式提供了更为彻底的隔离效果,是 Qiankun 中推荐的沙箱隔离方式。

2. 动态执行 JS 代码

Qiankun 通过动态执行 JS 代码的方式加载子应用,避免了脚本直接在全局环境下执行可能导致的变量污染。具体来说,它可以动态获取子应用的 JS 资源,然后在沙箱环境中运行这些代码,确保代码执行的全局变量不会泄露到主应用的全局环境中。

3. 生命周期隔离

Qiankun 给每个子应用定义了一套生命周期钩子,如 bootstrapmountunmount 等,确保在应用加载、激活和卸载的过程中正确管理和隔离资源。通过在 unmount 生命周期钩子中正确清理子应用创建的全局监听器、定时器等,进一步保证了不同子应用间的独立性和隔离性。

4. 样式隔离

虽然主要针对 JS 隔离,Qiankun 也提供了样式隔离机制,通过动态添加和移除样式标签,保证子应用样式的独立性,避免不同子应用间的样式冲突。

通过以上机制,Qiankun 能够有效实现微前端架构中子应用的 JS 隔离,加强了应用间的独立性和安全性,使得不同子应用可以无缝集成在一起,同时又能够保持各自的运行环境独立不受影响。

782.[微前端] 为何通常在 微前端 应用隔离, 不选择 iframe 方案【热度: 280】【工程化】【出题公司: 阿里巴巴】

关键词: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彻底,但在整体的应用性能、用户体验和开发效率上通常会有更好的表现。

783.应用如何做应用灰度发布【热度: 247】【工程化】【出题公司: 腾讯】

关键词:灰度发布

应用的灰度发布是将新版本逐步推出给有限的用户群体,以在完全发布之前监控其性能和搜集用户反馈的过程。这可以确保新版本的稳健性,减少因新版本可能引起的问题对所有用户的影响。以下是实现应用灰度发布的几种常见方法:

通过识别用户的 HTTP 请求头(如 User-Agent)或特定的 Cookie,决定用户请求被路由到新版本还是旧版本的应用。这种方法通常需要负载均衡器或网关支持特定路由规则。

2. 使用服务网格(Service Mesh)

服务网格如 Istio 提供了复杂的流量管理能力,可以在微服务架构中实现灰度发布。通过定义路由规则,Istio 可以将特定比例或特定条件的流量导向新版本服务。

3. 功能开关(Feature Toggles)

功能开关允许开发者在代码中嵌入开关,根据配置动态激活或关闭某些功能。这样,新版本的功能可以被隐藏,直到你决定通过更改配置为特定用户群体开放。

4. DNS 路由

通过 DNS 管理,将部分用户的请求解析到部署了新版本应用的服务器上。这种方法简单,但切换和回退可能不如其他方法灵活。

5. CDN 切换

对于前端应用或静态资源,可以通过 CDN 配置,将部分用户的请求路由到包含新版本资源的 CDN 上。通过调整 CDN 的缓存规则控制版本切换。

6. A/B 测试平台

将灰度发布作为 A/B 测试的一部分,使用专门的 A/B 测试平台来控制哪些用户看到新版本。这种方法不仅可以实现灰度发布,还能搜集用户反馈和使用情况数据。

7. 容器编排和管理

在支持容器编排(如 Kubernetes)的环境中,可以通过部署新版本的 Pod 副本,并逐步增加新版本副本的数量,同时减少旧版本副本的数量实现灰度发布。

在实施灰度发布时,应该配合监控和日志记录工具,以便快速识别并解决新版本可能引入的问题。同时,在决定完全推出新版本之前,逐渐增加访问新版本的用户比例,确保在所有阶段都能够保持应用的稳定性和高性能。

785.如何清理源码里面没有被应用的代码, 主要是 JS、TS、CSS 代码【热度: 329】【web 应用场景】【出题公司: 腾讯】

关键词:代码清理

清理源码中未被应用的 JavaScript (JS)、TypeScript (TS) 和 CSS 代码的关键在于合理利用工具和策略,来识别和移除这些废弃的代码。下面是一份指南,帮助你高效完成这一任务:

对于 JavaScript 和 TypeScript

1. 使用 ESLint

  • 初始化 ESLint :如果你还没有使用 ESLint,可以通过npx eslint --init命令来初始化配置。
  • 配置规则 :确保在.eslintrc配置文件中启用了no-unused-vars规则,以识别未使用的变量和函数。
json 复制代码
{
  "rules": {
    "no-unused-vars": "warn"
  }
}
  • 使用 ESLint 的 --fix 选项: 虽然 ESLint 主要用于识别问题,但它的 --fix 选项可以自动修复一些问题,包括删除未使用的变量等。不过,这种方式相对保守,无法删除大块的未使用代码

2. 使用 TypeScript 编译器选项

  • 对于 TypeScript 项目,可以在tsconfig.json文件中启用noUnusedLocalsnoUnusedParameters选项,以识别未使用的本地变量和函数参数。
json 复制代码
{
  "compilerOptions": {
    "noUnusedLocals": true,
    "noUnusedParameters": true
  }
}

3. 利用 Webpack 的 Tree Shaking

  • 确保在生产模式下使用 Webpack,它自带 Tree Shaking 功能,可以去除死代码(未被使用的代码)。
  • 使用 ES6 模块语法(即importexport),因为 Tree Shaking 仅支持静态导入。

对于 CSS

1. 使用 PurgeCSS

  • PurgeCSS分析你的内容和 CSS 文件,去除不匹配的选择器。非常适用于清楚在 HTML 或 JS 文件中未引用的 CSS 代码。
  • 可以通过 Webpack、Gulp 或 PostCSS 等多种方式与 PurgeCSS 集成。
bash 复制代码
npm install purgecss

使用 PurgeCSS 时,配置你的内容文件路径(如 HTML 或 JSX 文件),它会扫描这些文件以确定哪些 CSS 选择器被使用:

javascript 复制代码
// 一个基本的PurgeCSS配置例子
new PurgecssPlugin({
  paths: glob.sync(`${PATHS.src}/**/*`, { nodir: true }),
});

使用 Codemods

Codemods 是 Facebook 提出的一种工具,允许你对代码库进行大规模的自动化重构。通过编写特定的脚本,你可以自定义删除或修改未被调用的代码的逻辑。例如,使用 jscodeshift 工具可以配合具体规则进行代码修改。

注意事项

  • 测试:自动删除代码后,务必执行完整的测试套件,确认改动不会影响现有功能。
  • 版本控制:在进行删除操作之前,确保代码已经提交到版本控制系统,以便必要时可以恢复。
  • 逐步执行:尤其是在较大或复杂的项目中,建议分步骤、逐渐移除未使用的代码,每次删除后都进行测试和评估。

使用这些策略和工具可以帮助自动化清理未使用的代码,但是请注意,完全自动化的过程可能会有风险,依然需要人工审核和测试以确保代码的质量和应用的稳定性。

786.一般是怎么做代码重构的【热度: 191】【web 应用场景】【出题公司: PDD】

关键词:代码重构

在前端项目中进行代码重构,一般可以遵循以下步骤:

  1. 明确重构目标

    • 确定需要解决的问题,例如提高代码的可读性、可维护性、性能,或者去除重复代码等。
  2. 代码分析

    • 对现有代码进行全面的审查和理解,包括代码结构、逻辑流程、函数和模块之间的关系等。
    • 可以使用工具如 ESLint 检查代码风格和潜在问题,使用性能分析工具如 Chrome DevTools 的 Performance 面板来检测性能瓶颈。
  3. 制定重构计划

    • 根据分析结果,确定重构的步骤和顺序。
    • 将大型的重构任务分解为较小的、可管理的子任务。
  4. 重写代码结构

    • 对模块和组件进行合理的拆分和组织,使代码结构更加清晰。
    • 例如,将功能相关的代码提取到单独的函数或模块中,提高代码的内聚性和复用性。
  5. 优化函数和方法

    • 检查函数的长度和复杂性,对过长或过于复杂的函数进行分解。
    • 去除不必要的参数传递和全局变量的使用。
  6. 处理数据结构

    • 评估数据的存储和使用方式,选择更合适的数据结构(如从数组切换到对象,或者使用 Map、Set 等)来提高数据操作的效率。
  7. 优化性能

    • 例如,减少不必要的计算、优化 DOM 操作、合理使用缓存等。
  8. 测试和验证

    • 对重构后的代码进行全面的单元测试、集成测试和端到端测试,确保功能的正确性和稳定性。
  9. 代码审查

    • 邀请团队成员对重构后的代码进行审查,获取反馈和建议,进一步优化代码。
  10. 文档更新

    • 对重构后的代码功能、接口和使用方法进行文档更新,方便其他开发人员理解和使用。

以一个简单的前端项目为例,假设有一个处理用户数据展示的模块,最初的代码可能是所有功能都写在一个大型的函数中,并且数据存储在全局变量中。

重构时:

  • 将数据处理、数据获取和数据展示的功能分别提取到不同的函数中。

  • 将数据从全局变量改为使用模块内部的私有变量或通过参数传递。

  • 对数据处理函数进行优化,去除重复的代码逻辑。

  • 为新的函数和模块添加必要的注释和文档说明。

通过这样的重构过程,可以使前端项目的代码质量得到显著提升,为后续的开发和维护提供更好的基础。

791.判断一个对象是否为空,包含了其原型链上是否有自定义数据或者方法。 该如何判定?【热度: 546】【JavaScript】【出题公司: PDD】

关键词:原型链相关知识

要判断一个对象是否为空,并且考虑到对象自身及其原型链上是否有自定义数据或方法,您可以使用以下的 JavaScript 函数来实现:

javascript 复制代码
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;
}

可以使用这个函数来判断对象是否为空,例如:

javascript 复制代码
function MyClass() {}

MyClass.prototype.myMethod = function () {};

const instance = new MyClass();

console.log(isObjectEmpty(instance));
相关推荐
小阮的学习笔记3 分钟前
Vue3中使用LogicFlow实现简单流程图
javascript·vue.js·流程图
YBN娜4 分钟前
Vue实现登录功能
前端·javascript·vue.js
阳光开朗大男孩 = ̄ω ̄=4 分钟前
CSS——选择器、PxCook软件、盒子模型
前端·javascript·css
minDuck9 分钟前
ruoyi-vue集成tianai-captcha验证码
java·前端·vue.js
小政爱学习!29 分钟前
封装axios、环境变量、api解耦、解决跨域、全局组件注入
开发语言·前端·javascript
魏大帅。35 分钟前
Axios 的 responseType 属性详解及 Blob 与 ArrayBuffer 解析
前端·javascript·ajax
花花鱼41 分钟前
vue3 基于element-plus进行的一个可拖动改变导航与内容区域大小的简单方法
前端·javascript·elementui
k093344 分钟前
sourceTree回滚版本到某次提交
开发语言·前端·javascript
EricWang13581 小时前
[OS] 项目三-2-proc.c: exit(int status)
服务器·c语言·前端
September_ning1 小时前
React.lazy() 懒加载
前端·react.js·前端框架