乐趣区

关于前端:8月血泪史助力金九银十

本文呈现的问题都是在 8 月阶段理论面试过程中遇到的,大小公司都有波及,心愿可能助力大家金九银十,也心愿本人可能顺利找到工作。当初里面大环境确实太难了……

文中局部题目提供了参考资料以及本人整顿的相干答案,可能不肯定是最精确的,大家酌情参考。

如波及到其余文章内容援用没有表明出处的,能够私聊补充更新。

Javascript

1.JS 根本数据类型有哪些

根本数据类型:

  1. 字符串(String):示意文本数据,应用引号(单引号或双引号)括起来。例如:”Hello World”。
  2. 数字(Number):示意数值数据,包含整数和浮点数。例如:10、3.14。
  3. 布尔值(Boolean):示意逻辑值,只有两个值:true(真)和 false(假)。
  4. null:示意空值,示意一个空对象指针。
  5. undefined:示意未定义的值,示意变量未被赋值。
  6. Symbol:示意惟一的标识符,用于对象属性的键。

【扩大】说一下 undefined 和 null 的区别

当谈到 JavaScript 中的 undefined 和 null 时,它们是两个不同的值,但都示意短少值。它们之间的区别如下:

  1. undefined 是示意变量申明但未赋值的默认值。当尝试拜访未初始化的变量时,它的值为 undefined。全局属性 undefined 示意原始值 undefined。它是一个 JavaScript 的 原始数据类型。undefined 是全局对象的一个属性。也就是说,它是全局作用域的一个变量。undefined 的最后值就是原始数据类型 undefined。
  2. 值 null 特指对象的值未设置。它是 JavaScript 根本类型 之一,在布尔运算中被认为是 falsy。它是一个非凡的关键字,示意变量不指向任何对象、数组或函数。值 null 是一个字面量,不像 undefined,它不是全局对象的一个属性。null 是示意短少的标识,批示变量未指向任何对象。把 null 作为尚未创立的对象,兴许更好了解。在 API 中,null 常在返回类型应是一个对象,但没有关联的值的中央应用。

2.const、let 和 var 的区别

【参考资料】

面试官:说说 var、let、const 之间的区别

3.call、apply 和 bind 的区别

  • 三者都能够扭转函数的 this 对象指向。
  • 三者第一个参数都是 this 要指向的对象,如果如果没有这个参数或参数为 undefined 或 null,则默认指向全局 window。
  • 三者都能够传参,然而 apply 是数组,而 call 是参数列表,且 apply 和 call 是一次性传入参数,而 bind 能够分为屡次传入。
  • bind 是返回绑定 this 之后的函数,便于稍后调用;apply、call 则是立刻执行。

【参考】

彻底弄懂 bind,apply,call 三者的区别

深刻理解 call, apply, bind 和模仿实现

5 分钟带你搞懂 Javascript 中的 this(蕴含 apply、call、bind)

4. 对闭包的了解

概念

一个函数和对其四周状态(lexical environment,词法环境)的援用捆绑在一起(或者说函数被援用突围),这样的组合就是闭包(closure)。也就是说,闭包让你能够在一个内层函数中拜访到其外层函数的作用域。在 JavaScript 中,每当创立一个函数,闭包就会在函数创立的同时被创立进去。

重点的一句:闭包让你能够在一个内层函数中拜访到其外层函数的作用域。

应用场景

  • return 一个外部函数,读取外部函数的变量;
  • 函数作为参数
  • IIFE(自执行函数)
  • 封装对象的公有对象和公有办法;

5.new 一个构造函数的过程

用 new 操作符创建对象时产生的事件:

  • 创立新对象
  • 新对象原型[[prototype]] = 构造函数 prototype
  • this 指向新对象
  • 执行构造函数
  • 如果构造函数返回非空对象,就返回这个对象援用,不然返回创立的新对象

函数调用时后面不加 new 就按一般函数来 执行。加 new 后对其进行了相应的变动,按构造函数来执行。

new 的具体过程如下:

// 例子:
function Person(name,age) {
  this.userName = name;
  this.userAge = age;
}
var personl = new Person('LULU',20)

1、创立一个新的空对象。(即实例对象)

obj = {}

2、设 置 原 型 链

将新对象 obj 的 __proto__属性指向构造函数的 prototype 对象。(即所有实例对象通过__proto__能够拜访原型对象。构造函数的原型被其所有实例对象共享。)

obj.__proto__= Person.prototype

3、将构造函数的 this 改指向新对象 obj 并执行函数代码。

(即给实例对象设置属性 userName, userAge 并赋值。)

var result = Person.apply(obj,['LULU',20])

4、如果构造函数中没有人为返回一个对象类型的值,则返回这个新对象 obj。否则间接返回那个对象类型值。(个别定义的构造函数中不写返回值。)

if (typeof(result) == 'object') {return result;}else{return obj;}

【扩大】手动实现一个 new 操作,参数为构造函数 及其传参

// 构造函数
function Person(name,age) {
  this.userName = name;
  this.userAge = age;
}
Person.prototype.sayAge = function(){console.Iog(this.userAge)
}
// new 操作函数 newFun
function newFun() {var obj = {};
  Constructor = [].shift.call(arguments);
  obj.__proto__ = Constructor.prototype;
  var ret = Constructor.apply(obj, arguments);
  return typeof ret === 'object' ? ret: obj;
};
// 调用 newFun
var s1 = newFun(Person, 'xiaoyun',20);
console.log(s1.userName) //xiaoyun
console.log(s1.sayAge()) //20

备注:[].shift.call(arguments):删除并返回参数列表 arguments 中第一个参数,即取得构造函数。arguments 残余参数为构数传参。arguments 是类数组,没有数组办法 shift, 可更改 shift 办法的 this 指向从而使用到 arguments 上。

6.ES6 高阶函数有哪些

ES6 中的高阶函数有以下几种:

  1. map:对数组中的每个元素进行操作,并返回一个新的数组。
  2. filter:依据指定的条件过滤出数组中的元素,并返回一个新的数组。
  3. reduce:对数组中的元素进行累加或累积操作,并返回一个后果。
  4. forEach:对数组中的每个元素进行操作,没有返回值。
  5. some:判断数组中是否存在满足指定条件的元素,返回布尔值。
  6. every:判断数组中的所有元素是否都满足指定条件,返回布尔值。
  7. find:查找数组中满足指定条件的第一个元素,并返回该元素。
  8. findIndex:查找数组中满足指定条件的第一个元素的索引,并返回该索引。
  9. sort:对数组中的元素进行排序。
  10. flat:将多维数组转换为一维数组。
  11. map、filter、reduce 等办法的变种,如 flatMap、find、findIndex 等。

这些高阶函数能够使代码更简洁、可读性更高,并且提供了一种函数式编程的形式来操作数组。

Typescript

1.TS 比照 JS 的劣势有哪些

CSS

1. 说下什么是 BFC

BFC 到底是什么货色

BFC 全称:Block Formatting Context,名为 “ 块级格式化上下文 ”。

W3C 官网解释为:BFC 它决定了元素如何对其内容进行定位,以及与其它元素的关系和相互作用,当波及到可视化布局时,Block Formatting Context 提供了一个环境,HTML 在这个环境中依照肯定的规定进行布局。

简略来说就是,BFC 是一个齐全独立的空间(布局环境),让空间里的子元素不会影响到里面的布局。那么怎么应用 BFC 呢,BFC 能够看做是一个 CSS 元素属性

怎么触发 BFC

这里简略列举几个触发 BFC 应用的 CSS 属性

  • overflow: hidden
  • display: inline-block
  • position: absolute
  • position: fixed
  • display: table-cell
  • display: flex

【参考】

面试官:请说说什么是 BFC?大白话讲清楚

区块格式化上下文

Vue

1.Vue2、3 的模版编译渲染过程有哪些区别

2. 虚构 Dom 的劣势是什么?性能肯定更好吗?

虚构 DOM(Virtual DOM)这个概念置信大家都不生疏,从 React 到 Vue,虚构 DOM 为这两个框架都带来了跨平台的能力(React-Native 和 Weex)

实际上它只是一层对实在 DOM 的形象,以 JavaScript 对象 (VNode 节点) 作为根底的树,用对象的属性来形容节点,最终能够通过一系列操作使这棵树映射到实在环境上

在 Javascript 对象中,虚构 DOM 体现为一个 Object 对象。并且起码蕴含标签名 (tag)、属性 (attrs) 和子元素对象 (children) 三个属性,不同框架对这三个属性的名命可能会有差异

创立虚构 DOM 就是为了更好将虚构的节点渲染到页面视图中,所以虚构 DOM 对象的节点与实在 DOM 的属性一一呼应

长处:

保障性能上限:框架的虚构 DOM 须要适配任何下层 API 可能产生的操作,它的一些 DOM 操作的实现必须是普适的,所以它的性能并不是最优的;然而比起粗犷的 DOM 操作性能要好很多,因而框架的虚构 DOM 至多能够保障在你不须要手动优化的状况下,仍然能够提供还不错的性能,即保障性能的上限;无需手动操作 DOM:咱们不再须要手动去操作 DOM,只须要写好 View-Model 的代码逻辑,框架会依据虚构 DOM 和 数据双向绑定,帮咱们以可预期的形式更新视图,极大进步咱们的开发效率;

跨平台:虚构 DOM 实质上是 JavaScript 对象, 而 DOM 与平台强相干,相比之下虚构 DOM 能够进行更不便地跨平台操作,例如服务器渲染、weex 开发等等。

毛病:

无奈进行极致优化:尽管虚构 DOM + 正当的优化,足以应答绝大部分利用的性能需求,但在一些性能要求极高的利用中虚构 DOM 无奈进行针对性的极致优化。

【参考资料】

面试官:什么是虚构 DOM?如何实现一个虚构 DOM?说说你的思路

3.Vue2 和 Vue3 的响应式区别

  • Vue2 中响应式是通过 defineProperty 实现的
  • Vue3 中响应式是通过 ES6 的 Proxy 实现的
  • Vue3 中实现响应式数据的办法是 ref 和 reactive

【扩大内容】盘点 Vue3 与 Vue2 的区别

4.ref 和 reactive 的区别

reactive 特点

  • reactive 的参数个别是 对象或者数组, 他可能将简单数据类型变为响应式数据。
  • reactive 的响应式是深层次的,底层实质是将传入的数据转换为 Proxy 对象

ref 特点

  • ref 的参数个别是根本数据类型,也能够是对象类型
  • 如果参数是对象类型,其实底层的实质还是 reactive,零碎会主动将 ref 转换为 reactive,例如 ref(1) ===> reactive({value:1})
  • 在模板中拜访 ref 中的数据,零碎会主动帮咱们增加.value, 在 JS 中拜访 ref 中的数据,须要手动增加.value
  • ref 的底层原理同 reactive 一样,都是 Proxy

5.Vue 的双向数据绑定 / 响应式原理

【参考资料】

面试官:双向数据绑定是什么

6.Vue2 为什么不能监控到数组 / 对象的属性变更

首先 Object.defineProperty 是能够监听局部数组的更新动作的,然而因为它是通过遍历的形式来实现,当数组数据量小的时候没有问题,然而当数据量大的时候,解决上会呈现显著的性能问题。也是出于“性能代价和取得的用户体验收益不成正比”的起因,所以 Vue2 没有去实现监听。Vue3 的 Proxy 的劫持形式完满解决了这个问题。

7. 说下 Vue 中的 Slot,应用场景

应用场景

通过插槽能够让用户能够拓展组件,去更好地复用组件和对其做定制化解决

如果父组件在应用到一个复用组件的时候,获取这个组件在不同的中央有大量的更改,如果去重写组件是一件不明智的事件

通过 slot 插槽向组件外部指定地位传递内容,实现这个复用组件在不同场景的利用

比方布局组件、表格列、下拉选、弹框显示内容等

分类

slot 能够分来以下三种:

  • 默认插槽
  • 具名插槽

    • 子组件用 name 属性来示意插槽的名字,不传为默认插槽
  • 作用域插槽

    • 子组件在作用域上绑定属性来将子组件的信息传给父组件应用,这些属性会被挂在父组件 v -slot 承受的对象上。父组件中在应用时通过 v -slot:(简写:#)获取子组件的信息,在内容中应用

      <!-- 子组件 -->
      <template> 
        <slot name="footer" testProps="子组件的值">
        <h3> 没传 footer 插槽 </h3>
      </slot>
      </template>
      <!-- 父组件 -->
      <child> 
      <!-- 把 v -slot 的值指定为作⽤域高低⽂对象 -->
      <template v-slot:default="slotProps">
        来⾃⼦组件数据:{{slotProps.testProps}}
      </template>
      <template #default="slotProps">
        来⾃⼦组件数据:{{slotProps.testProps}}
      </template>
      </child>
  • 小结:

    • v-slot 属性只能在 <template> 上应用,但在只有默认插槽时能够在组件标签上应用
    • 默认插槽名为 default,能够省略 default 间接写 v -slot

      • 缩写为 #时不能不写参数,写成#default
      • 能够通过解构获取 v -slot={user},还能够重命名 v -slot=”{user: newName}” 和定义默认值 v -slot=”{user = ‘ 默认值 ’}”

【参考资料】

面试官:说说你对 slot 的了解?slot 应用场景有哪些?

8. 为什么 data 返回一个函数

  • vue 中组件是用来复用的,为了避免 data 复用,将其定义为函数。
  • vue 组件中的 data 数据都应该是互相隔离,互不影响的,组件每复用一次,data 数据就应该被复制一次,之后,当某一处复用的中央组件内 data 数据被扭转时,其余复用中央组件的 data 数据不受影响,就须要通过 data 函数返回一个对象作为组件的状态。
  • 当咱们将组件中的 data 写成一个函数,数据以函数返回值模式定义,这样每复用一次组件,就会返回一份新的 data,领有本人的作用域,相似于给每个组件实例创立一个公有的数据空间,让各个组件实例保护各自的数据。
  • 当咱们组件的 date 单纯的写成对象模式,这些实例用的是同一个构造函数,因为 JavaScript 的个性所导致,所有的组件实例共用了一个 data,就会造成一个变了全都会变的后果。

9.JSX 有理解吗,和 template 的区别;

10.vue 外面针对谬误的生命周期函数应用

11. 说下 Vuex or Pinia 的应用

12.Vue.set 的原理

Vue.set 是 Vue.js 提供的一个全局办法,用于在响应式对象上设置响应式属性。它的原理是通过调用 Vue 实例的外部办法 defineReactive 来为对象设置新属性,并使其成为响应式属性。

具体原理如下:

  1. Vue.set 办法承受三个参数:对象、属性名和属性值。
  2. 首先,它会判断对象是否是响应式的,如果不是,则间接返回。
  3. 而后,它会判断属性是否曾经存在于对象中,如果存在,则间接更新属性的值。
  4. 如果属性不存在于对象中,它会通过 defineReactive 办法将属性设置为响应式属性。
  5. defineReactive 办法会为属性创立一个 Dep 实例对象,用于收集依赖和触发更新。
  6. 而后,它会应用 Object.defineProperty 办法将属性设置为响应式,并在 getter 和 setter 中触发依赖收集和更新。
  7. 最初,它会触发属性的更新,告诉相干的 Watcher 进行视图的更新。

React

1.React 中 setState 什么时候是同步,什么时候是异步

在 React 中,setState 有时是同步的,有时是异步的。

当在 React 事件处理函数(如 onClick)或生命周期办法(如 componentDidMount)中调用 setState 时,setState 是异步的。这是因为 React 会对间断的 setState 调用进行批处理,以进步性能。

例如,在以下代码中,setState 是异步的:

class MyComponent extends React.Component {handleClick() {this.setState({ count: this.state.count + 1});
    console.log(this.state.count); // 输入的是更新前的值
  }

  render() {
    return (<button onClick={this.handleClick.bind(this)}>Click me</button>
    );
  }
}

然而,当在 setTimeout、Promise、原生事件监听器等异步场景中调用 setState 时,setState 是同步的。

例如,在以下代码中,setState 是同步的:

class MyComponent extends React.Component {componentDidMount() {setTimeout(() => {this.setState({ count: this.state.count + 1});
      console.log(this.state.count); // 输入的是更新后的值
    }, 1000);
  }

  render() {
    return (<div>{this.state.count}</div>
    );
  }
}

网络 / 浏览器

1. 说下 Http1.0 和 Http2.0 的区别

HTTP 超文本传输协定是位于 TCP/IP 体系结构中的应用层协定,它是万维网数据通信的根底。

HTTP1.1 的缺点

  1. 高提早 — 队头阻塞(Head-Of-Line Blocking)
  2. 无状态个性 — 妨碍交互
  3. 明文传输 — 不安全性
  4. 不反对服务端推送

http2 是 HTTP 协定的的第二个次要版本,应用于万维网。HTTP/ 2 是 HTTP 协定自 1999 年 HTTP 1.1 公布后的首个更新,次要基于 SPDY 协定。和 HTTP1 的次要区别有:

  • 1、http2 采纳二进制格局而非文本格式。
  • 2、http2 是齐全多路复用的,而非有序并阻塞的。
  • 3、http2 采纳了报头压缩,升高了老本。
  • 4、http2 让服务器能够被动将响应推送到客户端缓存中

HTTP2 的缺点

  1. TCP 以及 TCP+TLS 建设连贯的延时
  2. TCP 的队头阻塞并没有彻底解决
  3. 多路复用导致服务器压力回升
  4. 多路复用容易 Timeout

HTTP3 是一个基于 UDP 协定的 QUIC 协定,它真正“完满”地解决了“队头阻塞”问题。

HTTP3 的次要特点

  1. 改良的拥塞管制、牢靠传输
  2. 疾速握手
  3. 集成了 TLS 1.3 加密
  4. 多路复用
  5. 连贯迁徙

【参考资料】:

  • 一文读懂 HTTP/1HTTP/2HTTP/3
  • HTTP2、HTTP3 优缺点整顿

2. 说下 HTML 渲染过程

  1. 构建 DOM 树

<!—->

    1. 将 HTML 解析成许多 Tokens
    2. 将 Tokens 解析成 Objects
    3. 将 Objects 组合成为一个 DOM 树
  1. 构建 CSSOM – CSS 对象模型

<!—->

    1. 解析 CSS 文件,并构建出一个 CSSOM 树(过程相似于 DOM 构建)
  1. 构建 Render 树

<!—->

    1. 联合 DOM 和 CSSOM 构建出一颗 Render 树

构建过程遵循以下步骤:

      1. 浏览器从 DOM 树开始,遍历每一个“可见”节点
      2. 对于每一个 ” 可见 ” 节点,在 CSSOM 上找到匹配的款式并利用
      3. 生成 Render Tree
  1. Layout 布局

<!—->

    1. 计算出元素绝对于 viewport 的绝对地位

<!—->

  1. Paint 渲染

<!—->

    1. 将 Render 树转换成像素,显示在屏幕上

残缺流程:

扩大】JS 文件引入对 CRP 的影响

  1. 解析 HTML 构建 DOM 时,遇到 JavaScript 会被阻塞;
  2. JavaScript 执行会被 CSSOM 构建阻塞,也就是说,JavaScript 必须等到 CSS 解析实现后才会执行(这只针对在头部搁置 <style> 和 <link> 的状况,如果放在尾部,浏览器刚开始会应用 User Agent Style 构建 CSSOM)
  3. 如果应用异步脚本,脚本的网络申请优先级升高,且网络申请期间不阻塞 DOM 构建,直到申请实现才开始执行脚本

应用异步脚本,其实就是通知浏览器几件事:

    1. 无需阻塞 DOM,在对 Script 执行网络申请期间能够持续构建 DOM,直到拿到 Script 之后再去执行
    2. 将该 Script 的网络申请优先级升高,缩短响应工夫,确保执行它之前 DOM 和 CSSOM 都构建实现

须要留神如下几点:

    1. 异步脚本是网络申请期间不阻塞 DOM,拿到脚本之后马上执行,执行时还是会阻塞 DOM,然而因为响应工夫被缩短,此时往往 DOM 曾经构建结束(上面的测验图片将会看到,CSSOM 也曾经构建结束而且页面很快就产生第一次渲染),异步脚本的执行产生在第一次渲染之后
    2. 只有内部脚本能够应用 async 关键字变成异步,而且留神其与提早脚本(<script defer />)的区别,后者是在 Document 被解析结束而 DOMContentLoaded 事件触发之前执行,前者则是在下载结束后执行
    3. 对于应用 document.createElement 创立的 <script />,默认就是异步脚本

扩大】link 标签在不同地位对 CRP 的影响

  1. link 标签在结尾的时候,会引起重绘
  2. link 标签在 DOM 两头地位时,会阻塞渲染

扩大】CSS 匹配规定为何从右向左

CSS 匹配就产生在 Render Tree 构建时(Chrome Dev Tools 里叫做 Recalculate Style),此时浏览器构建出了 DOM,而且拿到了 CSS 款式,此时要做的就是把款式跟 DOM 上的节点对应上,浏览器为了进步性能须要做的就是疾速匹配。

首先要明确一点,浏览器此时是给一个 ” 可见 ” 节点找对应的规定,这和 jQuery 选择器不同,后者是应用一个规定去找对应的节点,这样从左到右或者更快。然而对于前者,因为 CSS 的宏大,一个 CSS 文件中或者有上千条规定,而且对于以后节点来说,大多数规定是匹配不上的,到此为止,略微想一下就晓得,如果从右开始匹配(也是从更准确的地位开始),能更快排除不适合的大部分节点,而如果从左开始,只有深刻了才会发现匹配失败,如果大部分规定层级都比拟深,就比拟浪费资源了。

除了下面这点,咱们后面还提到 DOM 构建是 ” 循序渐进的 ”,而且 DOM 不阻塞 Render Tree 构建(只有 CSSOM 阻塞),这样也是为了能让页面更早有元素出现。思考如下状况,如果咱们此时构建的只是局部 DOM,而此时 CSSOM 构建实现,浏览器此时须要构建 Render Tree,如果对每一个节点,找到一条规定进行从左向右匹配,则必须要求其子元素甚至孙子元素都在 DOM 上,但此时 DOM 未构建实现,显然会匹配失败。如果反过来,咱们只须要查找该元素的父元素或先人元素,它们必定在以后的 DOM 中。

扩大】window.onload 和 DOMContentLoaded 的区别

  1. onload 事件是当页面所有资源 (包含 DOM, 样式表, 脚本, 图片等资源) 全副加载实现后执行
  2. DOMContentLoaded 事件在 DOM 构造加载实现后即触发,不期待其余资源的加载。更快

扩大】重绘和重排

概念

重排 是什么:从新生成布局。当 DOM 的变动影响了元素的几何属性(宽和高)– 比方扭转边框宽度或给段落减少文字导致行数减少 – 浏览器须要从新计算元素的几何属性,同样其余元素的几何属性和地位也会因而受到影响。浏览器会使渲染树中受到影响的局部生效,并从新结构渲染树。这个过程称为重排。

重绘 是什么:从新绘制。实现重排后,浏览器会从新绘制受影响的局部到屏幕中。这个过程称为重绘。

产生重排的状况

当页面布局和几何属性扭转时产生“重排”,如下:

  • 增加或删除可见的 DOM 元素
  • 元素地位扭转
  • 元素尺寸扭转(包含外边距、内边距、边框厚度、宽度、高度等属性扭转)
  • 内容扭转,例如:文本扭转后图片被另一个不同尺寸的图片代替
  • 页面渲染器初始化
  • 浏览器窗口尺寸扭转
强制刷新队列

获取布局信息的操作会导致列队刷新,以下属性和办法须要返回最新的布局信息,最好防止应用。

offsetTop, offsetLeft, offsetWidth, offsetHeight

scrollTop, scrollLeft, scrollWidth, scrollHeight

clientTop, clientLeft, clientWidth, clientHeight

getComputedStyle() (currentStyle in IE)

clientTop:元素上边框的厚度,当没有指定边框厚底时,个别为 0。

scrollTop:位于对象最顶端和窗口中可见内容的最顶端之间的间隔,简略地说就是滚动后被暗藏的高度。

offsetTop:获取对象绝对于由 offsetParent 属性指定的父坐标 (css 定位的元素或 body 元素) 间隔顶端的高度。

clientHeight:内容可视区域的高度,也就是说页面浏览器中能够看到内容的这个区域的高度,个别是最初一个工具条以下到状态栏以上的这个区域,与页面内容无关。

scrollHeight:IE、Opera 认为 scrollHeight 是网页内容理论高度,能够小于 clientHeight。FF 认为 scrollHeight 是网页内容高度,不过最小值是 clientHeight。

offsetHeight:获取对象绝对于由 offsetParent 属性指定的父坐标 (css 定位的元素或 body 元素) 的高度。IE、Opera 认为 offsetHeight = clientHeight + 滚动条 + 边框。FF 认为 offsetHeight 是网页内容理论高度,能够小于 clientHeight。offsetHeight 在新版本的 FF 和 IE 中是一样的,示意网页的高度,与滚动条无关,chrome 中不包含滚动条。

Window.getComputedStyle()办法返回一个对象,该对象在利用流动样式表并解析这些值可能蕴含的任何根本计算后报告元素的所有 CSS 属性的值。公有的 CSS 属性值能够通过对象提供的 API 或通过简略地应用 CSS 属性名称进行索引来拜访。

缩小重排和重绘
  • 合并款式批改
  • 批量操作 DOM

<!—->

    • 脱离规范流的操作有以下 3 中:

<!—->

      • 暗藏元素
      • 应用文档碎片
      • 拷贝节点

<!—->

  • 防止屡次触发布局
  • 对于页面中比较复杂的动画,尽量将元素设置为相对定位,操作元素的定位属性,这样只有这一个元素会回流,如果不是定位的话,容易引起其父元素以及子元素的回流。

【参考资料】

  • 深刻了解浏览器解析渲染 HTML
  • How browsers work
  • 浏览器渲染之 link 标签和 script 标签对页面的影响
  • 重排 (reflow) 和重绘(repaint)
  • 从输出 URL 到浏览器显示页面过程中都产生了什么 👍
  • 讲清楚重排或回流、重绘

3. 说下浏览器缓存

扩大】Last-Modified 和 Etag 的区别?哪个更好一些?

Last-Modified 和 ETag 是 HTTP 协定中用于缓存验证的两种机制。

  1. Last-Modified:服务器在响应中增加一个 Last-Modified 头,该头蕴含了资源的最初批改工夫。客户端在后续申请中能够通过发送 If-Modified-Since 头,将上次获取到的 Last-Modified 值发送给服务器进行验证。如果资源的最初批改工夫与客户端提供的工夫雷同,则服务器返回 304 Not Modified 状态码,示意资源未被批改,客户端能够应用缓存正本。
  2. ETag:服务器在响应中增加一个 ETag 头,该头蕴含了资源的惟一标识符(通常是一个哈希值)。客户端在后续申请中能够通过发送 If-None-Match 头,将上次获取到的 ETag 值发送给服务器进行验证。如果资源的 ETag 与客户端提供的值雷同,则服务器返回 304 Not Modified 状态码,示意资源未被批改,客户端能够应用缓存正本。

两者区别:

  • Last-Modified 是基于资源的最初批改工夫进行验证,而 ETag 是基于资源的惟一标识符进行验证。
  • Last-Modified 的精度是以秒为单位,而 ETag 没有这个限度,能够更准确地示意资源的变动。
  • ETag 能够在某些状况下提供更好的性能,因为它不依赖于文件系统的工夫戳,而是依据资源内容生成惟一标识符。比方回滚时 Etag 更加便捷。

扩大】日常我的项目中波及到哪些缓存?

  • HTML:应用协商缓存。
  • CSS&JS& 图片:应用强缓存,文件命名带上 hash 值。

扩大】webpack 打包中的几种 hash 模式的区别

webpack 给咱们提供了三种哈希值计算形式,别离是 hash、chunkhash 和 contenthash。那么这三者有什么区别呢?

  • hash:跟整个我的项目的构建相干,构建生成的文件 hash 值都是一样的,只有我的项目里有文件更改,整个我的项目构建的 hash 值都会更改。
  • chunkhash:依据不同的入口文件 (Entry) 进行依赖文件解析、构建对应的 chunk,生成对应的 hash 值。
  • contenthash:由文件内容产生的 hash 值,内容不同产生的 contenthash 值也不一样。

显然,咱们是不会应用第一种的。改了一个文件,打包之后,其余文件的 hash 都变了,缓存天然都生效了。这不是咱们想要的。那 chunkhash 和 contenthash 的次要利用场景是什么呢?在理论在我的项目中,咱们个别会把我的项目中的 css 都抽离出对应的 css 文件来加以援用。如果咱们应用 chunkhash,当咱们改了 css 代码之后,会发现 css 文件 hash 值扭转的同时,js 文件的 hash 值也会扭转。这时候,contenthash 就派上用场了。

参考资料

  • 前端缓存最佳实际
  • 彻底弄懂前端缓存

4. 说下 V8 垃圾回收机制?

在 JavaScript 中,数据类型分为两类,简略类型和援用类型,对于简略类型,内存是保留在栈(stack)空间中,简单数据类型,内存是保留在堆(heap)空间中。

  • 根本类型:这些类型在内存中别离占有固定大小的空间,他们的值保留在栈空间,咱们通过按值来拜访的
  • 援用类型:援用类型,值大小不固定,栈内存中寄存地址指向堆内存中的对象。是按援用拜访的。

而对于栈的内存空间,只保留简略数据类型的内存,由操作系统主动调配和主动开释。而堆空间中的内存,因为大小不固定,零碎无奈进行主动开释,这个时候就须要 JS 引擎来手动的开释这些内存。

V8 将堆分为两类新生代和老生代,新生代中寄存的是生存工夫短的对象,老生代中寄存的生存工夫久的对象。

对于这两块区域,V8 别离应用两个不同的垃圾回收器,以便更高效地施行垃圾回收。

  • 副垃圾回收器 – Scavenge:次要负责新生代的垃圾回收。
  • 主垃圾回收器 – Mark-Sweep & Mark-Compact:次要负责老生代的垃圾回收。

Scavange 算法将新生代堆分为两局部,别离叫 from-space 和 to-space,工作形式也很简略,就是将 from-space 中存活的流动对象复制到 to-space 中,并将这些对象的内存有序的排列起来,而后将 from-space 中的非流动对象的内存进行开释,实现之后,将 from space 和 to space 进行调换,这样能够使得新生代中的这两块区域能够反复利用。

简略的形容就是:

  • 标记流动对象和非流动对象
  • 复制 from space 的流动对象到 to space 并对其进行排序
  • 开释 from space 中的非流动对象的内存
  • 将 from space 和 to space 角色调换

那么,垃圾回收器是怎么晓得哪些对象是流动对象和非流动对象的呢?

有一个概念叫对象的可达性,示意从初始的根对象(window,global)的指针开始,这个根指针对象被称为根集(root set),从这个根集向下搜寻其子节点,被搜寻到的子节点阐明该节点的援用对象可达,并为其留下标记,而后递归这个搜寻的过程,直到所有子节点都被遍历完结,那么没有被标记的对象节点,阐明该对象没有被任何中央援用,能够证实这是一个须要被开释内存的对象,能够被垃圾回收器回收。

新生代中的对象什么时候变成老生代的对象呢?

在新生代中,还进一步进行了细分,分为 nursery 子代和 intermediate 子代两个区域,一个对象第一次分配内存时会被调配到新生代中的 nursery 子代,如果进过下一次垃圾回收这个对象还存在新生代中,这时候咱们挪动到 intermediate 子代,再通过下一次垃圾回收,如果这个对象还在新生代中,副垃圾回收器会将该对象挪动到老生代中,这个挪动的过程被称为降职。

老生代垃圾回收 – Mark-Sweep & Mark-Compact

新生代空间中的对象满足肯定条件后,降职到老生代空间中,在老生代空间中的对象都曾经至多经验过一次或者屡次的回收所以它们的存活概率会更大,如果这个时候再应用 scavenge 算法的话,会呈现两个问题:

  • scavenge 为复制算法,反复复制流动对象会使得效率低下
  • scavenge 是就义空间来换取工夫效率的算法,而老生代反对的容量交大,会呈现空间资源节约问题

所以在老生代空间中采纳了 Mark-Sweep(标记革除)和 Mark-Compact(标记整顿)算法。

Mark-Sweep

Mark-Sweep 解决时分为两阶段,标记阶段和清理阶段,看起来与 Scavenge 相似,不同的是,Scavenge 算法是复制流动对象,而因为在老生代中流动对象占大多数,所以 Mark-Sweep 在标记了流动对象和非流动对象之后,间接把非流动对象革除。

  • 标记阶段:对老生代进行第一次扫描,标记流动对象
  • 清理阶段:对老生代进行第二次扫描,革除未被标记的对象,即清理非流动对象

Mark-Compact

因为 Mark-Sweep 实现之后,老生代的内存中产生了很多内存碎片,若不清理这些内存碎片,如果呈现须要调配一个大对象的时候,这时所有的碎片空间都齐全无奈实现调配,就会提前触发垃圾回收,而这次回收其实不是必要的。

为了解决内存碎片问题,Mark-Compact 被提出,它是在是在 Mark-Sweep 的根底演出进而来的,相比 Mark-Sweep,Mark-Compact 增加了流动对象整顿阶段,将所有的流动对象往一端挪动,挪动实现后,间接清理掉边界外的内存。

全进展 Stop-The-World

因为垃圾回收是在 JS 引擎中进行的,而 Mark-Compact 算法在执行过程中须要挪动对象,而当流动对象较多的时候,它的执行速度不可能很快,为了防止 JavaScript 应用逻辑和垃圾回收器的内存资源竞争导致的不一致性问题,垃圾回收器会将 JavaScript 利用暂停,这个过程,被称为全进展(stop-the-world)。

【扩大】如何判断哪个变量导致内存透露的?

在 V8 的垃圾回收机制中,内存透露通常指的是无奈被垃圾回收器回收的对象。上面是一些判断哪个变量内存透露的常见办法:

  1. 应用内存剖析工具:能够应用 V8 提供的内存剖析工具,如 Chrome DevTools 中的 Memory 面板,来检测内存透露。这些工具能够显示内存应用状况、对象援用关系等信息,帮忙定位内存透露的对象。
  2. 监测内存应用状况:通过监测内存应用状况,能够察看变量的内存占用是否随工夫增长,如果是,则可能存在内存透露。能够应用 V8 提供的内存应用状况 API,如 process.memoryUsage(),来获取以后过程的内存应用状况。
  3. 剖析代码逻辑:查看代码中是否存在未开释的资源,比方未敞开的文件句柄、未革除的定时器、未解绑的事件监听器等。这些资源如果没有正确开释,就会导致内存透露。
  4. 应用堆快照:能够应用 V8 提供的堆快照工具,如 Chrome DevTools 中的 Heap Snapshot,来捕捉以后堆中的对象快照,并剖析对象的援用关系。通过剖析堆快照,能够找到哪些对象被援用了但实际上不再须要,从而判断是否存在内存透露。
  5. 监测内存增长:能够在代码中设置定时器,定期检测内存的增长状况。如果发现内存持续增长,可能是因为有对象没有被正确开释,从而导致内存透露。

须要留神的是,判断内存透露并不是一件简略的事件,须要综合思考多个因素。有时候内存增长可能是失常的,例如缓存的对象或者长久化的数据。因而,联合以上办法进行综合剖析,能够更精确地判断哪个变量存在内存透露。

【参考资料】

  • 你真的理解垃圾回收机制吗
  • 深刻了解谷歌最强 V8 垃圾回收机制
  • Javascript 的垃圾回收机制知多少?👍
  • JavaScript 中的垃圾回收和内存透露

5. 说下浏览器事件循环机制的了解

【扩大】微工作和宏工作

【扩大】如何了解微工作优先于宏工作

从上图咱们就能够晓得,js 的事件循环机制规定了

1. 优先执行一个宏工作

2. 判断是否有微工作(无:继续执行宏工作)

3. 有:执行所有的微工作

4. 开始下一个宏工作

在 js 中 script 自身就是一个宏工作,所以执行异步队列的时候,会优先执行它的所有微工作,而后执行宏工作

【参考资料】

说下浏览器事件循环机制

🔥「吊打面试官」彻底了解事件循环、宏工作、微工作

齐全了解【宏工作和微工作】

6. 两个 tab 页如何通信

【参考资料】

面试官:你是如何实现浏览器多标签页之间通信的?

JS 如何跨标签页通信

7. 说下 Websocket 的了解

【参考资料】

面试官:说说对 WebSocket 的了解?利用场景?

8. 为什么呈现跨域?哪些形式可能解决跨域?CORS 是设置哪个申请头?

1. 什么是跨域?

跨域全称为 Cross-Origin Resource Sharing,意为 跨域资源共享 ,是一种 容许以后域(domain)的资源被其余域(domain)的脚本申请拜访 的机制,通常因为 同源安全策略,浏览器会禁止这种跨域申请。

而咱们所说的跨域问题是因为违反了 浏览器 的同源安全策略而产生的。

同源 是指 协定、域名、端口 三者雷同,即便两个不同的域名指向同一个 ip 地址也是非同源。

留神:跨域并不是申请发不进来,申请能收回去,服务端能收到申请并失常返回后果,只是后果被浏览器拦挡了。

2. 跨域的解决办法

  • jsonp
  • cors
  • websocket
  • postMessage
  • document.domain+iframe
  • Node 代理
  • Nginx 代理

【参考】

10 种跨域解决方案(附终极计划)

3.CORS 是设置哪个申请头

  1. Origin(起源):批示申请的源地址,即发动申请的网页的地址。例如,Origin: http://example.com
  2. Access-Control-Request-Method(访问控制申请办法):用于预检申请(OPTIONS 申请)中,批示理论申请应用的 HTTP 办法。例如,Access-Control-Request-Method: GET。
  3. Access-Control-Request-Headers(访问控制申请头):用于预检申请(OPTIONS 申请)中,批示理论申请中会蕴含的额定的自定义申请头。例如,Access-Control-Request-Headers: X-Custom-Header。
  4. Access-Control-Allow-Origin(容许的起源):用于服务器响应中,批示容许拜访资源的起源。能够是单个的源地址,也能够是通配符(*)示意容许所有起源。例如,Access-Control-Allow-Origin: http://example.com
  5. Access-Control-Allow-Methods(容许的办法):用于服务器响应中,批示容许的 HTTP 办法。例如,Access-Control-Allow-Methods: GET, POST, PUT。
  6. Access-Control-Allow-Headers(容许的头部):用于服务器响应中,批示容许的自定义申请头。例如,Access-Control-Allow-Headers: X-Custom-Header。
  7. Access-Control-Allow-Credentials(容许凭证):用于服务器响应中,批示是否容许发送凭证(如 cookies、HTTP 认证或客户端 SSL 证实)。例如,Access-Control-Allow-Credentials: true。

9.Options 预检申请呈现的机会是什么

  • 只有跨域的状况下,才会产生预申请
  • 与前述简略申请不同,“需预检的申请”要求必须首先应用 OPTIONS 办法发动一个预检申请到服务器,以获知服务器是否容许该理论申请。“预检申请”的应用,能够防止跨域申请对服务器的用户数据产生未预期的影响。
  1. withCredentials 为 true 不会产生预申请
  2. 申请头 Content-Type 为 application/json 会产生预申请
  3. 设置了用户自定义申请头会产生预检申请
  4. delete 办法产生预检申请

预检申请不肯定每一次都会产生

  • 这个因为浏览器会对预检申请进行缓存
  • 同时通过服务器端设置 Access-Control-Max-Age 字段来设置缓存工夫
  • 那么当第一次申请该 URL 时会收回 OPTIONS 申请,浏览器会依据返回的 Access-Control-Max-Age 字段缓存该申请的 OPTIONS 预检申请的响应后果(具体缓存工夫还取决于浏览器的反对的默认最大值,取两者最小值,个别为 10 分钟)。在缓存有效期内,该资源的申请(URL 和 header 字段都雷同的状况下)不会再触发预检。(chrome 关上控制台能够看到,当服务器响应 Access-Control-Max-Age 时只有第一次申请会有预检,前面不会了。留神要开启缓存,去掉 disable cache 勾选。)
  • 在 Firefox 中,下限是 24 小时(即 86400 秒)。
  • 在 Chromium v76 之前,下限是 10 分钟(即 600 秒)。
  • 从 Chromium v76 开始,下限是 2 小时(即 7200 秒)。
  • Chromium 同时规定了一个默认值 5 秒。
  • 如果值为 -1,示意禁用缓存,则每次申请前都须要应用 OPTIONS 预检申请。

10.Http2.0 有哪些新个性

  • 二进制分帧
  • 首部压缩
  • 流量管制
  • 多路复用
  • 申请优先级
  • 服务器推送

【参考】

HTTP—-HTTP2.0 新个性

11. 什么是 CDN,它的实现原理

【参考资料】

到底什么是 CDN?

CDN 原理简略介绍

一文搞懂 CDN 的技术架构和原理

面试官:如何了解 CDN?说说实现原理?

12.TCP 三次握手说一下

在 TCP/IP 协定中,TCP 协定提供牢靠的连贯服务,连贯是通过“三次握手”进行初始化的。三次握手的目标是同步连贯单方的序列号和确认号并替换 TCP 窗口大小信息,实现了三次握手,客户端和服务器端就能够开始传送数据。

第一次握手,是客户端向服务器端发动的,这是用来去发动一个连贯建设的申请,那么这个报文中的 SYN 位会被标记为:1,所以咱们也常把它叫做一个 SYN 包;

第二次握手,是由服务器向客户端发动的,是来确认服务器的一个申请链接的,这个报文中咱们的 ACK 位和 SYN 位都被标记为:1,所以咱们也把它叫做一个 SYN-ACK 报文;

第三次握手,同样是由客户端发个服务器端的,这是对服务器的上一个报文的一个确认报文,这个报文中的 ACK 位被标记为:1,所以咱们也把它叫做一个 ACK 包。

以上就是 TCP 的三次握手。

延长 -TCP 四次离别的含意

当客户端和服务器通过三次握手建设了 TCP 连贯当前,当数据传送结束,相应的就要断开 TCP 连贯。那对于 TCP 的断开连接,这里就有了“四次离别”。

1. 第一次离别:客户端发送断开申请
2. 第二次离别:服务器收到断开申请,发送批准断开连接的申请
3. 第三次离别:服务器发送申请断开连接
4. 第四次离别:客户端收到,发送批准断开连接

1. 当客户端发送断开申请,只是示意客户端曾经没有数据要发送了,客户端通知服务器,它的数据曾经全副发送结束了,然而,这个时候客户端还是能够承受来自服务器的数据(第一次离别)
2. 当服务器收到断开申请时,示意它曾经晓得客户端没有数据发送了并发送批准断开连接的申请,然而服务器还是能够发送数据到客户端的(第二次离别)
3. 当服务器发送批准断开连接的申请后,这个时候就示意服务器也没有数据要发送了,就会通知客户端,我也没有数据要发送了(第三次离别)
4. 当客户端收到服务器发送申请断开连接后,再去通知服务端我曾经晓得你没有数据要发给我了,批准断开连接申请(第四次离别)

13.UDP 协定理解吗

【参考】

UDP 协定详解

工程化

1. 构建工具应用的哪个?比照一下 webpack 和 vite 的差异

webpack core 是一个纯打包工具(对标 Rollup),而 Vite 其实是一个更下层的工具链计划,对标的是(webpack + 针对 web 的罕用配置 + webpack-dev-server)。

从底层原理上来说,Vite 是基于 esbuild 预构建依赖。而 esbuild 是采纳 go 语言编写,因为 go 语言的操作是纳秒级别,而 js 是以毫秒计数,所以 vite 比用 js 编写的打包器快 10-100 倍。

webpack 启动图:

Vite 启动图:

webpack:

剖析依赖 => 编译打包 => 交给本地服务器进行渲染。首先剖析各个模块之间的依赖,而后进行打包,在启动 webpack-dev-server,申请服务器时,间接显示打包后果。webpack 打包之后存在的问题:随着模块的增多,会造成打出的 bundle 体积过大,进而会造成热更新速度显著拖慢。

vite:

启动服务器 => 申请模块时按需动静编译显示。是先启动开发服务器,申请某个模块时再对该模块进行实时编译,因为古代游览器自身反对 ES-Module,所以会主动向依赖的 Module 发出请求。所以 vite 就将开发环境下的模块文件作为浏览器的执行文件,而不是像 webpack 进行打包后交给本地服务器。

Webpack

Webpack 是一个基于打包器的构建工具,同一个入口文件的代码会打包成一个 Bundle 文件。Webpack 长期来的一个痛点是对于大规模利用的利用启动和热更新速度很慢。

当文件产生变动时,整个 JavaScript Bundle 文件会被 Webpack 从新构建,这也是为什么应用 Webpack 的大规模利用在利用启动和热更新时速度很慢的起因。这给进行大规模 JavaScript 利用的开发者造成了很差的开发体验。

Webpack 工作流程

Webpack 打包过程:

  • 从一个入口文件开始,基于代码文件中的所有 import,export,require 构建依赖树;
  • 编译 JS/CSS 等模块;
  • 应用算法排序、重写、连贯代码;
  • 优化。

开发环境的 Webpack:

  • 打包所有代码;
  • 启动 webpack-dev-server 托管打包好的代码;
  • 启动 websocket 解决热更新 HMR。

利用规模越大,启动和热更新代码越慢。及时启动了热更新,每次代码变更也须要从新生产 Bundle 文件。

Vite

Vite 外围借助了浏览器的原生 ES Modules 和像 esbuild 这样的将代码编译成 native code 的打包工具。

Vite 次要有两方面组成:

  • 一个开发服务器,基于 ESM 提供丰盛的内建能力,比方速度快到惊人的模块热更新(HMR);
  • 一套构建指令,应用 rollup 进行代码打包,且零配置即可输入用于生产环境的高度优化的动态代码。

Vite 的外围能力和 webpack + webpack-dev-server 类似,然而在开发者体验上有一些晋升:

  • 无论我的项目大小有多大,启动利用都只需更少的工夫;
  • 无论我的项目大小有多大,HMR(Hot Module Replacing)热更新都能够做到及时响应;
  • 按需编译;
  • 零配置,开箱即用;
  • Esbuild 能力带来的 Typescript/jsx 的原生反对。

大型的 JavaScript 我的项目在开发和生产环境有比拟差的性能体现,往往是因为咱们应用的构建工具没有充沛做到并行处理、内存优化和缓存。

Vite 核心理念:Bundless 开发环境构建

浏览器的原生 ES Modules 能力容许在不将代码打包到一起的状况下运行 JavaScript 利用。Vite 的核心理念很简略,就是借助浏览器原生 ES Modules 能力,当浏览器发出请求时,为浏览器按需提供 ES Module 文件,浏览器获取 ES Module 文件会间接执行。

Vite 利用启动

Vite 将利用中的模块分为依赖和源码两类,别离进行服务器启动工夫的优化。

  • 依赖模块,开发过程中根本不会变动。Vite 对依赖采纳了 esbuild 预构建的形式,esbuild 应用 Go 编写,并且比以 JavaScript 编写的打包器预构建依赖快 10-100 倍;
  • 源码模块,是用户本人开发的代码,会常常变动。

Vite 在浏览器申请时按需转换并以原生 ESM 形式提供源码,让浏览器接管了打包程序的局部工作。

Vite 工作原理

Vite 通过原生 ES Modules 托管源代码,实质上是让浏览器来接管局部打包器的工作。Vite 只会在浏览器申请产生时,按需将源码转成 ES Modules 格局返回给浏览器,由浏览器加载并执行 ES Modules 文件。

Vite 的热更新

在基于 Bundle 构建的构建器中,当一个文件变动时,从新构建整个 Bundle 文件是十分低效的,且随着利用规模的回升,构建速度会直线降落。

传统的构建器尽管提供了热更新的能力,然而也会存在随着利用规模回升,热更新速度显著降落的问题。

Vite 基于 ESM 按需提供源码文件,当一个文件被编辑后,Vite 只会从新编译并提供该文件。因而,无论我的项目规模多大,Vite 的热更新都能够放弃疾速更新。

此外,Vite 正当利用浏览器缓存来减速页面加载,源码模块申请依据 304 Not Modified 进行协商缓存;依赖模块申请通过 Cache-Control: max-age=31536000,immutable 进行强缓存,因而一旦缓存,不会再次申请。

Vite 依靠反对原生 ESM 模块的古代浏览器,极大的升高了利用的启动和从新构建工夫。Vite 实质上是一个在开发环境为浏览器按需提供文件的 Web Server,这些文件蕴含源码模块和在第一次运行时应用 esbuild 预构建的依赖模块。

Vite 和 Webpack 的次要不同在于开发环境下对于源码如何被托管以及反对哪种模块标准。


【参考】:

如果能重来,你要选 Vite 还是 Webpack?

【打包工具】- Vite 和 webpack 原理、优缺点比照

2. 打包构建上做过哪些优化 / 我的项目构建部署比较慢,怎么来优化

3. 动静引入组件的原理是什么,生成的 chunk 文件和代码的组件引入如何匹配

小程序

1.Taro 开发 小程序中遇到过哪些兼容性问题

2. 当有新的小程序 api 呈现后,taro 没有实现该怎么应用

3. 小程序渲染原理有理解吗

【参考资料】

小程序疾速渲染的原理及流程解析

4. 小程序滚动穿透如何解决

解决办法:

  • 先说 H5 的,比较简单,只须要通过弹窗的显示状态,来动静设置底层页面最外层 View 的 overflow 属性即可。当弹窗显示的时候,document.body.style.overflow = ‘hidden’; 当弹窗暗藏的时候,document.body.style.overflow = ‘scroll’;
  • 因为小程序没有 DOM 构造,下面的计划天然是不起任何作用(就是这么傲娇)。小程序要 分两步 来解决,先是 mask 蒙层会滑动穿透的问题,这个能够采纳 Taro 的 catchMove 属性 来解决,官网文档上也有说,见下方图片。而后是弹窗外部滚动穿透的问题,采纳的是将弹窗外部 最外层 View 改为 ScrollView,光这样改还不行,还要在 ScrollView 的外层再加个 View 并且加上catchMove 属性。间接在 ScrollView 下面加 catchMove 不行,ScrollView 不反对这个属性。

注:Taro 的 CatchMove 属性是 Taro 3.0.21 版本才开始反对

如上操作,Taro 弹窗滚动穿透问题的问题就完满解决了。可同时兼容 H5 和小程序。

Hybrid 混合开发

1. 说一下 hybrid 混合开发中,app 端和 js 端是如何实现通信的?或说下 JSBridge 的原理

在 Hybrid 模式下,H5 会常常须要应用 Native 的性能,比方关上二维码扫描、调用原生页面、获取用户信息等,同时 Native 也须要向 Web 端发送推送、更新状态等,而 JavaScript 是运行在独自的 JS Context 中(Webview 容器、JSCore 等),与原生有运行环境的隔离,所以须要有一种机制实现 Native 端和 Web 端的 双向通信 ,这就是JSBridge:以 JavaScript 引擎或 Webview 容器作为媒介,通过 协定协定 进行通信,实现 Native 端和 Web 端双向通信的一种机制。

通过 JSBridge,Web 端能够调用 Native 端的 Java 接口,同样 Native 端也能够通过 JSBridge 调用 Web 端的 JavaScript 接口,实现彼此的双向调用。

Web 端和 Native 能够类比于 Client/Server 模式,Web 端调用原生接口时就如同 Client 向 Server 端发送一个申请相似,JSB 在此充当相似于 HTTP 协定的角色,实现 JSBridge 次要是两点:

  1. 将 Native 端原生接口封装成 JavaScript 接口
  2. 将 Web 端 JavaScript 接口封装成原生接口

【参考资料】

  • 深入浅出 JSBridge:从原理到应用
  • Hybrid 入门及 JSBridge 原理

性能优化

1. 日常开发中性能优化做过哪些

基本思路

整体的逻辑大体如下:

构建工具剖析

  • 体积:包体积是否存在杂质,能不能过滤进来?
  • 构建速度:构建速度影响开发调试效率和公布相干环境的节奏。

针对体积的剖析伎俩,能够应用 webpack-bundle-analyzer 来剖析以后我的项目中是否存在 反复依赖 , 影子依赖 , 模块体积差别 等等问题,应用交互式可缩放树图可视化的来梳理本人我的项目利用中的问题,而后去找到并且优化它。

针对速度剖析, 能够应用 speed-measure-webpack-plugin 来剖析以后每个 bundle 模块打包构建时的工夫数据,别离会蕴含 loader, plugin 等解决的工夫。依据相干的数据来定点模块,判断是否有优化的空间。

项目分析

  • NetWork: 网络面板,用于侦测浏览器资源下载与上传的能耗视图。
  • Performance: 性能面板:用于侦测浏览器运行时的性能体现,得出我的项目运行的时序图,同时也能够剖析页面的一些隐式问题,如 (内存透露)。
  • Lighthouse: 性能评测(灯塔),基于以后浏览器页面的阶段性加载现状来为用户提供后果剖析数据指标报告。
  • 探针: 前端框架如 React 有相干 Profiler 的探针 API 和 chrome 插件,可能为开发者探测代码编写时的问题,防止反复或者是频繁异样的 Repeat rendering。

性能监控平台,为线上的前端进行侦测,从 接口调用 首屏性能 流量保活 等等一系列的性能指标做度量平台。

如何针对通用问题进行标准化解决???

优化伎俩

构建工具优化

包体积大小

首先,针对包体积大小做一些调整,能做的事件大体上有以下几点:

  • 按需加载:针对我的项目中比拟大型的工具类库,如 lodash、UI Design、use-hooks 等类库,如果只是应用很小的一部分,就须要防止全量加载的状况。
  • tree-shaking:通过 ESM 的个性,能够在构建阶段将我的项目模块中的死代码进行移除,从而保障洁净的构造和最小的文件大小。
  • minify:通过 webpack 相干的插件,能够为代码产物做混同压缩,如 JS minify 与 CSS minify,当我的项目的代码越大的时候,压缩的成果就越显著。
  • CDN 依赖:对于局部共有依赖能够将其搁置在 CDN 中来进行加载,可能极大幅度的缩小本地我的项目产物的体积大小,毛病就是无奈进行按需加载,占用浏览器申请资源,具体的收益能够本人衡量。
构建速度

其次就是构建速度了,能够从以下几个方面来着手:

  • 长久化缓存:webpack5 自身集成,通过 cache 字段开启即可,webpack4 能够通过 cache-loader 来实现,不过有余的中央是 cache-loader 仅对 loader 起作用。(目前该 loader 看起来曾经暂停保护了)。
  • 多过程构建:通过 thread-loader 能够开启多过程构建,能够进步局部环节的性能效率,比方 babel-ast 解析等等。webpack4 的话则是应用 happypack 来实现。
  • Building:应用基于 Rust 编写的高性能编译工具来替换默认的 babel,比方将 babel-loader 替换为 swc-loader,这个是一个可选的操作,目前市面上少数 cli 都反对了该选项,以供开发者抉择。
  • Dll: dll 能够将一些依赖库提前进行打包,可能无效的升高打包类库的次数,只有其批改后才会进行从新的构建,通常用于专用类库的解决。能够应用 DllPlugin 来配置对应的成果。
  • Bundless: Vite 是市面上十分炽热的 Bundless 计划之一,曾经能够看出越来越多的人开始应用它来代替传统 Bundle 计划,其优良的构建速度, HMR 深受好评。

我的项目计划

资源加载
  • 懒加载:懒加载我对齐的定义是提早资源的加载,直到齐第一次触达时在进行资源的引入和展现,这项技术被宽泛用于 spa-router、图片资源等非阻塞性资源的优化上。对于缩短要害门路渲染十分有帮忙,如各位同学耳熟能详的首屏优化。
  • 预加载:预加载的目标就是通过在渲染前事后拿到须要展现的数据,从而缩短要害承流页面出现给用户的渲染工夫,大多数状况下能够分为首页预载和要害页预载两方面。
  • 首页预载:Web 端提供了相干的 pre-load、pre-render、pre-fetch 相干 API 实现。小程序方面也提供了相干冷启动获取数据的能力,例如 getBackgroundFetch。对于谋求极致的性能体验的话不妨一试。
  • 要害页预载:要害页预载的话就依据本身业务来实现了,参考商城业务中商品详情页、营销会场等页面都是用户比拟相熟的页面。因而,少数时候都会将页面跳转与数据申请独特进行的。
  • 接口优化:耗时较久的接口如果阻塞外围链路,能够将其拆成多个粒度小的接口来渐进式的调用以此来保障页面优先加载。反之亦然,如果接口零散占用了太多的申请资源,则能够将其聚合成一个残缺的大接口来获取数据。
  • 缓存:浏览器缓存策略大体上分为强缓存和协商缓存。利用好缓存策略能够无效的升高资源的反复加载,从而进步网页的整体加载速度。
网络相干

在这个过程中,会有两个阶段大家耳熟能详,别离是 DNS 解析和 HTTP 连贯(三次握手)。它们是这个过程中绝对耗时的两个局部。基于此,能够针对网络来对我的项目做以下一些优化:

  • DNS 预解析:理解 DNS 解析过程的同学都晓得,相当耗时。而浏览器也提供了 dns-prefetch 的相干操作,用于领先对 origin 进行解析。
  • PreConnect:既然 DNS 能够预解析,那么申请连贯的话也绝对应的能够领先执行。
  • 内容散发网络 CDN:通过内容散发网络 CDN 能够使用户就近获取所须要的内容资源,从而升高网络拥塞,进步用户拜访响应速度和保障拜访资源的命中率。
  • HTTP/2:尽可能应用 HTTP/ 2 的新个性,它会带来一些意想不到的小惊喜。感兴趣的能够看《HTTP2 简介和基于 HTTP2 的 Web 优化》
  • Gzip:很早之前会开启 Gzip 的压缩计划来缩小网络理论传输数据的大小,服务器会将申请资源进行压缩后传输给客户端。
交互体验
  • 加载态:提到加载态,可能大多数同学第一想到的就是 loading,基于 loading 的实现之上,很多产品都会对局部页面制作骨架组件 / 骨架屏,它的长处就是表达能力强,相比于简略的加载 loading 而言,骨架组件可能给用户出现预期的页面与区块的显示成果。
  • 虚构列表:虚构列表曾经是近年来陈词滥调的一个优化了,其场景次要用于页面中须要展现大量数据时的一个措施伎俩,如播种地区抉择、有限滚动场景,任何须要重叠数据显示的页面中或多或少都会呈现它的身影。
  • 想理解具体的场景和实现过程的能够查阅我之前写的相干文章《百万 PV 商城实际系列 – 前端长列表渲染优化实战》
浏览器
  • 回流和重绘: 回流和重绘是一个生产资源的事件,一旦浏览器产生频繁的回流,会造成浏览器资源的缓和,导致我的项目或多或少呈现卡顿的问题。现如今浏览器尽管曾经为咱们做了 Layout 相干的合并优化。但还是要从根本上去缩小不必要的状况产生。而解决的计划大多数如下几种:

<!—->

    • 代码编写:款式集中扭转,能够无效缩小重排次数,尽可能不要频繁的去获取和写入 DOM,须要操作时应提前在内存中筹备好对应的操作元素,从而一次性的将其进行读写。
    • 暗藏批改:当波及到 DOM 元素的频繁操作时,尽可能让其先排出文档流,直到最终操作实现后在将其渲染到页面上,以此来升高回流和重绘的频率。
    • GPU 合成:对于动画,能够应用 transform 代替 position 做一些事件,现如今大部分浏览器都会对 transform 进行借用 GPU 资源来做渲染优化。

<!—->

  • RAF&RDC:RAF&RDC 别离是 requestAnimationFrame 和 requestIdleCallback 的简称,正当的应用能够防止频繁逻辑批改导致提早要害事件,如动画和输出响应的交互。一个是针对动画帧成果,一个针对页面执行的逻辑。
  • 事件委托:通过事件委托来绑定对应的操作,能够无效的缩小相干内存的耗费。现如今,咱们曾经不在须要太过关注原生事件的解决了,少数状况下框架类库都曾经可能很好的开始工作。
图片
  • WebP:webp 与 png 一样,是一种图片格式,因为应用更优的图像数据压缩算法、更小的图片体积因而可能带来更好的访问速度劣势。须要留神的是可能会敌人不反对 webp 格局的环境问题,这时候就须要进行降级解决避免出现用户无法访问图片的体验。
  • 渐进式加载:渐进式是一个过程,渐进式加载的话就是让一张图片从含糊到清晰缓缓展现的一个过程,防止用户因为网络问题导致图片加载失败看到不敌对的体验,实质上的话属于加载状态的一种。和骨架屏区别不大。
  • 图片切割:在业务当中,会看到相似商品详情图、宣传海报等相干的图片展现,它自身是一个十分长的图片,且体积因为要保真的起因无奈做一些压缩。因而针对相似的场景就能够抉择图片切割的计划,将长图切割成若干份小图进行展现,搭配之前提到过的一些优化计划能够完满的防止加载渲染问题。
  • 图片 CDN:应用图片 CDN 的益处除了能够取得内容散发来进步响应速度外,还能够应用其提供的图片裁切性能动静的定义分辨率大小、清晰度展现等辅助其在页面上的展现。

总结

【参考】

当面试官问我前端能够做的性能优化有哪些

5000 字总结面试常问前端我的项目优化到底怎么来做?

面试时我是如何答复“前端性能优化”的

指标 - 掂量性能和用户体验的规范

2. 谬误监控上报怎么做

3. 性能监控指标有哪些?首屏工夫如何计算

【扩大】如何优化首屏工夫

  1. http 缓存(强缓存和协商缓存), 性价比最高的一个优化形式。需注意的是,浏览器不缓存 XHR 接口,本人可依据业务个性动静地抉择缓存策略。比方一个体积很大的接口,但内容变更频率不是很频繁,这种状况就适宜用协商缓存。
  2. cdn 散发(缩小传输间隔)。通过在多台服务器部署雷同的正本,当用户拜访时,dns 服务器就近(物理间隔)抉择一台文件服务器并返回它的 ip。
  3. 前端的资源动静加载:

<!—->

    1. 路由动静加载
    2. 组件动静加载
    3. 图片懒加载(offScreen Image),越来越多的浏览器反对原生的懒加载,通过给 img 标签加上 loading=”lazy 来开启懒加载模式。

<!—->

  1. 合并申请。这点在串行接口的场景中作用很显著,比方有两个串行的接口 A 和 B,须要先申请 A,而后依据接口 A 的返回后果去申请接口 B。如果 server 和 client 的物理间隔为 D,那么这个串行的场景传输的物理途程为 4D。如果合并成一个接口,物理途程可减小为 2D。
  2. 页面应用骨架屏。意思是在首屏加载实现之前,通过渲染一些简略元素进行占位。骨架屏的益处在于能够缩小用户期待时的急躁情绪。这点很无效,在很多成熟的网站都有大量利用。没有骨架屏的话,一个 loading 图也是能够的。
  3. 应用 ssr 渲染。
  4. service worker:通过 sw 离线更新缓存的能力,实践上能做到每次拜访页面都无需下载 html,且所有动态资源都曾经在本地有缓存。
  5. 引入 http2.0。http2.0 比照 http1.1,最次要的晋升是传输性能,在接口小而多的时候性能劣势会更加显著。
  6. 利用好 http 压缩。即便是最一般的 gzip,也能把 bootstrap.min.css 压缩到原来的 17%。可见,压缩的成果非常明显,特地是对于文本类的动态资源。另外,接口也是能压缩的。接口不大的话能够不必压缩,因为性价比低(思考压缩和解压的工夫)。值得注意的是,因为压缩算法的个性,文件越大,压缩成果会越好。
  7. 利用好 script 标签的 async 和 defer 这两个属性。
  8. 应用 WebP 代替 jpg/png。

【参考】

web 的首屏加载工夫

4. 如果让你设计一个监控零碎,你会如何设计?

5. 单页利用性能指标有 FMP,理解吗

【参考】

FP、FCP、FMP、LCP 都是什么 P?

定位性能指标 FMP

6. 单页利用首屏优化形式有哪些

【参考资料】

面试官:SPA 首屏加载速度慢的怎么解决?

设计模式

1. 依赖注入是什么

【参考资料】

依赖注入 in 前端 && Typescript 实现依赖注入

前端架构利器 - 依赖注入

写给前端同学的依赖注入(DI) 介绍

2. 前端框架设计外面常见的设计模式有哪些

  • 工厂模式
  • 单例模式
  • 代理模式
  • 策略模式
  • 观察者 / 公布 - 订阅模式
  • 迭代器模式
  • 装璜器模式
  • 原型模式

【参考资料】

前端须要理解的 9 种设计模式

平安

1. 对称加密和非对称加密

【参考资料】

对称加密和非对称加密

2. 网站攻击方式有哪些

【参考资料】

面试官:web 常见的攻击方式有哪些?如何进攻?

开放性题

1. 前端如何解决并发

比方页面初始化须要 10 个申请,后端只让每次 3 个,怎么做

思路拆解:

  • urls 的长度为 0 时,results 就没有值,此时应该返回空数组
  • maxNum 大于 urls 的长度时,应该取的是 urls 的长度,否则则是取 maxNum
  • 须要定义一个 count 计数器来判断是否已全副申请实现
  • 因为没有思考申请是否申请胜利,所以申请胜利或报错都应把后果保留在 results 汇合中
  • results 中的程序需和 urls 中的保持一致
// 并发申请函数
const concurrencyRequest = (urls, maxNum) => {return new Promise((resolve) => {if (urls.length === 0) {resolve([]);
            return;
        }
        const results = [];
        let index = 0; // 下一个申请的下标
        let count = 0; // 以后申请实现的数量

        // 发送申请
        async function request() {if (index === urls.length) return;
            const i = index; // 保留序号,使 result 和 urls 绝对应
            const url = urls[index];
            index++;
            console.log(url);
            try {const resp = await fetch(url);
                // resp 退出到 results
                results[i] = resp;
            } catch (err) {
                // err 退出到 results
                results[i] = err;
            } finally {
                count++;
                // 判断是否所有的申请都已实现
                if (count === urls.length) {console.log('实现了');
                    resolve(results);
                }
                request();}
        }

        // maxNum 和 urls.length 取最小进行调用
        const times = Math.min(maxNum, urls.length);
        for(let i = 0; i < times; i++) {request();
        }
    })
}
function sendRequest(urls, max, callbackFunc) {
  const REQUEST_MAX = max;
  const TOTAL_REQUESTS_NUM = urls.length;
  const blockQueue = []; // 期待排队的那个队列
  let currentReqNumber = 0; // 当初申请的数量是
  let numberOfRequestsDone = 0; // 曾经申请结束的数量是
  const results = new Array(TOTAL_REQUESTS_NUM).fill(false); // 所有申请的返回后果,先初始化上

  async function init() {for (let i = 0; i < urls.length; i++) {request(i, urls[i]);
    }
  }

  async function request(index, reqUrl) {
    // 这个 index 传过来就是为了对应好哪个申请,// 放在对应的 results 数组对应地位上的,放弃程序
    if (currentReqNumber >= REQUEST_MAX) {await new Promise((resolve) => blockQueue.push(resolve)); // 阻塞队列减少一个 Pending 状态的 Promise,// 进外面排队去吧,不放你进去,不 resolve 你,你就别想进行上面的申请
    }
    reqHandler(index, reqUrl); // {4}
  }

  async function reqHandler(index, reqUrl) {currentReqNumber++; // {5}
    try {const result = await fetch(reqUrl);
      results[index] = result;
    } catch (err) {results[index] = err;
    } finally {
      currentReqNumber--;
      numberOfRequestsDone++;
      if (blockQueue.length) {
        // 每实现一个就从阻塞队列里剔除一个
        blockQueue[0](); // 将最先进入阻塞队列的 Promise 从 Pending 变为 Fulfilled,// 也就是执行 resolve 函数了,前面不就能持续进行了嘛
        blockQueue.shift();}
      if (numberOfRequestsDone === TOTAL_REQUESTS_NUM) {callbackFunc(results);
      }
    }
  }

  init();}

// 测试数据
const allRequest = [
  "https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?index=1",
  "https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?index=2",
  "https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?index=3",
  "https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?index=4",
  "https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?index=5",
  "https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?index=6",
  "https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?index=7",
  "https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?index=8",
  "https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?index=9",
  "https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?index=10",
];
sendRequest(allRequests, 2, (result) => console.log(result));

【参考资料】

对于前端:如何实现并发申请数量管制?

30 行代码,高大上解决 Promise 的多并发问题

2. 前端解决大数据会有哪些影响

【参考资料】

前端优化之分分钟学会渲染百万条数据不卡顿

3. 比照 React 和 Vue

4. 说一下对 SEO 的理解

【参考】

什么是 SEO?如何进行 SEO 优化?

5. 设计离线包计划

从 WebView 初始化到 H5 页面最终渲染的整个过程:

能够看到,一个残缺的 WebView 加载流程:

  1. 初始化 webview;
  2. 建设连贯,从后端下载开启 WebView 须要的相干文件数据包,包含网页中须要申请的文件以及模板 Html;
  3. 加载与承受数据,页面的渲染,并通过实现 android 和 js 的交互

目前支流的优化形式次要包含:

  • 针对 WebView 初始化:该过程大抵需消耗 70~700ms。当客户端刚启动时,能够先提前初始化一个全局的 WebView 待用并暗藏,当用户拜访了 WebView 时,间接应用这个 WebView 加载对应网页并展现。
  • 针对向后端发送接口申请:在客户端初始化 WebView 的同时,间接由 Native 开始网络申请数据,当页面初始化实现后,向 Native 获取其代理申请的数据。
  • 针对单页面加载的 JS 动静拼接 Html:采纳多页面打包,服务端渲染,以及构建时预渲染等形式。
  • 针对加载页面资源的大小:可采纳懒加载等形式,将须要较大资源的局部分离出来,等整体页面渲染实现后再异步申请分离出来的资源,以晋升整体页面加载速度。

离线包的具体思路

默认状况下,咱们先将页面须要的动态资源打包并事后内置到客户端的安装包中,当用户装置利用后,解压资源到本地存储中,当 WebView 加载某个 H5 页面时,拦挡收回的所有 http 申请,查看申请的资源是否在本地存在,如果存在则间接加载本地资源;否则加载网络的资源。

【参考】

Hybrid App 离线包计划实际

一起学跨端技术 - 离线包

基于 prefetch 的 H5 离线包计划

7.Vue3 的 Compisition API 和 React hooks 有哪些区别

React Hooks 底层是链表,每一个 Hook 的 next 是指向下一个 Hook 的;

Vue 对数据的响应是基于 proxy 的,对数据间接代理察看。这种场景下,只有任何一个更改 data 的中央,相干的 function 或者 template 都会被从新计算,因而避开了 React 可能遇到的性能上的问题。

代码执行上:

  • Vue Composition API 的 setup() 晚于 beforeCreate 钩子,早于 created 钩子被调用
  • React Hooks 会在组件每次渲染时候运行,而 Vue setup() 只在组件创立时运行一次

因为 React Hooks 会屡次运行,所以 render 办法必须恪守某些规定,比方:

不要在循环外部、条件语句中或嵌套函数里调用 Hooks

申明状态上:

React

useState 是 React Hooks 申明状态的次要路径

  • 能够向调用中传入一个初始值作为参数
  • 如果初始值的计算代价比拟低廉,也能够将其表白为一个函数,就只会在首次渲染时才会被执行

Vue

Vue 应用两个次要的函数来申明状态:ref 和 reactive。

ref() 返回一个反应式对象,其外部值可通过其 value 属性被拜访到。能够将其用于根本类型,也能够用于对象

reactive() 只将一个对象作为其输出并返回一个对其的反应式代理

TODO ……

【参考】

Vue Composition API 和 React Hooks 比照

8. 前端灰度怎么做;如果让你设计一个灰度过程的话如何设计,我的项目版本灰度如何来做;如何防止再次发版;

【参考】

前端灰度 - 前端我的项目如何进行灰度?

你想晓得的前端灰度计划都在这了

9. 说下单页利用和多页利用的区别,优缺点

【参考资料】

vue 单页面 (SPA) 和多页面 (MPA) 的区别(具体答案)

10. 管理系统鉴权如何做的

个别咱们来说次要针对 RBAC 鉴权模式进行管制

页面鉴权用户有没有权限拜访该页面,或者有没有查看通往该页面路由的权限。(组件和路由)

组件:一. 有没有权限拜访页面的时候: 通过路由守卫联合后端返回来的 token,进行页面跳转之前的鉴权,查看 token 是否过期并且是否领有该页面的权限。

路由:判断用户是否有权限查看通过指定页面的路由(或菜单或导航): 1. 纯前端解决:在写路由表的时候,咱们会在每个路由下加一个 meta,而后在 meta 外面写出能够拜访或查看该路由或页面的角色信息,而后咱们能够通过该 meta 下的信息应用 addrouter 管制该路由下的显隐 2. 前后端配合解决:每次登陆的时候,都在后端那里获取 token 的路由表保留到 vuex 里,在通过 addrouter 动静渲染 token 下的路由及导航

UI 鉴权:它的颗粒度很细,所以说难度较大,咱们能够通过对立的自定义指令来进行配置。

一般来说 UI 鉴权指的是按钮鉴权解决 UI 鉴权 简略的办法是咱们能够获取 token 下的角色信息,用 v -if 解决该 UI 的显隐,然而这种形式毛病很显著,不易于对立治理,所以咱们须要集中封装一个自定义指令,在自定义指令中,集中处理鉴权的逻辑,而后散发在每个鉴权的按钮上。

Node

1.Node 用过么,起一个 http server 的伪代码大略是?

const {createServer} = require('http');

const HOST = 'localhost';
const PORT = '8080';

const server = createServer((req, resp) => {
  // the first param is status code it returns
  // and the second param is response header info
  resp.writeHead(200, { 'Content-Type': 'text/plain'});

  console.log('server is working...');

  // call end method to tell server that the request has been fulfilled
  resp.end('hello nodejs http server');
});

server.listen(PORT, HOST, (error) => {if (error) {console.log('Something wrong:', error);
    return;
  }

  console.log(`server is listening on http://${HOST}:${PORT} ...`);
});

手写局部

1. 括号匹配

2. 数组排重

3. 数组惟一呈现

4. 面向对象设计

5. 版本号排序

6. 函数柯里化

7. 数组中有一个数字呈现的次数超过数组长度的一半,请找出这个数字

8. 手写单例模式

9. 手写疾速排序

10. 单向链表的反转取值,通过迭代 / 递归形式实现

其余

1.git merge 和 git rebase 的区别是什么

  • git merge 会创立一个新的合并提交,保留残缺的提交历史记录,但可能会导致分支历史变得复杂。
  • git rebase 会将提交复制到指标分支上,并从新利用指标分支上的更改,改写提交历史记录,使分支历史放弃线性,但可能会失落一些提交信息。

【参考资料】:

Merge vs Rebase

面试官:说说你对 git rebase 和 git merge 的了解?区别?


原文连贯 欢送关注

退出移动版