字节面经收集

线程与进程之间的关系

进程是指在系统中正在运行的一个应用程序,线程是系统分配处理器时间资源的基本单元, 或者说进程之内独立执行的一个单元。进程至少包括一个线程

获取元素的位置、大小信息

dom.getBoundingClientRect()

实现一个属性不可修改

Object.defineProperty(obj, 'name', {writable: false})
Object.freeze(obj)

Object.defineProperty第三个参数属性值

configure,
writable,
enumerable,
value,
get,
set

Proxy

Proxy,代理,是ES6新增的功能,可以理解为代理器
var proxy = new Proxy(target, handler)
target:目标对象
handler:一个对象,其属性是当执行一个操作时定义代理的行为的函数。

Reflect属于内置的静态类 ,提供了一些静态方法,来操作对象;Proxy实际也是调用Reflect的方法去操作对象的;

// vue3中的ref()方法是使用的Object.defineProperty

promise、async await、generate关系

都是js中用于处理异步的方法
promise es6的新api,可以通过then方法链式调用处理异步
generate 函数遇到yield就停止执行; 需要调用next方法; 实际例子:koa1  (co库)
async 函数 内置了迭代器 去执行函数内的 await 后的异步;并最终返回的一个promise对象 

1. 写一个处理加法可能产生精度的函数0.1 + 0.2 = 0.3

原因:Number类型的有效整数范围是-0XFFFFFFFFFFF至0X1FFFFFFFFFF,所以无法精确到超过这个范围的整数.

解法:(0.1 * 1000+0.2 * 1000)/1000==0.3
原因:JS的浮点数实现也是遵循IEEE 754标准,采用双精度存储(double precision),使用64位固定长度来表示,其中1位用来表示符号位,11位用来表示指数,52位表示尾数。


image.png
function bigAdd (a, b) {
  const maxLength = Math.max(a.length, b.length)
  a = a.padStart(maxLength, '0')
  b = b.padStart(maxLength, '0')

  let x = 0
  let y = 0
  let sum = ''
  for (let i = maxLength - 1; i >= 0; i--) {
    x = Number(a[i]) + Number(b[i]) + y
    y = Math.floor(x / 10)
    sum = (x % 10) + sum 
  }
  if (y == 1) {
    sum = '1' + sum
  }
  return sum
}
// 小数点
function smallAdd (a, b) {
  let s1, s2
  try {
    s1 = a.split('.')[1].length
  } catch (e) {
    s1 = 0
  }
  try {
    s2 = b.split('.')[1].length
  } catch (e) {
    s2 = 0
  }
  const max = Math.max(s1, s2)
  const n = Math.pow(10, max)
  return ((a*n) + (b*n)) / n
}

2.写 new 的执行过程

答案:

  1. 创建一个新对象obj
  2. 将obj.proto = Person
  3. Person.call(obj)
  4. 判断构造函数有没有返回值 没有就直接return obj
function myNew(constrc, ...args) {
    const obj = {}; // 1. 创建一个空对象
    obj.__proto__ = constrc.prototype; // 2. 将obj的[[prototype]]属性指向构造函数的原型对象
    constrc.apply(obj, args); // 3.将constrc执行的上下文this绑定到obj上,并执行
    return obj;  //4. 返回新创建的对象
}

3.Cookie和Session有什么区别?

(1)Session 是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中;

(2)Cookie 是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现 Session 的一种方式。

浏览器如何实现Session的?
1、用户向服务器发送用户名和密码。

2、服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户角色、登录时间等等。

3、服务器向用户返回一个 session_id,写入用户的 Cookie。

4、用户随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器。

5、服务器收到 session_id,找到前期保存的数据,由此得知用户的身份。

jwt登录

JSON Web Token(缩写 JWT)
JWT 的原理是,服务器认证以后,生成一个 JSON 对象,发回给用户,就像下面这样。
它是一个很长的字符串,中间用点(.)分隔成三个部分。
Header(头部)base64url加密
Payload(负载)base64url加密
Signature(签名)

单点登录

同一级域名
1.系统共享session
2.客户端共享一级域名下cookie

不同域名
1.app1.com 初次登录 请求sso 账号密码权限认证 完成登录;跳回系统app1并返回ticket
2.app1系统拿到ticket后,从后台向SSO发送请求,验证ticket是否有效。
3.验证通过写入app1系统的session,cookie
4.访问app2.com 请求sso,发现是已登录,跳回系统app2并返回ticket
5.验证通过写入app2系统的session,cookie

说一下浏览器缓存;

强缓存
浏览器在加载资源时,会先根据本地缓存资源的 header 中的信息判断是否命中强缓存,如果命中则直接使用缓存中的资源不会再向服务器发送请求。

这里的 header 中的信息指的是 expires 和 cahe-control.
Expires
该字段是 http1.0 时的规范,它的值为一个绝对时间的 GMT 格式的时间字符串,\这种方式有一个明显的缺点,由于失效时间是一个绝对时间,所以当服务器与客户端时间偏差较大时,就会导致缓存混乱。

Cache-Control
Cache-Control 是 http1.1 时出现的 header 信息,主要是利用该字段的 max-age 值来进行判断,它是一个相对时间,例如 Cache-Control:max-age=3600,代表着资源的有效期是 3600 秒。cache-control 除了该字段外,还有下面几个比较常用的设置值:

no-cache:需要进行协商缓存,发送请求到服务器确认是否使用缓存。

no-store:禁止使用缓存,每一次都要重新请求数据。

public:可以被所有的用户缓存,包括终端用户和 CDN 等中间代理服务器。

private:只能被终端用户的浏览器缓存,不允许 CDN 等中继缓存服务器对其缓存。

Cache-Control 与 Expires 可以在服务端配置同时启用,同时启用的时候 Cache-Control 优先级高。

协商缓存
当强缓存没有命中的时候,浏览器会发送一个请求到服务器,服务器根据 header 中的部分信息来判断是否命中缓存。如果命中,则返回 304 ,告诉浏览器资源未更新,可使用本地的缓存。

这里的 header 中的信息指的是 Last-Modify/If-Modify-Since 和 ETag/If-None-Match.

Last-Modify/If-Modify-Since
浏览器第一次请求一个资源的时候,服务器返回的 header 中会加上 Last-Modify,Last-modify 是一个时间标识该资源的最后修改时间。

当浏览器再次请求该资源时,request 的请求头中会包含 If-Modify-Since,该值为缓存之前返回的 Last-Modify。服务器收到 If-Modify-Since 后,根据资源的最后修改时间判断是否命中缓存。

如果命中缓存,则返回 304,并且不会返回资源内容,并且不会返回 Last-Modify。

缺点:

短时间内资源发生了改变,Last-Modified 并不会发生变化。

周期性变化。如果这个资源在一个周期内修改回原来的样子了,我们认为是可以使用缓存的,但是 Last-Modified 可不这样认为,因此便有了 ETag。
ETag/If-None-Match
与 Last-Modify/If-Modify-Since 不同的是,Etag/If-None-Match 返回的是一个校验码。ETag 可以保证每一个资源是唯一的,资源变化都会导致 ETag 变化。服务器根据浏览器上送的 If-None-Match 值来判断是否命中缓存。

与 Last-Modified 不一样的是,当服务器返回 304 Not Modified 的响应时,由于 ETag 重新生成过,response header 中还会把这个 ETag 返回,即使这个 ETag 跟之前的没有变化。

Last-Modified 与 ETag 是可以一起使用的,服务器会优先验证 ETag,一致的情况下,才会继续比对 Last-Modified,最后才决定是否返回 304。

避免缓存

1. Cache-Control:no-cache, Expires: 0;
2. 请求query加时间戳或随机字符串

webpack动态加载的原理是什么?

  1. 查看缓存里是否加载过模块,没有的话异步加载chunk
  2. 用promise 包装的动态创建script标签去加载资源
  3. 加载成功后,执行resolve, 执行自定义的回调

回流重绘

1.解析生产dom tree
2.解析css生产cssom
3.根据每个可见节点以及其对应的样式,组合生成渲染树
回流:计算节点确切位置和大小
重绘:渲染树的每个节点都转换为屏幕上的实际像素
回流一定触发重绘。重绘不一定触发回流

减少回流和重绘
合并所有的改变然后依次处理:使用cssText、修改class类名

dom:
当我们需要对DOM对一系列修改的时候,可以通过以下步骤减少回流重绘次数:
使元素脱离文档流
对其进行多次修改
将元素带回到文档中。
(隐藏元素,应用修改,重新显示)
对于复杂动画效果,使用绝对定位让其脱离文档流

css3硬件加速(GPU加速)

划重点:使用css3硬件加速,可以让transform、opacity、filters这些动画不会引起回流重绘 。但是对于动画的其它属性,比如background-color这些,还是会引起回流重绘的,不过它还是可以提升这些动画的性能。

常见的触发硬件加速的css属性:

  • transform
  • opacity
  • filters
  • Will-change

get post 区别

1.get能被缓存、对数据长度的限制
2.post option请求
3.报文结构,不同, 
4.post 提交在body,get在url

性能优化指标

fp: 收字节到达时间
fcp:首容绘制时间
lcp: 主要内容绘制时间
fid:用户可交互时间
cls: 页面抖动

script标签defer async区别

defer/async 都是异步加载js
defer用于开启新的线程下载脚本文件,并使脚本在文档解析完成后执行。
async是异步下载,下载完后立即执行

window.onload 和document.ready区别

页面加载完成有两种事件
一是ready,表示文档结构已经加载完成(不包含图片等非文字媒体文件)
二是onload,指示页面包含图片等文件在内的所有元素都加载完成。

性能优化

1.合并请求,减少 HTTP 请求数量,
2.使用http2;HTTP2 是基于帧的协议、多路复用(避免队头堵塞)、首部压缩、服务器推送
3.使用服务端渲染,更快的内容到达时间
4.静态资源使用 CDN,全站加速dcdn
5.将 CSS 放在文件头部,避免没有样式;JavaScript 文件放在底部,避免阻塞浏览器渲染;defer属性可以异步加载;preload/prefetch/dns-prefetch
6.使用字体图标 iconfont 代替图片图标;字体图标是矢量图,不会失真。字体压缩后文件特别小;
7.善用缓存(强缓存,协商缓存,服务端渲染秒级缓存)不重复加载相同的资源;
8.压缩文件,content-encoding br, gzip
9.图片优化: 图片压缩(webp)、图片剪裁、图片懒加载
10.通过 webpack 按需加载代码,提取第三库代码,减少 ES6 转为 ES5 的冗余代码
11.减少重绘重排,
12.使用事件委托
13.js动画使用requestAnimationFrame、
14.使用 transform 和 opacity 属性更改来实现动画,硬件加速(will-change/translateZ(0))

vue
15.避免响应所有数据,不需要响应式的数据可以定义在实例上
16.v-for添加key且避免同时使用v-if
17区分computed和watch使用场景
18.区分v-if与v-show使用场景
19.路由懒加载

dns过程

本地缓存,本地host,本地dns服务器递归查询、然后遍历查询跟dns服务器,定级dns服务器;

百度用ip可以访问,知乎不可以

因为知乎访问的是cdn节点服务器 缺少 host时无法判断是谁的服务,拒绝响应

常见header

Accept
Accept-Encoding
host
referer
access-controll-allow-origin/header/methods/credentials/
authreztion
cookie
last-modified
Etag
Cache-control
if-modified
expires
connection:keep-alive
content-type: x-www-form-urlencoded,multipart/form-data
content-encoding: gzip、br
user-agent

Vue渲染过程

1.Observer
2.Dep
3.Watcher
4.遍历整个组件树

vue组件实例化时,会对data属性深度遍历(遇到数组或者对象)为每一个属性添加数据劫持。数据劫持就是使用Object.defineProperty(de fai in pro pu tei)方法添加get/set方法。
在这个过程中会实例化一个Dep类。
1.在get拦截器里触发dep实例的depend方法,进行依赖收集,实质是在dep的实例属性sub数组中添加依赖这个属性的watcher实例。
2.在set拦截器里触发了dep实例的notify(nao te fai)方法,对收集到的所有依赖派发更新,(watcher的update方法)


vue组件实例化时会实例化一个渲染watcher,渲染watcher实例化过程会做两件事情。
1.创建vnode,在这个过程中,访问了data属性,触发了get方法,完成了依赖收集。
2.触发了子组件的实例化,子组件实例化又会重复上述数据劫持的过程。
这个过程就是对组件树的深度遍历。

结合组件生命周期来看整个过程,父组件会先触发created钩子,子组件后触发created钩子。而子组件mouted钩子会先执行,父组件的mouted钩子后执行。

vue diff算法

完整的diff算法时间复杂度是O(n^3),显示是一个很昂贵的算法;
Vue选择了一个折中的方式;时间复杂度为O(n);
策略就是只进行同层比较,跨层级的移动认为是删除和创建操作;
不同的类型的节点认为是新创建;不递归比较其子节点;
列表结构的节点,通过key值进行确认是删除还是创建或者移动;
时间复杂度之所以是O(n),因为比较新旧节点时使用了首位各两个指针;遍历一次

vue组件通信

1.props $emit (v-modle)
2.vuex
3.eventbus
4.$refs
5.$parent $children
6.provide /inject 

文件断点续传

大文件上传

前端上传大文件时使用 Blob.prototype.slice 将文件切片,并发上传多个切片,最后发送一个合并的请求通知服务端合并切片
服务端接收切片并存储,收到合并请求后使用流将切片合并到最终文件
原生 XMLHttpRequest 的 upload.onprogress 对切片上传进度的监听
使用 Vue 计算属性根据每个切片的进度算出整个文件的上传进度

使用 spark-md5 根据文件内容算出文件 hash
通过 hash 可以判断服务端是否已经上传该文件,从而直接提示用户上传成功(秒传)
通过 XMLHttpRequest 的 abort 方法暂停切片的上传
上传前服务端返回已经上传的切片名,前端跳过这些切片的上传

nodejs 集群

ipc通信:传递数据,和句柄(socket,server,udp socket)
利用cpu多核

nodejs/webpack 热更新

webpack  HMR
是webpack一个重要的特性,当代码文件修改并保存之后,webapck通过watch监听到文件发生变化,
会对代码文件重新打包生成两个模块补丁文件manifest(json)和一个(或多个)updated chunk(js),websocket链接发送manifest,浏览器按照创建script标签更新,触发
将结果存储在内存文件系统中,通过websocket通信机制将重新打包的模块发送到浏览器端,
浏览器动态的获取新的模块补丁替换旧的模块,浏览器不需要刷新页面就可以实现应用的更新。
用到了webpack-dev-middleware、webpack-hot-middleware、webpack-dev-server

nodemon

vue ssr

vue-server-renderer
插件:VueSSRServerPlugin、VueSSRClientPlugin

VueSSRClientPlugin
vue-ssr-client-manifest.json就是通过VueSSRClientPlugin生成的clientManifest文件,称为客户端构建清单。

它将输入到renderer中,为模板的资源加载提供信息,推断和注入资源预加载和数据预取指令和首次渲染需要加载script标签。

1、获取编译对象中的包含编译信息的stats对象

2、从stats.assets得到应用用到所有资源数组allFiles(元素是文件名)

3、从入口文件对象中找出初次渲染加载的js和css文件数组initialFiles

4、allFiles与initialFiles的差集,过滤出js和css,作为可异步加载资源asyncFiles

5、分别从stats.modules找出每个模块对应的chuck代码文件,在将chunk依赖文件转换为allFiles中的indexArray,然后moduleId做key,indexArray作为value组成map对象。

6、最后组装成json对象,序列化后挂上compilation.assets对象上,最为json文件输出。

VueSSRServerPlugin

vue服务端构建插件做的事情跟客户端构建插件一样,都是输出一个json文件,源码中的逻辑几乎也一样,不一样的地方在于输出的文件内容 
var bundle = {
  entry: entry,
  files: {}, // 编译的源码
  maps: {}  // sourceMap
};

构建出clientManifest和server bundle两个json文件主要是为了让vue-server-renderer支持一些高级特性,比如自动注入(预加载和预取,inlineStyle关键css)、state等,已达到对输出html更细粒度的控制能力。

完整过程:
VueSSRServerPlugin 生产serveBundle
VueSSRClientPlugin生产clientBundle(客户端资源清单)
vue-server-renderer 提供了方法createBundleRanderer方法接受serveBundle;
根绝url找到对应sourceMap, 组成当前路由的vue 实例;
然后返回renderToString、renderToStream;

执行renderToString输出html文件拼接到模板HTML文件上;

在通过clientBundle 拼接当前使用到的js和css,以及preload、prefetch link标签;

vue ssr 流式渲染 就是nodejs stream流;data、end、error等事件

你可能感兴趣的:(字节面经收集)