[前端高频面试] 2023年初级、中级面试题解

单纯为了自己面试方便,整理一些高频面试题,2023希望每个前端小伙伴找到心仪的工作!!

目录

「自我介绍(仅供参考)」

「HTML、CSS相关」

 » H5有哪些新特性?

 » 浏览器渲染机制、重排、重绘

» 简述CSS盒模型

» 如何让盒子水平垂直居中

» 如何实现一个三角形?

» CSS样式优先级

» display:none 和 visibility: hidden 的区别?

» 去除浮动的几种方式?(问可顺带答出BFC)

» 什么是BFC?BFC布局规则是什么?如何创建BFC?

» cookies、localStorage、sessionStorage的区别?

「JS」

» 什么是闭包?

» 作用域、作用域链、变量提升、函数提升分别是什么?

» JS有哪些数据类型?typeof、instanceof、类型转换

» 原型和原型链

» 继承(含ES6)、多种继承方式

» this指向、new关键字

» bind、call、apply的区别?

» JS 的设计模式?

» 深拷贝和浅拷贝

» TS 和 JS 有什么区别,优点和缺点

» 什么是事件冒泡、事件捕获、事件委托机制?

» 普通事件和事件绑定有什么区别?

» EventLoop(线程机制)

» 防抖和节流

» 函数柯里化

「ES6」

» var 、 const、 let 的区别?

» Promise的理解

» Promise.all 和 Promise.race

» 箭头函数和普通函数的区别?

» 什么时候不能使用箭头函数?

» Set 和 Map 的区别?

» map 和 forEach 的区别?

» for...in 和 for...of 的区别?

「Ajax API模块」

» ajax同源策略的理解,如何解决跨域?

» HTTP状态码有哪些?

» get 和 post 的区别?

» HTTP 和 HTTPS 的区别?

» 浏览器从输入url到渲染页面,发生了什么?

» 输入url 后 http请求的完整过程是什么?

» 浏览器缓存原理?(网站怎么优化)

「Vue2和Vue3」

» vue2 和 vue3的区别?

» vue2相比vue3有什么缺点?

» vue3有哪些新特性吗?它们带来什么影响

» vue2 数据双向绑定原理

» v-model的实现以及实现原理?

» MVVM开发模式的理解?

» vue组件通信有哪几种方式?

» 的作用是什么?或者vue切换页面如何保存状态?

» vuex有哪几种属性?

» vuex和pinia的区别,优缺点?

» vue-router路由实现原理是什么?有什么不同

» $route 和 $router的区别?

» vue中key 的作用是什么?

» 虚拟DOM和真实DOM的区别?

» computed 、watch 、methods的区别?

» 组件中的data为什么是一个函数?

» v-if 和 v-show的区别?

» vue数据变化视图不更新如何解决?

» 父子组件生命周期渲染顺序?

「前端性能优化」

» 前端性能优化的几种方式

» Vue首屏渲染优化有哪些?

» Vue项目做了哪些优化?

» CDN加速静态资源是什么?

「前端工程化」

» express 和 koa 有什么区别?

» 谈谈你对webpack的看法

» 前端为什么要进行打包和构建?

» webpack的基本功能有哪些?

» 常见的Webpack Loader?解决什么问题

» Loader 和 Plugin 的不同?

» 有哪些常见的 Plugin插件?解决什么问题的?

» Vite 和 Webpack区别?

» 为什么Vite 比 webpack速度快?

「职业技能规划、人事面试」

» 你的职业规划是什么?

» 为什么从上一家公司离职?

» 前端是如何学习的?

» 项目中有遇到什么难题?

» 还有什么想问的

「自我介绍(仅供参考)」

注意: 别紧张,目光注视面试官,不要有小动作,自信!!自信!!自信!!

面试官您好,我叫XXX,目前从事前端开发工作已经有X年,我上家公司是一个外包公司做别人的项目的,主要从事H5页面,后台管理系统和小程序等项目开发,其中H5和PC端是一套做保险相关业务的,在做H5的时候遇到过一个bug就是做微信SKD分享签名无效,请求的后台数据也返回了,后端就反映不是自己的问题,但反复排查发现是后端返回的签名和公众号配置的签名不一致,导致签名无效,分享失败...我呢平常喜欢逛一些技术社区丰富自己的技术,像知乎,掘金之类,并且自己也独立开发了个人博客网站,记录自己的工作总结和学习心得。 我的性格比较温和,跟同事朋友相处时比较外向,在工作中代码开发时我喜欢全心全意的投入,对于工作我也抱着认真负责的态度。面试官,以上是我的介绍,谢谢。

「HTML、CSS相关」

 » H5有哪些新特性?

  • 语义化标签: main、header、nav、article、section、aside、footer 
语义化指的就是合理正确的使用语义化标签来创建页面结构,如:
header、nav、footer,从标签上就能看出这个标签的作用,而不是滥用div
语义化的优点:
代码结构清晰,易于阅读、利于开发和维护
方便其他设备解析(如屏幕阅读器)根据语义渲染网页
有利于搜索引擎优化(SEO),搜索引擎爬虫会根据不同的标签来赋予不同的权重

 » 浏览器渲染机制、重排、重绘

网页生成过程:

  • HTMl被HTML解析器解析成DOM树
  • css被css解析器解析成CSSOM树
  • 结合DOM树和CSSOM树,生成一颗渲染树(Render Tree)
  • 生成布局(flow),将所有渲染树的所有节点进行平面合成
  • 将布局绘制(paint)在屏幕上

重排(也叫回流):当改变DOM元素位置和大小时,浏览器需要重新计算元素的几何属性,将其安放在界面的正确位置,这个过程叫做重排

触发:

  1. 添加或者删除DOM元素
  2. 元素位置改变、元素尺寸改变、内容改变
  3. 浏览器窗口尺寸改变

重绘: 当一个元素的外观发生改变,但布局没有发生变化,重新绘制外观的过程

触发: 改变元素的 color、 background、box-shadow等属性

总结:重排一定会引起重绘,重绘不一定会引起重排,重排的性能消耗比重绘大

如何避免性能影响?

  1. css避免使用table布局,避免设置多层内联样式
  2. 避免频繁操作DOM,对于大量插入DOM操作,建议使用文档片段;用js修改样式,最好不要直接写样式,而是通过class来修改样式

» 简述CSS盒模型

  • 标准盒子(内容盒子): 给盒子设置box-sizing:content-box  , 宽度 = 内容的宽度,如果后期添加了 padding 或border就会使盒子向外扩张
  • IE盒模型(边框盒子): box-sizing:border-box, 设置的宽度 = 内容宽度 + padding + border; 添加的padding和border就在原先设置的width里,padding和border越大就会向内部content扩张,导致content变小

» 如何让盒子水平垂直居中

  1. 绝对定位方法: top、bottom、left、right设置为0, margin:auto
  2. 绝对定位方法:top:50%;left:50%; margin-left: 宽度的一半; margin-top:高度的一半
  3. 绝对定位方法:不确定div的宽度和高度,利用transform: translate(-50%,-50%)
  4. flex弹性盒布局:父:display:flex; 垂直居中-align-items:center; 水平居中-justify-content:center

» 如何实现一个三角形?

.box {
    width: 0;
    height: 0;
    line-height: 0;
    font-size: 0;
    border: 50px solid transparent;
    border-left-color: pink;
}
想要哪个方向的三角设置哪个方向的边框颜色

» CSS样式优先级

!important(无穷大)  > 行内样式(1000) > id选择器(100) > 类选择器(10)、属性选择器、伪类选择器  > 标签选择器、元素选择器(1) > 继承或 *通配符选择器(0)

  1. id选择器 ( # myid)
  2. 类选择器( .box )    属性选择器 (a[resl = 'external'])  伪类选择器 (a:hover, li:nth-child())
  3. 标签选择器 (p,div,h1-h6)       伪元素选择器 (p::after    p::before)

» display:none 和 visibility: hidden 的区别?

共同点: 都用来隐藏元素

不同点:

  • display:none; 隐藏元素不占据任何空间,会导致重排和重绘
  • visibility:hidden; 隐藏元素的时候还会占据空间,只是内容不可见,只会导致重绘
  • display:none; 非继承属性,子孙节点的消失是因为渲染树消失造成的,通过修改子孙节点属性无法显示
  • visibility:hidden; 继承属性,子孙节点的消失是由于继承了hidden,通过设置visibility:visible;可以让子孙节点显示

» 去除浮动的几种方式?(问可顺带答出BFC)

浮动的元素会脱离文档流,什么叫脱离文档流,举个栗子: 有一天你和你老板说:世界那么大,你想去看看,那之后你老板就管不了你了。脱离文档流也同理,一个元素一旦浮动,就会脱离文档流,那父元素也就管不了他了,布局就会往前推进, 父元素就会出现 高度塌陷 的问题。

  1. 将父级设置成浮动,float:left 缺点:父级设置浮动了,那爷爷不也受影响了,又得解决爷爷的高度塌陷问题,无限套娃🪆
  2. 给父级设置定位absolute, 缺点:也会脱离文档流,影响布局
  3. 给父级设置overflow:hidden,缺点:文本过长,且包含英文时,会出现英文文本被隐藏
  4. 给父级设置对应的高度,缺点:浮动元素定高还好,如果不定高的情况就不灵活了
  5. 末尾增加空的div,设置clear:both,缺点:增加了一个div标签
  6. 给父级添加伪元素进行clear, ::after,伪元素不会被当作dom标签渲染出来(目前最优解)

» 什么是BFC?BFC布局规则是什么?如何创建BFC?

BFC是块级格式化上下文,是CSS布局的一个概念,是一个环境,里边的元素布局不会影响到外面的元素布局

布局规则(原理):

A.内部的box会在垂直方向,一个接一个的放置

B.box垂直方向的距离由margin决定,属于同一个BFC的两个box 的margin会发生重叠

C.BFC的区域不会与float元素box重叠

D.BFC是一个独立的容器,里边的子元素不会影响外边的元素

E.计算BFC高度的时候,浮动元素也会参与计算

哪些元素会生成BFC?

  • 根元素
  •  float属性为left 和 right
  • position为absolute和fixed
  • overflow不为 visible,为auto、scroll、hidden
  • display为inline-block, table-cell, flex, inline-flex

» cookies、localStorage、sessionStorage的区别?

  • 数据生命周期: 
    • cookie:可以设置失效时间,默认浏览器关闭失效
    • local:本地持久化存储,除非手动删除否则永久保存
    • session:仅在当前页有效,浏览器关闭或者页面关闭就会被清除
  • 存放数据大小:
    • cookie:数据不能超过4k,主要用于保存登录信息,比如登录某个网站看到“记住密码”,通过往cookie存入一段辨识用户的数据来实现的
    • local、session:虽然也有存储大小限制,但比cookie大很多,一般在5M或更大
  • http请求
    • cookie每次请求都会携带http头中,如果使用cookie保存过多数据会带来性能问题;
    • local、session在浏览器中保存,不参与和服务器的通信
  • 作用域不同
    • sessionStorage:不在不同的浏览器窗口中共享,即使在同一个页面
    • localStorage和cookie:在所有同源窗口中都是共享的;只要浏览器不关闭,数据仍然在

  从安全性来说,因为每次http请求都会携带cookie,这样浪费了带宽,所以cookie尽可能少用,此外cookie需要指定作用域,不能跨域调用,限制很多;local和session可以用来在页面传递参数,session可以保存一些临时的数据,防止用户刷新页面丢失一些参数

「JS」

» 什么是闭包?

通俗一点就是打通了一条函数外部访问函数内部作用域的通道,正常情况下函数外部是访问不到函数内部作用域变量的

如何判断闭包: 函数嵌套函数、内部函数被return、内部函数调用外层函数的局部变量

优点: 隔离作用域,不造成全局污染

缺点: 导致函数变量一直保留在内存中,过多的闭包会导致内存泄露(任何对象不需要它后仍然还在)

闭包的主要作用: 延伸变量的作用范围

适用场景: 封装组件、for循环和定时器结合、节流和防抖也会用到

vue中出现内存泄露的情况: 

  1. 全局变量引起的内存泄露
  2. 监听事件没有解绑,在页面销毁的时候,顺便解绑,释放内存,原则不用的东西及时归还
  3. echarts页面切换的时候,还保留在内存中,导致浏览器卡顿

» 作用域、作用域链、变量提升、函数提升分别是什么?

作用域: js识别变量的范围,包括全局作用域(任何地方都可访问的变量)、局部作用域(只在函数内部定义的变量范围)、块级作用域(ES6用const、let定义的变量都认为是块级作用域中的变量)

作用域链: 从当前作用域开始一层一层向上寻找某一个变量,直到找到全局作用域还是没找到,就放弃!这查找的过程,就是作用域链

变量提升: 使用var关键字定义的变量,只提变量,不提赋值

函数提升: 使用function声明的函数,只提函数,不调用函数

» JS有哪些数据类型?typeof、instanceof、类型转换

js属于弱类型语言(支持隐式转换)

数据类型: String、Number、Boolean、Null、undefined、Object(function,array--堆内存)、symbol(ES10BigInt--栈内存)

typeof: 基本数据类型除了null返回Object,其他都返回对应的类型; 引用数据类型除了函数其他都返回Object

instanceof: 判断一个对象是否是另一个对象的实例,注意只能用来判断对象

null表示空对象,undefined表示已在作用域中声明但未赋值的变量

» 原型和原型链

原型: js中万物皆对象,每一个对象都有自己的属性,js中如何让多个对象共用一个或多个方法呢?原型的出现就是为了解决这个问题,每一个对象都有一个与他关联的原型对象,每次获取对象属性都是一次查找过程,在对象的自有属性中找不到就会去查找它的原型对象

原型链: 原型连成一条链,当我们访问一个对象的属性时,如果这个对象内部没有这个属性,我们就会去它的原型对象中查找这个属性,这个原型对象又会有自己的原型,于是这样一直向上查找,直到找到Object为null的时候,这个查找的过程就是原型链

总结: 

  1. 原型存在的意义就是组成原型链
  2. 原型链存在的意义就是继承:访问对象属性时,在对象本身找不到,就会按原型链一层一层找,说白了就是一个对象可以访问其他对象的属性
  3. 继承存在的意义就是属性共享:好处:1.代码重用2.可扩展,不同对象可以继承相同的属性,也可以定义只属于自己的属性

» 继承(含ES6)、多种继承方式

  • 原型链继承 – 重写prototype
  • 构造函数继承 – Parent.call(this)
  • 组合继承 – 重写prototype + Parent.call(this)
  • 原型式继承 – Object.create(Parent),不添加额外的属性和方法
  • 寄生式继承 – Object.create(Parent),添加额外的属性和方法
  • 寄生组合式继承 – Object.create(Parent) + Parent.call(this)
  • ES6类的继承 – extends + super(props)
  • 原型链继承口述:写个父类、子类,子类的原型为父类的实例,子类.prototype = new 父类,修正子类原型为子类本身, 子类.prototype.constructor = 子类, new 子类即可调用父类方法,构造函数继承,写个父类、子类,在子类中父类.call(this)即可实现

» this指向、new关键字

  • 构造器函数调用:this指向new创建的新对象
  • 箭头函数不绑定this: 捕获在上下文的this,在哪里定义指向谁
  • 全局函数中,this指向的是window
  • 普通函数this,谁调用指向谁

     new操作符干了什么?

  1. 内存中创建一个新对象
  2. this指向这个新对象
  3. 执行构造函数里边代码添加属性
  4. 返回新对象(所以构造函数里不用return)

» bind、call、apply的区别?

  • 都可以改变函数内部this指向
  • call、apply可以调用函数,bind不会调用函数
  • call和apply传递的参数不一样,call传递参数列表,apply传递的是数组形式

» JS 的设计模式?

  1. JS的设计模式有很多,我知道的有单例模式、工厂模式、观察者模式
  2. 单例模式:全局的,唯一的实例,比如:window对象、document对象、vuex的store
  3. 工厂模式:用一个函数来创建实例,返回new创建的实例(隐藏new关键字),场景:jq的$函数
  4. 代理模式: Proxy 访问一个对象属性之前做一个拦截(做一些额外的业务或逻辑操作),如:vue3的响应式原理,通过get,set来获取和修改
  5. 观察者模式:定义了对象间一种一对多的依赖关系,当目标对象的状态发生改变的时候,所有依赖它的对象都会得到通知

» 深拷贝和浅拷贝

浅拷贝: 只拷贝最外层对象,如果属性是基本数据类型,拷贝的就是基本数据类型的值,如果属性是引用类型,拷贝的就是内存地址,所以如果其中一个对象改变了这个地址,就会影响到另一个对象,即互相影响; 用ES6的 Object.assign({}, {})浅拷贝进行对象合并,ES6扩展运算符

深拷贝: 将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象且修改新对象不会影响原对象,可以通过JSON.parse(JSON.stringify(对象))的方式实现深拷贝,缺点 不可拷贝 undefined、function、RegExp类型的数据

» TS 和 JS 有什么区别,优点和缺点

TS是JS的超集,可以在ts中使用原生的 js 语法; JS是一种脚本语言,用于创建动态网页

TS是强类型语言,支持静态和动态类型; JS是动态弱类型语言

TS可以在编译期间发现错误; JS只能在运行时发现错误

TS不允许改变变量的数据类型; JS变量可以被赋予不同类型的值

TS优点:

1. 提供了静态类型检查,可以在编译时捕获一些常见的错误

2.包含了更多类型信息和注释,便于阅读和理解

3.提供更好的代码提示和自动补全功能,可以提高开发效率

JS的优点:

1. 直接在浏览器中运行,不需要编译成其他语言

2.具有广泛的应用和强大的生态系统,有大量的库和工具

3.可以快速迭代开发,适合小型项目

» 什么是事件冒泡、事件捕获、事件委托机制?

事件冒泡:子元素事件的触发会影响到父元素事件;从内而外,关闭事件冒泡: e.stopPropagation()

事件捕获:父元素的事件会影响到子元素的事件;从外而内,阻止默认行为:e.preventDefault()

事件委托:某个事件本来是自己干,但自己不做交给别人来干,利用冒泡处理子元素的事件

提高性能: 如当有很多li同时需要注册事件时,如果使用传统方法来注册事件的话,就需要给每一个li注册事件,但如果使用事件委托,只需要将事件委托给父元素即可

» 普通事件和事件绑定有什么区别?

普通事件的onclick只支持单个事件,而且会被其他onclick事件覆盖;且不可以取消

事件绑定addEventListener可以添加多个事件,也不会被覆盖,绑定后可用removeEventListener取消

» EventLoop(线程机制)

js一大特点就是单线程,也就是说,同一个时间只能做一件事

单线程就意味着,所有任务都需要排队,前一个任务结束,后一个任务才会执行,如果前一个任务耗时很长,后一个任务不得不等着; 于是所有任务分为两种同步任务和异步任务,异步队列又分为宏任务和微任务队列,宏任务执行时间长,所以微任务优先于宏任务; 

微任务: .then、async、await、.catch、.finally

宏任务: setImmediate、setTimeout、setInterval、ajax请求、读取文件、DOM事件

执行顺序: 同步——promise、process.nextTick——微任务——宏任务

同步任务: 代码一行行执行,前面代码没有执行完.后面代码就不会执行,代码阻塞,等待任务返回结果

如: 烧水做饭,必须等水开10分钟之后,再去切菜、炒菜(强调执行的顺序)

给银行打电话查询余额,柜台帮您查询期间您肯定不会挂断电话,而是一直在等着回复您卡里余额已不足

异步任务: 当前代码没有执行完,可以执行后面代码

如:烧水的同时,利用10分钟切菜、炒菜(不按顺序执行,解决代码阻塞,提升代码执行效率和性能)

当你女朋友打电话给你说她想要一个新iphone,然后就把电话挂了,这个时候她肯定不会等待你的反馈,她默认你一定会完成这个任务,而你接到这个任务就去买手机了,女朋友通知完你就去干自己的事了,这个线程也不会被阻塞,等你有了结果在去通知她,这就是异步

» 防抖和节流

防抖(debounce): 确保只有最后一次的执行,比如:搜索列表请求,用户在不断的输入值时,用防抖来节约请求资源,只有最后一次回车才能返回结果; 用户按钮的点击事件,为了防止多次重复提交也会使用防抖,减少请求次数,节约请求资源,不消耗性能

如: 坐公交,司机需要等最后一个人上车才能关门,每上一个人,司机就会多等待几秒在关门

节流(throttle): 确保正在执行的时候,不要再次重复触发执行; 如:页面滚动事件,触底加载更多;csdn写文章,边写边保存

如: 一个水龙头在滴水,可能一次会滴很多,但我们希望500ms滴一滴,保持这个频率;希望函数在一个可以接受的频率重复调用

防抖(简单版本):
function debounce(fn, delay) {
    let timer = null
    clearTimeout(timer); 清除上次定时器,然后重新延迟
    timer = setTimeout(function () {
        fn()
    }, delay) 
}

» 函数柯里化

将多个参数的函数改为一个参数的函数

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>函数柯里化-经典面试题</title>
</head>
<body>
  <script>
    // function sum(a, b, c, d, e) {
    //   return a + b + c + d + e
    // }
    // 需求: 改写函数sum实现: 参数传递5个实现累加
    // sum(1)(2)(3)(4)(5)
    // sum(1)(2,3)(4)(5)
    // sum(1)(2,3,4)(5)
    // sum(1)(2,3)(4,5)

    function sumMaker(length) {
      let nums = []
      function sum(...argus) {
        nums.push(...argus)
        // 2.判断参数的个数
        if (nums.length >= length) {
          // 2.1 长度到5就累加
          const res = nums.slice(0, length).reduce((p, v) => p + v, 0)
          // 注意:累加之后要清空数组,保证下次累加的值是正确的
          nums = []
          return res
        } else {  
          // 2.2 长度没到5返回函数接收剩余参数
          return sum
        }
      }
      return sum
    }
	const sum1 = sumMaker(5)
    console.log(sum1(1)(2)(3)(4)(5))
    const sum2 = sumMaker(6)
    console.log(sum(1, 2, 3)(4, 6),(7))
  </script>
</body>

</html>
  // 通过typeofTest生成: 
  // 动态传入判断的类型
    const typeofTest = function (type) {
      function isUndefined(thing) {
        return typeof thing === type
      }
      return isUndefined
    }
    // 简化版:
    const typeofTest1 = type => thing => typeof thing === type

    const isString = typeofTest('string')
    console.log(isString('1111'))

「ES6」

新增symbol类型 表示独一无二的值,用来定义独一无二的对象属性名;
const/let 都是用来声明变量,不可重复声明,具有块级作用域。存在暂时性死区,也就是不存在变量提升。(const一般用于声明常量);
变量的解构赋值(包含数组、对象、字符串、数字及布尔值,函数参数),剩余运算符(...rest);
模板字符串(${data});
扩展运算符(数组、对象);;
箭头函数;
Set和Map数据结构;
Proxy/Reflect;
Promise;
async函数;
Class;
Module语法(import/export),解决命名冲突,提高复用性,提高代码可维护性

» var 、 const、 let 的区别?

  • var声明的变量可以重复声明,let和const重复声明会报错
  • var声明的变量不赋值是undefined,let和const声明变量不赋值报错
  • var定义的变量没有块级作用域的概念,全局变量,let和const有块级作用域,局部变量
  • const定义的常量不能修改值,引用类型可以修改; 
  • var 声明的变量会成为 window对象的属性,let 和 const不会
  • let和const存在暂时性死区,如果在声明变量之前使用该变量就会报错;
  • 定义对象、函数、用const; 计算的一些变量用let

» Promise的理解

  • promise是ES6提出的异步编程解决方案,相比传统的回调地狱,promise会让异步操作变得更加的优雅。
  • ES6规定promise是一个构造函数,需要通过new关键字来生成一个promise实例对象
  • Promise的构造函数接受一个函数作为参数,函数的代码在 new Promise()的时候立刻执行,可以在这里执行异步代码的操作。并且该函数存在默认两个参数,resolve和reject,这两个参数也是函数,用来标记异步执行的状态。
  • 比如resolve,当promise异步操作完成的时候,可以调用resolve函数,标记当前异步操作已经完成
  • 而 reject在异步操作失败调用,用来标记当前异步操作失败了。
  • 标记的状态可以通过promise实例对象.then和.catch获取, .then是异步完成的回调, .catch是异步失败的回调
  • promise对象有三种状态, pending(等待)、fulfilled(成功)、rejected(失败),一个Promise对象初始化的状态是pending,调用 resolve 后将Promise 的状态扭转为 fulfilled, 调用 reject 后将Promise状态扭转为 rejected, 这两种扭转一旦发生就不能改变到其他状态!!
  • async/await是ES8提出的搭配promise,编写形似同步代码来处理异步流程,提高代码的简洁性和可读性
  • async 返回的是promise对象,await直接获取resolve传递出来的异步数据
async function fun1() {
          let data = await fun2()
          console.log(data) //then中执行的代码
      }
      async function fun2() {
          console.log(200) // 同步
          return 100
      }
      fun1()     // 200 100
      
   // 1、3、5、8、2、6、7、4
        console.log(1)
        async function async1() {
            await async2()
            console.log(2)
        }
        async function async2() {
            console.log(3)
        }
        async1()
        setTimeout(() => console.log(4),0)
        new Promise(resolve => {
            console.log(5)
            resolve()
        }).then(function() {
            console.log(6)
        }).then(function() {
            console.log(7)
        })
        console.log(8)

» Promise.all 和 Promise.race

promise.all可以将多个promise实例包装成一个新的promise实例。同时,成功和失败的返回值是不同的,成功的时候返回一个数组,失败的时候返回最先被reject失败状态的值。

promise.race 就是赛跑的意思,哪个结果获取的最快,就返回哪个结果,不管结果本身是成功还是失败状态

  • Promise.all 可以比作接力跑,必须都成功才能胜利
  • Promise.race可以比作短跑,谁跑的快谁就胜利

» 箭头函数和普通函数的区别?

     定义参数:如果只有一个参数可以不写括号,函数体只有返回值可以不写return

      this指向不同: 普通函数this谁调用指向谁,箭头函数this在哪里定义,this就指向谁 

» 什么时候不能使用箭头函数?

普通函数支持 arguments, 箭头函数不支持 arguments

  • 箭头函数不能作为对象方法,定义一个对象里边的函数不能用箭头函数
  • 箭头函数不能为原型
  • 箭头函数不能为构造函数
  • 箭头函数不能为vue生命周期函数写法

» Set 和 Map 的区别?

  • 应用场景: set 用于数据重组; map用于数据存储
  • set: 1. 成员不能重复 2. 只有key没有value,类似数组 3. 可以遍历,方法有 add,delete,has
  • map: 1.键值对的集合 2.可以遍历,方法有get set

» map 和 forEach 的区别?

共同点: 都是用来做循环的; 每次循环都支持三个参数,当前项、索引、原数组; 函数this都指向window

不同点: 1.forEach没有返回值; 2. map有返回值可以return,返回一个新数组

» for...in 和 for...of 的区别?

  • 推荐循环对象属性时用for..in, 循环数组的时候用 for..of
  •  for..in 循环出来的是key, for...of 循环出来的是value
  • for...of不能循环普遍的对象,需要搭配 Object.keys()使用

「Ajax API模块」

» ajax同源策略的理解,如何解决跨域?

同源策略是浏览器的一种安全机制,防止他人恶意攻击网站,如果协议、域名、端口号有一个不同就会产生跨域(及域名和ip地址访问);跨域请求产生时,请求是发出去了,也是有响应的,仅仅是浏览器同源策略,认为不安全拦截了结果,不将数据传递给前端使用

img、link、script标签允许跨域加载

如何解决跨域:

  1. jsonp(利用script标签没有跨域限制的漏洞实现,缺点:只支持get请求)
  2. CORS跨域资源共享(设置Access-Control-Allow-Origin:指定可访问资源的域名)
  3. 代理(前端代理和nginx后端反向代理)config文件中配置proxy
  4. postMessage:h5新增API用于多窗口消息,页面嵌套iframe消息传递,通过onmessage监听传递的数据
  5. websocket:H5的持久化协议,实现了浏览器与服务器的全双工通信
  6. 各种嵌套iframe的方式,不常用

» HTTP状态码有哪些?

  • 1xx——临时响应,请求已经发送,继续处理
  • 2xx——请求成功
  • 3xx——重定向,完成请求必须进行更进一步的操作
  • 4xx——客户端错误,请求的语法错误或请求无法实现
  • 5xx——服务器错误,服务器处理请求时内部发生错误

» get 和 post 的区别?

  1. get参数通过url传递,post放在Request body中
  2. get参数暴露在url上不安全,不能用来传递敏感信息
  3. get请求在url中传递的参数是有长度限制的(基本上是2KB),post没有
  4. get请求在浏览器回退不会再次请求,post会再次提交请求
  5. get请求会被浏览器主动缓存,post不会除非手动设置
  6. get请求参数会被完整保留在浏览器历史记录里,post参数不会
  7. get一般用于查询信息,post一般用于提交某种信息进行修改操作

» HTTP 和 HTTPS 的区别?

  1. http传输的协议都是未加密的,它就是明文的,所以http传输隐私的信息是不安全的,为了保证这些数据能加密传输就诞生了https
  2. http标准端口是80,而https标准端口是443
  3. 在 OSI 网络模型中,http工作于应用层, 而https 的安全传输机制工作在传输层
  4. http 无法加密, 而 https 对传输的数据进行加密
  5. http 无需证书, 而 https需要CA机构WoSign的颁发的SSL证书

» 浏览器从输入url到渲染页面,发生了什么?

三个方面:
网络篇:
        构建请求
            查找强缓存
            DNS解析
            建立TCP连接(三次握手)
            发送HTTp请求(网络请求后网络响应)
浏览器解析篇:
         解析html构建DOM树
            解析css构建CSS树,样式计算
            生成布局树(Layout Tree)
浏览器渲染篇:
        建立图层树(Layer Tree)
        生成绘制列表
        生成图块并栅格化
        显示器显示内容
        最后断开连接:TCP 四次挥手
        (浏览器会将各层的信息发送给GPU,GPU会将各层合成,显示在屏幕上)

» 输入url 后 http请求的完整过程是什么?

建立TCP连接——发送请求行——发送请求头——(到达服务器)发送状态行——发送响应头——发送响应数据——断开TCP连接

» 浏览器缓存原理?(网站怎么优化)

缓存: 浏览器本身就有缓存功能,会把上一次代码存起来,再次访问时,没有拿新的代码,直接拿的缓存

强制缓存: 不发请求到服务器,直接拿缓存

协商缓存: 发送请求到服务器,服务器告诉你拿,你就拿缓存, 不拿缓存就拿新的代码

协商缓存可以解决强制缓存的情况下,资源不更新的问题

H5的manifest也可以缓存一下,优化网站(现在很少用了)

「Vue2和Vue3」

» vue2 和 vue3的区别?

  1. 响应式原理:vue2响应式原理是Object.defineProperty(), vue3响应式原理基础是proxy
  2. 生命周期钩子名称: vue3前面加了 on
  3. Composition API(组合式API): vue2是选项API,一个代码逻辑拆分在不同的位置,导致可读性很差, vue3组合式API,逻辑复用,增强代码可读性
  4. 对于TS的支持: vue3更好的拥抱TS
  5. 根节点: vue2支持一个根节点, vue3支持多个根节点
  6. 新的内置组件
  7. diff算法

» vue2相比vue3有什么缺点?

  • 性能问题: vue2采用的是双向数据绑定和脏检查的方式,对于大型应用或复杂组件来说,性能可能会受影响
  • 大量代码: vue2需要引入大量的代码来支持其功能,使得包体积很大
  • 逻辑复用: vue2对逻辑复用支持不是很友好
  • ts支持不佳: vue2对于ts支持比较弱

» vue3有哪些新特性吗?它们带来什么影响

  • 性能提升 更小巧、更快速 支持自定义渲染器 支持摇树优化:一种在打包时去除无用代码的优化手段 支持Fragments和跨组件渲染
  • API变动 模板语法99%保持不变 原生支持基于class的组件,并且无需借助任何编译及各种stage阶段的特性 在设计时也考虑TypeScript的类型推断特性 重写虚拟DOM可以期待更多的编译时提示来减少运行时的开销 优化插槽生成可以单独渲染父组件和子组件 静态树提升降低渲染成本 基于Proxy的观察者机制节省内存开销
  • 不兼容IE11 检测机制更加全面、精准、高效,更具可调试式的响应跟踪

» vue2 数据双向绑定原理

核心主要通过数据劫持结合发布者-订阅者模式来实现的。

通过Object.defineproperty()来劫持各个属性的getter和setter,在数据变动的时候发布给订阅者,触发相应的监听回调渲染视图,缺点就是没办法监听到数组的变化, vue3的proxy可以直接监听对象和数组的变化,并且有多达13种拦截方法

» v-model的实现以及实现原理?

本质上就是一个语法糖,可以看成是value+input方法的语法糖。可以通过model属性的prop和event属性来进行自定义。原生的model会根据标签的不同生成不同的事件和属性。

  • text和textarea元素使用value属性和input事件
  • checkbox和radio元素使用checked属性和change事件
  • select字段将value作为prop将change作为事件

» MVVM开发模式的理解?

MVVM是Model-View-ViewModel的缩写,Model代表数据模型,View代表ui组件,ViewModel代表桥梁,当数据发生变化的时候,视图也发生变化,当视图发生变化的时候,数据也会跟着同步变化

» vue组件通信有哪几种方式?

  • 父传子: 子props接收,通过ref属性给子组件设置一个名字,父通过$refs组件名来获取子组件,子通过$parent来获取父
  • 子传父: 子组件通过 $emit 方法来传递参数
  • 兄弟组件: 兄弟组件之间本身没有任何的依赖关系,也就是说他们两个之间其实没有关系 对于两个没有关系的组件,那如果想建立联系传值的话,肯定需要一个第三方,让第三方来建立两个没有任何关系的组件
  • 第一个EventBus事件总线,vue3移除了$on/$off等方法,不在使用eventBus,用mitt.js替换(用于任意组件通信);   第二个就是vuex来进行数据共享
  • 跨级组件通信: $attr / $listeners , Provide / inject

» <keep-alive>的作用是什么?或者vue切换页面如何保存状态?

内置组件,一般结合路由和动态组件,实现组件缓存,一般用在用户频繁点击的地方

keep-alive提供include、exclude属性,两者都支持正则表达式和字符串

include: 只要匹配的组件就会被缓存

exclude: 任何匹配的组件都不会被缓存,优先级高

keep-alive对应两个钩子函数: activated 组件渲染后调用; deactivated组件销毁后调用

例如: 有一个列表页和详情页,用户一直来回高频率切换两个页面,一直在进行挂载销毁挂载销毁,消耗资源,可以使用keep-alive进行缓存,这样来回切换的时候直接从缓存里渲染,而不是重新渲染

» vuex有哪几种属性?

  • state: 默认的初始状态
  • getters: 有点类似vue.js中计算属性
  • mutations: 唯一更改state状态的方法,且必须是同步函数
  • actions: 提交mutations,通过store.commit(),而不是直接更改状态,包含任何异步操作,store.dispatch()
  • modules: 当state很复杂臃肿的时候,可以分模块,每个模块拥有自己的state,mutations,actions

实现流程:

  1. 在vue组件里面,通过dispatch来触发actions提交修改数据的操作。
  2. 然后再通过actions的commit来触发mutations来修改数据。
  3. mutations接收到commit的请求,就会自动通过Mutate来修改state(数据中心里面的数据状态)里面的数据。
  4. 最后由store触发每一个调用它的组件的更新

» vuex和pinia的区别,优缺点?

  • 都是数据状态管理仓库,pinia是vuex 的升级版,轻量级,体积更小,性能更优
  • pinia有两种语法, 一种是选项式API,一种是组合式API, 同时支持Vue2/Vue3,对ts支持会更好
  • pinia可以创建多个全局仓库,vuex 一个仓库嵌套模块,结构复杂
  • pinia核心概念有state、getter、action, vuex有五部分
  • pinia可以直接修改state状态值,vuex通过mutation中的方法修改
  • pinia不支持时间旅行和编辑等调试功能, vuex支持

» vue-router路由实现原理是什么?有什么不同

路由本质上就是建立URL和页面之间的映射关系,路由实现原理的核心之一就是“更新视图但不重新请求页面”,目前实现路由主要两种方式:1.url中的hash  2.H5中的history

不同点: 

  1. hash路由地址上有#, history没有
  2. 回车刷新的时候hash会加载对应的页面,history会报错404,需要后台的配置
  3. history有历史记录,h5新增了pushState和replaceState修改历史记录,并不会立刻发送请求
  4. hash兼容性好,history针对高级浏览器
  5. hash不会重新加载页面,单页面应用必备

» $route 和 $router的区别?

$router: 为路由实例,是全局路由对象,包含路由跳转方法,钩子函数等,$router.push() $router.go(1)

$route: 路由信息对象|| 跳转的路由对象,包括:name,params,path,query,hash,fullPath,matched参数

» vue中key 的作用是什么?

key的作用主要是为了高效的更新虚拟DOM

好比给你起名字,如果没有名字,渲染列表是不是从0-100得重新渲染,如果你有名字的情况下,刚好你名字对应的数据变动了,然后根据名字直接找到这个值修改就好,就不需要重新渲染了

» 虚拟DOM和真实DOM的区别?

  • 虚拟dom 不会进行排版和重绘操作
  • 虚拟dom 就是把真实dom 转换为 js代码
  • 虚拟dom 进行频繁修改,然后一次性比较并修改真实dom 中需要改的部分,最后并在真实DOM中进行排版与重绘,减少过多dom节点排版与重绘损耗

» computed 、watch 、methods的区别?

  实际得到的数据不能直接在页面中展示,需要做一些处理,就使用computed

  • watch应用场景
    • 一个数据影响多个数据
    • 支持异步
    • 不支持缓存,数据改变会直接触发相应的操作
  • computed适用场景:
    • 一个数据被多个数据影响,上来就执行
    • 不支持异步
    • 有缓存,只有依赖的数据发生变化,才会重新进行计算
    • 和data里边属性名不能相同
  • methods:
    • 上来就执行一次,只要页面重新渲染就会执行
    • 必须有一定触发条件(点击事件)
    • 不需要缓存可以使用methods

» 组件中的data为什么是一个函数?

组件是一个可复用的实例,当你引用一个组件的时候,组件里的data如果是一个普通的对象,所有用到这个组件的引用都是同一个data,就会造成数据污染.

将data封装成函数后,在实例化组件的时候,我们只是调用了data函数生成的数据副本,避免了数据污染

» v-if 和 v-show的区别?

v-if 直接创建和销毁DOM让元素显示和隐藏

v-show 通过css属性 display:none来控制显示和隐藏

如果频繁切换,使用v-show; 反之v-if, v-if消耗性能, v-for比v-if优先级更高,虽然用起来不报错,但是性能不高,如果循环5个元素,v-if 也会被执行5次, 会造成内存的浪费

» vue数据变化视图不更新如何解决?

在created中操作DOM报错,在这个时候vue实例没有挂载,获取不到DOM,可以使用this.$nextTick(()=>{})回调函数获取DOM

» 父子组件生命周期渲染顺序?

加载渲染过程: 父beforeCreate、父created、父beforeMount、子beforeCreate、子created、子beforeMount、子mounted、父mounted

子组件更新过程: 父beforeUpdate、子beforeUpdate、子updated、父updated

父组件销毁过程:父beforeDestory、子beforeDestory、子destoryed、父destoryed

常用的生命周期: created获取数据, mounted操作Dom, beforeDestory销毁实例防止内存泄露

「前端性能优化」

» 前端性能优化的几种方式

1. 浏览器缓存
2. 防抖、节流
3. 资源懒加载、预加载
4.开启Nginx gzip压缩
三个方面来说明前端性能优化
一: webapck优化与开启gzip压缩
    1.babel-loader用 include 或 exclude 来帮我们避免不必要的转译,不转译node_moudules中的js文件
    其次在缓存当前转译的js文件,设置loader: 'babel-loader?cacheDirectory=true'
    2.文件采用按需加载等等
    3.具体的做法非常简单,只需要你在你的 request headers 中加上这么一句:
    accept-encoding:gzip
    4.图片优化,采用svg图片或者字体图标
    5.浏览器缓存机制,它又分为强缓存和协商缓存
二:本地存储——从 Cookie 到 Web Storage、IndexedDB
    说明一下SessionStorage和localStorage还有cookie的区别和优缺点
三:代码优化
    1.事件代理
    2.事件的节流和防抖
    3.页面的回流和重绘
    4.EventLoop事件循环机制
    5.代码优化等等

» Vue首屏渲染优化有哪些?

  • 图片压缩/懒加载
  • 禁止生成 .map 文件
  • 路由懒加载: 如果不进行懒加载,整个项目的代码都会打包到一个js文件里,js文件体积会非常大,当用户进行网络请求时,首屏加载的速度会非常慢,使用模块懒加载的话,大js文件会分成多个小js文件,网页会按需加载,大大提升了首屏加载速度
  • cdn 引入公共库
  • 开启 GZIP 压缩
  • 渲染优化,尽量减少重绘和重排

» Vue项目做了哪些优化?

  • v-if 和 v-show区分使用场景
  • computed 和 watch 区分使用场景
  • v-if 避免和 v-for 一起使用, v-for循环必须添加唯一的key
  • 数据过多的时候,做分页
  • 合理利用浏览器缓存
  • 第三方插件按需引入
  • 路由懒加载 / 小图转base64
  • vuex、vue-router模块化,提高开发效率

» CDN加速静态资源是什么?

首先呢您肯定在京东自营买过东西,在京东买东西基本隔天就能到货,这是为什么呢?

细心的话就会发现京东发货地址离我们很近,那是因为京东将商品放在八大仓库里,我们从京东买东西会从就近的仓库发货,这样就省去了物流时间,基本第二天就能送到,速度很快

那当我们访问网络时,我在北京访问一个网络,站点服务器在上海,这个时候访问速度是不是会变慢,那怎么才能向京东买东西一样快速到货,就用到了CDN

CDN就是你的网站缓存,而CDN节点相当于京东八大仓库,把网站内容存到了CDN节点,然后每个请求网站的用户就近获取网站数据,大大减短了长距离传输和网络拥堵的情况下的访问速度

「前端工程化」

» express 和 koa 有什么区别?

1. 语法区别

  • express 异步使用回调
  • koa1 异步使用generator + yeild
  • koa2 异步使用 async / await

2. 中间件区别

  • express 本身无洋葱模型,需要引入插件,不支持 context
  • koa 采用洋葱模型,进行顺序执行,出去反向执行,支持 context 传递数据

3. 集成度区别

  • express 内置了很多中间件,集成度高,使用省心
  • koa 轻量简洁,容易定制

» 谈谈你对webpack的看法

webpack是一个 js 的模块打包工具,可以使用webpack管理项目中的 js模块依赖

  • webpack提供了一些默认的配置,比如:devServer,可以利用devServer快速启动一个开发时的web服务器
  • webpack默认只能打包js文件,所以webpack额外的提供了loader概念,可以使用loader做一些预处理文件,并且可以打包除js之外的任何静态资源文件
  • 另外webpack提供了插件的概念,可以使用很多的插件在webpack上做一些辅助性的工作,比如:HtmlWebPackPlugin用来创建HTML文件的插件

» 前端为什么要进行打包和构建?

代码层面: 

  • 体积更小(Tree-shaking、压缩、合并),加载更快
  • 编译高级语言和语法(TS、ES6、模块化、scss)
  • 兼容性和错误检查(polifill、postcss、eslint)

研发流程方面:

  • 统一、高效的开发环境
  • 统一的构建流程和产出标准
  • 集成公司构建规范(提测、上线)

» webpack的基本功能有哪些?

  1. 代码转换:TS编译成JS、SCSS编译成css
  2. 文件优化:压缩js、css、html代码,压缩合并图片等
  3. 代码分割:提取多个页面公共代码,提取首屏不需要执行部分代码让其异步加载
  4. 模块合并:在采用模块化的项目有很多模块和文件,需要构建功能把模块分类合并成一个文件
  5. 自动刷新:监听本地代码的变化,自动构建,刷新浏览器
  6. 代码校验:提到仓库前检验代码是否规范,以及单元测试是否通过
  7. 自动发布:更新完代码之后自动构建出线上发布代码

» 常见的Webpack Loader?解决什么问题

  • file-loader: 把文件输出到一个文件中,在代码中通过相对url引用输出的文件
  • url-loader: 在文件很小的情况下以base64的方式把文件内容注入到代码中
  • source-map-loader: 方便断点调试,快速定位到错误代码的位置
  • image-loader: 加载并压缩图片
  • babel-loader: 把ES6/ES7转换为ES5
  • scc-loader: 加载css,支持模块化、压缩、文件导入等
  • style-loader: 把css注入到js中,通过DOM操作加载CSS
  • eslint-loader: 通过Eslint检查代码

» Loader 和 Plugin 的不同?

不同的作用:

  • Loader: 直译加载器,webpack原生只能解析js文件,如果想将其他文件一起打包,就要用到loader,所以loader的作用就是让webpack拥有加载和解析非js文件的能力
  • Plugin: 直译插件,拓展webpack的功能,让webpack更具灵活性

不同的用法:

  • Loader 在module.rules中配置的,也就是他作为模块的解析而存在。类型为数组,每一项都是一个Object,里边描述对于什么类型的文件使用加载什么loader
  • Plugin 在plugins中单独配置。类型为数组,每一项是一个plugin实例,参数都通过构造函数传入

» 有哪些常见的 Plugin插件?解决什么问题的?

  • html-webpack-plugin:简化 HTML 文件创建 (依赖于 html-loader)
  • uglifyjs-webpack-plugin:压缩js文件
  • clean-webpack-plugin:目录清除
  • commons-chunk-plugin:提取公共代码
  • define-plugin:定义环境变量

» Vite 和 Webpack区别?

  1. 构建原理: webpack是一个静态模块打包器,通过对项目的js、css、图片等文件进行分析,生成对应的静态资源,并且可以通过一些插件和加载器来实现各种功能; Vite则是基于浏览器原生ES模块解析的构建工具
  2. 打包速度: Vite打包速度非常快,webpack相对慢些
  3. 配置难度: webpack配置难度复杂,因为需要通过各种插件和加载器实现各种功能; Vite配置简单,可以根据不同的开发场景自动配置相应的环境变量和配置选项
  4. 插件和加载器: webpack有大量的插件和加载器可以使用,可以实现各种复杂的构建场景,例如:代码分割、按需加载、css预处理器; Vite的插件和加载器相对较少
  5. Vite是按需加载: webpack是全部加载,在热更新方面,当改动一个模块后,Vite需要浏览器重新请求该模块即可; webpack需要把该模块相关的依赖全部编译一次, vite效率更高
  6. webpack是先打包再启动开发服务器:Vite是直接启动开发服务器,然后按需加载依赖文件,由于Vite在启动的时候不需要打包文件,也就意味着不用像webpack那样分析模块的依赖、不需要编译,因此速度非常快。这种按需动态编译的方式,极大缩减了编译时间。

» 为什么Vite 比 webpack速度快?

  1. webpack会先打包,然后启动开发服务器,请求服务器时给予打包结果; Vite是直接启动服务器,实时编译
  2. Vite在启动时不需要打包,不需要分析模块的依赖,不需要编译,所以速度非常快
  3. Vite是在请求哪个模块在对哪个模块进行按需加载,极大缩减了编译时间
  4. 在热更新(HMR)方面,当改动一个模块,仅需浏览器重新请求该模块即可,webpack需要把相关依赖重新加载编译一次
  5. 当需要打包到生产环境时,Vite使用rollup进行打包,因此,Vite 的主要优势在开发阶段, Vite利用的是ES module,因此在代码中不可以使用CommonJS

「职业技能规划、人事面试」

» 你的职业规划是什么?

一专多精

» 为什么从上一家公司离职?

我一般回答主要两个原因: 1、想学更多的东西 2、老生常谈当然薪资呗

» 前端是如何学习的?

面试官问怎么学习前端,这种问题一般考察: 学习能力

一开始学习前端,主要靠视频和书籍,主要是总结很多知识点,但发现学习过程中理论大于实操,所以自己把知识点总结后开始找项目实操,这样一步步成长起来的,平时在工作中也会碰到很多问题,一般都是自己查找原因并和同事一起来交流,然后自己会总结一些学习文档,方便日后查看和积攒经验,所以我感觉我的学习都是实操加总结

» 项目中有遇到什么难题?

可以说由于之前的业务相对常规,没有遇到太大的困难,但是也比较期待在今后的工作中遇到一些难题,因为这样才能让我成长; 也可以说些自己项目遇到了什么难题bug

» 还有什么想问的?

一般问技术团队的规模

  • 技术团队有几个人
  • 几个前端、几个后端
  • 高级、中级、初级分别有多少人

然后就是技术栈

  • 目前使用什么技术栈
  • 将来打算使用什么技术栈
  • 自己是否可以决定未来技术栈的走向

然后是自己在团队中的角色

  • 负责什么工作
  • 对于项目的决策度

问清楚工资什么时候发,公积金的缴纳比例,有没有涨薪和晋升的机会,有没有普调,在几月调薪,能不能提前通过试用期.....

最后祝大家,都能找到满意的工作,升职加薪,一个个成为富婆!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值