【精品】前端知识梳理

前端大神之路

共 16606字,需浏览 34分钟

 ·

2022-02-01 13:28

作者:广东靓仔

一、前言

最近有小伙伴私聊广东靓仔,能整理一份目前主流的前端知识点么?

因此有了这篇文章。

目录

  • 原型链

  • 继承

  • 闭包

  • es6

  • this指向

  • 事件循环

  • 缓存相关

  • http相关

  • 浏览器渲染相关

  • css相关

  • React源码

  • vue源码

  • 算法和手写编程题

  • 性能优化

  • webpack相关

  • git高级操作

  • 前端微服务

  • 设计模式

原型链

什么是原型?

任何对象实例都有一个原型,也叫原型对象,这个原型对象由对象的 内置属性_proto_指向它的构造函数的 prototype 指向的对象,即任何对 象都是由一个构造函数创建的,但是不是每一个对象都有 prototype, 只有方法才有 prototype

什么是原型链?

    原型链基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。我们知道,每个构造函数都有一个原型对象,每个原型对象都 有一个指向构造函数的指针,而实例又包含一个指向原型对象的内部指 针。    原型链的核心就是依赖对象的_proto_的指向,当自身不存在的属性 时,就一层层的扒出创建对象的构造函数,直至到 Object 时,就没有 _proto_指向了。因为_proto_实质找的是 prototype,所以我们只要找这个链条上的构造 函数的 prototype    其中 Object.prototype 是没有_proto_属性的,它 ==null。每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的 指针,而实例都包含指向原型对象内部的指针。    我们让原型对象(1)等 于另一个原型对象的实例(2), 此时原型对象(2)将包含一个指向原型对象(1)的指针, 再让原型对象(2)的实例等于原型对象(3),如此层层递进就构成了实 例和原型的链条,这就是原型链的概念 每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数想 指针(constructor),而实例对象都包含一个指向原型对象的内部指针 (__proto__)。如果让原型对象等于另一个原型对象的实例,此时的原型 对象将包含一个指向另一个原型的指针(__proto__),另一个原型也包含 着一个指向另一个构造函数的指针(constructor)。假如另一个原型又是 另一个类型的实例……这就构成了实例与原型的链条。也叫原型链

原型继承?

原型继承是 js 的一种继承方式,原型链作为实现继承的主要方法,其基本 思路是利用原型让一个引用类型继承另一个引用类型的属性和方法。原型继承:利用原型中的成员可以被和其相关的对象共享这一特性,可 以实现继承,这种实现继承的方式,就叫做原型继承。

继承

继承有六种

原型继承

原型继承是 js 的一种继承方式,原型链作为实现继承的主要方法,其基本 思路是利用原型让一个引用类型继承另一个引用类型的属性和方法, 原型继承:利用原型中的成员可以被和其相关的对象共享这一特性,可 以实现继承,这种实现继承的方式,就叫做原型继承特点:
  • 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
  • 父类新增原型方法/原型属性,子类都能访问到
  • 简单,易于实现

缺点:

  • 要想为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,不能放到构造器中
  • 无法实现多继承
  • 来自原型对象的所有属性被所有实例共享(来自原型对象的引用属性是所有实例共享的)
  • 创建子类实例时,无法向父类构造函数传参

借用构造函数继承

使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)

特点:

  • 解决了1中,子类实例共享父类引用属性的问题
  • 创建子类实例时,可以向父类传递参数
  • 可以实现多继承(call多个父类对象)

缺点:

  • 实例并不是父类的实例,只是子类的实例
  • 只能继承父类的实例属性和方法,不能继承原型属性/方法
  • 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能


组合继承

核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

特点:

  • 弥补了方式2的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法
  • 既是子类的实例,也是父类的实例
  • 不存在引用属性共享问题
  • 可传参
  • 函数可复用

缺点:

  • 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)

原型式继承


寄生式继承


寄生组合式继承

核心:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性,避免的组合继承的缺点

闭包

什么是闭包?

闭包可以简单理解成:定义在一个函数内部的函数。其中一个内部函数 在包含它们的外部函数之外被调用时,就会形成闭包。

闭包的特点?

特点:1.函数嵌套函数。2.函数内部可以引用外部的参数和变量。3.参数和变量不会被垃圾回收机制回收使用:1.读取函数内部的变量;2.这些变量的值始终保持在内存中,不会在外层函数调用后被自动清除优点:1:变量长期驻扎在内存中;2:避免全局变量的污染;3:私有成员的存在 ; 缺点:会造成内存泄露

JS 的闭包

闭包是指有权访问另外一个函数作用域中的变量的函数。闭包就是函数的局部变量集合,只是这些局部变量在函数返回后会继 续存在。闭包就是就是函数的“堆栈”在函数返回后并不释放,我们 也可以理解为这些函数堆栈并不在栈上分配而是在堆上分配。当在一 个函数内定义另外一个函数就会产生闭包。为什么要用? 匿名自执行函数:我们知道所有的变量,如果不加上 var 关键字,则 默认的会添加到全局对象的属性上去,这样的临时变量加入全局对象 有很多坏处,比如:别的函数可能误用这些变量;造成全局对象过于 庞大,影响访问速度(因为变量的取值是需要从原型链上遍历的)。除 了每次使用变量都是用 var 关键字外,我们在实际情况下经常遇到 这样一种情况,即有的函数只需要执行一次,其内部变量无需维护, 可以用闭包。

es6

Let 与 var 与 const 的区别?

Var 声明的变量会挂载在 window 上,而 letconst 声明的变量不会 Var 声明的变量存在变量提升,let const 不存在变量提升 同一作用域下 var 可以声明同名变量,let const、不可以 Let const 声明会形成块级作用域Let 暂存死区Const 一旦声明必须赋值,不能用 null 占位,声明后不能再修改,如果 声明的是复合类型数据,可以修改属性,简单理解就是:const定义的常量是基本数据类型的时候不可以被更改

const定义的常量是引用数据类型的时候,其值可以被更改。

数组方法有哪些?

push() 从后面添加元素,返回值为添加完后的数组的长度arr.pop() 从后面删除元素,只能是一个,返回值是删除的元素arr.shift() 从前面删除元素,只能删除一个 返回值是删除的元素arr.unshift() 从前面添加元素, 返回值是添加完后的数组的长度arr.splice(i,n) 删除从 i(索引值)开始之后的那个元素。返回值是删除的元 素arr.concat() 连接两个数组 返回值为连接后的新数组str.split() 将字符串转化为数组arr.sort() 将数组进行排序,返回值是排好的数组,默认是按照最左边的数 字进行排序,不是按照数字大小排序的arr.reverse() 将数组反转,返回值是反转后的数组arr.slice(start,end) 切去索引值 start 到索引值 end 的数组,不包含 end 索引的值,返回值是切出来的数组arr.forEach(callback) 遍历数组,无 return 即使有 return,也不会返回 任何值,并且会影响原来的数组arr.map(callback) 映射数组(遍历数组),有 return 返回一个新数组 。arr.filter(callback) 过滤数组,返回一个满足要求的数组

普通函数和构造函数的区别?

1.构造函数也是一个普通函数,创建方式和普通函数一样,但是构造函数 习惯上首字母大写2.调用方式不一样,普通函数直接调用,构造函数要用关键字 new 来调 用3.调用时,构造函数内部会创建一个新对象,就是实例,普通函数不会创 建新对象4.构造函数内部的 this 指向实例,普通函数内部的 this 指向调用函数的 对象(如果没有对象调用,默认为 window5.构造函数默认的返回值是创建的对象(也就是实例),普通函数的返回 值由 return 语句决定6.构造函数的函数名与类名相同

Promise 的理解?

promise 有三种状态:pending 初始状态也叫等待状态,fulfiled 成功状态,rejected 失败状态;状态一旦改变,就不会再变Promise 的两个特点:1、Promise 对象的状态不受外界影响2、Promise 的状态一旦改变,就不会再变,任何时候都可以得到这个结果,状态不可以逆,Promise 的三个缺点:1)无法取消 Promise,一旦新建它就会立即执行,无法中途取消2)如果不设置回调函数,Promise 内部抛出的错误,不会反映到外部3)当处于 pending(等待)状态时,无法得知目前进展到哪一个阶段, 是刚刚开始还是即将完成

async 的用法?

Async 就是 generation promise 的语法糖,async 就是将 generator 的*换成 async,将 yiled 换成 await 函数前必须加一个 async异步操作方法前加一个 await 关键字,意思就 是等一下,执行完了再继续走,注意:await 只能在 async 函数中运行, 否则会报错 Promise 如果返回的是一个错误的结果,如果没有做异常处理,就会报 错,所以用 try..catch 捕获一下异常就可以了

this指向

this指向是什么?

this 通常指向的是我们正在执行的函数本身,或者是, 指向该函数所属的对象。全局的 this 指向的是 Window对象中的 this 指向其本身事件中 this 指向事件对象

改变函数内部 this 指针的指向函数(bind,apply,call 的区 别)

通过 apply 和 call 改变函数的 this 指向。他们两个函数的第一个 参数都是一样的表示要改变指向的那个对象,第二个参数,apply 是 数组,而 call 则是 arg1,arg2...这种形式。通过 bind 改变 this 作 用域会返回一个新的函数,这个函数不会马上执行

实现一个 bind 函数

原理:通过 apply 或者 call 方法 来实现。

Function.prototype.bind=function(obj,arg){
    var arg = Array.prototype.slice.call(arguments,1);
    var context = this;
    var bound=function(newArg){
        arg=arg.co ncat(Array.prototype.slice.call(newArg) );
        return context.apply(obj,arg);
    }
    var F=function(){}
    // 这里需要一个寄生组 合继承
    F.prototype=context.prototype; 
    bound.prototype=new F();
    return bound;
}

事件循环

JavaScript 是一门单线程语言,异步操作都是放到事件循环队列里面,等待主执行栈来执行的,并没有专门的异步执行线程。同步和异步任务分别进入不同的执行环境,同步的进入主线程,即主执行栈,异步的进入任务队列。主线程内的任务执行完毕为空,会去任务队列读取对应的任务,推入主线程执行。关键的步骤1.在此次 tick 中选择最先进入队列的任务( oldest task ),如果有则执行(一次)2.检查是否存在 Microtasks ,如果存在则不停地执行,直至清空Microtask Queue3.更新 render宏任务主要包含:script( 整体代码)、setTimeout、setInterval、I/O、UI 交互事件、setImmediate(Node.js 环境)微任务主要包含:Promise、MutaionObserver、process.nextTick(Node.js 环境)microtask 优先于 task 执行

缓存相关

本地存储与 cookie 的区别

Cookie 是小甜饼的意思。顾名思义,cookie 确实非常小,它的大小限 制为 4KB 左右。它的主要用途有保存登录信息,比如你登录某个网站市 场可以看到“记住密码”,这通常就是通过在 Cookie 中存入一段辨别用 户身份的数据来实现的localStoragelocalStorage 是 HTML5 标准中新加入的技术,它并不是什么划时代的 新东西。早在 IE 6 时代,就有一个叫 userData 的东西用于本地存储, 而当时考虑到浏览器兼容性,更通用的方案是使用 Flash。而如今, localStorage 被大多数浏览器所支持,如果你的网站需要支持 IE6+, 那以 userData 作为你方案是种不错的选择sessionStoragesessionStorage 与 localStorage 的接口类似,但保存数据的生命周期 与 localStorage 不同。做过后端开发的同学应该知道 Session 这个词 的意思,直译过来是“会话”。而 sessionStorage 是一个前端的概念, 它只是可以将一部分数据在当前会话中保存下来,刷新页面数据依旧存 在。但当页面关闭后,sessionStorage 中的数据就会被清空。三者的异同特性Cookie localStorage sessionStorage 数据的生命期 一般由服务器生成,可设置失效时间。如果在浏览器端生成 Cookie,默 认是关闭浏览器后失效 除非被清除,否则永久保存 仅在当前会话下有效,关闭页面或浏览器后被清除存放数据大小4K 左右 一般为 5MB 与服务器端通信 每次都会携带在 HTTP 头中,如果使用 cookie 保存过多数据会带来性能 问题仅在客户端(即浏览器)中保存,不参与和服务器的通信 易用性 需要程序员自己封装,源生的 Cookie 接口不友好 源生接口可以接受,亦可再次封装来对 Object Array 有更好的支持

http相关

http特性

HTTP0.9 特性

  • 没有请问头和请求体只有请求行
  • 只能发送html文件


HTTP1.0 特性

  • 可以发送javaScript、CSS、图片、音频
  • 加上请求头和请求体
  • 状态码
  • cache 机制可传参
  • 每进行一次 HTTP 通信,都需要经历建立 TCP 连接、传输 HTTP 数据和断开 TCP 连接三个阶段


HTTP1.1 特性

  • 持久连接的方法,它的特点是在一个 TCP 连接上可以传输多个 HTTP 请求,只要浏览器或者服务器没有明确断开连接,那么该 TCP 连接会一直保持(提高性能)
  • 持久连接虽然能减少 TCP 的建立和断开次数,但是它需要等待前面的请求返回之后,才能进行下一次请求。如果 TCP 通道中的某个请求因为某些原因没有及时返回,那么就会阻塞后面的所有请求,这就是著名的队头阻塞的问题(在 HTTP 1.1 中,每一个链接都默认是长链接,因此对于同一个 TCP 链接,HTTP 1.1 规定:服务端的响应返回顺序需要遵循其接收到相应的顺序。但这样存在一个问题:如果第一个请求处理需要较长时间,响应较慢,将会“拖累”其他后续请求的响应,这是一种队头阻塞。)
  • 引入了 Cookie、虚拟主机的支持、对动态内容的支持
  • 浏览器为每个域名最多同时维护 6 个 TCP 持久连接(提高性能)
  • 使用 CDN 的实现域名分片机制。(提高性能)
  • 对带宽的利用率却并不理想(tcp 启动慢、头部堵塞、tcp 竞争)


HTTP2 特点

  • 只使用一个 TCP 长连接来传输数据,实现资源的并行请求,也就是任何时候都可以将请求发送给服务器,解决头部堵塞(多路复用)
  • 二进制传输
  • 多路复用(原理二进制分帧层,携带id的帧流到服务器)

  • 头部压缩

  • 头部被压缩,减少了数据传输

  • 服务端推送

  • 有了二进制分帧层还能够实现请求的优先级、服务器推送、头部压缩等特性,从而大大提升了文件传输效率


  • 但是HTTP2还是会存在 tcp 堵塞(理解,http 堵塞就是需要等待前面的http包发送完成而造成的等待,tcp 堵塞是TCP 传输过程中,由于单个数据包的丢失而造成的阻塞)


HTTP3

QUIC 看成是集成了“TCP+HTTP/2 的多路复用 +TLS 等功能

TCP与UDP的区别

1、基于连接与无连接;

2、对系统资源的要求(TCP较多,UDP少);

3、UDP程序结构较简单;

4、流模式与数据报模式 ;

5、TCP保证数据正确性,UDP可能丢包;

6、TCP保证数据顺序,UDP不保证。

TCP 握手过程

  • 建立tcp链接(三次握手,客户端发送 syn=j 给服务端然后处于 syn_send 状态;
  • 服务端接受到syn,然后发送自己的syn包syn=k,和 ack=j+1(确认客户端包),状态为 syn_recv;
  • 客户端收到ack和syn则发送 ack=k+1给服务端表示确认,服务端和客户端都进入了establish状态),

  • 为什么要3次握手:确认客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常

HTTP 缓存

强缓存(浏览器内部完成)max-age:数值,单位是秒,从请求时间开始到过期时间之间的秒数。基于请求时间(Date字段)的相对时间间隔,而不是绝对过期时间expires:和max-age一样指缓存过期时间,但是他的指定了具体时间GMT格式,HTTP/1.1,Expire已经被Cache-Control替代,原因在于Expires控制缓存的原理是使用客户端的时间与服务端返回的时间做对比,那么如果客户端与服务端的时间因为某些原因(例如时区不同;客户端和服务端有一方的时间不准确)发生误差,那么强制缓存则会直接失效,这样的话强制缓存的存在则毫无意义
协商缓存(需要询问服务器)Last-Modified/If-Modified-Since(服务端时间对比):本地文件在服务器上的最后一次修改时间。缓存过期时把浏览器端缓存页面的最后修改时间发送到服务器去,服务器会把这个时间与服务器上实际文件的最后修改时间进行对比,如果时间一致,那么返回304,客户端就直接使用本地缓存文件。(浏览器最后修改时候和服务端对比,如果一致则走缓存)
问题:1. 现在大多数服务端都采用了负载均衡策略,可能导致不同虚拟主机返回的Last-Modified时间戳不一致,导致对比失败~2. 文件也许会周期性的更改,但是他的内容并不改变,不希望客户端重新getEtag/If-None-Match:(EntityTags,内容摘要)是URL的tag,用来标示URL对象是否改变,一般为资源实体的哈希值。和Last-Modified类似,如果服务器验证资源的ETag没有改变(该资源没有更新),将返回一个304状态告诉客户端使用本地缓存文件。Etag的优先级高于Last-ModifiedEtag主要为了解决 Last-Modified无法解决的一些问题。
文件缓存策略1. 有文件指纹:index.html 用不用缓存,其他用强缓存+文件指纹2. 无指纹:index.html 用不用缓存,其他用协商etag缓存(文件变了就缓存)

浏览器渲染相关

一个页面从输入 URL 到页面加载显示完成,这个过程中都 发生了什么?

1、HTTP 请求准备阶段

  • 构建请求--浏览器构建请求行信息,准备发起网络请求 GET /index.html HTTP1.1
  • 查找缓存--如果浏览器发现请问资源在浏览器中存在副本,根据强缓存规则,如没有过期那么直接返回资源,如何查找失败进入下一个环节:

    --准备 ip 地址和端口

    --DNS(域名和ip的映射系统) 域名解析,拿到ip之后找端口,默认为80

    --建立tcp链接(三次握手)

    --如果是https 还需要建立TLS连接

2、HTTP 发送请求

浏览器向服务端发起http请求,把请求头和请求行一起发送个服务器,服务端解析请求头如发现cache-control和etag(if-none-match),if-modified(if-modified-since)字段就会判断缓存是否过期,如果没有返回304,否则返回200

3、HTTP 响应返回

  • 浏览器拿到响应数据,首先判断是否是4XX或者5XX是就报错,如果是3XX就重定向,2XX就开始解析文件,如果是gzip就解压文件
  • TCP断开连接
  • 4次挥手

  • 浏览器解析渲染

  • 建立根据html建立dom树和css树,如果遇到script首选判断是否defer和async否则会阻塞渲染并编译执行js,如果没有则组合生成render tree,最后浏览器开启GPU进行绘制合成图层,将内容显示屏幕。

css相关

画一条 0.5px 的线

方式一:采用 meta viewport 的方 式方式二:采用 border-image 的方 式 采用 transform: scale() 的方式

css盒子模型

margin、border、padding\content
默认情况盒子的width和height设置只是跟content的宽高真正宽:内容宽度+左右填充+左右边距+左右边框真正的高:内容高度+上下填充+上下边距+上下边框

css3新特性

边框:border-radios 添加圆角边框border-shadow:给框添加阴影 (水平位移,垂直位移,模糊半径,阴 影尺寸,阴影颜色,insetr(内/外部阴影))border-image:设置边框图像border-image-source 边框图片的路径border-image-slice 图片边框向内偏移border-image-width 图片边框的宽度border-image-outset 边框图像区域超出边框的量border-image-repeat 图像边框是否平铺(repeat 平铺 round 铺满 stretch 拉伸)背景:Background-size 背景图片尺寸Background-origin规定background-position属性相对于什么位置定位Background-clip 规定背景的绘制区域(padding-box,border-box, content-box)渐变:Linear-gradient()线性渐变Radial-gradient()径向渐变文本效果:Word-break:定义如何换行Word-wrap:允许长的内容可以自动换行Text-overflow:指定当文本溢出包含它的元素,应该干啥Text-shadow:文字阴影(水平位移,垂直位移,模糊半径,阴影颜色)转换:Transform 应用于 2D3D 转换,可以将元素旋转,缩放,移动,倾斜Transform-origin 可以更改元素转换的位置,(改变 xyz 轴)Transform-style 指定嵌套元素怎么样在三位空间中呈现2D 转换方法:rotate 旋转 translate(x,y)指定元素在二维空间的位移 scale(n) 定义缩放转换3D 转换方法:Perspective(n)为 3D 转换 translate rotate scale过渡:Transition-proprety 过渡属性名Transition-duration 完成过渡效果需要花费的时间Transition-timing-function 指定切换效果的速度Transition-delay 指定什么时候开始切换效果动画:animation Animation-name 为@keyframes 动画名称animation-duration 动画需要花费的时间animation-timing-function 动画如何完成一个周期animation-delay 动画启动前的延迟间隔animation-iteration-count 动画播放次数animation-direction 是否轮流反向播放动画

JS 动画和 css3 动画的差异性

渲染线程分为 main thread compositor thread,如果 css 动画 只 改 变 transform 和 opacity , 这 时 整 个 CSS 动 画 得 以 在 compositor trhead 完成(而 JS 动画则会在 main thread 执行, 然后出发 compositor thread 进行下一步操作),特别注意的是如果 改变 transform 和 opacity 是不会 layout 或者 paint 的。区别:功能涵盖面,JS 比 CSS 大 实现/重构难度不一,CSS3 比 JS 更加简单,性 能跳优方向固定对帧速表现不好的低版本浏览 器,css3 可以做到自然降级 css 动画有天然事件支持 css3 有兼容性问题    

React源码

react 组件之间的数据传递

正向传值用 props 逆向传值用函数传值 通过事件调用函数传递 同级传值用 pubsub-js 用 pubsub.publish(事件名,数据)抛出数据 用 pubsub.subscribe(监听的事件,()=){})接收数据 跨组件传递 用 context 要使用 context 进行跨组件传值就需要使用 createContext() 方 法 , 这 个 方 法 有 两 个 对 象 provider 生 产者 Consumer 消费者

调用 setState 之后发生了什么

React 在调用 setstate 后,react 会将传入的参数对象和组件当前的状态 合并,触发调和过程, 在调和过程中,react 会根据新的状态构建 react 元素树重新渲染整个 UI 界面,在得到元素树之后,react 会自动计算新老节点的差异,根据差 异对界面进行最小化重新渲染

react 生命周期函数


componentWillMount 组件渲染之前调用componentDidMount 在第一次渲染之后调用componentWillReceiveProps 在组件接收到一个新的 props 时调用shouldComponentUpdate 判断组件是否更新 htmlcomponentWillupdate 组件即将更新 html 时调用componentDidupdate 在组件完成更新后立即调用componentWillUnmount 在组件移除之前调用

为什么虚拟 dom 会提高性能?

虚拟 dom 相当于在 js 和真实 dom 中间加了一个缓存,利用 dom diff 算法避免了没有必要的 dom 操作,从而提高性能

请简述虚拟 dom 与 diff 算法

虚拟DOM也就是常说的虚拟节点,它是通过js的object对象模拟DOM 中的节点,然后再通过特定的渲染方法将其渲染成真实的 DOM 节点。频繁的操作 DOM,或大量造成页面的重绘和回流Diff 算法:把树形结构按照层级分解,只比较同级元素,给列表结构的 每个单元添加唯一的 key 值,方便比较

diff 原理


tree diff web UI 中 dom 节点跨层级的移动操作特别少,可以忽略不计component diff 拥有相同类的两个组件将会生成相似的树形结构,拥有 不同类的两个组件会生成不同的树形结构element diff 对于同一层级的一组子节点,他们可以通过唯一的 id 进行 区分

何为高阶组件(higher order component)?

高阶组件是一个以组件为参数并返回一个新组件的函数。HOC 运行你重用代码、逻辑和引导抽象。最常见的可能是 Redux connect 函数。除了简单分享工具库和简单的组合,HOC 最好 的方式是共享 React 组件之间的行为。如果你发现你在不同的 地方写了大量代码来做同一件事时,就应该考虑将代码重构为可 重用的 HOC。

vue源码

vue 中 keep-alive 的理解?

概念:keep-alive 是 vue 的内置组件,当它动态包裹组件时,会缓存不 活动的组件实例,它自身不会渲染成一个 DOM 元素也不会出现在父组 件链中 作用:在组件切换过程中将状态保留在内存中,防止重复渲染 DOM,减 少加载时间以及性能消耗,提高用户体验。生命周期函数:Activated 在 keep-alive 组件激活时调用,deactivated 在 keep-alive 组件停用时调用

Vue 组件中的 data 为什么是函数

Data 是一个函数时,每个组件实例都有自己的作用域,每个实例相互独 立,不会相互影响如果是引用类型(对象),当多个组件共用一个数据源时,一处数据改变, 所有的组件数据都会改变,所以要利用函数通过 return 返回对象的拷贝, (返回一个新数据),让每个实例都有自己的作用域,相互不影响。

组件中写 name 选项有什么作用?

项目使用 keep-alive 时,可搭配组件 name 进行缓存过滤DOM 做递归组件时需要调用自身 namevue-devtools 调试工具里显示的组见名称是由vue中组件name决定的

Vue 双向绑定的原理

Vue2 双向绑定就是:数据变化更新视图,视图变化更新数据 Vue 数据双向绑定是通过数据劫持和观察者模式来实现的, 数据劫持,object.defineproperty 它的目的是:当给属性赋值的时候, 程序可以感知到,就可以控制改变属性值 观察者模式 当属性发生改变的时候,使用该数据的地方也发生改变vue3使用了proxyproxy优点1、直接监听对象而非属性2、直接监听数组的变化3、拦截的方式有很多种(有13种,set,get,has)4、Proxy返回一个新对象,可以操作新对象达到目的proxy缺点:1、 proxy有兼容性问题,不能用polyfill来兼容(polyfill主要抚平不同浏览器之间对js实现的差异)

说说对 proxy 的理解


vue 的数据劫持有两个缺点: 1、无法监听通过索引修改数组的值的变化 2、无法监听 object 也就是对象的值的变化所以 vue2.x 中才会有 $set 属性的存在proxy 是 es6 中推出的新 api,可以弥补以上两个缺点,所以 vue3.x 版本用 proxy 替换 object.defineproperty

计算属性与 watch 区别

Computed watch 区别就是 computed 的缓存功能,当无关数据数 据改变时,不会重新计算,直接使用缓存中的值。计算属性是用来声明 式的描述一个值依赖了其他的值,当所依赖的值后者变量发生变化时, 计算属性也跟着改变, Watch 监听的是在 data 中定义的变量,当该变量变化时,会触发 watch 中的方法

Route 与 router 区别

1. router 是 VueRouter 的一个对象,通过 Vue.use(VueRouter)和 VueRouter 构造函数得到一个 router 的实例对象,这个对象中是一个全 局的对象,他包含了所有的路由包含了许多关键的对象和属性。2.route 是一个跳转的路由对象,每一个路由都会有一个 route 对象,是 一个局部的对象,可以获取对应的 name,path,params,query 等

Vue 的 nextTick 的原理是什么?

1)为什么需要 nextTickVue 是异步修改 DOM 的并且不鼓励开发者直接接触 DOM,但有时候业务需要必须对数据更改--刷新后的 DOM 做相应的处理,这时候就可以使用 Vue.nextTick(callback)这个 api 了。2)理解原理前的准备首先需要知道事件循环中宏任务和微任务这两个概念(这其实也是面试常考点)。常见的宏任务有: script, setTimeout, setInterval, setImmediate, I/O, UI rendering常见的微任务有:process.nextTick(Nodejs),Promise.then(), MutationObserver;3)理解 nextTick而 nextTick 的原理正是 vue 通过异步队列控制 DOM 更新和 nextTick 回调函数先后执行的方式。如果大家看过这部分的源码,会发现其中做了很多 isNative()的判断,因为这里还存在兼容性优雅降级的问题。可见 Vue 开发团队的深思熟虑,对性能的良苦用心

Vue3.0 是如何变得更快的?

diff 方法优化Vue2.x 中的虚拟 dom 是进行全量的对比。Vue3.0 中新增了静态标记(PatchFlag):在与上次虚拟结点进行对 比的时候,值对比带有 patch flag 的节点,并且可以通过 flag 的信息 得知当前节点要对比的具体内容化。hoistStatic 静态提升Vue2.x : 无论元素是否参与更新,每次都会重新创建。Vue3.0 : 对不参与更新的元素,只会被创建一次,之后会在每次渲染时 候被不停的复用。cacheHandlers 事件侦听器缓存 默认情况下 onClick 会被视为动态绑定,所以每次都会去追踪它的 变化但是因为是同一个函数,所以没有追踪变化,直接缓存起来复用 即可。

算法和手写编程题

算法:链表、栈和队列、二叉树

性能优化

web 性能优化?

降低请求量:合并资源,减少 HTTP 请求数,minify / gzip 压缩, webP,lazyLoad。加快请求速度:预解析 DNS,减少域名数,并 行加载,CDN 分发。缓存:HTTP 协议缓存请求,离线缓存 manifest,离线数据缓存 localStorage。渲染:JS/CSS 优化,加载顺序,服务端渲染,pipeline

如何提高小程序的首屏加载时间?

提前请求:异步数据数据请求不需要等待页面渲染完成利用缓存:利用 storage API 对异步请求数据进行缓存,二次启动时 先利用缓存数据渲染页面,再进行后台更新避免白屏:先展示页面骨架和基础内容 及时反馈:及时地对需要用户等待的交互操作给出反馈,避免用户以 为小程序没有响应性能优化:避免不当使用 setdata 和 onpagescroll

webpack相关

webpack 的打包原理

Webpack 是把项目当做一个整体,通过给定一个主文件,webpack 将从这个主文件开始找到项目中所有依赖的文件,使用 loaders 类处 理,最后打包成一个或者多个浏览器可识别的 js 文件

webpack 中的 loaders 与 plugin 的区别

什么是 loaders:loaders 是文件加载器,能够加载资源文件,并对 这些文件进行处理,例如,编译,压缩等,最终一起打包到指定文件 中。什么是 plugin:在 webpack 运行的生命周期会有许多事件,plugin 可以监听这些事件 区别:加载器是用来加载文件的,webpack 本身只能加载 js 文件(内 置 babel-loader),加载其他文件就需要安装别的 loader,比如: css-loader file-loader Plugin 是扩展 webpack 功能的,通过 plugin ,webpack 可以实 现 loader 不能完成的复杂功能

Webpack 与 gulp 区别

Gulp 是一种能够优化前端开发流程的工具webpack 是一种模块化的解决方案 (grunt

git高级操作

git checkout -  导航 —— 跳到之前的分支

查看历史

# 每个提交在一行内显示 
git log --oneline   # 在所有提交日志中搜索包含「homepage」的提交  git log --all --grep='homepage'  # 获取某人的提交日志  git log --author="Maxence"
不想提交,又要回滚

git reset --hard <提交的哈希值>

查看我的分支和 master 的不同

git diff master..my-branch

# 在最近 3 个提交上运行 `npm test` 命令

git rebase HEAD~3 --exec "npm test"

前端微服务

特点

  • 独立部署
  • 独立开发
  • 技术无关

  • 不影响用户体验

6种方式

路由分发式微前端通过路由将不同的业务分发到不同的、独立前端应用上。其通常可以通过 HTTP 服务器的反向代理来实现,又或者是应用框架自带的路由来解决。


使用 iFrame 创建容器iframe 可以创建一个全新的独立的宿主环境,这意味着我们的前端应用之间可以相互独立运行。采用 iframe 有几个重要的前提:
  • 网站不需要 SEO 支持
  • 需要设置加载机制
  • 需要设置通讯机制


框架之上设计通讯、加载机制不论是基于 Web Components 的 Angular,或者是 VirtualDOM 的 React 等,现有的前端框架都离不开基本的 HTML 元素 DOM。那么,我们只需要:1. 在页面合适的地方引入或者创建 DOM2. 用户操作时,加载对应的应用(触发应用的启动),并能卸载应用。


通过组合多个独立应用、组件来构建一个单体应用常见的方式有:
  • 独立构建组件和应用,生成 chunk 文件,构建后再归类生成的 chunk 文件。(这种方式更类似于微服务,但是成本更高)
  • 开发时独立开发组件或应用,集成时合并组件和应用,最后生成单体的应用。
  • 在运行时,加载应用的 Runtime,随后加载对应的应用代码和模板。

    但是,首先它有一个严重的限制:必须使用同一个框架。

    其次,采用这种方式还有一个限制,那就是:规范!**规范!**规范!。在采用这种方案时,我们需要:

  • 统一依赖。统一这些依赖的版本,引入新的依赖时都需要一一加入。

  • 规范应用的组件及路由。避免不同的应用之间,因为这些组件名称发生冲突。

  • 构建复杂。在有些方案里,我们需要修改构建系统,有些方案里则需要复杂的架构脚本。

  • 共享通用代码。这显然是一个要经常面对的问题。

  • 制定代码规范。


纯 Web Components 技术构建Web Components 组件可以拥有自己独立的 Scripts 和 Styles,以及对应的用于单独部署组件的域名。然而它并没有想象中的那么美好,要直接使用纯 Web Components 来构建前端应用的难度有:
  • 重写现有的前端应用。是的,现在我们需要完成使用 Web Components 来完成整个系统的功能。
  • 上下游生态系统不完善。缺乏相应的一些第三方控件支持,这也是为什么 jQuery 相当流行的原因。需要设置通讯机制
  • 系统架构复杂。当应用被拆分为一个又一个的组件时,组件间的通讯就成了一个特别大的麻烦。
  • 浏览器兼容问题

现有框架(single-spa、qiankun、mooa)其中single-spa已经实现了大部分框架(vue、react、angular)的启动和卸载处理,但不适用于生产环境qiankun是基于spa-single实现的以运行在生产环境为目标的微前端服务框架Mooa是一个仅仅基于angular框架的微前端框架

设计模式

简单工厂- 处理变与不变的工厂模式:将创建对象的过程单独封装,实现无脑传参,核心:处理变与不变的
抽象工厂- 开放封闭原则简单工厂因为没有遵守开放封闭原则, 暴露一个很大的缺陷。例如若我们添加管理层一些考评权限,难道我们要重新去修改Factory函数吗?这样做会导致Factory会变得异常庞大,而且很容易出bug,最后非常难维护
单例模式 - 保证一个类只有一个实例保证一个类仅有一个实例,并提供一个访问它的全局访问点。单例模式要求不管我们尝试去创建多少次,它都只给你返回第一次所创建的那唯一的一个实例
装饰器模式 - 实现只添加不修改适配器模式 - 兼容就是一把梭代理模式事件代理:点击子元素,用父元素代理缓存代理
观察者模式

结束

关注前端早茶,一起携手进阶

欢迎关注前端早茶,与广东靓仔携手共同进阶~

浏览 34
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报