新Web前端开发面试八股文(精简版)

4 阅读26分钟

个人介绍

面试官好,我是黄伟,拥有10年Web前端开发经验,最近几年主要做PC端系统如企业级后台管理平台(erp,crm,sass),技术栈以Vue3为主。

我能独立搭建项目架构、封装通用组件、制定开发规范。擅长处理B端常见问题,并通过工程化和性能优化提升系统体验与稳定性。

多年多人协作与大型项目迭代经验,让我能很好地理解业务、对接需求,保证项目稳定高效落地。希望能加入团队,贡献自己的经验和能力。

var 、let、const区别

1.var声明的变量会挂载在window上,而let和const声明的变量不会

2.var声明变量存在变量提升,而let和const不存在变量提升

3.let和const声明形成块作用域,var声明不形成块作用域

4.同一作用域下let和const不能声明同名变量,而var可以声明同名变量

5.暂存死区:在使用let、const命令声明变量之前,该变量都是不可用的。这在语法上,称为暂时性死区。使用var声明的变量不存在暂时性死区。

6.let声明的变量允许重新赋值,const不允许

const声明的对象属性和数组的值是可以修改的

  • 我们都知道const 是声明常量的,如果用const声明过后的变量是不可以修改的
  • 但如果是const 声明了对象,其实对象的属性是可以修改的,对象属性被赋值为常量是不受保护的
  • 如果是const声明了数组,数组里面的值也是可以修改的,数组的被定义为常量也是不受保护的

如何判断一个对象是空对象

使用 JSON 自带的.stringify 方法来判断: image.png 使用 ES6 新增的方法 Object.keys()来判断: image.png

对eventLoop机制的理解?

浏览器中的事件循环机制又叫事件队列

image.png

同源策略

同源策略是浏览器里的一种安全协议确保两页面间的协议、域名和端口都相同(一段脚本只能读取来自同一来源的窗口和文档的属性)

如何解决跨域问题

  1. jsonp(原理是动态插入script标签,但是只支持get请求)
  2. 服务器上设置代理页面
  3. CORS(跨域资源共享——服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求,前后端都需要设置)
  4. window.name+ iframe
  5. window.postMessage
  6. document.domain+iframe
  7. websocket
  8. location.hash+iframe

浏览器缓存机制

分为强缓存和协商缓存,根据响应的header内容来决定

强缓存:浏览器直接从本地缓存中获取数据,不与服务器进行交互(不用请求服务器,直接使用本地缓存)-expires、cache-control

协商缓存:浏览器发送请求到服务器,服务器判断是否可使用本地缓存(浏览器发现本地有资源的副本,单不太确定要不要使用于是去问服务器)-Last-Modified/If-Modified-Since,Etag/If-None-Match

Vue数据双向绑定的原理

通过数据劫持结合发布者-订阅者模式的方式实现的,利用了Object.defineProperty()重新定义了对象获取属性值(get)和设置属性值(set)

双向绑定原理

  • 数据通过响应式更新视图
  • 视图通过 input 事件同步数据
  • v-model 本质是 :value + @input

Map和weakMap的区别

  1. Map可以被遍历,weakMap不能被遍历
  2. Map的键可以是任意类型,weakMap只接受对象作为键(null除外,不接受其他类型的值作为键)
  3. Map的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键, weakMap的键是弱作用,键所指指向的对象可以被垃圾回收,此时键是无效的

weakMap的应用

数据缓存

部署私有属性

在DOM对象上保存相关数据

介绍下Set和Map的区别

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

Set:

  1. 成员不能重复
  2. 只有键值没有键名,类似数组
  3. 可以遍历,方法有add,delete,has

Map:

  1. 本质上是键值对的集合,类似集合
  2. 可以遍历,可以跟各种数据格式转换

WeakMap / WeakSet 为什么能优化内存?

  • 键是弱引用
  • 不会阻止垃圾回收
  • 没有强引用时,对象会被正常回收
  • 适合做缓存、DOM 关联数据,避免内存泄漏

Vue3和Vue2的区别

一、Vue3比Vue2更加友好地集成Typescript

二、响应式原理不同(Vue2是通过ES5的Object.defineProperty,Vue3是通过更快的原生proxy和reflect)

三、Vue3在Vue2的基础更加完善处

1.Vue3性能更比Vue2强(diff算法的优化、静态提升、事件监听缓存)

1680446703077.png

2.打包更科学,不再打包没用到的模块

3.Composition API(组合式API)

4.Fragment(碎片)、Teleport(传送门)、Suspense(悬念)

5.更加友好的支持和兼容TS

6.Custom Renderer API(自定义渲染API)

Vue3 相比 Vue2 有哪些主要改进?

  • 响应式从 definePropertyProxy
  • 支持 Tree-shaking
  • Composition API 更好逻辑复用
  • 更小体积、更好 TS 支持
  • 新生命周期(setup 替代 beforeCreate/created)

Vue3 响应式原理简单说下

**答:**Vue3 使用 Proxy 代理对象,拦截 get/set 等操作,在 get 时收集依赖,set 时触发依赖更新,实现响应式。相比 Vue2 的 Object.defineProperty,能监听数组、新增属性、删除属性,性能更好。

Vue2 和 Vue3 响应式区别

  • Vue2:Object.defineProperty 劫持对象属性,数组需重写方法,对象新增 / 删除属性不响应
  • Vue3:Proxy 代理整个对象,支持数组、对象新增删除,性能更好

Vue3 父子组件通信方式?

  • props / emit
  • v-model
  • defineExpose
  • provide / inject
  • mitt / pinia

Vue3 组件通信方式

  • props-父 → 子:props /emit-子 → 父:emit
  • v-model(双向绑定,语法糖)
  • ref / $parent-父访问子:ref / $children子访问父:$parent
  • provide /inject(跨层级传递,祖孙 / 深层)
  • Pinia/Vuex(全局状态,大型项目)
  • eventBus 事件总线(跨组件任意通信)
  • attrs /listeners(透传属性 / 事件
  • defineExpose / 组件暴露(控制对外暴露)

Pinia 相比 Vuex 优势?

  • 简洁,无 mutation,action 支持同步异步
  • 更好 TS,类型推导友好
  • 无需命名空间,模块更清晰
  • 天然支持组合式 Pinia 数据持久化怎么做?pinia-plugin-persistedstate 配合 localStorage/sessionStorage。银行注意:敏感信息不能存在本地

Vue2 组件通信方式

  • 父子通信:props-父 → 子 / $emit子 → 父:$emit
  • 父访问子:$ref / $children
  • 子访问父:$parent / $root
  • 跨层级通信:provide /inject -适用于祖先 → 子孙深层传递
  • 中央事件总线 eventBus(任意组件通信)
  • Vuex (state、getters、mutations、actions)
  • 组件封装透传: $attrs / $listeners

vue中父子组件钩子的执行顺序

父组件先于子组件created,而子组件先于父组件mounted

父子组件加载渲染过程:

父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted

子组件更新:父beforeUpdate->子beforeUpdate->子updated->父updated

父组件更新过程:父beforeUpdate->父updated

销毁:父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

created 和 mounted 的区别

  • created:在模板渲染成 html 前调用,即通常初始化某些属性值,然 后再渲染成视图。
  • mounted:在模板渲染成 html 后调用,通常是初始化页面完成后,再 对 html 的 dom 节点进行一些需要的操作。

Vue项目前端性能优化

  1. vue-router路由懒加载
  2. 打包优化-工程文件打包的时候不生成.map文件
  3. 切换多入口模式
  4. 使用字体图标代替切图
  5. 使用雪碧图
  6. 提取公共样式和方法
  7. 销毁控件
  8. 销毁定时器
  9. 路由离开及时解绑事件vm.off()
  10. 使用keep-alive
  11. 代码层面的优化
  12. iframe的内存释放

Vue数据更新而视图不更新问题?

  1. 使用Vue.set/Vue.delete (this.$set(this.obj,'e',5))
  2. 使用vm.forceUpdate强制更新视图(this.obj.d=4(this.forceUpdate强制更新视图(this.obj.d='4')(this.forceUpdate()对视图进行强制更新)
  3. 使用深拷贝,重新赋值替换

哪些操作会造成内存泄漏?

内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存

垃圾回收器定期扫描对象,并计算引用了每个对象的其他对象的数量。如果一个对象的引用数量为 0(没有其他对象引用过该对象),或对该对象的惟一引用是循环的,那么该对象的内存即可回收。

针对JavaScript的来及回收机制有以下两种方法(常用):标记清除法,引用计数法

  1. setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏。
  2. 闭包
  3. 控制台日志console
  4. 没有清理对DOM元素的引用
  5. 意外的全局变量

前端最常见内存泄漏场景(必考)

  1. 意外的全局变量未声明变量、挂载到 window 上
  2. 闭包滥用闭包长期持有 DOM 或大对象
  3. 定时器 / 延时器未清理setInterval 不清除,回调里持有引用
  4. DOM 引用未释放节点删了,但 JS 变量还拿着它
  5. 事件监听未移除addEventListener 没对应 removeEventListener
  6. 控制台打印console.log 会阻止对象被回收
  7. 第三方库 / 缓存未清理如图表库、地图 SDK 没有销毁实例

如何避免内存泄漏?

  • 减少不必要全局变量,使用严格模式 'use strict'
  • 及时清理 setTimeout/setInterval
  • 组件销毁时移除事件监听、取消订阅
  • DOM 删除后,手动把引用变量置为 null
  • 避免滥用闭包,及时释放大对象引用
  • 控制台打印只用于开发,生产环境关闭
  • 使用 WeakMap/WeakSet 辅助缓存(不影响 GC)

px、em与rem的区别?

px像素相对于显示器屏幕分辨率是固定不变的,不受浏览器缩放影响

em相对于父元素的字体大小font-size

rem相对于根元素html的字体大小font-size

首屏加载白屏怎么进行优化?

  1. 使用CDN减小代码体积,加快请求速度;
  2. 通过SSR服务端渲染,把所有数据全部渲染完成后,再返回给客户端;
  3. 路由懒加载,当用户访问的时候,再加载相应模块;
  4. 使用外链CSS,JS文件;
  5. 项目打包不生成.map文件;
  6. 使用骨架屏,在首页加载时,显示页面结构轮廓

BFC(块级格式化上下文,用于清除浮动,防止margin重叠等)

块级格式化上下文,是页面上一个隔离的独立渲染区域,并且有一定的布局规则(容器里面的资源元素不会影响到外面的元素)。

  • BFC区域不会与float box重叠
  • BFC是页面上的一个独立容器,子元素不会影响到外面
  • 计算BFC的高度时,浮动元素也会参与计算

那些元素会生成BFC/如何触发BFC:

  • 根元素
  • float不为none的元素
  • position为fixed和absolute的元素
  • display为inline-block、table-cell、table-caption,flex,inline-flex的元素
  • overflow不为visible的元素

GET和POST区别(高频)

  1. GET在浏览器回退不会再次请求,POST会再次提交请求
  2. GET请求会被浏览器主动缓存,POST不会,要手动设置
  3. GET请求参数会被完整保留在浏览器历史记录里,POST中的参数不会
  4. GET请求在URL中传送的参数是有长度限制的,而POST没有限制
  5. GET参数通过URL传递,POST放在Request body中
  6. GET参数暴露在地址栏不安全,POST放在报文内部更安全
  7. GET一般用于查询信息,POST一般用于提交某种信息进行某些修改操作
  8. GET产生一个TCP数据包;POST产生两个TCP数据包

Get和post的选择: 1.私密性的信息请求使用post(如注册、登陆)。 2.查询信息使用get。

WebSocket的理解

WebSocket是HTML5开始提供的一种浏览器与服务器间进行全双工通讯的网络技术。依靠这种技术可以实现客户端和服务器端的长连接,双向实时通信。

特点:

  • 事件驱动
  • 异步
  • 使用ws或者wss协议的客户端socket
  • 能够实现真正意义上的推送功能

缺点:

  • 少部分浏览器不支持,浏览器支持的程度与方式有区别。

web前端项目性能优化

  • 减少HTTP请求
  • 使用内容发布网络(CDN)
  • 添加本地缓存
  • 压缩资源文件
  • 将CSS样式表放在顶部,把javascript放在底部(浏览器的运行机制决定)
  • 避免使用CSS表达式
  • 减少DNS查询
  • 使用外部javascript和CSS
  • 避免重定向
  • 图片lazyLoad懒加载

arguments的理解

  • arguments是类数组对象,有length属性,不能调用数组方法
  • 可用Array.from()转换
  • 箭头函数获取arguments(可用…rest参数获取)

箭头函数和function有什么区别

  1. 箭头函数只能是匿名函数。
  2. 箭头函数不能用于构造函数,不能用new创建实例对象。
  3. 箭头函数的this从其所在上下文捕获,而普通函数拥有自己的this。注意:箭头函数的this一旦被捕获,就不会再发生改变,call、apply、bind均无法改变。
  4. 箭头函数没有arguments参数,取而代之的是三点运算符(...)
  5. 箭头函数不能使用Generator函数,不能使用yeild关键字。
  6. 箭头函数不具有prototype原型对象。
  7. 箭头函数不具有super。
  8. 箭头函数不具有new.target。

数组去重的方法

  1. ES6的set
  2. indexOf循环去重
  3. Object 键值对去重;把数组的值存成 Object 的 key 值,比如 Object[value1] = true,在判断另一个值的时候,如果 Object[value2]存在的话,就说明该值是重复的。
  4. splice循环去重
  5. 哈希集合循环去重
  6. for循环嵌套,利用splice()去重;
    创建新数组,利用indexOf()去重;
    ES6中利用(new Set())去重;
    利用数组的sort()去重(相邻元素比较法);
    利用数组的includes()去重;
    利用数组的filter()去重。

图片的懒加载和预加载

预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染。 懒加载:懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求数。

两种技术的本质:两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载。 懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。

null 和 undefined 的区别?

相同:

在 if 语句中 null 和 undefined 都会转为false两者用相等运算符比较也是相等

首先 Undefined 和 Null 都是基本数据类型,这两个基本数据类型分别都只有一个值,就是 undefined 和 null。

不同:

undefined 代表的含义是未定义,

  • 定义了形参,没有传实参,显示undefined
  • 一般变量声明了但还没有定义的时候会返回 undefined
  • 对象属性名不存在时,显示undefined
  • 函数没有写返回值,即没有写return,拿到的是undefined
  • null 代表的含义是空对象。也作为对象原型链的终点
  • null 主要用于赋值给一些可能会返回对象的变量,作为初始化。

盒子模型的理解

  • 标准盒子模型 (content-box)

盒子总宽度 = width + padding + border + margin;

盒子总高度 = height + padding + border + margin

width/height 只是内容高度,不包含 padding 和 border值

  • IE 怪异盒子模型 (border-box)

盒子总宽度 = width + margin;

盒子总高度 = height + margin;

width/height 包含了 padding和 border值

怎么理解回流跟重绘

在HTML中,每个元素都可以理解成一个盒子,在浏览器解析过程中,会涉及到回流与重绘:

回流:布局引擎会根据各种样式计算每个盒子在页面上的大小与位置

当渲染树中部分或者全部元素的尺寸、结构或者属性发生变化时,浏览器会重新渲染部分或者全部文档的过程

重绘:当计算好盒模型的位置、大小及其他属性后,浏览器根据每个盒子特性进行绘制

当页面中某些元素的样式发生变化,但是不会影响其在文档流中的位置时,浏览器就会对元素进行重新绘制

typeof 与 instanceof 区别

typeof 对于原始数据类型来说,除了null都可以正确的显示类型

instanceof 可以正确显示数据类型, 因为它是通过对象的原型链来进行判断的

typeof与instanceof都是判断数据类型的方法,区别如下:

  • typeof会返回一个变量的基本类型,instanceof返回的是一个布尔值
  • instanceof 可以准确地判断复杂引用数据类型,但是不能正确判断基础数据类型

而typeof 也存在弊端,它虽然可以判断基础数据类型(null 除外),但是引用数据类型中,除了function 类型以外,其他的也无法判断

对闭包的理解?闭包使用场景

闭包就是可以访问其他函数内部变量的函数

我们通常用它来定义私有化的变量和方法,创建一个闭包最简单的方法就是在一个函数内创建一个函数,闭包让你可以在一个内层函数中访问到其外层函数的作用域

它有三个特性是

  1. 函数内可以再嵌套函数,
  2. 内部函数可以访问外部的方法和变量,
  3. 方法和变量不会被垃圾回收机制回收

闭包使用场景 任何闭包的使用场景都离不开这两点:

  • 创建私有变量(好比vue里的data 每个data都是一个闭包所以他们互不干扰)
  • 延长变量的生命周期
  1. setTimeout(原生的setTimeout传递的第一个函数不能带参数,通过闭包可以实现传参效果)
  2. 回调
  3. 函数防抖
  4. 封装私有变量

优缺点:它的优点就是可以实现封装和缓存,缺点就是可能会造成内存泄漏的问题

bind、call、apply 区别?

1、相同点

  • 三个都是用于改变this指向;
  • 接收的第一个参数都是this要指向的对象;
  • 都可以利用后续参数传参。

2、不同点

  • call和bind传参相同,多个参数依次传入的;
  • apply只有两个参数,第二个参数为数组;
  • call和apply都是对函数进行直接调用,而bind方法不会立即调用函数,而是返回一个修改this后的函数。

对BOM的理解,常见的BOM对象你了解哪些?

BOM (Browser Object Model),浏览器对象模型,提供了内容与浏览器窗口进行交互的对象 window、location、navigator 、screen、history

如何通过js判断一个数组?

  1. 通过instanceof判断
  2. 通过constructor判断
  3. 通过Object.prototype.toString.call()判断
  4. 通过Array.isArray()判断

for循环、map方法和forEach区别以及使用

for性能最好,其次是forEach,再者是map。

image.png

对promise的了解

Promise 是异步编程的一种解决方案,其实是一个构造函数,自己身上有all、reject、resolve这几个方法,原型上有then、catch等方法。

Promise对象封装了一个异步操作并且还可以获取成功或失败的结果,主要是解决回调地狱问题(异步任务比较多且之间有相互依赖的关系,代码可读性差,可维护性差)有三种状态:pending初始状态、fullfilled成功状态、rejected失败状态

缺点:

  1. 无法取消promise,一旦创建它就会立即执行,不能中途取消
  2. 如果不设置回调,promise内部抛出的错误就无法反馈到外面
  3. 若当前处于pending状态时,无法得知目前在哪个阶段

原理:

  • 构造一个Promise实例,实例需要传递函数的参数,这个函数有两个形参,分别都是函数类型,一个是resolve,一个是reject
  • Promise上还有then方法,这个方法就是指定状态改变时的确定操作,resolve是执行第一个函数,reject是执行第二个函数

promise和async await的区别

1.两者都是处理异步请求的方式

2.promise是es6的语法,async await是es7的新特性

3.async await 是基于promise实现的,不能用于普通函数,它和promise一样都是非阻塞的

优缺点:

1.promise是返回的对象要用then().catch()去处理数据和捕获异常,而且书写方式是链式的,容易造成代码多层堆叠难以维护;async await 则是通过try{}.cathc{}进行捕获直接抛出异常

2.async await 最大的有点是使代码看起来向同步一样,一遇到await就立即先返回结果然后再执行后面的操作;promise.then()的方式返回就可能在请求还没返回时就先执行了外面的操作

vue路由的原理

  1. 监听地址栏中hashchange事件驱动界面变化
  2. 用pushState记录浏览器的历史,驱动界面发送变化

hash 模式(核心通过监听url中的hash来进行路由跳转)

history模式(模式核心借用 HTML5 history api,api 提供了丰富的 router 相关属性先了解一个几个相关的api)

  • history.pushState 浏览器历史纪录添加记录
  • history.replaceState修改浏览器历史纪录中当前纪录
  • history.popState 当 history 发生变化时触发

Vue路由的hash模式和history模式有什么区别?

  1. hash的路由地址上有#号,history模式没有
  2. 在做回车刷新的时候,hash模式会加载对应页面,history会报错404
  3. hash模式支持低版本浏览器,history不支持,因为是H5新增的API
  4. hash不会重新加载页面,单页面应用必备
  5. history需要后台配置
  6. history有历史记录,H5新增了pushState和replaceState()去修改历史记录,并不会立刻发送请求

vuex刷新数据会丢失吗?怎么解决

vuex肯定会重新获取数据,页面也会丢失数据

  1. 把数据直接保存在浏览器缓存里(cookie localstorage sessionstorage)

  2. 页面刷新的时候,再次请求数据,达到可以动态更新的方法

    监听浏览器的刷新书简,在刷新前把数据保存到sessionstorage里,刷新后请求数据,请求到了用vuex,如果没有那就用sessionstorage里的数据

vuex和windows全局变量有什么区别

  1. vuex做的就是状态管理,主要时管理状态的一个库,把项目中公用的一些数据进行存储,某一个组件更改了vuex中的数据,其他相关的组件也会得到快速更新,但是全局变量可以任意修改,不是很安全
  2. 全局变量可能操作命名污染,但是vuex不会,每个组件可以根据自己vuex的变量名引用不受影响
  3. vuex处理复杂的项目,嵌套关系复杂的项目效果很明显,针对于demo或者小项目,全局变量也就够用了

$attrs是为了解决什么问题出现的?

  • 主要作用是为了实现批量传递数据。
  • provide/inject更适合应用在插件中,主要实现跨级数据传递。

浏览器的渲染过程:

  1. 解析 HTML 构建 DOM(DOM 树),并行请求 css/image/js
  2. CSS 文件下载完成,开始构建 CSSOM(CSS 树)
  3. CSSOM 构建结束后,和 DOM 一起生成 Render Tree(渲染树)
  4. 布局(Layout):计算出每个节点在屏幕中的位置
  5. 显示(Painting):通过显卡把页面画到屏幕上

vue中的diff算法

diff的过程就是调用名为patch的函数,比较新旧节点,一边比较一边给真实的DOM打补丁

其有两个特点:

  1. 比较只会在同层级进行, 不会跨层级比较
  2. 在diff比较的过程中,循环从两边向中间比较

Diff算法的步骤:

  • Vue的diff算法是平级比较,不考虑跨级比较的情况。内部采用深度递归的方式+双指针方式比较
  • 先比较两个节点是不是相同节点
  • 相同节点比较属性,复用老节点
  • 先比较儿子节点,考虑老节点和新节点儿子的情况
  • 优化比较:头头、尾尾、头尾、尾头
  • 比对查找,进行复用

for...in 迭代和 for...of 有什么区别

  1. 推荐在循环对象属性的时候,使用 for...in,在遍历数组的时候的时候使用for...of。
  2. for in遍历的是数组的索引,而for of遍历的是数组元素值
  3. for...of 不能循环普通的对象,需要通过和 Object.keys()搭配使用
  4. for...in 遍历顺序以数字为先 无法遍历 symbol 属性 可以遍历到公有中可枚举的
  5. 从遍历对象的角度来说,for···in会遍历出来的为对象的key,但for···of会直接报错。

vue路由的钩子函数

首页可以控制导航跳转,beforeEach,afterEach,beforeEnter(单个路由独享守卫),beforeRouteEnter,beforeRouteUpdate,beforeRouteLeave等,一般用于页面title的修改。一些需要登录才能调整页面的重定向功能。

  • 路由的钩子函数主要有3个参数to,from,next。
  • to:route即将进入的目标路由对象。
  • from:route当前导航正要离开的路由。
  • next:function一定要调用该方法resolve这个钩子。执行效果依赖next方法的调用参数。可以控制网页的跳转

keep-alive的作用是什么?

keep-alive可以实现组件缓存,当组件切换时不会对当前组件进行卸载

  • 包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免重新渲染
  • 常用的两个属性include/exclude,允许组件有条件的进行缓存
  • 两个生命周期activated/deactivated,用来得知当前组件是否处于活跃状态

Generator

是 ES6中新增的语法,和 Promise 一样,都可以用来异步编程。Generator函数可以说是Iterator接口的具体实现方式。Generator 最大的特点就是可以控制函数的执行。

  • function* 用来声明一个函数是生成器函数,它比普通的函数声明多了一个*,*的位置比较随意可以挨着 function 关键字,也可以挨着函数名
  • yield 产出的意思,这个关键字只能出现在生成器函数体内,但是生成器中也可以没有yield 关键字,函数遇到 yield 的时候会暂停,并把 yield 后面的表达式结果抛出去
  • next作用是将代码的控制权交还给生成器函数

scoped

在vue文件中的style标签上,有一个特殊的属性:scoped。

原理:

当一个style标签拥有scoped属性时,它的CSS样式就只能作用于当前的组件,通过该属性,可以使得组件之间的样式不互相污染。

为组件实例生成一个唯一标识,给组件中的每个标签对应的dom元素添加一个标签属性,data-v-xxxx 给中的每个选择器的最后一个选择器添加一个属性选择器,原选择器[data-v-xxxx],如:原选择器为.container #id div,则更改后选择器为.container #id div[data-v-xxxx]

Vue 组件 data 为什么必须是函数

主要是为了防止组件与组件之间声明的变量互相影响。

每个组件都是 Vue 的实例。 组件共享 data 属性,当 data 的值是同一个引用类型的值时,改变其中一个会影响其他

Proxy 相比较于 defineProperty 的优势

Object.defineProperty 是监听对象的字段而非对象本身,因此对于动态插入对象的字段,它无能为了,只能手动为其设置设置监听属性。同时,Object.defineProperty 无法监听对象中数组的变化

Proxy 叫做代理器,它可以为一个对象设置代理,即监听对象本身,任何访问当前被监听的对象的操作,无论是对象本身亦或是对象的字段,都会被 Proxy 拦截,因此可以使用它来做一些双向绑定的操作。

鉴于兼容性的问题,目前仍然主要是使用 Object.defineProperty 更多,但是随着 Vue/3 的发布,Proxy 应该会逐渐淘汰 Object.defineProperty。

Vue3为什么使用proxy

  1. proxy补兼容ie浏览器
  2. proxy对代理对象的监听更加丰富
  3. proxy代理对象会生成新的对象,不会修改代理对象本身
  4. proxy可以代理整个对象,defineproperty只代理对象上的某个属性

为何v-for要用key

  • 必须用key,而且不能用index和random
  • key是vue中Vnode的唯一标记,通过这个key,我们的diff操作可以更准确,更快捷
  • 在diff算法中用tag和key来判断,是否是sameNode
  • 可以减少渲染次数,提高渲染性能
  • 给 Vue 标识节点,方便diff 算法复用节点,提升渲染效率
  • 不要用 index 做 key(顺序变化会错乱)

列表渲染为什么 key 很重要?

key 帮助 diff 算法识别节点,避免原地复用错误节点,减少不必要 DOM 操作与重排。

Vue的普通Slot以及作用域Slot的区别

  • 普通插槽是渲染后做替换的工作。父组件渲染完毕后,替换子组件的内容。
  • 作用域插槽可以拿到子组件里面的属性。在子组件中传入属性然后渲染。

鼠标事件 mouseenter与mouseover区别

  • mouseenter: 鼠标进入被绑定事件监听元素节点时触发一次,再次触发是鼠标移出被绑定元素,再次进入时。而当鼠标进入被绑定元素节点触发一次后没有移出,即使鼠标动了也不再触发。
  • mouseover: 鼠标进入被绑定事件监听元素节点时触发一次,如果目标元素包含子元素,鼠标移出子元素到目标元素上也会触发。
  • mouseenter 不支持事件冒泡 mouseover 会冒泡

vue中 methods,computed, watch 的区别

  • computed 是vue中的计算属性,具有缓存性,当他的依赖于值,发生改变的时候才会重新调用
  • methods 是没有缓存的,只要调用,就会执行,一般结合事件来使用
  • watch 没有缓存性 监听data中的属性 属性值只要发生变化就会执行 可以利用他的特性做一些异步的操作

computed 和 watch 区别

  1. computed是计算属性,watch是监听,监听的是data中数据的变化
  2. computed是支持缓存,依赖的属性值发生变化,计算属性才会重新计算,否则用缓存;watch不支持缓存
  3. computed不支持异步,watch是可以异步操作
  4. computed是第一次加载就监听,watch是不监听
  5. computed函数中必须有return,watch不用
  • computed:计算属性,有缓存,依赖变才重新计算
  • watch:监听属性,无缓存,适合异步 / 复杂逻辑

watch 和 watchEffect 区别?

  • watch 需指定依赖,手动指定监听的数据源,不会立即执行
  • watchEffect 自动追踪,立即执行一次(会自动收集依赖,初始化就会执行一次,适合依赖不固定、需要自动监听的场景,比如表单联动、数据实时计算。)

cookies,sessionStorage 和 localStorage 的区别?

  • cookie:一个大小不超过4K的小型文本数据,一般由服务器生成,可以设置失效时间;若没有设置时间,关闭浏览器cookie失效,若设置了 时间,cookie就会存放在硬盘里,过期才失效,每次http请求,header都携带cookie
  • localStorage:5M或者更大,永久有效,窗口或者浏览器关闭也会一直保存,除非手动永久清除或者js代码清除,因此用作持久数据,不参与和服务器的通信
  • sessionStorage关闭页面或浏览器后被清除。存 放数据大小为一般为 5MB,而且它仅在客户端(即浏览器)中保存,不参与和服务器的通信。

相同:都可以用来存储数据。

区别:

  • cookie一条数据大小不能超过4KB ,最多不能存储超过20条,如果没有设置过期时间,那么在浏览器关闭后消失。
  • sessionStorage是会话存储,一条大小不能超过5M,数量没有限制,关掉页面数据消失。
  • localStorage本地存储,一条大小不超过5M,数量没有限制,除非主动删除,否则数据不会消失。

浏览器的本地存储

浏览器的本地存储主要分为Cookie、WebStorage和IndexDB, 其中WebStorage又可以分为localStorage和sessionStorage。

共同点: 都是保存在浏览器端、且同源的

不同点:

cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下sessionStorage和localStorage不会自动把数据发送给服务器,仅在本地保存。

存储大小限制也不同, cookie数据不能超过4K,sessionStorage和localStorage可以达到5M sessionStorage:仅在当前浏览器窗口关闭之前有效; localStorage:始终有效,窗口或浏览器关闭也一直保存,本地存储,因此用作持久数据; cookie:只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭

作用域不同 sessionStorage:不在不同的浏览器窗口中共享,即使是同一个页面; localstorage:在所有同源窗口中都是共享的;也就是说只要浏览器不关闭,数据仍然存在 cookie: 也是在所有同源窗口中都是共享的.也就是说只要浏览器不关闭,数据仍然存在

offset、client、scroll的用法?

  • offset系列 经常用于获得元素位置 offsetLeft offsetTop
  • client经常用于获取元素大小 clientWidth clientHeight
  • scroll 经常用于获取滚动距离 scrollTop scrollLeft

虚拟dom

虚拟dom他不并不是真实的 dom ,是根据模板生成一个js对象(使用createElement,方法),根据这个js对象再去生成真实的dom,对复杂的文档DOM结构,提供一种方便的工具,进行最小化的DOM操作 ,是可以快速的渲染和高效的更新元素,提高浏览器的性能。

Virual DOM是用JS对象记录一个dom节点的副本,当dom发生更改时候,先用虚拟dom进行diff,算出最小差异,然后再修改真实dom。

虚拟DOM的缺点:

  1. 代码更多,体积更大
  2. 内存占用增大
  3. 小量的单一的dom修改使用虚拟dom成本反而更高,不如直接修改真实dom快

为什么要用虚拟DOM来描述真实的DOM呢?

创建真实DOM成本比较高,如果用 js对象来描述一个dom节点,成本比较低,另外我们在频繁操作dom是一种比较大的开销。所以建议用虚拟dom来描述真实dom。

1680446819731.png

Vue为什么要用虚拟Dom

虚拟dom就是用js对象来描述真实Dom,是对真实Dom的抽象

image.png

由于直接操作Dom性能低,但是js层的操作效率高,可以将Dom操作转化成对象操作。最终通过diff算法比对差异进行更新Dom

虚拟Dom不依赖真实平台环境,可以实现跨平台

虚拟 DOM 的解析过程

首先对将要插入到文档中的 DOM 树结构进行分析,使用 js 对象将 其表示出来,比如一个元素对象,包含 TagName、props 和 Children 这些属性。然后将这个 js 对象树给保存下来,最后再将 DOM 片段 插入到文档中。

当页面的状态发生改变,需要对页面的 DOM 的结构进行调整的时候, 首先根据变更的状态,重新构建起一棵对象树,然后将这棵新的对象 树和旧的对象树进行比较,记录下两棵树的的差异。

后将记录的有差异的地方应用到真正的 DOM 树中去,这样视图就 更新了。

虚拟DOM的实现原理

虚拟 DOM 的实现原理主要包括以下 3 部分:

  1. 用 JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象
  2. diff 算法 — 比较两棵虚拟 DOM 树的差异
  3. patch 算法(打补丁) — 将两个虚拟 DOM 对象的差异应用到真正的 DOM 树

JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象。vue 是通过 createElement 生成 VNode。

虚拟 DOM 就是用 JS 来描述一个真实的 DOM 节点。而在 Vue 中就存在了一个 VNode 类,通过这个类,我们就可以实例化出不同类型的虚拟 DOM 节点。Vue 中 VNode 的类型:

  • 注释节点
  • 文本节点
  • 元素节点
  • 组件节点
  • 函数式组件节点
  • 克隆节点

虚拟 DOM 其实说白了就是以 JS 的计算性能来换取操作真实 DOM 所消耗的性能。当数据发生变化时,我们对比变化前后的虚拟DOM节点,通过DOM-Diff算法计算出需要更新的地方,然后去更新需要更新的视图。
creatElement()

  • context 表示 VNode 的上下文环境,是 Component 类型
  • tag 表示标签,它可以是一个字符串,也可以是一个 Component 类型
  • data 表示 VNode 的数据,它是一个 VNodeData 类型
  • children 表示当前 VNode 的子节点,它是任意类型的
  • normalizationType 表示子节点规范的类型,类型不同规范的方法也就不一样,主要是参考 render 函数是编译生成的还是用户手写的

normalizeChildren 方法调用场景分为下面两种:

  • render 函数是用户手写的

  • 编译 slot、v-for 的时候会产生嵌套数组

createComponent

createComponent 生成 VNode 的三个关键流程:

  • 构造子类构造函数 Ctor
  • installComponentHooks 安装组件钩子函数
  • 实例化 vnode

vue 初始化页面闪动问题。

能够解决插值表达式闪烁问题,需要在style中设置样式[v-clock]{display:none}

Javascript拖拽功能事件

  • dragstart、drag、dragenter、dragover、dragleave、drop、dragend
  • mousedown、mousemove、mouseup (涉及到拖拽效果中的事件)

git基础命令

  1. git init 把这个目录变成git可以管理的仓库
  2. git add 不但可以跟单一文件,也可以跟通配符,更可以跟目录。一个点就把当前目录下所有未追踪的文件全部add了
  3. git commit -m ‘first commit’把文件提交到仓库
  4. git remote add origin +//仓库地址 //关联远程仓库
  5. git push -u origin master //把本地库的所有内容推送到远程库上

git 发生冲突的场景?如何解决?

当Git无法自动合并分支时,就必须首先解决冲突,解决冲突后,再提交,合并完成

解决冲突就是把Git合并失败的文件手动编辑为我们希望的内容,再提交

Http和Https区别

HTTP —— 超文本传输协议
HTTPS —— 超文本传输安全协议
  1. HTTP 的URL 以http:// 开头,而HTTPS 的URL 以https:// 开头
  2. HTTP 是不安全的,而 HTTPS 是安全的
  3. HTTP 标准端口是80 ,而 HTTPS 的标准端口是443
  4. 在OSI 网络模型中,HTTP工作于应用层,而HTTPS 的安全传输机制工作在传输层
  5. HTTP 无法加密,而HTTPS 对传输的数据进行加密,证的网络协议,安全性高于HTTP协议。
  6. HTTP无需证书,而HTTPS 需要CA机构wosign的颁发的SSL证书,一般免费证书少,因而需要一定费用。

http状态码301和302详解及区别

一般以3XX开头的代表重定向,表示网页发生了转移,需要重定向到对应的地址中去,两者区别是:

301:表示永久性转移(Permanently Moved)

302:表示临时性转移(Temporarily Moved)

vue.mixin的使用场景和原理?

Vue的mixin的作用就是抽离公共的业务逻辑,原理类似对象的继承,当组件初始化的时候,会调用mergeOptions方法进行合并,采用策略模式针对不同的属性进行合并。

如果混入的数据和本身组件的数据有冲突,采用本身的数据为准。

缺点:命名冲突、数据来源不清晰

深拷贝和浅拷贝的区别

深拷贝和浅拷贝都是指的对象的拷贝

1.浅拷贝,只会拷贝基本数据类型的值和实际的引用地址,实际指向还是同一个对象,对基本类型数据修改,原对象也会紧接着修改

常见的浅拷贝:

  • Object.assign
  • Object.create
  • slice
  • concat()
  • 拓展运算符

2.深拷贝,基本数据类型和所指向的对象都会进行复制,内部实际指向的不是一个对象,所以在做修改时两者不会同时改变

常见的深拷贝方式有:

  • _.cloneDeep()
  • jQuery.extend()
  • 手写循环递归
  • JSON.parse(JSON.stringify())

JSON.stringify() 的缺点

  1. 无法处理函数和 undefined

  2. 无法处理循环引用

  3. 无法处理一些特殊对象

  4. 对象的原型链上的属性会丢失

bindtap和catchtap的区别

bind事件绑定不会阻止冒泡事件向上冒泡 catch事件绑定可以阻止冒泡事件向上冒泡

TypeScript 的主要特点是什么?

  • 跨平台:TypeScript 编译器可以安装在任何操作系统上,包括 Windows、macOS 和 Linux。
  • ES6 特性:TypeScript 包含计划中的 ECMAScript 2015 (ES6) 的大部分特性,例如箭头函数。
  • 面向对象的语言:TypeScript 提供所有标准的 OOP 功能,如类、接口和模块。
  • 静态类型检查:TypeScript 使用静态类型并帮助在编译时进行类型检查。因此,你可以在编写代码时发现编译时错误,而无需运行脚本。
  • 可选的静态类型:如果你习惯了 JavaScript 的动态类型,TypeScript 还允许可选的静态类型。
  • DOM 操作:您可以使用 TypeScript 来操作 DOM 以添加或删除客户端网页元素。

使用 TypeScript 有什么好处?

  • TypeScript 更具表现力,这意味着它的语法混乱更少。
  • 由于高级调试器专注于在编译时之前捕获逻辑错误,因此调试很容易。
  • 静态类型使 TypeScript 比 JavaScript 的动态类型更易于阅读和结构化。
  • 由于通用的转译,它可以跨平台使用,在客户端和服务器端项目中。

TypeScript 中的接口是什么?

接口为使用该接口的对象定义契约或结构。 接口是用关键字定义的interface,它可以包含使用函数或箭头函数的属性和方法声明。

interface和type的主要区别

  1. 类型别名可以用于其他类型(联合类型、元组类型、基本类型(原始值)),interface不支持
  2. interface可以多次定义并被视为合并所有声明成员,type不支持
  3. type能使用in关键字生成映射类型,但interface不行
  4. interface支持同时声明,默认导出,而type必须先声明后导出
    1. interface可以重复声明,type定义后不能重复声明
    1. interface可以通过“extends”来继承接口,这样既高效又不用重新定义。而type只能通过&来实现类似于继承的功能
    1. type 能够表示非对象类型, 而 interface 则只能表示对象类型。
  • 可以定义基本类型别名,如type StringType = string

  • 可以声明联合类型,如 type paramType = number | string;

  • 可以声明元组类型,如type arrType = [string, string, number]

定义对象时严谨的来说,type 是引用,而 interface是定义。

组合方式:interface使用extends来实现继承,type使用&来实现联合类型

扩展方式:interface可以重复声明来扩展,type一个类型只能申明一次

范围不同:type适用于基本类型,interface一般不行

命名方式不一样:interface会创建一个新的类型,type只是给类型起一个别名而已

前端性能提升长列表优化解决方案

  • 分片渲染(通过浏览器事件环机制,也就是 EventLoop,分割渲染时间)
  • 虚拟列表(只渲染可视区域)

浏览器中输入url会发生什么?

  1. DNS解析 没有找到缓存时url解析成ip服务器地址
  2. 建立TCP连接 三次握手
  3. 发送HTTP请求
  4. 服务器处理请求服务器返回响应结果 服务器处理后将响应报⽂通过TCP连接发送回浏览器
  5. 关闭TCP链接 四次挥手
  6. 浏览器解析HTML等资源 获取到资源HTML、CSS、js ->构建DOM树 > 构建styleSheets树
  7. 浏览器将页面布局渲染 布局(构建render树) > 分层 > 绘制 >分块 > 光栅化 > 合成

虚拟列表

虚拟列表其实是按需显示的一种实现,即只对可见区域进行渲染,对非可见区域中的数据不渲染或部分渲染的技术,从而达到极高的渲染性能。

假设有1万条记录需要同时渲染,我们屏幕的可见区域的高度为500px,而列表项的高度为50px,则此时我们在屏幕中最多只能看到10个列表项,那么在首次渲染的时候,我们只需加载10条即可。

虚拟列表如何实现

虚拟列表的实现,实际上就是在首屏加载的时候,只加载可视区域内需要的列表项,当滚动发生时,动态通过计算获得可视区域内的列表项,并将非可视区域内存在的列表项删除。

  • 计算当前可视区域起始数据索引(startIndex)
  • 计算当前可视区域结束数据索引(endIndex)
  • 计算当前可视区域的数据,并渲染到页面中
  • 计算startIndex对应的数据在整个列表中的偏移位置startOffset并设置到列表上
  1. 【分片渲染】 启用使用API setTimeout 分片渲染 每次渲染50条,进入宏任务列队进行页面渲染以提高效率。
  2. 开发一个【虚拟列表】组件
  1. 列表项高度固定,且每个列表项高度相等
  2. 列表项高度固定不相等,但组件调用方可以明确的传入形如(index: number)=>number的getter方法去指定每个索引处列表项的高度
  3. 列表项高度不固定,随内容适应,且调用方无法确定具体高度

element 组件的二次封装

首先,我们需要使用到 Vue API 中的两大高级应用属性

  • vm.$attrs
  • vm.$listeners
  • 在组件上使用 v-model

自定义事件也可以用于创建支持 v-model 的自定义输入组件。记住:

<input v-model="searchText">

等价于:

<input
  v-bind:value="searchText"
  v-on:input="searchText = $event.target.value"
>

当用在组件上时,v-model 则会这样:

<custom-input
  v-bind:value="searchText"
  v-on:input="searchText = $event"
></custom-input>

为了让它正常工作,这个组件内的 <input> 必须:

  • 将其 value attribute 绑定到一个名叫 value 的 prop 上
  • 在其 input 事件被触发时,将新的值通过自定义的 input 事件抛出

vue3项目中如何使用懒加载

  1. 路由懒加载:把路由组件改成动态import即可和打包分块优化(给chunk命名)
  2. 组件懒加载(局部组件异步加载)v-if加defineAsyncComponent 3.Suspense+异步组件 4.图片懒加载 loading=“lazy”或vue-lazyload插件

首屏加载时间

从用户输入 URL / 点击链接开始,到页面主要内容渲染完成、可交互的时间。

### 首屏慢常见原因?

  • 资源体积大(JS/CSS/ 图片)
  • 请求多、串行加载、阻塞渲染
  • 网络慢、弱网、跨域多
  • 渲染阻塞(JS 阻塞 DOM/CSSOM 构建)
  • 服务端响应慢(接口、SSR 慢)
  • 第三方脚本拖慢

首屏优化有哪些手段?

网络层

  • 开启 Gzip/Brotli 压缩
  • 使用 CDN 加速静态资源
  • DNS 预解析 dns-prefetch
  • TCP 预连接 preconnect
  • HTTP/2、HTTP/3 多路复用

资源体积

  • 代码 压缩、混淆、Tree Shaking
  • 图片优化:WebP/AVIF、懒加载、响应式图片
  • 字体:font-display: swap、子集化

加载顺序

  • 关键 CSS 内联,非关键异步加载
  • JS 用 async/defer、动态 import 懒加载
  • 移除未使用代码、第三方冗余脚本

渲染层面

  • 减少重排重绘
  • 骨架屏、loading 提升体验
  • 避免长任务阻塞主线程

架构层面

  • SSR / SSG / ISR
  • 微前端、路由懒加载
  • 接口合并、接口缓存

页面卡顿一般怎么排查?

  • 打开 Performance 录制,看长任务、频繁重排重绘
  • Lighthouse 查看 CLS、INP
  • 看是否有大量 JS 执行、频繁 DOM 操作
  • 看图层是否爆炸、内存泄漏
  • 看是否有强制同步布局

Vue 渲染性能优化手段

  • v-once 只渲染一次
  • v-show 代替 v-if(频繁切换)
  • computed 缓存计算结果
  • 组件合理拆分,避免大组件 rerender
  • key 正确使用
  • 虚拟列表
  • 懒加载组件、路由

虚拟列表原理?

只渲染可视区域内的 DOM,滚动时复用节点、更新内容,极大减少 DOM 数量,降低重排开销。

什么是重排(回流)、重绘、合成?

  • 重排(Reflow) :元素几何属性变化(宽高、位置、display、增删节点),浏览器重新计算布局,开销最大
  • 重绘(Repaint) :颜色、背景等样式变化,不改变布局,开销中等
  • 合成(Composite) :只改 transform、opacity,不触发布局和重绘,开销最小

哪些操作会触发重排?

  • 宽高、位置、边距、display 变化
  • 增删 DOM 节点
  • 获取布局相关属性(offsetTop、clientWidth、getComputedStyle 等)
  • 窗口 resize、字体变化
  • 修改伪类(:hover)

Vite 核心原理

Vite其核心原理是利用浏览器现在已经支持ES6的import,碰见import就会发送一个HTTP请求去加载文件。Vite启动一个 connect 服务器拦截这些请求,并在后端进行相应的处理将项目中使用的文件通过简单的分解与整合,然后再以ESM格式返回返回给浏览器。整个过程中没有对文件进行打包编译。

Webpack是先解析依赖、打包构建再启动开发服务器,Dev Server 必须等待所有模块构建完成,当我们修改了 bundle模块中的一个子模块, 整个 bundle 文件都会重新打包然后输出。项目应用越大,启动时间越长。

而 Vite利用浏览器对ESM的支持,当 import 模块时,浏览器就会下载被导入的模块。先启动开发服务器,当代码执行到模块加载时再请求对应模块的文件,本质上实现了动态加载。 开发阶段vite的速度远快于webpack,主要是因为:

** # webpack和vite的区别

  1. vite是go语言编写的,而webpack是原生js编写的,性能不如go语言(go语言操作是纳秒级别,而js是毫秒级别)
  2. vite利用了ES的modules,不需要打包,所以速度快
  3. webpack先打包,再启动开发服务器,请求服务器时直接给予打包后的结果;vite直接启动开发服务器,请求哪个模块再对哪个模块进行实时编译
  4. vite的主要优势就是快,缺点是相关生态还不完善webpack是先打包再启动开发服务器,vite是直接启动开发服务器,然后按需编译依赖文件。**

webpack和vite的区别

  1. vite是go语言编写的,而webpack是原生js编写的,性能不如go语言(go语言操作是纳秒级别,而js是毫秒级别)
  2. vite利用了ES的modules,不需要打包,所以速度快
  3. webpack先打包,再启动开发服务器,请求服务器时直接给予打包后的结果;vite直接启动开发服务器,请求哪个模块再对哪个模块进行实时编译
  4. vite的主要优势就是快,缺点是相关生态还不完善

webpack的核心概念

  • Entry:入口,Webpack 执行构建的第一步将从 Entry 开始,可抽象成输入。告诉webpack要使用哪个模块作为构建项目的起点,默认为./src/index.js
  • output :出口,告诉webpack在哪里输出它打包好的代码以及如何命名,默认为./dist
  • Module:模块,在 Webpack 里一切皆模块,一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块。
  • Chunk:代码块,一个 Chunk 由多个模块组合而成,用于代码合并与分割。
  • Loader:模块转换器,用于把模块原内容按照需求转换成新内容。
  • Plugin:扩展插件,在 Webpack 构建流程中的特定时机会广播出对应的事件,插件可以监听这些事件的发生,在特定时机做对应的事情。

Webpack的基本功能有哪些?

  • 代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等等
  • 文件优化:压缩 JavaScript、CSS、html 代码,压缩合并图片等
  • 代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载
  • 模块合并:在采用模块化的项目有很多模块和文件,需要构建功能把模块分类合并成一个文件
  • 自动刷新:监听本地源代码的变化,自动构建,刷新浏览器
  • 代码校验:在代码被提交到仓库前需要检测代码是否符合规范,以及单元测试是否通过
  • 自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。

Webpack打包优化常见的几种方案

减少打包时间

优化Loader的搜索范围、HappyPack、DllPlugin、代码压缩相关(启用gzip压缩)

减少包大小

按需加载、Scope Hoisting、 Tree shaking

如何利用webpack来优化前端性能?

  • 代码压缩(按需加载
  • 代码分割 splitChunks - 在optimization配置项中配置
  • 使用Dll进行分包
  • 使用路由懒加载

Babel 的原理是什么?

babel 的转译过程也分为三个阶段,这三步具体是:

  1. 解析 Parse: 将代码解析⽣成抽象语法树(AST),即词法分析与语 法分析的过程;
  2. 转换 Transform: 对于 AST 进⾏变换⼀系列的操作,babel 接受得 到 AST 并通过 babel-traverse 对其进⾏遍历,在此过程中进⾏添 加、更新及移除等操作;
  3. ⽣成 Generate: 将变换后的 AST 再转换为 JS 代码, 使⽤到的模 块是 babel-generator。

XSS、CSRF 怎么防范?

  • XSS:转义、CSP、不渲染危险 HTML
  • CSRF:token、SameSite、Referer 校验银行项目必问。

如何防止 XSS 和 CSRF?

  • XSS:对用户输入转义,不直接使用 v-html,配置 CSP,过滤特殊字符
  • CSRF:接口携带 token,设置 SameSite,校验 Referer银行项目还会做环境检测、设备风险识别、异地登录提醒

ref 和 reactive 区别,项目怎么选?

  • ref 用于基础类型(string/number/boolean),访问需要 .value
  • reactive 用于对象和数组,不需要 .value,但解构会丢失响应式项目中:表单简单字段用 ref,复杂数据对象用 reactive,解构时用 toRefs。

ref 和 reactive 区别

  • ref:支持所有类型,取值要 .value,可整体替换
  • reactive:只能对象 / 数组,不用 .value,不能整体替换
  • 解构 reactive 会丢失响应式,ref 不会

银行项目里路由权限怎么做?

  1. 登录后获取用户角色 / 菜单列表
  2. 通过 addRoute 动态添加路由
  3. 全局路由守卫 beforeEach 校验是否登录、是否有权限
  4. 无权限跳 403 或登录页同时配合 按钮级权限指令,控制增删改查按钮显示隐藏。