分类涵盖所有面试官问题

101 阅读44分钟

1.css3与H5

1.H5的新特性有哪些

  1. 拖拽释放
  2. 自定义data-id
  3. 语义化更好的标签(header,nav,footer,aside, article, section)
  4. 视频音频标签,添加autoplay自动播放
  5. 新增画布canvas
  6. 地理api
  7. 新增本地离线缓存localStorage,sessionStorage
  8. 表单控件 calendar , date , time , email , url , search , tel , file , number
  9. 新的技术 webworker, websocket , Geolocation

2、C3新特性

  • 边框圆角
  • box-sizing
  • rgba颜色
  • 渐变
  • 过渡
  • 动画
  • 2D转换
  • 3D转换
  • flex布局
  • 字体图标等

3、三种存储方式对比

  • localStorage:长期存储于浏览器,可存储5M,所有同源窗口共享数据
  • sessionStorage:浏览器关闭数据就被销毁,也可存储5M,只在同一浏览器窗口共享数据
  • cookie:有有效期,可存储4kB,所有同源窗口共享数据,数据会随着请求存储到请求报文中

4.BFC

BFC (Block Formatting Context),即块级格式化上下文,它是页面中的一块渲染区域,并且有一套

属于自己的渲染规则:

  1. 内部的盒子会在垂直方向上一个接一个的放置
  2. 对于同一个BFC的俩个相邻的盒子的margin会发生重叠,与方向无关。
  3. 每个元素的左外边距与包含块的左边界相接触(从左到右),即使浮动元素也是如此
  4. BFC的区域不会与float的元素区域重叠
  5. 计算BFC的高度时,浮动子元素也参与计算
  6. BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之亦然
  7. BFC 目的是形成一个相对于外界完全独立的空间,让内部的子元素不会影响到外部的元素
  8. 触发BFC的条件包含不限于: 根元素,即HTML元素 浮动元素:float值为left、right overflow值不为 visible,为 auto、scroll、hidden display的值为inline-block、inltable-cell、table-caption、table、inline-table、flex、inline-flex、grid、inline-grid position的值为absolute或fixed

5.em/px/rem/vh/vw的理解

  1. **px:**绝对单位,页面按精确像素展示

  2. em:相对单位,基准点为父节点字体的大小,如果自身定义了 font-size 按自身来计算,整个页面内

    1em 不是一个固定的值

  3. rem:相对单位,可理解为 root em , 相对根节点 html 的字体大小来计算

  4. vhvw:主要用于页面视口大小布局,页面的百分之一,在页面布局上更加方便简单

6、盒子/图片水平垂直居中

  • flex布局
  • 子绝父相定位,左、上距离父元素百分之五十,利用transform移动-50% (或者、margin-top: -50px;margin-left: -50px;)计算父盒子与子盒子的空间距离
  • 子绝父相定位,上下左右都为0,margin为auto
  • display设置成table-cell,text-align设置成center,verticle-align设置为middle
  • grid网格布局

7.flex:1

  1. flex-basis: 90px; 理解为width为父容器的宽度。
  2. flex-grow: 1; 决定容器剩余空间的分配方式
  3. flex-shrink: 1; 决定容器超出空间的分配方式

8.响应式设计实现方式

  • 媒体查询
  • 百分比
  • vw/vh
  • rem

9.清除浮动的方式

  1. 使用 CSS 中的 clear:both;(放一个空的标签,并设置上述 css,注意该标签必须是块
  2. 对于问题 1,添加如下样式,给父元素添加 clearfix 样式
  3. 给父级元素设置双伪元素
  4. 给父级元素设置 overflow:hidden;或 overflow:auto;本质是构建一个 BFC

2.js

1.数组常见方法

  • push()
  • unshift()
  • splice()
  • concat() 不会对原数组产生影响

  • pop()
  • shift()
  • splice()
  • slice() 不会对原数组产生影响

  • splice()

  • indexOf()
  • includes()
  • find()

排序

  • reverse() 反转
  • sort() 排序

转换

  • join()

迭代(都不改变原数组)

  • some()
  • every()
  • forEach()
  • filter()
  • map()

2.深拷贝

1.常见的深拷贝方式
  • _.cloneDeep() -------lodash里面封装好的方法
  • jQuery.extend()
  • JSON.stringify() -------但是这种方式存在弊端,会忽略 undefined 、 symbol 和 函数

const obj2=JSON.parse(JSON.stringify(obj1));

手写循环递归

原生的 WeakMap 持有的是每个键对象的“弱引用”,这意味着在没有其他引用存在时垃圾回收能正确进行。原生 WeakMap 的结构是特殊且有效的,其用于映射的 key 只有在其没有被回收时才是有效的。

正由于这样的弱引用,WeakMap 的 key 是不可枚举的 (没有方法能给出所有的 key)。如果key 是可枚举的话,其列表将会受垃圾回收机制的影响,从而得到不确定的结果。因此,如果你想要这种类型对象的 key 值的列表,你应该使用 Map

基本上,如果你要往对象上添加数据,又不想干扰垃圾回收机制,就可以使用 WeakMap。

function deepClone(obj, hash = new weakMap()) {
    if (obj === null) return obj  // 如果是null或者undefined就不需要进行拷贝操作
    if (obj instanceof Date) return new Date(obj) // 时间格式化数据
    if (obj instanceof RegExp) return new RegExp(obj) // 正则表达式
    // 可能是对象或者普通的值,如果是函数的话是不需要深拷贝
    if(typeof obj !== "object") return obj
    // 是对象的话就需要进行深拷贝
    if (hash.get(obj)) return hash.get(obj) //判断该对象是否已经被拷贝了,如果已经被拷贝则取出该对象并返回
    let cloneObj = new obj.constructor()
    // 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
    hash.set(obj, cloneObj)
    for (let key in obj){
        if (obj.hasOwnProperty(key)) {
            // 实现一个递归拷贝
            cloneObj[key] = deepClone(obj[key],hash)
        }
    }
    return cloneObj
    
}

3.作用域

什么是作用域

  • 简单理解就是变量和函数能被访问(或生效)的范围区域

作用域分为几种

  • 全局作用域
  • 函数作用域(局部作用域)
  • 块级作用域 ---ES6引入let 跟const 导致出现块级作用域

什么是作用域链

  • 在JavaScript中使用一个变量的时候,首先JavaScript会尝试在当前作用域下去寻找,如果没找到,会再到他上一级的作用域中去找,以此类推,一层一层寻找变量的过程俗称作用域链

4、原型和原型链

  • 原型:所有引用类型都有一个--proto--属性,所有的函数都有一个prototype属性,所有引用类型的--proto--属性指向它构造函数的prototype属性
  • 原型链:当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,就会去它的--proto--属性上查找,即他的构造函数的prototype,如果还没找到就会继续找他的构造函数的prototype属性,这样一层一层向上查找就会形成一个链式结构,即原型链

5、this指向问题

  • 函数形式调用指向window
  • 方法形式调用指向调用对象
  • 构造函数调用指向构造函数的实例对象
  • 箭头函数看外层是否有函数,如果有外层函数的this就是箭头函数的,如果没有就是window

6.说说事件循环机制(满分回答)

  1. JavaScript代码执行过程中,除了依靠函数调用栈来搞定函数执行顺序外,还需要任务队列来搞定其他一些代码的执行,整个执行过程我们称为事件循环过程。一个线程中,事件循环是唯一的,但是任务队列可以有很多个。任务队列又分为宏任务与微任务,在最新标准中又被称为task和objs。
  2. 宏任务大概包括:Script(整体代码),settimeout,setinterval ,setimmediate, I/O, UI render
  3. 微任务大概包括: Promise, process.nextTick, MutationObserver(html5新特性), Async/Await(实际就是promise)
  4. 所以先执行宏任务,然后执行该宏任务下产生的微任务,如果在其微任务的执行过程中又产生了微任务,则继续执行微任务,微任务执行完毕后,再回到宏任务中进行下一轮循环。
  5. 同步任务与异步任务执行,在主线程上先执行第一个同步任务,只有同步任务执行完毕才会执行后面的代码,执行过程中遇到异步任务时,将其放入任务队列,继续接着往下执行别的代码,执行完同步代码,去看任务队列里面有没有要执行的异步代码,有的话取出来执行,
  6. 宏任务与微任务的循环执行,同步任务与异步任务的循环执行构成了事件循环机制

7.ES6有哪些新增

  1. 新增了声明命令 letconst (说明let和const的使用与优点)

  2. 新增了模板字符串,简化了变量的拼接

  3. 新增了一些函数的扩展

    • 允许函数有默认参数
    • 出现了箭头函数,扩展了this的指向问题
  4. 新增了对象的扩展

    • 属性的简写,允许直接在对象中写变量
    • 增加object.keys() 获取对象的属性名或者方法名,返回一个数组
    • 增加Object.value() 获取对象的属性值
    • 增加Object.assign() 合并多个对象的属性方法在源对象上
  5. 新增 for... of 循环,多用于数组的循环

  6. 新增import 和 export

    ES6支持将js代码模块化,通过模块的导入导出大大增加了代码的复用性,减少了代码的臃肿

  7. 新增了Promise对象
    • Promise是异步编程的一种解决方案,使异步代码跟同步代码一样同步执行,避免了层层嵌套,解决了回调地狱

    • Promise构造函数中包含一个参数和一个带有resolve(成功)reject(失败)两个参数的回调,在回调中执行一些异步操作,如果执行成功则调用resolve,否则调用reject。对于已经实例化过的Promise对象可以调用Prmise.then()方法,传递resolve,reject方法作为回调。then()方法接受两个参数:onResolve和OnReject,分别代表当Promise对象成功和失败

    • Promise有三种状态:Fulfiled---成功的状态,Rejected----失败的状态,Pending----既不是成功也不是失败,理解为Promise的初始化状态

  8. 新增了解构赋值

    • 数组的解构赋值
    • 对象的解构赋值
  9. 新增Set数据结构

    • Size() 数据的长度
    • Add() 添加某个值,返回Set结构本身
    • Delete() 删除某个值,返回一个布尔值,表示是否删除成功
    • Has() 查找某条数据,返回一个布尔值
    • Clear() 清除所有成员,没有返回值
  10. class

    class 类的继承 ES6 中不再像 ES5 一样使用原型链实现继承,而是引入 Class 这个概念

8、 call、bind、apply ( 函数上下文调用模式 )

  • 都是用来更改函数this指向的
  • 这三者的第一个参数都是this的新指向
  • 第二个之后的参数call和bind是直接传入函数值,而apply第二个参数是数组或伪数组
  • bind方法和其他两种方法的不同点在于,bind不会执行该函数,而是返回一个新的函数,其他方法是直接执行该函数

9.Cookie跟Session区别

  1. 存储位置不同,Cookie数据保存在客户端里面,Session数据存储在服务器中,服务器存储相对于客户端会更为安全
  2. 存储的数据类型不同,两者都是key-value的数据结构,但是Cookie的value只能是字符串类型,Session的value可以是object的类型
  3. 存储的数据大小不同,Cookie受到浏览器的限制一般在4k左右,Session理论上来说只受内存的限制
  4. 生命周期的区别,Cookie一般是过期时间内生效,过期清除,Session取决于服务端的设定,
  5. 两者之间的联系,SessionCookie的一种应用

10、防抖和节流

  • 防抖是 n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
  • 节流 n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时,一般搜索框会做节流事件,减少请求次数,优化性能

11.一个页面从输入URL 到页面显示加载完成都经历了什么?

  • 浏览器查询域名对应的IP地址
  • 浏览器向web服务器发送一个http的请求
  • 服务器301重定向
  • 浏览器跟踪重定向地址,请求另一个带www的网址
  • 服务器处理请求
  • 服务器返回一个http的响应
  • 浏览器进DOM树构建
  • 浏览器发送请求获取嵌在HTML中的资源
  • 浏览器显示完成页面
  • 浏览器发送异步请求

12、闭包

,本质上是在函数内部和函数外部搭建一座桥梁,让子函数可以访问父函数中的局部变量;

  • 好处就是可以保护变量不受外界污染
  • 缺点就是常驻内存,消耗性能,所以开发中很少使用闭包

12、三次握手和四次挥手

  • 三次挥手
    • 发送端首先发送一个带有SYN标志地数据包给接收方
    • 接收方接收后,回传一个带有SYN/ACK标志的数据包传递确认信息,表示我收到了
    • 发送方再回传一个带有ACK标志的数据包,代表我知道了,表示‘握手’结束
  • 四次挥手
    • Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态
    • Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态
    • Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态
    • Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手

13、js的继承

  • 原型链继承:让新实例的原型等于父实例
    • 可继承:实例的构造函数的属性,父类构造函数属性,以及父类原型的属性
    • 不可继承:父类实例的属性
  • 借用构造函数继承:使用 apply()和 call()方法将父类构造函数引入子类函数
    • 可继承:父类构造函数的属性
    • 不可继承父类原型的属性
  • 组合继承:将原型链和借用构造函数的技术组合在一块,从而发挥两者之长的一种继承模式
  • 原型式继承:借助原型可以基于已有的对象创建新对象,类似复制了一个对象
  • 寄生式继承:就是给原型式继承外面套个壳子,没用到原型
  • 寄生组合式继承:通过借用函数来继承属性,通过原型链的混成形式来继承方法(常用)

14.尾递归

尾递归,即在函数尾位置调用自身(斐波那契数列)

在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储,递归次数过多容易造成栈溢

这时候,我们就可以使用尾递归,即一个函数中所有递归形式的调用都出现在函数的末尾,对于尾递归

来说,由于只存在一个调用记录,所以永远不会发生"栈溢出"错误

15.ES6 一共有 5 种方法可以遍历对象的属性

  1. for...in:循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)
  2. Object.keys(obj):返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol 属性)的键名
  3. Object.getOwnPropertyNames(obj):回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名
  4. Object.getOwnPropertySymbols(obj):返回一个数组,包含对象自身的所有 Symbol 属性的键名
  5. Reflect.ownKeys(obj):返回一个数组,包含对象自身的(不含继承的)所有键名,不管键名是Symbol 或字符串,也不管是否可枚举

16.常见的web攻击方式

  1. xss 跨站脚本攻击
  2. CSRF 跨站请求伪造
  3. SQL 注入攻击

17.箭头函数的区别

  1. this始终指向window,箭头函数中的this,永远指向外层作用域中最接近自己的普通函数的this
  2. bindcallapply等方法无法改变箭头函数的this指向。
  3. 箭头函数不能当做构造函数来使用
  4. 箭头函数没有arguments
  5. 箭头函数没有prototype属性
  6. 箭头函数不能当做Generator函数,不能使用yield关键字
  7. 箭头函数不能被new关键字调用,不具有new.target

18.对promise的理解

  • Promise是异步编程的一种解决方案,使异步代码跟同步代码一样同步执行,避免了层层嵌套,解决了回调地狱
  • Promise构造函数中包含一个参数(回调函数),一个带有resolve(成功)reject(失败)两个参数的回调,在回调中执行一些异步操作,如果执行成功则调用resolve,否则调用reject。对于已经实例化过的Promise对象可以调用Prmise.then()方法,传递resolve,reject方法作为回调。then()方法接受两个参数:onResolve和OnReject的回调,分别代表当Promise对象成功和失败
  • Promise有三种状态:Fulfiled---成功的状态,Rejected----失败的状态,Pending----既不是成功也不是失败,理解为Promise的初始化状态
  • new出来的promise实例有三个方法:参数都是一个数组
    • all:如果数组里面所有promise都resolve就进入then方法,有一个reject就进入catch
    • allSettled:在then方法中返回所有的promise数组结果,不管是成功的还是失败的
    • race:数组中的promise哪个先返回结果就使用哪个的结果
  • 如何捕获promise的错误
    1. 在catch中捕获
    2. 抛出一个错误 throw new Error('terminate now');
    3. async/await 搭配 try catch 进行捕获异步错误
    4. 对于同步错误,可以定义window.onerror进行兜底处理,或者使用window.addEventListener('error', errHandler)来定义兜底函数。
    5. 对于Promise异常,则可以同步使用window.onunhandledrejection或者window.addEventListener('unhandledrejection', errHandler)来定义兜底函数。
    6. then的第二个参数和catch方法都存在的情况下,只有then的第二个参数能捕获到,如果then的第二个参数不存在,则catch方法会被捕获到。

19.ES6的map跟set区别

区别

应用场景 Set 用于数据重组,Map 用于数据储存

Set:

成员不能重复

只有键值没有键名,类似数组

可以遍历,方法有 add, delete,has

  • Size() 数据的长度
  • Add() 添加某个值,返回Set结构本身
  • Delete() 删除某个值,返回一个布尔值,表示是否删除成功
  • Has() 查找某条数据,返回一个布尔值
  • Clear() 清除所有成员,没有返回值

Map:

本质上是健值对的集合,类似集合

可以遍历,可以跟各种数据格式转换

20.回流

布局引擎根据各种样式来计算每个盒子在页面上的大小与位置, 当盒子发生大小位置的变化,导致页面布局发生改变一定会触发回流,比如dom节点的删除与增加,浏览器的页面视口发生变化化,盒子的尺寸,内容等发生变化的都会一定几率触发回流。回流一定重绘

重绘

确定好每个盒子的大小与位置后,根据每个盒子的其他属性进行绘制,当盒子的某些属性发生改变,比如颜色,阴影的修改,这些不会改变盒子大小与位置的改变会触发重绘

浏览器如何优化回流与重绘

由于每次重排都会造成浏览器的计算属性浪费,因此,浏览器会通过队列化修改并批量执行来优化重排过程。浏览器会将修改操作放入到队列里,直到过了一段时间或者操作达到了一个阈值,才清空队列。

当你获取布局信息的操作的时候,会强制队列刷新,包括前面讲到的 offsetTop 等方法都会返回最新

的数据

因此浏览器不得不清空队列,触发回流重绘来返回正确的值

3.Ajax

1.Ajax原理

  • 通过XMLHTTPRequest对象来向服务器发送异步请求,从服务器获取数据,然后用JavaScript来操作DOM更新页面。

2.Ajax底层封装原理/Ajax实现流程

  • 先封装一个XMLHTTPRequest函数,也就是用来发送异步请求的函数
  • 创建一个新http请求,设置请求方式,URL路径,以及验证信息
  • 设置响应http请求状态变化的函数
  • 发送http请求
  • 获取异步调用返回的数据
  • JavaScript操作DOM刷新页面

3.GET和POST区别

  • GET在浏览器回退时是无害的,而POST会再次提交请求。
  • GET产生的URL地址可以被Bookmark,而POST不可以。
  • GET请求会被浏览器主动cache,而POST不会,除非手动设置。
  • GET请求只能进行url编码,而POST支持多种编码方式。
  • GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
  • GET请求在URL中传送的参数是有长度限制的,而POST没有。
  • 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
  • GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
  • GET参数通过URL传递,POST放在Request body中

4、图片懒加载

  • 设置图片的 data-set 属性值为其图片路径,由于不是 src,所以不会发送请求
  • 监听图片元素即将进入可视窗口时,将data-set属性中的地址存储到src属性中

5、常见的 HTTP 状态码

  • 2xx请求成功
  • 3xx重定向
  • 4xx哭护短错误,一般是语法错误或者请求无法实现 --- 403 禁止访问 --- 404 找不到地址的匹配页面
  • 5xx服务器端错误

6.手写一个Ajax请求

// 创建一个xhr 请求
var xhr = new XMLHTTPRequest()
// 调用open函数,第一个参数为请求方式GET/POST,第二个参数请求的地址,第三个参数true表示异步请求,false表示同步请求,默认true
xhr.open('GET','http://www.baidu.com/html',true)
// 调用send函数发送请求
xhr.send()
// 通过onreadystatechange来判断请求是否成功,是否返回
xhr.onreadystatechange = function(){
    //当readystate=4代表请求回来了
    if(xhr.readystate === 4){
        // 判断状态码
        if(xhr.status>=200 && xhr.status<300){
            console.log(xhr.respoonse)
        }
    }
}

7.跨域

  • 定义:浏览器执行脚本时会检查是否同源,只有同源才会执行,不同源即为跨域
  • 这里的同源是指访问的协议、域名、端口都相同
  • 解决方法:
    • jsonp:利用script标签不受同源策略的限制,但是只能是get请求,且有安全漏洞
    • CORS跨域资源共享:通过服务器设置响应头实现跨域
    • 代理:基于webpack中的webpack.config.js文件中在devServer对象中配置通过proxy
    • 可通过服务端实现代理请求转发 express 框架为例
    • 通过配置 nginx 实现代理

8.websocket

WebSocket,是一种网络传输协议,位于 OSI 模型的应用层。可在单个 TCP 连接上进行全双工通信,能更好的节省服务器资源和带宽并达到实时通迅

客户端和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输

而在 websocket 出现之前,开发实时 web 应用的方式为轮询

与HTTP不同的是,Websocket需要先创建连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息。而HTTP请求可能需要在每个请求都携带状态信息(如身份认证等)。

就目前现有的几种技术而言,主要有以下几类:
  • 1)客户端轮询:传统意义上的短轮询(Short Polling);
  • 2)服务器端轮询:长轮询(Long Polling);
  • 3)单向服务器推送:Server-Sent Events(SSE);
  • 4)全双工通信:WebSocket。

简单来说,SSE就是:

  • 1)SSE 使用 HTTP 协议,现有的服务器软件都支持。WebSocket 是一个独立协议。
  • 2)SSE 属于轻量级,使用简单;WebSocket 协议相对复杂。
  • 3)SSE 默认支持断线重连,WebSocket 需要自己实现。
  • 4)SSE 一般只用来传送文本,二进制数据需要编码后传送,WebSocket 默认支持传送二进制数据。
  • 5)SSE 支持自定义发送的消息类型。

它们的不同点:

  • 1)HTTP的协议标识符是http,WebSocket的是ws;
  • 2)HTTP请求只能由客户端发起,服务器无法主动向客户端推送消息,而WebSocket可以;
  • 3)HTTP请求有同源限制,不同源之间通信需要跨域,而WebSocket没有同源限制。
技术特征

WebSocket技术特征总结下就是:

  • 1)可双向通信,设计的目的主要是为了减少传统轮询时http连接数量的开销;
  • 2)建立在TCP协议之上,握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器;
  • 3)与HTTP兼容性良好,同样可以使用80和443端口;
  • 4)没有同源限制,客户端可以与任意服务器通信;
  • 5)可以发送文本,也可以发送二进制数据;
  • 6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL.

4.vue

1.vue实现双向绑定的流程

  • new vue( ) 首先执行初始化,对data执行响应化处理,observer开始监听所有数据的属性,这步发生在(observer中)
  • 同时compile对模板进行编译,找到动态绑定的数据,在data中获取数据并且初始化视图,这步在(compile中)
  • 同时定义一个更新函数一个watcher函数,当数据发生变化时,watcher函数调用更新函数处理
  • data中的某个key,可能在页面上使用多次,所以每个key都需要一个管家Dep来管理所有的watcher
  • 当数据发生变化时,首先找到其对应的Dep,Dep通知所有的watcher执行更新函数

2.vue的响应式原理

原理

vue的响应式原理主要是通过Es5中的Object.defineProperty来进行数据的劫持,然后利用get和set来进行获取和设置,在data中所有的属性都被添加到get跟set中,当读取data里面的数据时自动调用get方法,当修改data中的数据时,自动调用set方法,检测到数据发生改变,自动触发watcher函数,watcher函数自动触发重新reader当前组件,生成新的虚拟DOM,vue框架会遍历新DOM数和旧DOM树之间的区别,并记录下来,最后加载操作,将记录下来不同的点,局部修改到真实DOM数上

3.vue的响应式原理深度理解

数据发生改变自动触发watcher函数,watcher函数通过依赖收集在管家Deps中,通过对Deps数组的去重,确保不会反复reader组件,提高性能

4.vue2和vue3的区别:

  • 重新构建了响应式,使用 proxy 替换了Object.defineProperty
  • 使用reactive就可以直接监听数组和对象类型的变化
  • 采用了组合式API,而vue2是选项式API
  • 重构了虚拟DOM
  • 直接实现对象属性的增加和删除
  • 模板编译时会把一些静态的节点转换为常量
  • 监听的目标为对象本身,不需要像vue2采用Object.definePriperty遍历每一个属性,有一定的性能优化

5.vue2.0 与 vue3.0响应式区别

  1. Object.defineProperty
    1. 用于监听对象数据的变化
    2. 无法监听数组的变化(下标,长度),其他七种改变数组的方法可以监听到
    3. 只能劫持对象的自身属性,动态添加的劫持不到
  2. Proxy
    1. Proxy返回的是一个新对象,可以通过操作返回的新对象达到目标
    2. 可以监听到数组的变化,也可以监听带动态添加的数据

6.关于 cookie 、 sessionStorage 、 localStorage 三者的区别主要如下:

  • 存储大小: cookie 数据大小不能超过 4k , sessionStorage 和 localStorage 虽然也有存储大小的限制,但比 cookie 大得多,可以达到5M或更大

  • 有效时间: localStorage 存储持久数据,浏览器关闭后数据不丢失除非主动删除数据;sessionStorage 数据在当前浏览器窗口关闭后自动删除; cookie 设置的 cookie 过期时间之前

一直有效,即使窗口或浏览器关闭

  • 数据与服务器之间的交互方式, cookie 的数据会自动的传递到服务器,服务器端也可以写cookie 到客户端; sessionStorage 和 localStorage 不会自动把数据发给服务器,仅在本地保存
应用场景
  • 标记用户与跟踪用户行为的情况,推荐使用 cookie

  • 适合长期保存在本地的数据(令牌),推荐使用 localStorage

  • 敏感账号一次性登录,推荐使用 sessionStorage

  • 存储大量数据的情况、在线文档(富文本编辑器)保存编辑历史的情况,推荐使用 indexedDB

7.你的项目中是如何实现登录功能的?

通过下面五方面回答

  • token的获取
  • token的存储
  • token的持久化
  • token的使用
  • token的时效

具体实现步骤

  • 首先我们调用登录的api接口,把用户填的表单信息通过校验后提交给后端
  • 后端处理用户的登录信息,校验通过后,返回一个token字符串,里面记录的用户的基本信息,由于token字符串是base64格式的,很容易逆向还原泄露用户信息,所以token中不会携带敏感的用户信息
  • 在项目中可能其他请求会要求携带token,所以为了方便使用和获取token,我们会把token存储在Vuex中。
  • 由于vuex的数据是存储在内存中的,页面刷新就会消失,所以我们通过js-cookie这个模块来存储token,实现token的持久化保存在本地。
  • 以后用户在调用接口api时,我们通过axios的请求拦截器来判断该请求是否需要携带token,如果需要我们通过config.headers对象,来为请求添加Authorization的请求头,数据就是来着vuex中的token,这样就成功的完成了这次请求。
  • 如果token过期,会触发axios中的响应拦截器,通过err.response.status状态来判断,如果状态为401,那就是token过期,需要清除vuex中的token,通过路由跳转到首页,退出重新登录存储token

8.如何在项目中使用axios?

三个方面:

  • axios的作用
  • 封装axios
  • 使用axios
  1. axios在项目中主要用来发起Ajax请求的,实现前后端数据交互的
  2. 为了方便使用与维护,我们会在项目中对axios进行封装,在src/utils/request.js文件中调用axios.create()函数创建axios的实例对象,在函数中配置baseURL为请求根路径,针对token配置请求拦截器(判断哪些接口需要携带token进行请求,自动添加token)与响应拦截器(token过期,或者判断请求状态来是页面跳转到登录页面并且清除vuex中的token),如果要中断请求可以手动抛出一个错误
  3. 对于一些功能性的api,统一在src/api文件下进行情求封装接口,提高了api接口的复用性

9.什么是前后端分离项目?

  1. 所有的程序都是以数据为基础,所有的操作都是围绕数据进行增删改查

  2. 前后端分离就是把数据操作与显示分离出来。前端专注做数据的显示,通过文字,图片,图标等方式让数据形象直观的显示出来。后端专注做数据的操作。前端把数据发给后端,后端去修改数据

  3. 后端来进行对数据库的链接,并对数据进行操作

  4. 后端提供接口给前端调用,来触发后端对数据的修改

10.请说一下封装Vue组件的过程?

  1. 在项目中为了为了提高项目的开发效率会选择去做组件的封装,做成相对独立的模块,提高项目的开发效率,提高代码的复用性

  2. 分析需求

    根据业务需求,把页面中可以复用的结构,样式和功能抽离成单独的文件,方便其他模块的复用

  3. 具体步骤

    1. 使用Vue.compeonent方法注册组件,子组件需要数据可以在props里面接受父组件传来的值,子组件修改后的数据想传给父组件可以通过$emit通知父组件去修改数据
    2. 如果需要给组件传入模板,可以定义slot插槽实现
    3. 如果需要父组件主动调用子组件的方法,可以在methods里面开放方法
  4. Vue.cli中如何使用自定义组件

  • 在components文件夹下定义组件文件(indexPage.Vue),script中使用 export default {}

  • 在需要使用的页面导入 import 组件,在component中注册组件,然后在页面上使用组件标签

11.如何使用自定义指令

  1. 全局定义指令,在Vue.directive方法里有两个参数,一个是指令名称,一个是函数。组件内定义指令用directives
  2. 有三个钩子函数
    • bind(绑定事件触发)
    • inserted(节点插入时触发)
    • updata(组件内相关更新时触发)
  3. 钩子函数的参数(el,binding)

12.Vue中如何定义过滤器?

全局过滤方法:Vue.filter()注册一个自定义过滤器,接受两个参数,第一个参数为过滤器名称,第二个参数为过滤器函数,过滤器函数以传入的值为参数,返回过滤后的值

13.简述vuex中数据传递流程

在页面上通过store.dispath来调用action中的属性方法,action中又通过store.commit来执行mutation中方法,mutation中每个方法又有state参数,通过state来修改state里面的数据,当数据修改完毕后,传输给页面进行显示

14.vue项目优化的解决方案有哪些?

  1. 使用插件抽离css
  2. 配置optimization把公共的js代码抽离
  3. 通过webpack处理文件压缩
  4. 不打包框,库文件,通过cdn方式引入
  5. 小图片采用base64
  6. 配置项目文件懒加载
  7. UI库按需导入
  8. 使用Gzip压缩

15.vue项目如何做首屏加载优化?

  1. 把不常用的库,或者体积比较大的放在index.html中通过cdn来引入
  2. Vue路由的懒加载
  3. 不生成map文件
  4. Vue组件尽量不要全部引入,最好按需加载
  5. 使用更加轻量级的组件库
  6. 使用Gzip压缩
  7. 使用SSR搜索引擎优化
  8. 首页单独做服务端渲染

16.vue.$nextTick()的原理?

$nextTick就是把其回调函数延迟到下次DOM更新才去执行,也就是js的代码执行顺序,事件循环机制问题,牵扯到宏任务与微任务执行问题

17.前端如何进行性能优化?

  1. 减少http请求
  2. 减少对DOM的操作
  3. 使用JSON格式来进行数据交换
  4. 高效使用HTML和CSS样式
  5. 使用CDN加速
  6. 将js,css放在外部文件进行引用,css放在头,js放在尾
  7. 精简css和js代码
  8. 压缩图片和使用雪碧图
  9. 注意控制cookie大小与污染

18.一个页面上有大量的图片(大型电商网站),加载很慢,你有哪些方法优化这些图片的加载,给用户更好的体验?

  1. 图片懒加载 - 在页面上添加一个滚动条事件,计算图片位置与浏览器顶端的距离与页面的距离,如果前者小于后者则优先加载
  2. 图片预加载 - 在轮播图和幻灯片中,可以使用图片的预加载,将当前展示的图片的前一张与后一张优先加载
  3. 图片压缩 - 如果图片为 css 图片,可以使用 CSSsprite,SVGsprite,Iconfont、Base64 等技术处理再使用。如果图片特别大,可以在页面显示上先显示一个压缩特别小的图片,提高用户体验。

19.系统如何进行权限控制和实现?

  1. 权限控制一般分为:接口权限,路由权限,菜单权限,按钮权限
  2. 接口权限:部分接口只有用户登录后才可以访问,一般通过jwt进行验证,用户登录拿到token,将token存储起来,每次请求的时候在Ajax请求拦截器中拦截,在请求头部进行携带,如果有token放行访问,如果没有token则返回401,跳转到登录页面
  3. 路由权限: 部分页面只有用户登录后才可以访问,在路由前置守卫里面进行判断,如果访问的是登录页面或404页面则直接跳转,如果访问的其他页面,需要判断是否有token,如果有则允许访问跳转,如果没有则返会登录页面
  4. 菜单权限:不同的角色可以看到不同的页面。通过给角色配置不同的路由来实现,在路由前置守卫中,获取角色的登录权限信息,然后筛选用户有权限访问的路由页面,最后通过addRouters动态添加路由
  5. 按钮权限:不同的角色在页面上可以看到不同的按钮,一般通过定义全局方法,要么在vue的原型链上,要么使用全局混入方法,在页面按钮上通过v-if来使用该方法,通过判断用户的权限信息来判断用户是否有该按钮的权限

20.如何处理路由实例混淆

  1. 利用刷新网页

    1. 采用window.reload(),或者router.go(0) 刷新时,整个浏览器进行了重新加载,闪烁,体验不好
    2. 运用 this.$forceUpdate()强制刷新
    3. 通过调用全局的 location.refresh 方法来实现应用刷新来实现前端路由的重置。
  2. 初始化路由

  3. 1.replace对路由重新赋值
    this.$router.replace({path: '/login'}); // 对路由进行重新赋值
    location.reload(); // 刷新页面双重保证
    
    2.通过替换当前 vue-routermatcher 属性对象来实现在不刷新页面的情况下重置当前路由实例。

21.讲讲上传组件的实现思路

  1. 我们项目中使用组件上传主要是上传图片,用来显示用户的头像信息
  2. 核心思路是通过第三方云服务器来实现,将文件上传到第三方云服务器,将云服务器储存的URL存储在公司的自由服务器上
  3. 然后创建公共组件,使用饿了么upload组件,通过样式穿透来修改上传组件的默认样式,通过http来覆盖默认的上传方法
  4. 最后增加一些细节和功能,在上传文件之前判断文件的类型大小与文件上传数量的限制,增加上传进度条,上传成功后保存文件地址,通过组件实现预览与删除

22.扁平化数据如何转为树形数据?

  1. 我们项目中后端返回的扁平化数据里面一般会存在id跟pid属性,子级的pid等于父级的id,通过这两个字段就可以作为条件来转换为树形数据
  2. 在项目utile工具文件夹index下封装一个处理数据的函数,该函数接受两个参数,第一个参数为要转化的数据,第二个数据是用来判断的pid值(具体传什么看后端数据返回什么pid值,通常传入0或'')
  3. 定义一个空数组来接受最后转换完的数据,开始对传入的数据进行遍历,首先判断传入数据的pid是否跟第二个pid参数相等,如果相等继续调用该函数,传入两个参数,第二个参数变为父级的id,判断子级id是否跟父级pid相同,如果相同,就把该子级添加为父级数据的children的属性值。最后把每次循环遍历复合数据的对象push到空数组里面
  4. 最后在组件上使用该函数
22.1数据扁平化实现方法有哪些
  1. 使用基础的递归遍
  2. 使用reduce函数递归遍历
  3. 数组强制类型转换 return arr.toString().split(',').map((item) => Number(item));
  4. while循环结合findIndex与concat
  5. 直接使用ES6的flat方法
  6. 使用JSON的函数和正则表达式
  7. 使用堆栈stack
  8. 使用Generator 函数与递归结合

23.vue2生命周期中父子组件的加载顺序

加载渲染过程

父beforeCreate—>父created—>父beforeMount—>子beforeCreate—>子created—>子beforeMount—>子mounted—>父mounted

更新过程

父beforeUpdate—> 子beforeUpdate—>子updated—>父updated

销毁过程

父beforeDestroy—> 子beforeDestroy—>子destroyed—>父destroyed

24.vuex的辅助函数

25.覆盖elementui样式的几种办法

  1. 用 /deep/ 标签

    例如 /deep/ .class{ 要覆盖的样式 } 。

  2. 重新写一个style标签可以在这个写scope的style标签下面再写一个style标签,专门写一个要覆盖的样式就好了 。类似下面图。

    img

  3. 可以在vue项目中public文件夹下的index.html中引入一个外部样式,进行覆盖

26.vue2和vue3的生命周期

vue3的生命周期

  1. beforeCreate: 在实例初始化之后、进行数据侦听和事件/侦听器的配置之前同步调用
  2. created:在实例创建完成后被立即同步调用
  3. beforeMount:在挂载开始之前被调用
  4. mounted:在实例挂载完成后被调用
  5. beforeUpdate:在数据发生改变后,DOM 被更新之前被调用
  6. updated:在数据更改导致的虚拟 DOM 重新渲染和更新完毕之后被调用
  7. beforeUnmount(在Vue2中是:beforeDestroy):在卸载组件实例之前调用
  8. unmounted (在Vue2中是: destroyed):卸载组件实例后调用

27.如何实现大屏功能

  1. 借助rem
  2. 使用postcss-pxtorem(postcss-pxtorem会将px转换为rem)

28.vue3.0之setup函数的理解

  1. 新的组件选项,作为组件中使用组合API的起点

​ 它的执行在组件实例创建之前即vue2.x的beforeCreate执行

​ 由第二点得知,setup函数中this还不是组件实例,即this就是undefined

​ 在模板中使用的数据和函数,都需要setup将其return出来

​ setup只能是同步不能是异步

  1. setup函数的形参,第一个参数props是一个对象,包含了父组件传递给子组件所有的所有数据,在子组件中通过props属性配置进行接收,包含了配置并且声明的所有属性的对象。

  2. setup函数的形参,第二个参数,context是一个对象,里面包含了以下三个属性

  3. attrs:获取当前标签上的所有属性的对象,attrs是接收props没有声明的属性,如果子组件用props已经进行声明,那么子组件我们需要使用setup函数的props去接收,不能使用attrs

  4. emit:事件分发,应用于子传父,如果将子组件的数传递给父级就需要使用该方法。

  5. slots:插槽,带有dom属性,如果想把一段html或者一个组件当作属性传入到子组件中就使用slots,接收父级传递下来的html内容

29.如果后端传过来的数据列表过多怎么处理

  1. 通过 setTimeout 进行分页渲染

  2. 在渲染页面的时候,我们可以使用requestAnimationFrame来代替setTimeout,这样可以减少reflow次数,提高性能。

  3. 文档片段

  4. 延迟加载,懒加载

    虽然后端一次返回这么多数据,但用户的屏幕只能同时显示有限的数据。所以我们可以采用延迟加载的策略,根据用户的滚动位置动态渲染数据。

  5. 跟后端进行协商

小程序

1.简单描述下微信小程序的相关文件类型?

  1. WXML 是框架设计的一套标签语言,结合基础组件、事件系统,可以构建出页面的结构。内 部主要是微信自己定义的一套组件
  2. WXSS 是一套样式语言,用于描述 WXML 的组件样式
  3. js 逻辑处理,网络请求
  4. json 小程序页面配置,如页面注册,页面标题及 tabBar
  5. app.json 必须要有这个文件,如果没有这个文件,项目无法运行,因为微信框架把这个作为 配置文件入口,整个小程序的全局配置。包括页面注册,网络设置,以及小程序的 window 背景色,配置导航条样式,配置默认标题
  6. app.js 必须要有这个文件,没有也是会报错!但是这个文件创建一下就行 ,什么都不需要写, 以后我们可以在这个文件中监听并处理小程序的生命周期函数、声明全局变量

2.app.json 全局配置文件描述下?

pages : 用于存放当前小程序的所有页面路径

window : 小程序所有页面的顶部背景颜色,文字颜色配置。

tabBar : 小程序底部的Tab ,最多5个,最少2个。

3.简述微信小程序原理?

  1. 微信小程序采用 JavaScript、WXML、WXSS 三种技术进行开发,从技术讲和现有的前端开发差不多,但深入挖掘的话却又有所不同
  2. JavaScript:首先 JavaScript 的代码是运行在微信 App 中的,并不是运行在浏览器中,因此一 些 H5 技术的应用,需要微信 App 提供对应的 API 支持,而这限制住了 H5 技术的应用,且 其不能称为严格的 H5,可以称其伪 H5,同理,微信提供的独有的某些 API,H5 也不支持
  3. WXML:WXML 微信自己基于 XML 语法开发的,因此开发时,只能使用微信提供的现有标 签,HTML 的标签是无法使用的
  4. WXSS:WXSS 具有 CSS 的大部分特性,但并不是所有的都支持,而且支持哪些,不支持哪 些并没有详细的文档
  5. 微信的架构,是数据驱动的架构模式,它的 UI 和数据是分离的,所有的页面更新,都需要 通过对数据的更改来实现
  6. 小程序分为两个部分 webview 和 appService。其中 webview 主要用来展现 UI,appService 用来处理业务逻辑、数据及接口调用。它们在两个进程中运行,通过系统层 JSBridge 实现通信, 实现 UI 的渲染、事件的处理

4.分析下微信小程序的优劣势?

优势:

  1. 无需下载,通过搜索和扫一扫就可以打开
  2. 良好的用户体验:打开速度快
  3. 开发成本要比 App 要低
  4. 安卓上可以添加到桌面,与原生 App 差不多
  5. 为用户提供良好的安全保障。小程序的发布,微信拥有一套严格的审查流程,不能通过 审查的小程序是无法发布到线上的

劣势:

  1. 限制较多。页面大小不能超过 1M。不能打开超过 5 个层级的页面
  2. 样式单一。小程序的部分组件已经是成型的了,样式很难修改。例如:幻灯片、导航
  3. 推广面窄,不能分享朋友圈,只能通过分享给朋友,附近小程序推广。其中附近小程序也受到微信的限制

6.小程序常用组件?

view、swiper、scroll-view、text、button、input、image 等

7.数据绑定如何实现

使用 mustache 语法( {{ }} )实现数据和属性的绑定

8.bindtap 和 catchtap 的区别是什么?

相同点:

首先他们都是作为点击事件函数,就是点击时触发。在这个作用上他们是一样的,可以不做区分

不同点:

他们的不同点主要是 bindtap 是不会阻止冒泡事件的,catchtap 是阻止冒泡的

9.列表渲染如何实现?

在 wxml 标签添加 wx:for 属性并赋值循环数据即可渲染

10.小程序 wx:ifhidden 的区别?

wx:if : 以动态创建移除元素的方式,控制元素的展示与隐藏(有更高的切换消耗)。

hidden : 以切换样式的方式( display: none/block; ),控制元素的显示与隐藏(有更高的初始渲染消耗)。

使用频繁切换使用 hidden, 运行时条件变化使用wx: if

11.事件及事件绑定是什么?

事件是视图层到逻辑层的通讯方式

事件可以将用户的行为反馈到逻辑层进行处理

事件可以绑定在组件上,当达到触发事件,就会执行逻辑层中对应的事件处理函数

事件对象(event)可以携带额外信息,如 id, dataset, touches

事件分为冒泡事件和非冒泡事件:

冒泡事件:当一个组件上的事件被触发后,该事件会向父节点传递

非冒泡事件:当一个组件上的事件被触发后,该事件不会向父节点传递。

事件绑定的写法 类似于组件的属性,如

<view bindtap="handleTap">
  Click here!
</view>

12.小程序怎么跟随事件传值?

在 页面标签上通过 绑定 dataset-key = value , 然后绑定点击通过e.currentTarget.dataset.key 来获取标签上绑定的值:

<button bindtap="get"  data-name="测试"> 拿到传值</button>

get(e){
    console.log(e.currentTarget.dataset.name)
 }

13.小程序的双向绑定和 vue 哪里不一样?

小程序直接 this.data 的属性是不可以同步到视图的,必须使用 this.setData() 方法

小程序:

Page({
  data: { 
    items: [] 
  },
  onLoad: function(options) { 
    this.setData({ items: [1,2,3] })
  } 
})

Vue:

new Vue({ 
  data: { 
    items: [] 
  },
  mounted () { 
    this.items = [1, 2, 3] 
  } 
})

14.小程序尺寸单位 rpx?

为了适应广大的前端开发者,WXSS 具有 CSS 大部分特性。

同时为了更适合开发微信小程序,WXSS 对 CSS 进行了扩充以及修改。

与 CSS 相比,WXSS 扩展的特性有:

  • 尺寸单位
  • 样式导入

15.小程序如何实现下拉刷新?

  1. 在 json 文 件 中 配 置 enablePullDownRefresh 为 true (app.json 中 在 window 中 设 置 enablePullDownRefresh)
  2. 在 js 文件中的onPullDownRefresh 方法中监听下拉刷新,在网络请求完成后调用

16.页面跳转的方式有哪些?

  1. 声明式导航: 使用 navigator 组件实现页面的跳转
  2. 编程式导航:使用小程序提供的 API 实现页面的跳转
    1. wx.switchTab()
    2. wx.navigateTo()
    3. wx.navigateBack()等

17.页面生命周期包含那几个?

  1. onload() 页面加载时触发。一个页面只会调用一次,可以在 onLoad 的参数中获取打开 当前页面路径中的参数
  2. onShow() 页面显示/切入前台时触发
  3. onReady() 页面初次渲染完成时触发。一个页面只会调用一次,代表页面已经准备妥当, 可以和视图层进行交互
  4. onHide() 页面隐藏/切入后台时触发。 如 navigateTo 或底部 tab 切换到其他页面,小程序切入后台等
  5. onUnload() 页面卸载时触发。如 redirectTo 或 navigateBack 到其他页面时

18.小程序如何实现下拉刷新?

  1. 在 json 文 件 中 配 置 enablePullDownRefresh 为 true (app.json 中 在 window 中 设 置 enablePullDownRefresh)
  2. 在 js 文件中实现 onPullDownRefresh 方法,在网络请求完成后调用 wx.stopPullDownRefresh() 来结束下拉刷新

19.简 述 下 wx.navigateTo(), wx.redirectTo(), wx.switchTab(), wx.navigateBack(), wx.reLaunch()的区别?

  1. wx.navigateTo():保留当前页面,跳转到应用内的某个页面。但是不能跳到 tabbar 页面
  2. wx.redirectTo():关闭当前页面,跳转到应用内的某个页面。但是不允许跳转到 tabbar 页面
  3. wx.switchTab():跳转到 abBar 页面,并关闭其他所有非 tabBar 页面
  4. wx.navigateBack():关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages() 获取当前的页面栈,决定需要返回几层
  5. wx.reLaunch():关闭所有页面,打开到应用内的某个页面

24.如何封装微信小程序的数据请求(http-promise)?

  1. 将所有的接口放在统一的 js 文件中并导出
  2. 在 app.js 中创建封装请求数据的方法
  3. 在子页面中调用封装的方法请求数据

webpack

1、什么是webpack

​ webpack是一个打包模块化javascript的工具,在webpack里一切文件皆模块,通过loader转换文件,通过plugin注入钩子,最后输出由多个模块组合成的文件,webpack专注构建模块化项目

主要模块

入口entry 输出output 编译loader 插件plugin 模式mode(选择 development, productionnone

2.Webpack的优点是什么?

  1. 专注于处理模块化的项目,能做到开箱即用,一步到位
  2. 通过plugin扩展,完整好用又不失灵活
  3. 通过loaders扩展, 可以让webpack把所有类型的文件都解析打包
  4. 区庞大活跃,经常引入紧跟时代发展的新特性,能为大多数场景找到已有的开源扩展

3.webpack的构建流程是什么?从读取配置到输出文件

​ Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:

1. 初始化参数:从配置文件读取与合并参数,得出最终的参数
2. 开始编译:用上一步得到的参数初始化 Compiler (汇编)对象,加载所有配置的插件,开始执行编译
3. 确定入口:根据配置中的 entry 找出所有的入口文件
4. 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
5. 完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系
6. 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
7. 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。

在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果

4.说一下 Webpack 的热更新原理

​ webpack 的热更新又称热替换(Hot Module Replacement),缩写为 HMR。这个机制可以做到不用刷新浏览器而将新变更的模块替换掉旧的模块。

​ HMR的核心就是客户端从服务端拉去更新后的文件,准确的说是 chunk diff (chunk 需要更新的部分),实际上 WDS 与浏览器之间维护了一个 Websocket,当本地资源发生变化时,WDS 会向浏览器推送更新,并带上构建时的 hash,让客户端与上一次资源进行对比。客户端对比出差异后会向 WDS 发起 Ajax 请求来获取更改内容(文件列表、hash),这样客户端就可以再借助这些信息继续向 WDS 发起 jsonp 请求获取该chunk的增量更新。

​ 后续的部分(拿到增量更新之后如何处理?哪些状态该保留?哪些又需要更新?)由 HotModulePlugin 来完成,提供了相关 API 以供开发者针对自身场景进行处理,像react-hot-loader 和 vue-loader 都是借助这些 API 实现 HMR。

5.有哪些常见的Loader?他们是解决什么问题的?

1、 file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件

2、 url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去

3、 source-map-loader:加载额外的 Source Map 文件,以方便断点调试

4、 image-loader:加载并且压缩图片文件

5、 babel-loader:把 ES6 转换成 ES5

6、 css-loader:加载 CSS,支持模块化、压缩、文件导入等特性

7、 style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS。

8、 eslint-loader:通过 ESLint 检查 JavaScript 代码

6.Loader和Plugin的不同?

1) 不同的作用

​ Loader直译为"加载器"。Webpack将一切文件视为模块,但是webpack原生是只能解析js文件,如果想将其他文件也打包的话,就会用到loader。 所以Loader的作用是让webpack拥有了加载和解析非JavaScript文件的能力。

​ Plugin直译为"插件"。Plugin可以扩展webpack的功能,让webpack具有更多的灵活性。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。

2) 不同的用法

​ Loader在module.rules中配置,也就是说他作为模块的解析规则而存在。 类型为数组,每一项都是一个Object,里面描述了对于什么类型的文件(test),使用什么加载(loader)和使用的参数(options)

​ Plugin在plugins中单独配置。 类型为数组,每一项是一个plugin的实例,参数都通过构造函数传入。