前端面试题详解整理121| 事件委托与 react 中的事件机制击 tab 切换的效果实现一个正方形 CSS:position浏览器缓存,

84 阅读16分钟
  1. 浏览器缓存 浏览器缓存是指浏览器在本地存储一份网络资源的副本,以便在后续的访问中加快页面加载速度。浏览器缓存通常包括两种类型:强缓存和协商缓存。

强缓存(Cache-Control 和 Expires)

  1. Cache-Control

    • Cache-Control 是 HTTP 头部中控制缓存的主要指令之一,用于控制请求和响应的缓存机制。常见的指令包括:
      • public:表示响应可以被任何中间缓存(如代理服务器)缓存。
      • private:表示响应只能被终端用户的浏览器缓存,中间缓存不能缓存该响应。
      • max-age=seconds:表示资源在本地缓存中的有效时间,单位为秒。
      • no-cache:表示强制缓存服务器去重新验证资源的有效性,而不是直接使用缓存中的资源。
      • no-store:表示禁止缓存,请求和响应都不会被缓存。
  2. Expires

    • Expires 是另一个控制缓存的 HTTP 头部字段,它指定了资源过期的时间。它的值是一个日期/时间字符串,表示资源将在这个时间点后过期。

协商缓存(ETag 和 Last-Modified)

  1. ETag

    • ETag 是 HTTP 头部中的一个实体标签,用于标识一个资源的版本。当资源发生变化时,服务器会生成一个新的 ETag 值。浏览器在请求资源时,会将之前缓存的 ETag 值发送给服务器,如果服务器发现 ETag 值匹配,则返回 304 Not Modified 响应,表示可以继续使用缓存中的资源。
  2. Last-Modified

    • Last-Modified 是 HTTP 头部中的一个字段,表示资源最后修改的时间。当浏览器请求资源时,会将之前缓存的 Last-Modified 值发送给服务器,如果服务器发现资源在这个时间点后没有发生修改,则返回 304 Not Modified 响应。

浏览器缓存通过这些机制可以有效地减少网络流量和加快页面加载速度。合理地使用缓存机制可以提升 Web 应用的性能和用户体验。

  1. 如何实现七天免登陆 实现七天免登录的方法通常涉及在用户登录成功后生成一个持久性的身份标识,并在用户下次访问时检查该标识是否有效。以下是一个基本的实现步骤:

  2. 用户登录时

    • 当用户使用有效的用户名和密码登录成功后,生成一个包含用户身份信息的凭证,可以是一个加密的 token。
    • 设置凭证的过期时间为七天后。
  3. 存储凭证

    • 将生成的凭证存储在用户的本地浏览器中,可以选择使用 Cookie 或者 Local Storage。
  4. 下次访问时的身份验证

    • 每次用户访问页面时,检查本地存储中的凭证是否存在以及是否过期。
    • 如果凭证存在且未过期,则自动登录用户;如果凭证不存在或已过期,则要求用户重新登录。
  5. 更新凭证(可选):

    • 每次用户登录时,检查本地存储中的凭证是否存在以及是否过期。
    • 如果凭证存在且未过期,则更新凭证的过期时间为七天后。

下面是一个简单的示例,演示了如何使用 JavaScript 和 Local Storage 实现七天免登录功能:

// 用户登录成功后生成凭证并存储
function login(username, password) {
    // 假设这里是验证用户名和密码的过程
    if (username === "example" && password === "password") {
        const token = generateToken(username);
        saveToken(token);
        return true;
    } else {
        return false;
    }
}

// 生成凭证(示例中简单地使用用户名作为凭证)
function generateToken(username) {
    const expirationDate = new Date();
    expirationDate.setDate(expirationDate.getDate() + 7); // 设置过期时间为七天后
    return JSON.stringify({
        username: username,
        expiresAt: expirationDate.getTime() // 将过期时间转换为时间戳保存
    });
}

// 将凭证存储在本地存储中
function saveToken(token) {
    localStorage.setItem("token", token);
}

// 检查凭证是否存在以及是否过期
function checkToken() {
    const token = localStorage.getItem("token");
    if (!token) {
        return false; // 凭证不存在
    }
    const tokenData = JSON.parse(token);
    const expirationDate = new Date(tokenData.expiresAt);
    if (expirationDate < new Date()) {
        return false; // 凭证已过期
    }
    return true; // 凭证存在且未过期
}

// 模拟用户访问页面时的流程
function checkLoginStatus() {
    if (checkToken()) {
        // 凭证有效,自动登录用户
        const tokenData = JSON.parse(localStorage.getItem("token"));
        const username = tokenData.username;
        console.log("自动登录:" + username);
    } else {
        // 凭证不存在或已过期,要求用户重新登录
        console.log("请重新登录");
    }
}

// 示例:用户登录
login("example", "password");

// 示例:模拟用户访问页面时的流程
checkLoginStatus();

在这个示例中,login() 函数模拟用户登录过程,生成凭证并将其存储在本地存储中。checkLoginStatus() 函数模拟用户访问页面时的流程,检查本地存储中的凭证是否存在以及是否过期,从而决定是否自动登录用户。

二面

  1. 实习工作内容描述, 如何进行性能优化
  2. 怎么从 vue 转到 react 的?
  3. 介绍青训营项目
  4. 青训项目:npm 安装机制

1. CSS:position 有哪些值, 分别代表什么意思, 分别相对于谁定位

CSS 中的 position 属性有以下几个值:

  1. static
  2. relative
  3. absolute
  4. fixed
  5. sticky

下面是对每个值的解释:

  1. static

    • 默认值。元素处于正常的文档流中,不受 toprightbottomleft 属性的影响,不可以通过 z-index 属性进行层叠控制。
  2. relative

    • 元素的位置相对于其在文档流中的初始位置进行定位。使用 toprightbottomleft 属性可以调整元素的位置,但不会影响其在文档流中的位置。相对定位后,元素仍会占据原来的空间,即使移动了也不会影响其他元素的位置。
  3. absolute

    • 元素的位置相对于其最近的非 static 定位的父元素进行定位,如果没有非 static 定位的父元素,则相对于 html 元素进行定位。绝对定位后,元素会脱离文档流,不占据空间,可以通过 toprightbottomleft 属性来确定其位置。如果其父元素设置了 transformperspectivefilter 属性,那么绝对定位元素的位置将相对于该父元素。
  4. fixed

    • 元素的位置相对于浏览器窗口进行定位,即使页面滚动,元素的位置也不会改变。可以通过 toprightbottomleft 属性来确定其位置。固定定位后,元素会脱离文档流,不占据空间。
  5. sticky

    • 元素根据用户的滚动位置进行定位。在元素在容器中滚动时,它将保持位置不变,直到到达容器的边缘。粘性定位的效果类似于相对定位和固定定位的混合体,它会在跨越特定阈值前,保持元素的位置不变,然后随着用户滚动而滚动。

这些 position 属性值决定了元素的定位方式,分别相对于不同的参考对象进行定位,例如父元素、浏览器窗口等。

3. CSS: 如何实现一个正方形

要实现一个正方形,你可以通过 CSS 来设置元素的宽度和高度相等。以下是几种方法:

方法一:使用固定宽度和高度

.square {
  width: 100px; /* 设置宽度 */
  height: 100px; /* 设置高度 */
}

方法二:使用百分比

.square {
  width: 50%; /* 设置宽度为父元素宽度的一半 */
  padding-bottom: 50%; /* 设置高度为宽度的一半,使用 padding 建立高度 */
}

方法三:使用相对定位

.square {
  width: 100px; /* 设置宽度 */
  height: 100px; /* 设置高度 */
  position: relative; /* 设置相对定位 */
}

方法四:使用 flexbox 布局

.container {
  display: flex; /* 使用 flexbox 布局 */
}

.square {
  flex: 1; /* 设置 flex 值,使其自动填充父容器 */
}

以上方法都可以实现一个正方形,你可以根据具体情况选择其中一种方法来实现。

要实现一个正方形,你可以使用 CSS 的宽度和高度属性,并确保它们相等。以下是一种常见的方法:

.square {
  width: 100px; /* 设置正方形的宽度 */
  height: 100px; /* 设置正方形的高度 */
  background-color: red; /* 设置正方形的背景颜色,可根据需求修改 */
}

在这个示例中,.square 类被应用到一个元素上,通过设置宽度和高度相等,就可以实现一个正方形。你可以根据需要修改宽度、高度和背景颜色等样式属性。

  1. 手写代码, 原生实现一个点击 tab 切换的效果

当实现点击 tab 切换的效果时,你需要为每个 tab 绑定点击事件,并在点击时显示对应的内容,同时隐藏其他 tab 的内容。以下是一个简单的示例,演示了如何使用原生 JavaScript 实现这个功能:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tab 切换</title>
<style>
  .tab-content {
    display: none;
  }
  .tab-content.active {
    display: block;
  }
</style>
</head>
<body>

<div class="tabs">
  <div class="tab" data-tab="tab1">Tab 1</div>
  <div class="tab" data-tab="tab2">Tab 2</div>
  <div class="tab" data-tab="tab3">Tab 3</div>
</div>

<div id="tab1" class="tab-content active">Tab 1 Content</div>
<div id="tab2" class="tab-content">Tab 2 Content</div>
<div id="tab3" class="tab-content">Tab 3 Content</div>

<script>
  // 获取所有的 tab 元素
  const tabs = document.querySelectorAll('.tab');

  // 给每个 tab 绑定点击事件
  tabs.forEach(tab => {
    tab.addEventListener('click', () => {
      // 获取当前 tab 的 data-tab 属性值
      const tabId = tab.getAttribute('data-tab');

      // 隐藏所有 tab-content
      document.querySelectorAll('.tab-content').forEach(content => {
        content.classList.remove('active');
      });

      // 显示对应的 tab-content
      const tabContent = document.getElementById(tabId);
      tabContent.classList.add('active');
    });
  });
</script>

</body>
</html>

在这个示例中,.tabs 是 tab 的容器,每个 tab 都具有 .tab 类,并且通过 data-tab 属性指定了要显示的内容的 id。tab 对应的内容通过 id 为 tab1tab2tab3 的元素表示,初始状态下,tab1 的内容是可见的,其他内容是隐藏的。点击不同的 tab 时,会根据其 data-tab 属性值显示相应的内容,并隐藏其他内容。

  1. 事件委托与 react 中的事件机制

事件委托(Event Delegation)是一种常见的 JavaScript 技术,用于处理事件监听和事件处理。在事件委托中,我们将事件监听器绑定到其父元素上,而不是绑定到每个子元素上,然后通过事件冒泡机制来捕获子元素上触发的事件。这样可以减少事件监听器的数量,提高性能,特别是在处理大量子元素的情况下。

在 React 中,事件委托同样也有类似的概念,但它是由 React 自身来处理的。React 通过合成事件(SyntheticEvent)来统一处理所有事件,利用事件冒泡机制来管理事件的传播和处理。在 React 中,你可以直接在 JSX 中使用事件处理函数,并将其绑定到相应的元素上。

以下是比较事件委托与 React 中事件机制的一些不同之处:

事件委托(Event Delegation)

  1. 手动管理

    • 在原生 JavaScript 中,你需要手动管理事件监听器的绑定和解绑,通常通过 addEventListener()removeEventListener() 方法来实现。
  2. 性能优化

    • 通过事件委托,你可以减少事件监听器的数量,特别是在大量子元素的情况下,这样可以提高性能。
  3. 事件冒泡

    • 事件委托利用事件冒泡机制来捕获子元素上触发的事件,并在父元素上进行处理。

React 中的事件机制

  1. 合成事件

    • React 使用合成事件(SyntheticEvent)来统一处理所有事件,这样可以保证在不同浏览器和平台上的一致性。
  2. 声明式语法

    • 在 JSX 中,你可以直接使用事件处理函数,并将其绑定到相应的元素上,而无需手动管理事件监听器的绑定和解绑。
  3. 组件化

    • 在 React 中,事件处理函数通常与组件绑定在一起,这样可以更好地组织和管理代码。
  4. 性能优化

    • React 使用了一些优化策略来提高性能,比如事件的批量处理和事件池等。

总的来说,虽然事件委托在原生 JavaScript 中是一种优化性能的常见技术,但在 React 中,由于其采用了合成事件机制和组件化的思想,通常不需要手动实现事件委托,而是直接在 JSX 中声明事件处理函数即可。 React 自身会处理事件的绑定和事件传播,同时也会对性能进行优化。

作者:zbwer
链接:www.nowcoder.com/discuss/595…
来源:牛客网

一面

  1. 为什么离职

    这个问题需要你诚实地解释离职的原因,可以是个人发展、公司文化、工作环境等方面的考虑。

  2. 如何学习前端的

    你可以分享你的学习方法和资源,比如阅读文档、看视频教程、阅读优秀的代码、实践项目等等。

  3. TypeScript 常见的工具函数

    TypeScript 中常见的工具函数包括 Array.prototype.mapArray.prototype.filterArray.prototype.reduce 等。这些函数可以用于数组的操作和转换。

  4. flex 布局常用属性

    Flex 布局常用的属性包括 flex-directionjustify-contentalign-itemsflex-wrap 等,用于控制弹性容器和其中的弹性项的排列和布局。

  5. 如何理解 Promise,有哪些常见的方法

    Promise 是 JavaScript 中处理异步操作的一种机制,它可以让异步操作更加优雅和可控。常见的 Promise 方法包括 then()catch()finally() 等。

  6. 原型与原型链的理解

    原型是 JavaScript 中对象之间的一种关联关系,原型链是多个对象通过原型形成的链式关系。每个对象都有一个原型,通过原型链,可以访问到原型链上的属性和方法。

  7. npm 版本 ^~ 的区别

    ^ 表示兼容大版本号的更新,~ 表示兼容次版本号的更新。比如 ^1.2.3 表示允许安装 1.x.x 的任何版本,但不包括 2.0.0;而 ~1.2.3 表示允许安装 1.2.x 的任何版本,但不包括 1.3.0。

  8. npm 三位数版本号的含义

    npm 三位数版本号的含义是:主版本号(breaking change).次版本号(feature).修订版本号(bug fix)。每次更新都应该根据修改的内容适当地更新版本号。

  9. package-lock.json 的作用以及安装顺序

    package-lock.json 记录了项目中所有已安装的 npm 包的具体版本号及其依赖关系。安装顺序上,如果 package-lock.json 和 package.json 冲突,应该以 package.json 为准。

  10. 跨域问题与解决方案

    跨域问题是由浏览器的同源策略导致的,常见的解决方案包括 CORS、JSONP、代理、WebSocket、跨文档消息等。

  11. rem 理解

    rem 是 CSS 单位,相对于根元素(html)的字体大小。使用 rem 单位可以实现响应式布局和统一的缩放效果。

  12. iframe 的作用与使用场景,如何与父级通信,遇到跨域问题如何解决

    iframe 可以用于加载其他页面,常见的使用场景包括嵌入第三方内容和实现多页面间的通信。与父级通信可以通过 postMessage 方法来实现,遇到跨域问题可以使用 postMessage 或者设置合适的 CORS 头来解决。

  13. 普通函数与箭头函数的区别,箭头函数的 this 指向

    普通函数的 this 指向调用它的对象,而箭头函数的 this 指向其定义时的作用域。此外,箭头函数没有自己的 this,它会继承外层作用域的 this。

  14. 对于闭包的理解

    闭包是指函数和其周围的状态(词法环境)的组合。闭包允许函数访问其外部作用域中的变量,即使在函数执行完毕后,这些变量依然可以被访问。

  15. 实习业务介绍

    在这个问题中,你需要简要介绍你在实习期间参与的项目和工作内容,包括你的角色和贡献。

  16. 项目难点

    介绍在项目中遇到的挑战和难点,以及你是如何应对和解决的。

  17. 项目中遇到的性能优化是如何做的

    你可以分享你在项目中进行的性能优化措施,比如减少 HTTP 请求、资源压缩、懒加载、代码分割等。

  18. 前端性能优化有哪些方向

    前端性能优化可以从加载速度、渲染性能、代码质量等多个方向入手,包括资源优化、DOM 操作优化、网络请求优化、缓存优化等。

  19. 竞态问题如何解决,点击 TabA,数据未返回又点击 TabB,数据返回了,如何保证数据的正确性

    可以通过使用 Promise 或者 async/await 来解决竞态问题,确保数据的正确性。在 TabA 的请求中添加一个标识,当 TabB 的请求发起时,检查是否存在正在进行的 TabA 请求,如果存在,则等待其完成再发起 TabB 的请求。

  20. 浏览器缓存

    浏览器缓存是指浏览器在本地存储网络资源的副本,以提高页面加载速度。常见的浏览器缓存策略包括强缓存和协商缓存

  1. 如何实现七天免登陆

    七天免登录可以通过使用 cookie 或者 localStorage 存储登录凭证,并设置有效期为七天,用户下次访问网站时检查凭证是否过期,如果未过期则自动登录。

二面

  1. 实习工作内容描述,如何进行性能优化

    这个问题需要你详细描述你在实习期间的工作内容,并分享你是如何进行性能优化的。

  2. 怎么从 Vue 转到 React 的?

    你可以分享你是如何学习和适应 React 的,包括对比 Vue 和 React 的异同,以及学习过程中的挑战和收获。

  3. 介绍青训营项目

    介绍青训营项目的背景、目标、技术栈以及你在项目中承担的角色和贡献。

  4. 青训项目:npm 安装机制

    npm 的安装机制是指如何安装和管理依赖包,包括本地安装和全局安装,以及 npm 的包管理器特性等。

  5. CSS:position 有哪些值,分别代表什么意思,分别相对于谁定位

    CSS 中的 position 属性有 static、relative、absolute、fixed、sticky 五个值,分别代表不定位、相对定位、绝对定位、固定定位和粘性定位,分别相对于自身和父元素、最近的已定位祖先元素、视口进行定位。

  6. CSS: 如何实现一个正方形

    可以使用宽度和高度相等的盒子模型,并设置 overflow:hidden; 或者使用 padding 或 margin 实现。

  7. 手写代码,原生实现一个点击 tab 切换的效果

    <ul>
      <li onclick="showTab(1)">Tab 1</li>
      <li onclick="showTab(2)">Tab 2</li>
      <li onclick="showTab(3)">Tab 3</li>
    </ul>
    <div id="tabContent1">Tab 1 content</div>
    <div id="tabContent2" style="display:none;">Tab 2 content</div>
    <div id="tabContent3" style="display:none;">Tab 3 content</div>
    <script>
      function showTab(tabIndex) {
        for (let i = 1; i <= 3; i++) {
          document.getElementById('tabContent' + i).style.display = 'none';
        }
        document.getElementById('tabContent' + tabIndex).style.display = 'block';
      }
    </script>
    
  8. 事件委托与 React 中的事件机制

    事件委托是指将事件监听器绑定到父元素而不是每个子元素上,通过事件冒泡的方式来处理子元素的事件。在 React 中,事件也是通过事件冒泡的方式来处理的,但是 React 提供了 SyntheticEvent 来封装原生事件对象,以跨浏览器兼容性和性能优化。