前端_八股文

下划线的内容 代表可以面试的时候 简述出来的 , 部分摘自网络 侵删

JavaScript

  • es6新特性

1、var、let 及 const 区别

  1. var声明的变量会挂载在window上,而let和const声明的变量不会
  2. var声明变量存在变量提升,let和const不存在变量提升
  3. let和const声明形成块作用域
  4. 同一作用域下let和const不能声明同名变量,而var可以
  5. 使用let/const声明的变量在当前作用域存在暂存死区
  6. const一旦声明必须赋值,不能使用null占位,声明后不能再修改,如果声明的是复合类型数据,可以修改其属性

2、Set与Map

Map:
在JS中的默认对象的表示方式为{},即一组键值对,但是键必须是字符串。
为了使用Number或者其他数据类型作为键,ES6规范引入了新的数据类型Map。
Map是一组键值对的结构,具有极快的查找速度。初始化Map需要一个二维数组,或者直接初始化一个空Map。
Map 对象是键值对集合,和 JSON 对象类似,但是 key 不仅可以是字符串还可以是其他各种类型的值包括对象都可以成为Map的键
Api:set(key,value)、get(key)、Size、has(value)、delete( )、clear( )

Set:
Set也是一组key的集合,与Map类似。但是区别是Set不存储value,并且它的key不能重复。
创建一个Set,需要提供一个Array作为输入,或者直接创建一个空Set:
重复元素会在Set中自动被过滤
Set 对象类似于数组,且成员的值都是唯一的
Api:add(value)、delete(value)、has(value)、clear( )

总结 :

  • map其实就是更强大的对象 , 查找的比普通的对象快还可以吧数组和对象作为 map的键
  • set简单来说就是数组 , 只不过他里面的值是唯一的 常用来格式化数组
  • 原型链

谈谈对原型链的理解。

构造函数有个prototype对象(原型),该对象有个“constructor”属性,指向构造函数。
每个对象都有一个“proto”属性,指向它的构造函数的“prototype”属性。
构造函数的prototype对象,也有一个“proto”对象,它指向Object的prototype对象。
当我们访问一个对象的属性时,会先查找对象本身。如果这个对象内部查找不到这个属性,那么他就会去prototype里找这个属性,这个prototype又会有自己的prototype,一直查到到Object 这条链 就是原型链。 

 

为什么要使用原型链呢?

1.为了实现继承,简化代码,实现代码重用!
2.只要是这个链条上的内容,都可以被访问和使用到!

使用原型链有什么作用?

  • 继承
  • prototype用来实现基于原型的继承与属性的共享
  • 避免了代码冗余,公用的属性和方法,可以放到原型对象中,这样,通过该构造函数实例化的所有对象都可以使用该对象的构造函数中的属性和方法!
  • 减少了内存占用
  • js有哪些继承方

1. 原型继承

2. 借用构造函数继承

3. 组合继承

4. 寄生组合式继承

  • js有哪些数据类型

js数据类型有6种:

number string  boolean object function undefined

其中number、string、boolean、undefined是值类型,function和object是引用类型。

  • 如何判断一个变量是否数组?

答 : 通过es6 新方法 Array.isArray 和 数组原型上的constructor 判断

  • Null 和 undefined 的区别?

本身都表示“没有”,但null表示引用类型的对象为空,undefined则表示变量未定义。

在相等判断时候,null和undefined是相等的

但null和undefined在很多方面有区别。

含义不同

null表示对象空指针,undefined表示变量未定义。

类型不同

typeof null // 'object'
typeof undefined // 'undefined'


Number(null) // 0
Number(undefined) // NaN

应用场景不同 : 

null:

作为对象原型链的终点

undefined:

定义了变量,没有初始化,默认是undefined

函数不return,或者return后面没有值,则函数默认返回undefined

函数参数如果不传,默认是undefined

  • call bind apply的区别?

call、apply、bind都是用来改变this指向的,

call和apply调用时候立即执行,bind调用返回新的函数。

当需要传递参数时候,call直接写多个参数,apply将多个参数写成数组。

bind在绑定时候需要固定参数时候,也是直接写多个参数。

  • 防抖节流的概念?实现防抖和节流。

防抖函数 : 在每一次都有内容后进行清除是为了保证当前执行的函数就是当前规定的时间内执行的最后一次操作

节流函数 : 如此操作是为了保证,在规定的时间内只会执行一次这个操作,这就是两个函数从代码上看到的不同

  • 深拷贝、浅拷贝的区别?

深拷贝与浅拷贝是针对对象属性为对象的,因为基本数据类型在进行赋值操作时(也就是深拷贝(值拷贝)),是直接将值赋给了新的变量,也就是该变量是原变量的一个副本,这时,你修改两个中的任意一个都不会影响另一个;而对于对象或引用数据在进行浅拷贝时,只是将对象的引用复制了一份,也就是内存地址,即两个不同的变量指向了同一个内存地址,那么在改变任意一个变量的值都是改变内存地址所存储的值,因此两个变量的值都会改变。

浅 : 如果对一个对象进行浅拷贝的话 , 实际上是吧这个对象的内存地址指向新的变量 , 这就会导致在更改新变量的时候也会更改原先变量的值

深 : 而深拷贝的话是重新创建了一个内存地址 , 和值来指向这个新变量 。

  • new一个对象发生了什么

1. 创建了一个空的js对象(即{})

2. 将空对象的原型prototype指向构造函数的原型

3. 将空对象作为构造函数的上下文(改变this指向)

4. 判断构造函数的返回值,以决定最终返回的结果。

a. 如果返回值是基础数据类型,则忽略返回值;

b. 如果返回值是引用数据类型,则使用return 的返回,也就是new操作符无效;

  • this指向系列问题。

原理js函数中的this变量指向是什么?这和this所在的函数的声明和调用情况有关

1. 函数作为对象的属性被调用

作为哪个对象的函数调用的,函数中的this就指向哪个对象。如果在全局环境中调用,this指向window(strict模式时候是undefined)

2. 通过call、apply调用

call和apply都是Function.prototype上的方法。call和apply可以改变this指向。

3.使用new调用(当做构造函数调用)

如果函数被当做构造函数调用,函数中的this指向的是实例。优先级高于bind

 4. 箭头函数

箭头函数中的this指向的是它声明时候所在的函数的this。如果在全局声明,那this指向window箭头函数的优先级最高,使用call或者apply不会改变指向,使用bind的话,也不会改变this指向,但是bind指定的参数会传给函数。
  • 谈谈对闭包的理解?什么是闭包?闭包有哪些应用场景?闭包有什么缺点?如何避免闭包?

对闭包的理解:

1. 什么是闭包?函数和函数内部能访问到的变量的总和,就是一个闭包。

2. 如何生成闭包? 函数嵌套 + 内部函数被引用。

3. 闭包作用?隐藏变量,避免放在全局有被篡改的风险。

4. 使用闭包的注意事项?不用的时候解除引用,避免不必要的内存占用。

为什么有闭包的这种特性:如果形成闭包,外部函数执行完后,其中的局部变量可能被内部函数使用,所以不能销毁,因此内部函数能一致访问到这些局部变量,直到引用解除。

为什么需要闭包?隐藏变量,避免放在全局有被篡改的风险。

闭包的缺点使用时候不注意的话,容易产生内存泄漏。

  • 谈谈对js事件循环的理解?

事件循环是js引擎执行js的机制,用来实现js的异步特性。

事件循环的过程为:当执行栈空的时候,就会从任务队列中,取任务来执行。共分3步:

1. 取一个宏任务来执行。执行完毕后,下一步。

2. 取一个微任务来执行,执行完毕后,再取一个微任务来执行。直到微任务队列为空,执行下一步。

3. 更新UI渲染。

js执行代码的先后顺序

总结 : 执行顺序  同步代码 => 异步(微任务)=> dom渲染 => 异步(宏任务) 

  • 宏任务和微任务执行是怎么样的

 一、js宏任务和微任务分别有哪些?

1、js宏任务有:<script>整体代码、setTimeout、setInterval、setImmediate、Ajax、DOM事件

2、js微任务有:process.nextTick、MutationObserver、Promise.then catch finally

二、js宏任务和微任务谁先执行?

JS是单线程,碰见同步执行同步直到执行完毕,遇到异步放到执行队列中去,异步(宏任务和微任务),在异步中微任务是优于宏任务执行的

  • 如何实现数组去重?

1 . 借助es6提供的set结构 new set() 简单好用

2 . 利用filter去重 结合indexOf

3 . for循环 用indexof 判断

  • 柯里化

// 普通的add函数
function add(x, y) {
    return x + y
}
// currying后
function curryingAdd(x) {
  return function (y) {
      return x + y
  }
}
curryingAdd(1)(2)   // 3
柯里化,英语:Currying,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术
  • 地狱回调

Promise 简化异步操作的回调函数写法
Generator 可以让异步操作停止,达到同步效果
async 是 Generator 函数的语法糖

 

  • 事件委托

1. 基本概念
事件委托,简单的来说,就是把一个元素的响应事件的函数委托到另一个元素。一般来讲,会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素,当事件响应到需要绑定的元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数。

2. 事件传播
事件的传播分为三个阶段:捕获阶段、目标阶段、冒泡阶段。

捕获阶段 —— 从window,document 和根元素开始,事件向下扩散至目标元素的祖先
目标阶段 —— 事件在用户单击的元素上触发
冒泡阶段——最后,事件冒泡通过目标元素的祖先,一直到根元素 document 和 window。

3. 事件委托的好处
减少内存消耗
试想一下,如果我们有一个列表,列表之中有大量的列表项,我们需要在点击列表项的时候响应一个事件。如果给每个列表项一一都绑定一个函数,那对于内存消耗是非常大的,效率上需要消耗很多性能。因此,比较好的方法就是把这个点击事件绑定到他的父层,然后在执行事件的时候再去匹配判断目标元素。所以事件委托可以减少大量的内存消耗,节约效率。

动态绑定事件
比如上述的例子中列表项就几个,我们给每个列表项都绑定了事件。在很多时候,我们需要通过 AJAX 或者用户操作动态的增加或者去除列表项元素,那么在每一次改变的时候都需要重新给新增的元素绑定事件,给即将删去的元素解绑事件。如果用了事件委托就没有这种麻烦了,因为事件是绑定在父层的,和目标元素的增减是没有关系的,执行到目标元素是在真正响应执行事件函数的过程中去匹配的。所以使用事件在动态绑定事件的情况下是可以减少很多重复工作的。

  • JS执行机制

执行机制 : 执行的顺序就是,执行宏任务,再执行宏任务里面的微任务,如果执行微任务的过程中,产生了新的微任务,则继续执行微任务,等到微任务执行结束,再回到宏任务中进行下一轮循环。知识点参考

1.什么是作用域

作用域是在运行时代码中的某些特定部分中变量,函数和对象的可访问性。换句话说,作用域决定了代码区块中变量和其他资源的可见性。可能这两句话并不好理解,我们先来看个例子:

function outFun2() {
    var inVariable = "内层变量2";
}
outFun2();//要先执行这个函数,否则根本不知道里面是啥
console.log(inVariable); // Uncaught ReferenceError: inVariable is not defined

从上面的例子可以体会到作用域的概念,变量inVariable在全局作用域没有声明,所以在全局作用域下取值会报错。我们可以这样理解:作用域就是一个独立的地盘,让变量不会外泄、暴露出去。也就是说作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。

ES6 之前 JavaScript 没有块级作用域,只有全局作用域和函数作用域。ES6的到来,为我们提供了‘块级作用域’,可通过新增命令let和const来体现。

全局作用域、函数作用域、块级作用域、

2.什么是作用域链

 首先认识一下什么叫做 自由变量 。如下代码中,console.log(a)要得到a变量,但是在当前的作用域中没有定义a(可对比一下b)。当前作用域没有定义的变量,这成为 自由变量 。自由变量的值如何得到 —— 向父级作用域寻找(注意:这种说法并不严谨,下文会重点解释)。

var a = 100
function fn() {
    var b = 200
    console.log(a) // 这里的a在这里就是一个自由变量
    console.log(b)
}
fn()

如果父级也没呢?再一层一层向上寻找,直到找到全局作用域还是没找到,就宣布放弃。这种一层一层的关系,就是 作用域链 。

var a = 100
function F1() {
    var b = 200
    function F2() {
        var c = 300
        console.log(a) // 自由变量,顺作用域链向父作用域找
        console.log(b) // 自由变量,顺作用域链向父作用域找
        console.log(c) // 本作用域的变量
    }
    F2()
}
F1()

 

一句话总结:vm层(视图模型层)通过接口从后台m层(model层)请求数据,vm层继而和v(view层)实现数据的双向绑定。 (前后端分离)

前端框架MVVM中的vm层是干嘛的?

ViewModel 是由前端开发人员组织生成和维护的视图数据层。在这一层,前端开发者对从后端获取的 Model 数据进行转换处理,做二次封装,以生成符合 View 层使用预期的视图数据模型。需要注意的是 ViewModel 所封装出来的数据模型包括视图的状态和行为两部分,而 Model 层的数据模型是只包含状态的,比如页面的这一块展示什么,那一块展示什么这些都属于视图状态(展示),而页面加载进来时发生什么,点击这一块发生什么,这一块滚动时发生什么这些都属于视图行为(交互),视图状态和行为都封装在了 ViewModel 里。这样的封装使得 ViewModel 可以完整地去描述 View 层。由于实现了双向绑定,ViewModel 的内容会实时展现在 View 层,这是激动人心的,因为前端开发者再也不必低效又麻烦地通过操纵 DOM 去更新视图,MVVM 框架已经把最脏最累的一块做好了,我们开发者只需要处理和维护 ViewModel,更新数据视图就会自动得到相应更新,真正实现数据驱动开发。看到了吧,View 层展现的不是 Model 层的数据,而是 ViewModel 的数据,由 ViewModel 负责与 Model 层交互,这就完全解耦了 View 层和 Model 层,这个解耦是至关重要的,它是前后端分离方案实施的重要一环。

  • 讲讲Vuex的使用方法。

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态。

它主要有以下API

1. state

2. getters

3. mutations

4. actions

5. module

6. 辅助函数:mapState、mapGetters、mapMutations、mapActions

7. createStore

其中state和getters用来保存状态;mutations和actions用来改变状态;监听状态用的是Vue组件中的computed属性;module是用来组织整个应用的状态管理代码,使状态划分模块,更易于管理;辅助函数用来在监听状态时候简化代码,createStore则用来创建状态管理对象。

mutations和actions有区别,mutations不应该用于异步修改状态,因为这样做的话,Vuex是无法知道修改state.count的时机的,因为它是在异步回调里面指定的,因此Vuex无法在调试工具中打印出我们实际改变state的操作。

而actions中可以异步更新状态。

getters和state的关系类似于Vue组件data属性和computed属性的关系,getters根据state或者其他getters计算出另一个变量的值,当其依赖的数据变化时候,它也会实时更新。

  • Vue组件间通信方式有哪些?

父子组件通信主要有3种方式:

  1. 父组件通过props给子组件传递属性

  2. 子组件通过$emit方法给父组件抛出事件

  3. 父组件通过ref或得子组件引用,从而调用子组件的方法

  4. $parent $children

兄弟组件通信有2种常用方式:

  1. eventBus

  2. vuex

  • computed和watch的区别

应用场景不同

computed用在根据data属性或者其他computed计算得到一个新值的情况,computed的值一般被用在渲染中。

watch用在监听数据变化,然后做一些有副作用的操作的场景。

执行过程不同

在依赖的data属性变化后,computed并不会重新计算新的值,而是等到访问的时候再判断,如果依赖的data有改动则重新计算并返回结果,如果依赖的data没有改动,就不计算,直接返回当前结果。

依赖的数据变化后就会执行watch的回调。

  • v-for和v-if同时使用有问题吗?

v-if和v-for不要同时使用,因为会在每次渲染时候都要遍历列表并判断是否需要渲染,这个遍历操作其实是有一部分冗余或者完全不必要的。

1. 如果是为了过滤一个列表中的项目(v-for循环,v-if过滤条件),可以将列表作为计算属性,在computed中过滤出需要渲染的列表,再进行渲染。这样避免了每次渲染都计算(只在computed依赖的属性变化时候才计算),同时渲染列表是过滤了的,那么循环的次数也可能减少。

2. 如果是为了控制整个列表的展示和隐藏(v-for循环,v-if判断整个列表是否需要展示),可以将判断条件放到父元素(ul、ol)上。这样展示和隐藏的判断只需要执行一次(在列表最开始)。

Vue3修改了v-if和v-for的优先级,v-if没有权限访问v-for的变量,这个需要注意。

  • 前端路由原理。比较一下history和hash这两种路由。

前端路由有两种模式,HTML5和hash,这两种模式本质是不同的底层浏览器技术,但是上层Vue Router做了统一化的封装,因此在我们开发组件和配置路由时候使用这两种模式的区别并不大。默认是hash模式。

这两种模式有几个主要区别 : 

1. HTML5模式的路由没有"#"字符,而是在域名后直接写路径,更加优雅
2. 由于#后面的字符不会发给服务器,因此hash路由SEO比较差
3. HTML5需要服务器在访问不同的路径时候都能fallback到index.html,因此相对麻烦

前端路由的原理关键有2点 : 

1. 可以修改url,但不会引起刷新,从而在不刷新的页面的情况下跳转路由。

2. 监听url改变,根据url渲染对应组件。

hash模式和history模式的原理都是基于这两点。hash是通过浏览器提供的location API修改url,通过onhashchange方法监听hash改变;history通过浏览器提供的history.pushState或者history.replacestate修改url,通过popState事件监听url改变。

 

hash模式:

 

1、hash路由模式是比较好区分的,凡是我们的url后带有“#”的都属于hash模式;

 

2、hash“#”后的值,不会包含在http请求中,改变hash的值不会引起页面的重新加载(他是前端路由交互处理,#后面hash值变化不会导致浏览器像服务器发送请求,浏览器不发送请求,也就不会刷新页面);

 

3、hash原理:onhashchange事件,可以在window对象上监听这个事件(每次hash值的变化,都会出发hashchange事件,通过这个事件我们就可以知道hash值发生了哪些变化,通过监听hashchange来实现更新页面部分内容操作)

 

4、hash可以兼容到ie8以上

 

5、会创建hashhistory对象,在访问不同的路由会发生两种事件:hashhistory.push():将新的路由添加到浏览器访问的历史栈顶,hashhistory.replace()替换到当前栈

 

history模式:

 

1、url不带参数,所以看起来比较美观;

 

2、history依赖于前后端交互,即将url修改的就和正常请求的后端url一样,如后端没有配置对应的/user/id的路由处理,就会返回404错误;

 

3、history原理:onpopstate事件,当浏览器跳转到新的状态时会触发popstate事件;

 

4、history可以兼容到ie10;

 

博主常用的是hash模式,因为history更多的去依赖于前后端的交互,所以个人也不过多推荐使用history模式

 

  • 讲讲Vue的虚拟DOM,原理,好处是什么?相对于手动操作DOM,性能更好吗?

虚拟dom是什么 :

虚拟dom并不是完全拥有真实DOM的所有属性以及方法,它是一个节点树,它将元素、属性和内容作为对象以及属性,vue/react的渲染函数从组件里创建一个节点树,然后响应数据的变化来更新这个节点树,这个变化是用户或者系统完成的动作引起的。

虚拟DOM工作三个重要步骤 : 

1、底层数据发生变化时,整个ui都将在虚拟DOM中重新渲染。

2、计算之前DOM和更改数据的虚拟DOM之间不一样。

3、计算出不同时,真实DOM只渲染更改的差异,数据相同时不会进行重新渲染,节约渲染时间

  • 说说Vue的keep-alive使用及原理。

keep-alive是一个内置组件,它所包裹的组件会在不同的渲染中缓存状态。用在需要让自定义组件在不同的渲染中保持状态不变的场景。例如一些表单组件,如果已经填写好一些内容,然后切到其他组件,再切换回表单时候,应该保持已经填写好的内容,这时候可以选择使用keep-alive。

  • Vue中v-model指令实现原理 ? 

modal层通过defineProperty来劫持每个属性,一旦监听到变化通过相关的页面元素更新。另一方面通过编译模板文件,为控件的v-model绑定input事件,从而页面输入能实时更新相关data属性值。

  • 为什么Vue中组件 data 必须是一个函数 ?

1. 什么是函数?
函数是由事件驱动的或者当它被调用时执行的可重复使用的代码块。js函数语法,函数就是包裹在花括号中的代码块,前面使用了关键词 function: 当调用该函数时,会执行函数内的代码。可以在某事件发生时直接调用函数(比如当用户点击按钮时),并且可由 JavaScript 在任何位置进行调用。

2. 先抛出答案, 如果在面试的时候被面试官问道, 该如何回答?
我们可以采用总分的说法:

  1. 在Vue中根实例的data可以是对象也可以是函数, 但是在组件中的data必须要是一个函数, 组件就是可以复用的Vue实例, 把公共的模块抽离出来, 达到复用和直接使用的效果. 接下来我会采用对比的方式进行说明.
  2. 如果data是一个对象的话, 对象是一个引用类型; 它会在堆空间中开辟一片区域, 将内存地址存入. 这就使得所有的组件公共一个data, 当一个组件中修改了data中的数据就会出现一变全变的现象.
  3. 如果data是一个函数的话, 且使用return返回一个对象; 这就使得每复用一次组件就会返回一个全新的data(这就相当于scoped, 每一个组件data都是私有的, 互不干扰, 各个组件维护自己的data)

3. 从组件的角度来看为什么data要是一个函数?

  • 组件是可复用的vue实例,一个组件被创建好之后,就可能被用在各个地方
  • 组件不管被复用了多少次,组件中的data数据都应该是相互隔离,互不影响的
  • 基于这一理念,组件每复用一次,data数据就应该被复制一次,之后,当某一处复用的地方组件内data数据被改变时,其他复用地方组件的data数据不受影响

4. 从函数的作用域角度来看data要是一个函数?
只有函数才有作用域的问题, 当组件被复用的时候; 函数与函数之间是有作用域的, 所以两个不同的作用域之间, 也是互不干扰的.

5. 总结
data必须是一个函数, 函数的好处就是每一次组件复用之后都会产生一个全新的data. 组件与组件之间各自维护自己的数据, 互不干扰

  • vue中何时需要使用beforeDestory 和 destroyed

可以在这个方法里面执行一些 , 清除定时器、移除事件监听和清除组件等操作

beforeDestroy生命周期函数表示即将销毁,此时仍然可以使用子组件的实例、methods、watch

到了destroyed生命周期函数,此时已经被销毁,无法再使用子组件的实例,methods、watch

  • vuex中action和mutaion有何区别

Action:
actions函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。

第一点:流程顺序不同

“相应视图—>修改State”拆分成两部分,视图触发Action,Action再触发Mutation。

第二点:基于流程顺序,二者扮演不同的角色。

Mutation:专注于修改State,理论上是修改State的唯一途径。

Action:业务代码、异步请求。

第三点:基于角色不同,二者也有不同的限制。

Mutation必须同步执行;

Action可以异步,但不能直接操作State。而且可以通过 action 来提交 mutations

  • 为什么使用v-for的时候必须要添加唯一的key?

  1. 使用v-for更新已渲染的元素列表时,默认用就地复用策略;列表数据修改的时候,他会根据key值去判断某个值是否修改,如果修改,则重新渲染这一项,否则复用之前的元素;
  2. key的作用主要是为了高效的更新虚拟DOM。另外vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果。参考
  • Vue.nextTick

在了解nextTick原理之前,需要先理解JavaScript的任务队列。参考点

Vue.nextTick()方法用来设置让方法在下一个事件队列中执行。在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

Vue使用MutationObserver/Promise/setTimeout实现nextTick。Vue判断浏览器兼容性,按照MutationObserver -> Promise -> setTimeout的优先级实现nextTick。

Promise和setTimeout比较好理解,使用MutationObserver的目的是为了将回调加入到微任务队列,比宏任务队列更早执行。

使用时机

1、Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中,原因是在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。与之对应的就是mounted钩子函数,因为该钩子函数执行时所有的DOM挂载已完成。
2、当项目中你想在改变DOM元素的数据后基于新的dom做点什么,对新DOM一系列的js操作都需要放进Vue.nextTick()的回调函数中;通俗的理解是:更改数据后当你想立即使用js操作新的视图的时候需要使用它

3、在使用某个第三方插件时 ,希望在vue生成的某些dom动态发生变化时重新应用该插件,也会用到该方法,这时候就需要在 $nextTick 的回调函数中执行重新应用插件的方法。

 

  • vue框架设计模式

1.观察者模式(又称发布-订阅模式):Vue.js是基于观察者模式实现数据绑定的。当Vue实例中的数据发生变化时,通过观察者模式通知依赖该数据的组件进行响应式更新。

2.代理模式:在Vue实例中,采用proxy对象作为数据监听的实现方式。Proxy对象允许对一个目标对象进行拦截、修改和隐藏等操作,从而可以实现对数据的劫持和响应式更新。

3.工厂模式:在Vue.js中,通过全局API Vue.component() 和局部组件自定义选项component,创建和注册组件就使用了工厂模式,将组件的状态和行为进行封装和隔离。

4.Mixin 模式:通过混入Mixin实现代码复用和逻辑抽象,可以将一些通用逻辑和函数进行提取,封装成一个Mixin对象,以便在多个组件中共享。

5.单例模式:在Vue.js中,Vue实例是唯一的,因此可以看作是单例模式的应用。通过单例模式实现Vue的实例管理,保证了整个应用中只存在一个Vue实例,全局数据和事件的管理也更加方便。

vue3

  vue2和3的响应式区别 , 为什么2不能进行深度响应式

Vue 2 和 Vue 3 在底层响应式的实现上有一些区别。其中最明显的一个区别是 Vue 2 使用了基于 Object.defineProperty 的劫持方式来实现响应式,而 Vue 3 则使用了 Proxy 对象来实现。

  1. Vue 2 的响应式实现:Vue 2 通过遍历对象的属性,并使用 Object.defineProperty 方法将这些属性转换成 getter 和 setter,从而实现对属性的劫持。当数据发生变化时,Vue 会通过依赖追踪的方式,通知相关的视图进行更新。然而,这种劫持方式只能监听对象的属性访问和修改,对于深层嵌套的对象或数组,需要通过递归设置 getter 和 setter 来实现深度响应式,导致性能和内存开销较大。

  2. Vue 3 的响应式实现:Vue 3 使用 Proxy 对象来替代 Vue 2 中的 Object.defineProperty。Proxy 可以劫持整个对象,而不仅仅是对象的属性,可以更方便地进行监听和拦截。使用 Proxy 对象,Vue 3 可以捕获到更多类型的操作,例如对于数组的索引访问、新增或删除元素等。这样,Vue 3 能够更精确地追踪变化,并在需要更新视图时提供更高的性能。

为什么 Vue 2 不能进行深度响应式?

Vue 2 之所以无法直接实现深度响应式,是因为在初始化时,Vue 2 通过 Object.defineProperty 方法将对象的属性转换成 getter 和 setter,从而实现对属性的劫持。然而,Object.defineProperty 方法只能劫持对象的属性,无法对对象的内部属性进行递归劫持。这意味着,如果对象是一个嵌套结构,那么需要单独对其每一层进行劫持才能实现深度响应式。

由于递归劫持的性能和内存开销较大,Vue 2 在设计上做出了权衡,选择放弃对深度响应式的直接支持。而在 Vue 3 中,使用 Proxy 对象来实现底层响应式,可以更方便地捕获和处理深层对象的变化,从而实现了更好的深度响应式支持。

  •  双向绑定原理

vue双向数据绑定原理,又称vue响应式原理,是vue的核心,双向数据绑定是通过数据劫持结合发布者订阅者模式的方式来实现的,通过Object.defineProperty()来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调来渲染视图。也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变

  • 响应式原理

vue2(浅响应式)

  1. 遍历data,使用Object.defineProperty 拦截所有属性
  2. 当用户操作视图,会触发set拦截器
  3. set先改变当前数据,再通知watch,让watch去通知视图更新。
  4. 视图重绘,再次从get中获取对应的数据。

vue3(深度响应式):

  1. 使用proxy进行代理 : 拦截data任意属性的任意操作,包括属性的读写,添加,删除等等。
  2. 使用Reflect进行反射 : 动态对被代理的对象的相应属性进行特定的操作。
  3. 代理对象(proxy)和反射对象(Reflect)必须相互配合才能实现响应式。
  •  
  • setup

setup接受的参数

setup(props,{attrs,emit,slots})
//细节 : 
//props 参数是对象,父组件传递过来的值
//context.attrs获取父组件标签上的属性 
//context.emit 传递的方法 
//context.slots 插槽 

setup细节问题

  1. setup在beforeCreate生命周期回调前就创建了
  2. setup回调的时候组件还没创建,所以访问不到thist,his是underfined , 调用不了data/computed/methods/
  3. setup属性和data 会合并组件对象的属性,如果重复 setup优先
  4. setup方法和methods方法会合并组件对象的方法
  5. 尽量不要混用 data和setup 和methods
  • ref 和 reactive

使用方式

const m1 = ref(            'abc' )
const m2 = reactive({x: 1, y: {z:             'abc' }})
m1.value +=             '--' //ref
m2.x += 1             //reactive
//细节 : 
// 是Vue3的 composition API中2个最重要的响应式API
// ref用来处理基本类型数据, reactive用来处理对象(递归深度响应式)
// 如果用ref对象/数组, 内部会自动将对象/数组转换为reactive的代理对象
// ref内部: 通过给value属性添加getter/setter来实现对数据的劫持
// reactive内部: 通过使用Proxy来实现对对象内部所有数据的劫持, 并通过Reflect操作对象内部数据
// ref的数据操作: 在js中要.value, 在模板中不需要(内部解析模板时会自动添加.value)
  • watch 和 watchEffect

watch

  1. 默认初始时不执行回调, 但可以通过配置immediate为true, 来指定初始时立即执行第一次
  2. 监视指定的一个或多个响应式数据, 一旦数据变化, 就自动执行监视回调
  3. 通过配置deep为true, 来指定深度监视

watchEffect

 

  1. 不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据
  2. 默认初始时就会执行第一次, 从而可以收集需要监视的数据
  3. 监视数据发生变化时回调

 

  • vue2 3生命周期对比

beforeCreate -> 使用 setup()

created -> 使用 setup()

beforeMount -> onBeforeMount  组件挂载前

mounted -> onMounted 组件挂载后

beforeUpdate -> onBeforeUpdate 响应式更新前

updated -> onUpdated  响应式更新后

beforeDestroy -> onBeforeUnmount  组件卸载前

destroyed -> onUnmounted  组件卸载后

errorCaptured -> onErrorCaptured  捕获后代组件传递的错误时调用

小结 : 2 对比 3 吧create 和 beforeCreate 移出了 并新增加了 setup Api

新增了两个调试钩子函数

onRenderTracked // 页面渲染的时候追踪

onRenderTriggered //重新渲染的时候触发

 

性能优化

  • web 性能优化辅助工具

Lighthouse github.com/googleChrome/lighthouse

测试网站 webpagetest.org

  • 加载优化

1 . 加载优化

2 . 压缩合并

3 . 代码分割 , 可以基于路由或动态加载

4 . 第三方模块放在CDN

5 . 大模块异步加载 , 列如 Echarts , 可以使用 require.ensure , 在加载成功后, 在显示对应图表

6 . 小模块适度合并, 讲一些零散的小模块合并一起加载 , 速度较快 。

7 . 可以使用pefetch预加载 , 在分布场景中非常适合

图片优化

  1. 小图使用雪碧图, iconFont,base64内联
  2. 图片使用懒加载
  3. webp代替其他格式
  4. 图片一定要压缩
  5. 可以使用的img的 srcset,根据不同分辨率显示不同尺寸图片,这样既保证显示效果,又能节省带宽,提高加载速度

css优化

  1. css.写在头部
  2. 避免cSs,表达式
  3. 移除空置的css规则
  4. 避免行内style样式

js优化

  1. js写在 body 底部
  2. s用 defer 放在头部,提前加载时间 ,又不阻塞dom.解析
  3. script标签添加crossorigin,方便错误收集

渲染优化

  1. 尽量减少 reflow 和l repaint 涉及到样式,尺寸,节点增减的操作,都会触发 reflow 和 repaint。
  2. 用变量缓存 dom 样式,不要频繁读取
  3. 通过 DocumentFragment 或 innerHTML 批量操作 dom
  4. dom 隐藏,或复制到内存中,类似 virtual dom,进行修改,完成后再替换回去
  5. 动画元素一定要 absolute , 脱离文档流,不影响其他元素,动画不要用l eft , top 等操作要使用 transform 和 opacity , 同时开启渲染层( will-change 或 translate3d(0,0.0))
  6. 动画尽量用 requestAnimationFrame,不要用定时器
  7. 移动端硬件加速 , 触发GPU 渲染,还是translate3d(0,0,0) 

首屏优化

原则 : 显示快,滚动流畅,懒加载,懒执行,渐进展现

  1. 代码分离,将首屏不需要的代码分离出去
  2. 服务端渲染或预渲染 , 加载完html 直接渲染 , 减少白屏时间
  3. DNS prefetch,使用 dns-prefetch 减少dns查询时间 , PC端域名发散 , 移动端域名收敛
  4. 减少关键路径css,可以将关键的css内联,这样可以减少加载和渲染时间

打包优化

  1. 拆包externals dllPlugin
  2. 提取公共包commonChunkPlugin或 splitChunks
  3. 缩小范围各种 loader配置 include和 exclude,noParse跳过文件
  4. 开启缓存各种loader开启cache
  5. 多线程加速happypack,或 thead-loader6. tree-shaking ES模块分析.移除死代码
  6. Scope Hoisting ES6模块分析.将多个模块合并到一个函数里,减少内存占用.减小体积,提示运行速度

webpack长缓存优化

  1. 拆包 externals dllPlugin
  2. 提取公共包 commonChunkPlugin 或 splitChunks
  3. 缩小范围各种 loader 配置 include 和 exclude , noParse跳过文件
  4. 开启缓存各种 loader 开启 cache
  5. 多线程加速 happypack.或 thead-loader
  6. tree-shaking ES模块分析,移除死代码
  7. Scope Hoisting ES6 模块分析.将多个模块合并到一个函数里,减少内存占用.减小体积,提示运行速度

vue优化

  1. 路由懒加载组件
  2. keep-alive缓存组件 ,保持原显示状态
  3. 列表项添加 key,保证唯一
  4. 列表项绑定事件,使用事件代理(v-for)
  5. v-if 和v-for不要用在一个标签上,它会在每个项上进行v-if判断

seo优化

 

  1. 添加各种meta信息
  2. 预渲染
  3. 服务端渲染

 

页面退出清除定时器 和 事件监听

打包的时候 删除注释代码和打印代码

 

CSS

  • 盒模型

CSS把每个元素视为一个盒子,每个盒子包括分为内容(content)、填充(padding)、边界(margin)、边框(border)四个部分。这种对界面元素的抽象,称为盒模型。盒模型是CSS布局的基本单元。

有两种盒模型:IE盒模型(也称怪异盒模型)(border-box)、W3C标准盒模型(content-box)

IE盒模型和W3C标准盒模型的区别:

W3C标准盒模型:属性width,height只包含内容content,不包含border和padding。

IE盒模型:属性width,height包含content、border和padding,指的是content +padding+border。

  • CSS如何实现垂直居中

行内元素水平居中 : 对子元素设置 text-align: center;

行内元素垂直居中 : 设置子元素行高与高度相同

块级元素垂直居中 : margin : 0 auto; 

            left :             50% ;              /* 先让左边线居中 */
transform: translateX(            -50% ); 

使用Flex

display : flex;
justify- content :             center ; //主轴
//垂直居中

网络通信

UDP 与 TCP 的区别是什么?

udp : UDP 相比 TCP 简单的多,不需要建立连接,不需要验证数据报文,不需要流量控制,只会把想发的数据报文一股脑的丢给对端。

tcp :  TCP 基本是和 UDP 反着来,建立连接断开连接都需要先需要进行握手。在传输数据的过程中,通过各种算法保证数据的可靠性,当然带来的问题就是相比 UDP 来说不那么的高效。
建立连接需要三次握手,断开连接需要四次握手

  • http和https

1、HTTPS  协议需要到 CA (Certificate Authority,证书颁发机构)申请证书,一般免费证书较少,因而需要一定费用。(以前的网易官网是http,而网易邮箱是 https 。)

2、HTTP 是超文本传输协议,信息是明文传输,HTTPS 则是具有安全性的 SSL 加密传输协议。

3、HTTP 和 HTTPS 使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。

4、HTTP 的连接很简单,是无状态的。HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 HTTP 协议安全。(无状态的意思是其数据包的发送、传输和接收都是相互独立的。无连接的意思是指通信双方都不长久的维持对方的任何信息。) 参考自

  • 什么是跨域?为什么会出现跨域?如何解决跨域问题?

1 . 什么是跨域

当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域

2 . 为什么会出现跨域?

出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)
3 . 如何解决跨域问题?

普通跨域请求:只需服务器端设置Access-Control-Allow-Origin

axios : axios.defaults.withCredentials = true

代理跨域 vue中如何解决跨域问题?

在vue开发中实现跨域:在vue项目根目录下找到vue.config.js文件(如果没有该文件则自己创建),在proxy中设置跨域

devServer: {
    proxy: {              //配置跨域
      '/api' : {
        target:             'http://121.121.67.254:8185/' ,  //这里后台的地址模拟的;应该填写你们真实的后台接口
        changOrigin:             true ,              //允许跨域
        pathRewrite: {
          /* 重写路径,当我们在浏览器中看到请求的地址为:http://localhost:8080/api/core/getData/userInfo 时
            实际上访问的地址是:http://121.121.67.254:8185/core/getData/userInfo,因为重写了 /api
           */
          '^/api' :             ''
        }
      },
    }
  }
  • http常见状态码有哪些?

类别 原因短语
1xx Informational(信息性状态码) 接受的请求正在处理
2xx Success(成功状态码) 请求正常处理完毕
3xx Redirection(重定向) 需要进行附加操作以完成请求
4xx Client error(客户端错误) 客户端请求出错,服务器无法处理请求
5xx Server Error(服务器错误) 服务器处理请求出错

2xx (3种)

200 OK:表示从客户端发送给服务器的请求被正常处理并返回;

204 No Content:表示客户端发送给客户端的请求得到了成功处理,但在返回的响应报文中不含实体的主体部分(没有资源可以返回);

206 Patial Content:表示客户端进行了范围请求,并且服务器成功执行了这部分的GET请求,响应报文中包含由Content-Range指定范围的实体内容。

 

3xx (5种)

301 Moved Permanently:永久性重定向,表示请求的资源被分配了新的URL,之后应使用更改的URL;

302 Found:临时性重定向,表示请求的资源被分配了新的URL,希望本次访问使用新的URL;

301与302的区别:前者是永久移动,后者是临时移动(之后可能还会更改URL)

303 See Other:表示请求的资源被分配了新的URL,应使用GET方法定向获取请求的资源;

302与303的区别:后者明确表示客户端应当采用GET方式获取资源

304 Not Modified:表示客户端发送附带条件(是指采用GET方法的请求报文中包含if-Match、If-Modified-Since、If-None-Match、If-Range、If-Unmodified-Since中任一首部)的请求时,服务器端允许访问资源,但是请求为满足条件的情况下返回改状态码;

307 Temporary Redirect:临时重定向,与303有着相同的含义,307会遵照浏览器标准不会从POST变成GET;(不同浏览器可能会出现不同的情况);

 

4xx (4种)

400 Bad Request:表示请求报文中存在语法错误;

401 Unauthorized:未经许可,需要通过HTTP认证;

403 Forbidden:服务器拒绝该次访问(访问权限出现问题)

404 Not Found:表示服务器上无法找到请求的资源,除此之外,也可以在服务器拒绝请求但不想给拒绝原因时使用;

 

5xx (2种)

500 Inter Server Error:表示服务器在执行请求时发生了错误,也有可能是web应用存在的bug或某些临时的错误时;

503 Server Unavailable:表示服务器暂时处于超负载或正在进行停机维护,无法处理请求;

  • get和post区别是什么?

最直观的区别就是GET把参数包含在URL中,POST通过request body传递参数。

安全性不同

1、get安全性非常低。

2、post安全性较高。

传送数据量不同

1、get传送的数据量较小,不能大于2KB。

2、post传送的数据量较大,一般被默认为不受限制。但理论上,IIS4中最大量为80KB,IIS5中为100KB。

获取值不同

1、对于get方式,服务器端用Request.QueryString获取变量的值。

2、对于post方式,服务器端用Request.Form获取提交的数据。

  • 浏览器有哪些缓存?localStorage、sessionStorage、cookie的、session的区别是什么?

 localStorage : LocalStorage可跨浏览器窗口和选项卡间共享。不删除用久存在 限制5MB

 sessionStorage : SessionStorage数据独立于其他选项卡和窗口。关闭窗口的时候就会删除

 cookie : 是针对每个网站的信息,每个网站只能对应一个,其他网站无法访问,这个文件保存在客户端  限制 4k

session :  只有客户端才能访问,程序为该客户添加一个 session 此 session将在用户访问结束后自动消失(如果也是超时)。

浏览器 : 

  • 浏览器渲染页面过程 : 

1 . 解析获取到HTML , 生成DOM树 , 解析CSS , 生成CSSOM树。

2 . 讲DOM树和CSSOM树进行结合 , 生成渲染树 (render tree)。

3 . 根据生成的渲染树 , 进行回流 (Layout) , 得到节点的几何信息(位置 , 大小)。

4 . 重绘 (Painting) : 根据渲染树以及回流得到的几何信息 , 得到节点的绝对像素 (像素 , 背景色 , 外观等)。

5 . Display将像素发送给GPU , 展示在页面上。

  •  什么是回流和重绘 ? 它们的区别是什么 ? 

  • 回流重排 : 如果JavaScript做了修改DOM元素的几何属性(位置、尺寸)等操作,将会重新计算style,并且需要更新布局树,然后执行后面的渲染操作,即从1~9的步骤需要重新执行一遍。这个过程叫“重排”。
  • 重绘 : 如果JavaScript修改了DOM的非几何属性,如修改了元素的背景颜色,不需要更新布局树和重新构建分层树,只需要重新绘制。
  • 回流 : 当节点发生布局的变化比如宽高、位置、display : none、节点内容发生变化、浏览器窗口大小改变等都会触发回流重排和重绘的行为。回流必定引发重绘
  • 重绘 : color、透明度、transform、

在优化这方面 : 在前端开发尽量使用transform代替位移 , 使用visibility透明度代替display : none

  • 说说从输入url到页面展示出来的整个过程。

1. 检查缓存

2. DNS解析

3. 发送HTTP请求

4. 将响应数据提交给渲染进程处理

5. 构建DOM

6. 样式计算

7. 布局

8. 分层

9. 绘制

10. 分块

11. 栅格化

12. 合成

未分类 : 

  • W3C标准及规范 : 

概念:W3C标准

中文名:万维网联盟,外文名:World Wide Web Consortium

万维网联盟标准不是某一个标准,而是一些列标准的集合。网页主要有三部分组成:结构(Structure)、表现(Presentation)、行为(Behavior)。

标注规范:

1、 需要声明(DOCTYPE)

DOCTYPE(document type)文档类型的简写,用来说明你用的XHTML或者HTML是什么版本。其中DTD叫文档类型定义,里面包含了文档的规则,浏览器就根据你定义的DTD来解释你页面的标识,并展现出来。要建立符合标准的网页,DOCTYPE声明是必不可少的关键组成部分;除非你的XHTML确定了一个正确的DOCTYPE,否则你的标识和css都不会生效。 有过度的(Transitional)、严格的(strict)、框架的(frameset)。

2、需要定义语言编码

注:如果忘记了定义语言编码,可能会出现页面乱码现象。

3、JavaScript定义

Js必须要用

4、CSS定义

CSS必须要用

5、使用注释

正确的应用等号或者空格替换内部的虚线。

6、所有标签的元素和属性名字都必须使用小写

与HTML不一样,XHTML对大小写是敏感的,

7、所有属性值必须用引号括起来("" ‘’)双引号或单引号

8、把所有特殊符号用编码表示

空格为  、小于号(<)<、大于号(>)>、和号(&)&等。

9、所有属性必须有属性值

XHTML规定所有属性都必须有个值,没有值就是重复本身。

10、所有的标记都必须有相应的结束标记

双标记:

单标记:

11、所有的标记都必须合理嵌套

必须修改为:

12、图片添加有意义的alt属性

图片加载失败时可以用alt属性表明图片内容。同理添加文字链接的title属性,帮助显示不完整的内容显示完整。

 结论:
1、标签规范可以提高搜索引擎对页面的抓取效率,对SEO(搜索引擎优化)很有帮助。

2、尽量使用外链css样式表和js脚本。是结构、表现和行为分为三块,符合规范。同时提高页面渲染速度,提高用户的体验。

3、样式尽量少用行间样式表,使结构与表现分离,标签的id和class等属性命名要做到见文知义,标签越少,加载越快,用户体验提高,代码维护简单,便于改版

  • 盒子模型 : 

box-sizing:content-box; //w3c
box-sizing:border-box; //IE
box-sizing:inherit; //继承

W3C盒模型
兼容性:大多数浏览器,如Firefox、IE6及以上版本都采用了W3C规范,符合CSS规范的盒子模型的总宽度。
css样式: box-sizing:content-box;(主流浏览器默认此样式)
盒子的宽高=盒子内容的宽高+边框+内边距
设置padding和border会撑开盒子,使盒子比原本的宽高更大。
由 CSS2.1 规定的宽度高度行为。
宽度和高度分别应用到元素的内容框。
在宽度和高度之外绘制元素的内边距和边框。

IE盒模型
兼容性:ie6以下
css样式:box-sizing:border-box;
盒子的宽高=盒子内容的宽高+边框+内边距
包含了padding和border,设置两者不会撑开盒子,盒子大小不会改变。
为元素设定的宽度和高度决定了元素的边框盒。
就是说,为元素指定的任何内边距和边框都将在已设定的宽度和高度内进行绘制。
通过从已设定的宽度和高度分别减去边框和内边距才能得到内容的宽度和高度。

box-sizing:inherit;(规定应从父元素继承 box-sizing 属性的值。)

总结:
主流浏览器默认采用W3C模型,如果要使用ie盒模型则需在该盒子样式添加(box-sizing:border-box;)
ie6以下的浏览器默认采用ie模型,由于浏览器兼容性无法改变盒子模型(即不支持box-sizing属性)
相同点:盒子的宽高=盒子内容的宽高+边框+内边距
不同点:ie盒模型的width/height指的是盒子宽高。
W3C盒模型的width/height指的是盒子内容宽高。

  • Vite和Webpack区别

Vite : Vite是尤雨溪在开发vue3的时候开发的一个web开发构建工具。

  • 极速的服务启动: 使用原生 ESM 文件,无需打包!
  • 轻量快速的热重载:无论应用程序大小如何,都始终极快的模块热重载(HMR)
  • 丰富的功能: 对 TypeScript、JSX、CSS 等支持开箱即用。

  Vite使用简单,只需执行初始化命令,就可以可得到一个预设好的开发环境,开箱即可获得一堆功能,包括:css预处理、html预处理、异步加载、分包、压缩、HMR等。使用复杂度介于Paracel和Webpack的中间,只是暴露了极少数的配置项和plugin接口,既不会像Paracel一样配置不灵活,又不会像Webpack一样需要了解庞大的loader、plugin生态。灵活适中,复杂度适中

2.相比Webpack,Vite有什么优势?

a. vite开发服务器启动速度比webpack快。

  • webpack会先打包,然后启动开发服务器,请求服务器时直接给予打包结果
  • 由于vite启动的时候不需要打包 ,也就不需要分析模块依赖,编译,使用原生ES模块导入方式,所以启动速度非常快;
  • vite采用的是按需动态编译的模式,当浏览器请求需要的模块时,再对模块进行编译,这种处理模式极大的缩短了编译时间,当项目越大,文件越多,vite的开发时优势越明显

b. vite的热更新比webpack快。vite在HRM方面(HMR是指当你对代码进行修改并保存后,webpack对代码重新打包,并将新的模块发送到浏览器端,浏览器通过替换旧的模块,在不刷新浏览器的前提下,就能够对应用进行更新),当改动了一个模块后,vite仅需让浏览器重新请求该模块即可,不像webpack那样需要把该模块的相关依赖模块全部编译一次,效率更高。

c.由于现代浏览器本身就支持ES Module,会自动向依赖的Module发出请求。vite充分利用了这点,将开发环境下的模块文件,就作为浏览器要执行的文件,而不是像webpack那样进行打包合并

d. vite 使用esbuild(Go编写)预构建依赖,比webpack的nodejs,快10-100倍  

3.从Webpack方面能不能做一些编译构建方面的优化

a. 多入口的情况下,使用CommonsChunkPlugin来提取公共代码

b. 通过externals配置来提取常用库

c. 使用Happypack实现多线程加速编译

d. 使用Tree-shaking和Scope Hoisting来剔除多余代码

e. 使用 webpack-uglify-parallel来提升uglifyPlugin的压缩速度。原理上 webpack-uglify-parallel 采⽤了多核并⾏压缩来提升压缩速度

微信小程序 : 

  • 微信小程序登录流程 : 

  1. 通过wx.login()获取code。
  2. 将这个code发送给后端,后端会返回一个token,这个token将作为你身份的唯一标识。
  3. 将token通过wx.setStorageSync()保存在本地存储。
  4. 用户下次进入⻚面时,会先通过wx.getStorageSync() 方法判断token是否有值,如果有值,则可以请求其它数据,如果没有值,则进行登录操作。
  1. 前端调用uni.login/wx.login调用微信接口,获取code,code相当于临时身份证
  2. 前端调公司后台获取openid的接口,获取openid
  3. 前端调公司后台预支付接口,传递openid、商品id、商品单价、商品数量,获取那5个参数。【时间戳timeStamp,随机字符串nonceStr,预支付id package,签名算法signType,签名paySign】
  4. 前端调用uni/wx.requestPayment调用微信支付方法,传递5个参数,获取支付结果(成功或失败)

posted @ 2022-09-13 23:30  啊賢  阅读(163)  评论(0编辑  收藏  举报