24秋招美团一面🦘「八股文+代码输出+算法题」

八股文

讲讲异步组件和路由懒加载?异步加载的过程是怎么样的?为什么在router中使用回调的方式引入组件就可以实现异步加载?假如一个页面有特别多的异步组件会带来什么问题?

其实异步组件和异步路由是一样的东西,异步路由引入的也还是对应的组件。

异步加载的过程涉及到JavaScript运行时、事件循环机制以及打包工具的配合工作。过程:

  1. 代码分割 :当我们在代码中使用import()函数异步加载组件时,打包工具(如Webpack或Rollup)会将这些组件作为单独的JavaScript文件(通常被称为"chunks")进行打包。
  2. 初始加载:在页面初始加载时,只有主bundle会被下载。主bundle中包含了应用的主要代码,但不包括异步组件的代码。
  3. 异步请求:当用户触发了需要异步组件的操作(如访问一个使用了路由懒加载的路由),JavaScript运行时会发送一个网络请求,请求异步组件对应的JavaScript文件。
  4. 下载和执行:浏览器下载异步组件对应的JavaScript文件,并执行其中的代码。这个过程可能会涉及到一些延迟,因为需要等待网络请求完成,并且JavaScript是单线程的,不能同时执行多段代码。
  5. 渲染组件:异步组件的代码被执行后,组件会被注册到Vue.js应用中,并触发视图的更新。Vue.js会创建一个新的组件实例,并将其渲染到页面上。

在这个过程中,打包工具起到了关键作用。它负责将代码分割成多个小文件,并在构建时生成一份映射表,这样JavaScript运行时就知道每个异步组件对应哪个文件。

同时,JavaScript的事件循环机制也在这个过程中发挥了作用。由于JavaScript是单线程的,它需要使用事件循环来处理异步操作。当我们使用import()函数加载一个组件时,这个操作会被放入微任务队列中。只有当当前的同步代码执行完毕后,才会执行微任务队列中的操作。这也是为什么异步组件不会立即被加载和渲染的原因。

过多的异步组件可能会导致的问题:

  • 网络请求过多导致阻塞、请求失败
  • 加载异步组件导致页面闪烁、抖动,影响用户体验
  • 每个异步组件都可能下载失败,一一提供错误处理比较复杂

Vuex 的底层原理是什么?有了解过设计模式吗?发布订阅模式?

笔者对 vuex 也仅限于使用所以答得并不太好,在掘金上找到一篇讲的比较好的文章Vuex核心源码解析之手写Vuex:

使用方式:

vuex 是个状态管理模式,其基本工作流程是:当用户修改状态的时候,如果是 同步 修改状态,会先提交(commit)触发 mutations方法,改变 Store实例中的状态,这也是 唯一 修改状态的途径,如果是 异步 的修改状态的方法,比如发起数据请求等,会提交 dispatch 触发 actions方法,然后在 actions方法 中通过 commit 触发 mutations方法 进而 更新Store实例的状态,然后再将 状态更新 到 Vue组件中。如果我们的项目是大型的单页面应用,那么我们就需要使用vuex在组件外部管理状态,通过这个状态管理模式我们可以很轻松的实现组件之间的通信,这也是选择vuex的原因之一,当然vuex也有自己的缺陷,就是状态不能够持久化。

实现原理:

Vuex实现响应式原理 通过在 install方法 内通过 vue的 mixin方法 将 Store实例 注入到每个组件内,在 resetStoreVm方法内部主要是借助 vue本身的响应式原理 来实现状态的响应式,借助computed来实现 getters的缓存 效果,然后通过 发布订阅的思想 将用户定义的 mutations 和 actions 方法存储起来,当用户触发 commit、dispatch 的时候就去 订阅mutations 和 actions 找出对应的方法。如果存在 模块嵌套 的情况下,首先,vuex内部会通过一个 moduleCollection类 将所有模块 格式化 为一个 树形结构,其次,通过 installModule方法 将格式化好的树形结构 安装 到 Store实例上,最后,通过 resetStoreVm方法 借助 vue内部响应式的原理 将所有模块的状态都置为 响应式。vuex是 响应式 的同时还支持 插件 的使用,Store内部有 subscribe方法 和 replaceState方法,通过这两个方法我们实现 vuex的状态的持久化,以上说明了 vuex是高度依赖 vue响应式 和 插件系统的。

哪里跌倒在哪里爬起来之手写发布订阅模式:

美团一面:你了解发布-订阅模式吗?「每天搞透一道JS手写题💪Day8」 - 掘金 (juejin.cn)

HTTP和HTTPS的区别是什么?SSL协议的加密方式是什么?

HTTP (端口号80)和HTTPS(端口号443)的主要区别在于安全性和数据传输方式。

HTTP(超文本传输协议)是一种用于分布式、协作式和超媒体信息系统的应用层协议。它以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息。

HTTPS(超文本传输安全协议)是一种透过计算机网络进行安全通信的传输协议,需要CA证书。HTTPS经由HTTP进行通信,但利用SSL/TLS来加密数据包。HTTPS的主要目的是提供对网站服务器的身份认证,保护交换数据的隐私与完整性。

SSL (Secure Sockets Layer)安全套接层协议,目的是保证数据传输的安全和完整。SSL协议既用到了对称加密也用到了非对称加密 (公钥加密),在建立传输链路时,SSL首先对对称加密的密钥 使用公钥进行非对称加密 ,链路建立好之后,SSL对传输 内容使用对称加密。这样,既利用了非对称加密在公开环境下安全传输密钥的优点,又利用了对称加密算法在大量数据加密时的效率优势。

TCP 的三次握手?

TCP(传输控制协议)是一种面向连接的协议,它在数据传输之前需要在客户端和服务器之间建立连接。这个连接的建立过程就是我们所说的"三次握手"。以下是三次握手的具体步骤:

第一次握手:客户端将标志位SYN设置为1,随机产生一个值seq=J,并将该数据包发送给服务器,然后客户端进入SYN_SENT状态。

第二次握手:服务器收到客户端的SYN包,必须确认客户端的SYN,将标志位SYN和ACK都置为1,ack=J+1,同时为自己也要发送SYN请求信息,随机产生一个值seq=K,并将该数据包发送给客户端,此时服务器进入SYN_RCVD状态。

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=K+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

完成三次握手后,客户端与服务器就正式建立了连接,在这个状态下,双方都可以发送数据。

了解事件循环吗?微任务有哪些?

事件循环是JavaScript的执行机制。由于JavaScript是单线程的,事件循环机制可以解决JavaScript在处理高IO任务时可能出现的阻塞问题。

事件循环包括两种任务:宏任务和微任务。宏任务由宿主(浏览器/Node)发起,微任务由JavaScript自身发起。

微任务包括:

  • Promise.then()
  • process.nextTick(仅在Node.js环境中)
  • Object.observe(已废弃;被Proxy对象替代)
  • MutationObserver

执行顺序是:先执行同步代码,遇到异步宏任务则将异步宏任务放入宏任务队列中,遇到异步微任务则将异步微任务放入微任务队列中。当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行,微任务执行完毕后再将异步宏任务从队列中调入主线程执行,一直循环直至所有任务执行完毕。

代码输出题

结合刚才说的事件循环,判断下面的代码输出顺序

js 复制代码
console.log('start');

setTimeout(() => {
    console.log('Timeout1');
}, 1000);

Promise.resolve().then(() => {
    console.log('Promise1');
});

Promise.resolve().then(() => {
    console.log('Promise2');
    setTimeout(() => {
      Promise.resolve().then(() => {
        console.log('Promise3');
      })
      console.log('Timeout2');
    }, 0);
});

console.log('end');

打印顺序: start end Promise1 Promise2 Timeout2 Promise3 Timeout1

说明:

  1. 首先按顺序执行同步代码,打印 start 和 end,并且把异步任务分别放入宏任务和微任务队列中;
  2. 其次执行微任务队列中的回调,打印 Promise1 和 Promise2;
  3. 打印完 Promise2 之后,又向宏任务队列中推入了一个计时器;
  4. 此时宏任务队列中有两个计时器,优先执行延迟时间短的那个,打印了 Timeout2,并且向微任务队列中加入了要一个回调函数;
  5. 再次优先执行微任务队列中的回调,打印 Promise3。最后,打印 Timeout1。

这段代码输出什么?为什么会输出这样的结果?如何修改使其正常工作?

js 复制代码
for(var i = 0; i < 3; i++){
    setTimeout(function(){
        console.log(i)
    })
}

这段代码会输出三次 "3",因为 setTimeout 函数是异步的,当计时器执行时,for 循环已经结束,变量 i 的值为 3,并且 var 不存在块级作用域,因此三个计时器中的回调函数都会打印 3。

想要让这段代码顺序打印0,1,2,就要保证计时器中的 i 是当次循环的 i 值,这需要将这个 i 和计数器固定到一个作用域中。

有两种方法:一是直接将 var 修改为 let,let 关键字会创建块级作用域,在每次循环中 let 都会创建一个新的变量 i,并初始化其为当前的值;二是使用闭包给每一次循环创建一个新的作用域,代码如下:

js 复制代码
for(var i = 0; i < 3; i++){
    (function(i){
        setTimeout(function(){
            console.log(i)
        })
    })(i)
}

算法题

leetcode 102. 二叉树的层序遍历

js 复制代码
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[][]}
 */
var levelOrder = function (root) {
    if (!root) return [];
    let queue = [];
    let res = [];
    queue.push(root);
    while (queue.length) {
        let arr = [];
        let len = queue.length;
        for (let i = 0; i < len; i++) {
            let node = queue.shift();
            arr.push(node.val);
            if (node.left) queue.push(node.left);
            if (node.right) queue.push(node.right);
        }
        res.push(arr);
    }
    return res;
};

写完之后会让你结合代码讲一下思路。

相关推荐
陪我去看海1 小时前
被要求猛出小程序,做了这个多平台多环境的部署替我承受压力
前端·微信小程序·抖音小程序
Frag0ut2 小时前
告别插件时代:HTML5 视频播放对比 Flash/Silverlight/ActiveX 的性能与技术优势解析
前端·html5·视频播放·flash·activex·流媒体技术·silverlight
用户818618028002 小时前
MongoDB文档模型设计——从建模到索引
前端
弈栈录2 小时前
基于 Spring Boot 构建生产级 AI 应用平台
后端·面试·架构
無名路人2 小时前
小程序点餐页吸顶滚动之分类按需加载,上划切换
前端·vue.js·微信小程序
竹林8183 小时前
把神经网络塞进一个浏览器标签页:端侧视觉 AI 的工程真相
前端·浏览器
乘风gg3 小时前
别跟风 AI 副业,工程师最该学的是 AI Coding
前端·ai编程·claude
Frag0ut3 小时前
深度解析:Chrome 自启动与后台常驻服务的作用、关闭方法及利弊权衡
前端·chrome
OpsEye3 小时前
当企业不再只关心能不能调用模型,而是关心值不值得调用
javascript·ai编程
Amos_Web3 小时前
Rspack 源码解析(十五):Loader Runner 与 JS Loader 桥接
前端·rust·前端框架