Web前端面试题(持续更新中)

一、闭包是什么

JS中内层函数可以访问外层函数的变量,使内部私有变量不受外界干扰,起到保护和保存的作用,我们把这个特性称作闭包。

好处:

1.隔离作用域,保护私有变量;有了闭包才有局部变量,要不然都是全局变量了。

2.让我们可以使用回调,操作其他函数内部;

3.变量长期驻扎在内存中,不会被内存回收机制回收,即延长变量的生命周期;

 坏处:

内层函数引用外层函数变量,内层函数占用内存。如果不释放内存,过多时,易引起内存泄露。

引用场景:

for循环中的保留i的操作 / 防抖和节流

二、内存泄露、垃圾回收机制 

内存泄露:

        是指不再用的内存没有被及时释放出来,导致该段内存无法被使用就是内存泄漏,内存泄漏指我们无法在通过js访问某个对象,而垃圾回收机制却认为该对象还在被引用,因此垃圾回收机制不会释放该对象,导致该块内存永远无法释放,积少成多,系统会越来越卡以至于崩溃

垃圾回收机制:

        就是垃圾收集器按照固定的时间间隔,周期性地寻找那些不再使用的变量,然后将其清除或释放内存。(标记清除/引用计数)

三、浅拷贝与深拷贝 

        浅拷贝:将原对象或原数组的引用直接赋给新对象,新数组,新对象只是对原对象的一个引用,而不复制对象本身,新旧对象还是共享同一块内存。(拓展运算符。。。)

        深拷贝:开辟一个新的栈,两个对象的属性完全相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性。

四、route和router的区别详解 

 router是用来操作路由的

 route是用来获取路由信息的。 $route.path $route.params route.query等 

五、如何改变this指向(call、apply与bind区别) 

    call、bind、apply 都是 JavaScript 中用于改变函数执行上下文(即 this 指向)的方法。

    传参 call、bind可以传递无数个参数,apply只有两个参数,第二个参数为数组返回。

    call和apply方法是直接调用函数并改变函数上下文,而bind方法则是返回一个新函数,稍后调用时绑定指定的上下文。

六、箭头函数和普通函数的区别 

箭头函数是普通函数的简写,但是它不具备很多普通函数的特性this指向问题,箭头函数的this指向它定义时所在的对象,而不是调用时所在的对象。

没有arguments对象,不能使用arguments。
不会进行函数提升,不能new

七、浏览器存储,他们的区别? 

localStorage:永久保存,以键值对保存,存储空间5M

sessionStorage:关闭页签/浏览器时清空

cookie:随着请求发送,通过设置过期时间删除

session:保存在服务端

(localStorage/sessionStorage是window的属性,cookie是document的方法)

八、常用的数组方法有哪些? 

改变原数组:push、pop、shift、unshift、sort、splice、reverse

不改变原属组:concat、join、map、forEach、filter、slice

(slice切片的意思,根据传入的起始和终止下标,获取该范围数组。splice可根据传入参数个数不同实现删除、插入操作,直接操作原数组。第1个参数为起始下标,第2个为删除个数,第3个为要增加的数据)

九、Vue的生命周期 

beforeCreate:会在实例初始化完成、props 解析之后、data() 和 computed 等选项处理之前立即调用。此时不能获得DOM节点

created:在这个阶段vue实例已经创建,以下内容已经设置完成:响应式数据、计算属性、方法和侦听器。然而,此时挂载阶段还未开始,因此 $el 属性仍不可用。仍然不能获取DOM元素。

beforeMount:在组件内容被渲染到页面之前自动执行的函数,组件已经完成了其响应式状态的设置,但还没有创建 DOM 节点。

mounted:在组件被渲染之后自动执行的函数。一般我们的异步请求都写在这里。在这个阶段,数据和DOM都已被渲染出来。

beforeUpdate:数据变化的时候自动执行的函数,此时变化后的数据还未渲染到页面之上。.

updated:数据变化之后自动执行的函数,此时变化后的数据已经渲染到页面之上

beforeDestroy:当 Vue 应用被销毁时,自动执行的函数。

destroyed当 Vue 应用被销毁后,且 dom 完全销毁之后,自动执行的函数。

十、Vue的Key的作用 

key主要用在虚拟Dom算法中,每个虚拟节点VNode有一个唯一标识Key,通过对比新旧节点的key来判断节点是否改变,用key就可以大大提高渲染效率 

十一、diff算法 

diff算法是指对新旧虚拟节点进行对比,并返回一个patch对象,用来存储两个节点不同的地方,最后利用patch记录的消息局部更新DOM 

十二、虚拟DOM的优缺点 

优点

1、减少了dom操作,减少了回流与重绘 

2、跨平台:虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、weex 开发等等。

缺点:

1、首次渲染大量DOM时,由于多了一层虚拟DOM的计算,会比innerHTML插入慢

2、无法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化。

十三、回流和重绘 

render树中一部分或全部元素需要改变尺寸、布局、或着需要隐藏而需要重新构建,这个过程叫做回流,回流必将引起重绘 

十四、为什么避免 v-if 和 v-for 用在一起 

 Vue2 中,v-for 比 v-if 具有更高的优先级,这意味着 v-if 将分别重复运行于每个 v-for 循环中,带来性能方面的浪费。 

Vue3中解决了这个问题,可以一起使用

十五、什么是Promise 

Promise异步编程的一种解决方案。Promise是一个构造函数,接收一个函数作为参数,返回一个 Promise 实例。

Promise对象有三种状态,他们分别是 pending(等待中) resolved(已完成)rejected(拒绝)

Promise.all哪怕一个请求失败了也能得到其余正确的请求结果的解决方案

promise 的then会返回一个新的 promise 对象,能保证 then 方 可以进行链式调用

十六、async、await 

Async 和 await 是一种同步的写法,但还是异步的操作,两个必须配合一起使用 

十七、宏任务和微任务有哪些?执行顺序 

宏任务:script,setTimeout,setInterval。

微任务:Promise,process.nextTick。

微任务会优先于宏任务执行。这意味着在当前任务执行结束后,所有微任务都会被立即执行,而宏任务只有在所有微任务执行完毕后才会执行。

十八、var  let  const的区别 

var声明的变量存在变量提升,即变量可以在声明之前调用,var允许重复声明变量var不存在块级作用域

let和const不存在变量提升,即它们所声明的变量一定要在声明后使用,否则报错

let和const存在块级作用域

let和const在同一作用域不允许重复声明变量

十九、从浏览器输入url后都经历了什么 

大致可以分为如下7步:

1、输入网址;

2、发送到DNS服务器,并获取域名对应的web服务器的ip地址;

3、与web服务器建立TCP连接;

4、浏览器向web服务器发送http请求;

5、web服务器响应请求,并返回指定url的数据(或错误信息,或重定向的新的url地址);

6、浏览器下载web服务器返回的数据及解析html源文件;

7、生成DOM树,解析css和js,渲染页面,直至显示完成;

二十、HTTP 和 HTTPS,为什么HTTPS安全 

1、HTTP协议通常承载与 TCP协议之上,在HTTP和TCP之间添加一个安全协议层(SSL或TSL),这个时候,就成了我们常说的HTTPS

2、默认HTTP的端口号为80,HTTPS的端口号为443

3、因为网络请求需要中间有很多的服务器路由的转发,中间的节点都可能篡改信息,而如果使用HTTPS,密钥在你和终点站才有,https之所有说比http安全,是因为他利用ssl/tls协议传输。包含证书,流量转发,负载均衡,页面适配,浏览器适配,refer传递等,保障了传输过程的安全性

二十一、什么是跨域?前端如何实现跨域?

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

在config.js文件的serve下配置proxy代理

1.JSONP原理
利用script元素的这个开放策略,网页可以得到从其他来源动态产生的 JSON 数据。但是JSONP请求一定需要对方的服务器做支持才可以。JSONP优点是兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅支持get方法具有局限性。
2.CORS原理
实现CORS通信的关键是服务器,需要在服务器端做一些小小的改造。只要服务器实现了CORS接口,就可以跨源通信。在响应头上添加Access-Control-Allow-Origin属性,指定同源策略的地址。同源策略默认地址是网页的本身。只要浏览器检测到响应头带上了CORS,并且允许的源包括了本网站,那么就不会拦截请求响应。
3.Nginx
浏览器在访问受限时,可通过不受限的代理服务器访问目标站点。proxy代理是前端用的最多的解决跨域的方法。即配置一台和浏览器相同端口的服务器,浏览器访问代理服务器,代理服务器向目标服务器发送请求,由于服务器之间不存在跨域问题,代理服务器就可以拿到请求数据,而后因为浏览器和代理服务器端口号一致,不存在跨域问题,因此浏览器不会拦截从代理服务器收到的数据,顺利拿到请求数据。例如:浏览器端口号8080,目标服务器端口号5000,在vue中配置代理服务器来访问目标服务器

二十二、Webpack是什么 

Webpack是一个模块打包工具,可以使用它管理项目中的模块依赖,并编译输出模块所需的静态文件。

二十三、Webpack的基本功能? 

代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等等

代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载

代码校验:在代码被提交到仓库前需要检测代码是否符合规范,以及单元测试是否通过

文件优化:压缩 JavaScript、CSS、HTML 代码,压缩合并图片等

模块合并:在采用模块化的项目有很多模块和文件,需要构建功能把模块分类合并成一个文件

自动刷新:监听本地源代码的变化,自动构建,刷新浏览器

自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。

二十四、TCP 传输的三次握手、四次挥手策略

  • 三次握手:
    为了准确无误地吧数据送达目标处,TCP协议采用了三次握手策略。用TCP协议把数据包送出去后,TCP不会对传送后的情况置之不理,他一定会向对方确认是否送达,握手过程中使用TCP的标志:SYN和ACK
    • 发送端首先发送一个带SYN的标志的数据包给对方
    • 接收端收到后,回传一个带有SYN/ACK标志的数据包以示传达确认信息
    • 最后,发送端再回传一个带ACK的标志的数据包,代表“握手”结束
  • 如在握手过程中某个阶段莫明中断,TCP协议会再次以相同的顺序发送相同的数据包

  • 断开一个TCP连接需要“四次挥手”
    • 第一次挥手:主动关闭方发送一个FIN,用来关注主动方到被动关闭方的数据传送,也即是主动关闭方告诫被动关闭方:我已经不会再给你发数据了(在FIN包之前发送的数据,如果没有收到对应的ACK确认报文,主动关闭方依然会重发这些数据)。但是,此时主动关闭方还可以接受数据
    • 第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号收到序号 +1(与SYN相同,一个 FIN占用一个序号)
    • 第三次挥手:被动关闭方发送一个 FIN。用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会给你发送数据了
    • 第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手

二十五、javascript 的 typeof 返回哪些数据类型(至少六个)检测类型如下 

string、   number、  boolean  、undefined 、object  、function   、symbol(ES6之后新增的类型) 

二十六 、什么是VueX

        1.Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

        2.vue2一般使用的是VueX,Vue3使用的是Pinia。

VueX详情:

1)state - 存放store数据,是响应式的
2)mutation方法 - 修改数据
3)action异步操作 -提交mutation,不直接修改状态
4)getter - 获取store的计算属性

5)module,模块化Vuex,允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中

二十七、vue2与vue3的区别 

1.webpack和vite

   ---vue2使用的是webpack形式去构建项目 

   ---vue3使用vite构建项目

2.main.js文件

   ---vue2中我们可以使用pototype(原型)的形式去进行操作,引入的是构造函数
   ---vue3中需要使用结构的形式进行操作,引入的是工厂函数
   ---vue3中app组件中可以没有根标签

3.指令与插槽

   ---vue2中使用slot可以直接使用slot,而vue3中必须使用v-slot的形式
   ---v-for与v-if在vue2中优先级高的是v-for指令,而且不建议一起使用
   ---vue3中v-for与v-if,只会把当前v-if当做v-for中的一个判断语句,不会相互冲突

4.ref与reactive

   1)ref定义的是基本数据类型,reactive定义对象或数组数据类型
   2)ref通过Object.defineProperty()的get和set实现数据劫持,reactive通过Proxy实现数据劫持
   3)ref操作数据.value,reactive操作数据不需要.value

5.vue2的响应式原理

   用Object.defineProperty的getter和setter进行数据劫持,从而实现响应式

6.computed和watch与watchEffct区别

computed

   ---computed属性是用来声明计算属性的。如果你希望修改一个计算属性的值并使其生效,你需要通过修改计算属性所依赖的数据来触发重新计算。只有当计算属性依赖的数据发生变化时,计算属性才会重新计算并更新值。 

    data(){
        return {
            canRead:true
        }
    },
    computed:{
        readOnly:{
            get(){
                return this.canRead
            },
            set(val){
                //设置了set方法,可直接修改计算属性(生效)
                //在这里修改依赖数据
                this.canRead = val
            }
        }
    },
    methods:{
        changeRead(){
            //直接修改计算属性(不生效)
            this.readOnly = false
        }
    }

watch

   ---vue2中watch通过对象的形式去直接监听

   ---vue3监听多个属性采用数组形式,深度监听需要单个进行监听

watchEffect

watchEffect是vue3中新增的函数,看字面意思就知道他也有watch,实际上他跟watch功能一样

优势:

   - 默认开启 immediate:true

   - 需要用哪个就监听哪个

   - 值发生改变就调用一次,且不需要返回值

二十八、Vue2 与 Vue3 如何创建响应式数据     

vue2

        vue会遍历data数据对象,使用Object.definedProperty()将每个属性都转换为getter和setter,每个Vue组件实例都有一个对应的watcher实例,在组件初次渲染的时候会记录组件用到了那些数据,当数据发生改变的时候,会触发setter方法,并通知所有依赖这个数据的watcher实例调用update方法去触发组件的compile渲染方法,进行渲染数据。

vue3

        ref 的作用就是将一个原始数据类型转换成一个响应式数据,原始数据类型共有 7 个,分别是:String、Number、BigInt、Boolean、Symbol、Undefined、Null

​        当 ref 作为渲染上下文 (从 setup() 中返回的对象) 上的 property 返回并可以在模板中被访问时,它将自动展开为内部值。不需要在模板中追加(如以上代码中在 template 中可以直接使用 title,在 setup 函数中使用 title.value 来修改其值)

        reactive 的作用就是将一个对象转换成一个响应式对象

        toRefs 的作用是将一个响应式对象转化为一个普通对象,把其中每一个属性转化为响应式数据

二十九、为什么需要 toRefs ? 

·        ---reactive 转化的响应式对象在销毁或展开(如解构赋值)的时候,响应式特征就会消失。为了在展开的同时保持其属性的响应式特征,我们可以使用 toRefs 。

三十、es5和es6的区别 

1)块级作用域:ES5中是没有块级作用域的,ES6中新增let和const,两个都有块级作用域,并且var有变量提升
2)箭头函数:ES6中的函数定义不再使用关键字function(),而是利用了()=>来进行定义
3)模板字符串:模板字符串是增强版的字符串,用反引号(`)标识,可以当作普通字符串使用,也可以用来定义多行字符串
4)新增for of循环:for…of循环可以遍历数组、Set和Map结构、某些类似数组的对象、对象,以及字符串
5)新增set数据结构:Set数据结构,类似数组。所有的数据都是唯一的,没有重复的值。它本身是一个构造函数
6)新增… 展开运算符:可以将数组或对象里面的值展开;还可以将多个值收集为一个变量
7)新增class :类的继承ES6中不再像ES5一样使用原型链实现继承,而是引入Class这个概念

8)新增async、await:使用 async/await, 搭配promise,可以通过编写形似同步的代码来处理异步流程, 提高代码的简洁性和可读性async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成
9)新增promise:Promise是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理、强大
10)Proxy代理:使用代理(Proxy)监听对象的操作,然后可以做一些相应事情

 三十一、Set、Map的区别? 

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

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

 (4)在Set中,没有重复的key。所以可以用set()来进行去重。

Map:
 (1)本质上是健值对的集合,类似集合
 (2)可以遍历,可以跟各种数据格式转换 

三十二、forEach、for in、for of三者区别 

---forEach更多的用来遍历数组
---for in 一般常用来遍历对象或json
---for of数组对象都可以遍历,遍历对象需要通过和Object.keys()
---for in循环出的是key,for of循环出的是value 

三十三、innerHTML 与 innerText的作用与区别 

作用:都可以获取或者设置元素的内容
区别:innerHTML可以解析内容中的html标签
           innerText不能解析内容中的html标签 

三十四、JavaScript 由以下三部分组成 

ECMAScript(语法部分):JavaScript 语言基础
DOM(文档对象模型):规定了访问 HTML 和 XML 的方法
BOM(浏览器对象模型):提供了浏览器窗口之间进行交互的对象和方法 

三十五、 HTTP常见的状态码?

100 Continue 继续,一般在发送post请求时,已发送了http header之后服务端将返回此信息,表示确认,之后发送具体参数信息
200 OK 正常返回信息
201 Created 请求成功并且服务器创建了新的资源
202 Accepted 服务器已接受请求,但尚未处理
301 Moved Permanently 请求的网页已永久移动到新位置。
302 Found 临时性重定向。
303 See Other 临时性重定向,且总是使用 GET 请求新的 URI。
304 Not Modified 自从上次请求后,请求的网页未修改过。
400 Bad Request 服务器无法理解请求的格式,客户端不应当尝试再次使用相同的内容发起请求。
401 Unauthorized 请求未授权。
403 Forbidden 禁止访问。
404 Not Found 找不到如何与 URI 相匹配的资源。
500 Internal Server Error 最常见的服务器端错误。
503 Service Unavailable 服务器端暂时无法处理请求(可能是过载或维护)。

三十六、图片懒加载?

当页面滚动的时间被触发 -> 执行加载图片操作 -> 判断图片是否在可视区域内 -> 在,则动态将data-src的值赋予该图片

三十七、==和===的区别 

双等号判断时,只需要值相等
三等号判断时,需要值与类型都相等 

三十八、数组方法pop() push() unshift() shift() 

pop()尾部删除
push()尾部添加
unshift()头部添加
shift()头部删除 

三十九、解释什么是Json 

json是一种轻量级的数据交换格式,一般用于数据传递
里边只允许出现双引号
JSON的语法表示三种类型值,简单值(字符串,数值,布尔值,null), 数组,对象 

四十、dom事件委托什么原理,有什么优缺点 

事件委托原理: 事件冒泡机制(把子元素的事件行为委托给父级元素执行)

优点:
1. 可以大量节省内存占用,减少事件注册
2. 可以实现当新增子对象时,无需再对其进行事件绑定

缺点:
如果把所有事件都用事件代理,可能会出现事件误判

四十一、Javascript的事件流模型都有什么? 

“事件冒泡”:事件逐级向上传播
“事件捕捉”:事件逐级向下传播,一直到最具体的
“DOM事件流”:三个阶段:事件捕捉,目标阶段,事件冒泡 

四十二、如何阻止事件冒泡 

ev.stopPropagation(); 

四十三、如何阻止默认事件 

return false 或者 ev.preventDefault(); 

四十四、网页前端性能优化的方式有哪些?

1.压缩 css, js, 图片
2.减少 http 请求次数, 合并 css、js 、合并图片(雪碧图)
3.使用 CDN
4.减少 dom 元素数量
5.图片懒加载
6.静态资源另外用无 cookie 的域名
7.减少 dom 的访问(缓存 dom)
8.巧用事件委托
9.样式表置顶、脚本置低

四十五、移动端性能优化?

1、尽量使用css3动画,开启硬件加速

2、适当使用touch时间代替click时间

3、避免使用css3渐变阴影效果

4、可以用transform: translateZ(0) 来开启硬件加速

5、不滥用float。float在渲染时计算量比较大,尽量减少使用

6、不滥用web字体。web字体需要下载,解析,重绘当前页面

7、合理使用requestAnimationFrame动画代替setTimeout

8、css中的属性(css3 transitions、css3 3D transforms、opacity、webGL、video)会触发GUP渲染,耗电

四十六、函数柯里化 

把一个接收多个参数的函数变成接收单一参数 并且返回能够接收新参数的函数 

例如:add(1)(2)(3)(4) = 10; 

四十七、什么是AJAX?如何实现? 

ajax是用于浏览器与服务器之间使用异步数据传输的一种技术,做到局部请求以实现局部刷新。
ajax的实现主要步骤:
(1)创建XMLHttpRequest对象,也就是创建一个异步调用对象.
(2)创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息.
(3)设置响应HTTP请求状态变化的函数.
(4)发送HTTP请求.
(5)获取异步调用返回的数据.
(6)使用JavaScript和DOM实现局部刷新.

四十八、什么是高阶函数? 

高阶函数是对其他函数进行操作的函数;
高阶函数就是一个接收函数作为参数或将函数作为输出返回的函数。
例如,Array.prototype.map,Array.prototype.filter 和Array.prototype.reduce 是语言中内置的一些高阶函数。 

四十九、描述浏览器的渲染过程?,DOM树和渲染树的区别? 

浏览器的渲染过程:
1)解析 HTML 构建 DOM树,并行请求 css/image/js
2)CSS 文件下载完成,开始构建CSS树
3)CSS树构建结束后和DOM 一起生成 Render树


DOM 树 和 渲染树 的区别:
1)DOM 树与 HTML 标签一一对应,包括 head 和隐藏元素
2)渲染树不包括 head 和隐藏元素,大段文本的每一个行都是独立节点,每一个节点都有对应的 css 属性

 五十、Javascript 作用域链?

如果当前作用域没有找到属性或方法,会向上层作用域查找,
直至全局函数,这种形式就是作用域链 

五十一、eval是做什么的 

eval 的功能是把对应的字符串解析成 JS 代码并运行,如把JSON 字符串转换为 JSON 对象

应该避免使用 eval,不安全,非常耗性能

五十二、defer 和 async区别 

defer 并行加载 js 文件,会按照页面上 script 标签的顺序执行
async 并行加载 js 文件,下载完成立即执行,不会按照页面上 script 标签的顺序执行 

五十三、什么是防抖与节流 

防抖(debounce):就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
节流(throttle):就是每次触发事件的间隔至少要大于等于n秒,且不会重置计时 

五十四、事件绑定的三种方式 

在标签上直接设置事件  onclik="a()"
dom.onclick = function(){}
addEventListener('click',function(){},true) 

五十五、事件绑定和普通事件有什么区别? 

普通事件会覆盖掉,只执行后者方法
dom.onclick = function(){}
事件绑定不会覆盖掉,会依次执行
addEventListener('click',function(){},true) 

五十六、form中的input可以设置为readonly和disabled,请问二者有什么区别? 

readonly不可编辑,但可以选择和复制;值可以传递到后台
disabled不能编辑,不能复制,不能选择;值不可以传递到后台 

五十七、进程与线程的区别 

1)进程是操作系统分配资源的基本单位,线程是任务调度和执行的基本单位。

2)进程之间是独立的地址空间,而线程共享本进程的地址空间。

3)进程之间的资源是独立的,能很好的进行资源管理和保护,而线程共享本进程的资源如内存、I/O、cpu等,不利于资源的管理和保护。

4)多进程要比多线程健壮,一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。

5)进程独立执行,执行开销大。但线程不能独立执行,执行开销小。

6)进程切换时,消耗的资源大,效率低。所以涉及到频繁的切换时,使用线程要好于进程。

7)一个程序至少有一个进程, 一个进程至少有一个线程。

五十八、什么是回调函数 

回调函数是作为参数或选项传递给某个方法的普通JS函数。
它是一个函数,在另一个函数完成调用后执行,因此称为回调。 

五十九、数组map、filter, reduce三个方法的作用 

map()方法:把回调函数中返回的值,作为一个新数组返回
filter()方法:返回符合回调函数中条件的元素,形成一个新数组
reduce()方法:对数组中元素进行从左到右的累计,并返回最终结果 

六十、javascript 代码中的"use strict";是什么意思 ? 使用它区别是什么? 

use strict 是一种 ECMAscript5 添加的(严格)运行模式,

区别 :这种模式使得 Javascript 在更严格的条件下运行,使 JS 编码更加规范化的模式,消除 Javascript 语法的一些不合理、不严谨之处,减少一些怪异行为

六十一、什么是 Proxy? 

1)Proxy:代理,是ES6新增的功能,可以理解为代理器(即由它代理某些操作)。

2)对象用于定义或修改某些操作的自定义行为,可以在外界对目标对象进行访问前,对外界的访问进行改写。

3)Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。 

六十二、watch 和 computed 区别 

computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;

watch: 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作

运用场景

1、我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;

2、当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

六十三、Vue 的父组件和子组件生命周期钩子执行顺序是什么 

加载渲染过程
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
子组件更新过程
父beforeUpdate->子beforeUpdate->子updated->父updated
父组件更新过程
父beforeUpdate->父updated
销毁过程
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
总结:从外到内,再从内到外

六十四、父子组件间通讯方式 

(1)父组件向子组件传值(props将数据自上而下传递)

(2)子组件向父组件传递数据($emit和v-on来向上传递信息)

(3)通过EventBus进行信息的发布与订阅

六十五、webgis前端问题 

用过OpenLayers,功能全面、灵活、跨平台,处理矢量瓦片 

1.描述地理坐标系统和投影坐标系统的区别

        地理坐标系统是对整个地球设定一个椭球体和相应的数学公式,用来定义地球上的某点位置,通常,我们所指的经纬度就是在地理坐标系统下得到的数字明确表示出的高精度位置
        投影坐标系就是指当三维的经纬度位置信息要表示成平面的地图时,这个三维坐标到二维坐标的转化关系,由于球体不可展成平面,一定会有相应的变形,所以不同的投影坐标系通常会牺牲掉一些信息,比如角度保持不变但面积要改变(墨卡托)

2.Web端如何实时获取服务器数据

1)Ajax+定时器

2)WebSocket

3)MQTT

六十六、Koa 和 Express 的介绍

他们都是同一个开发者开发的,都是基于 Node.js 平台的 Web 开发框架

Express  框架提供了很多 Web 应用程序所需要的常用功能,所有比较臃肿。 

Koa 框架是 Express 框架的升级版,Koa 框架采用了 ES6 Generator 函数的编程风格,使得异步代码的编写更加简单和直观。

1、Koa 和 Express 的区别

      1)设计理念不同        

        Express 框架的设计理念是“约定优于配置”,提供了很多预定义的中间件和路由,使得开发者可以快速地搭建出一个 Web 应用程序。

        Koa 框架的设计理念是“中间件优先”,提供了一个类似于 Express 的中间件机制,使得它更加简单、易用和可控。
        2)路由处理方式不

        Express 框架使用 app.get()、app.post()、app.put() 等函数来定义路由,这些函数接收两个参数:路由路径和处理函数。这种方式比较直观,但是当路由数量增多时,代码会变得难以维护。

        Koa 框架使用 koa-router 中间件来定义路由,这个中间件提供了很多方便的函数来定义路由,例如 router.get()、router.post()、router.put() 等等。使得路由的定义更加清晰和易于维护

        3)异步编程方式不同

        Express 框架使用回调函数的方式来处理异步代码,这种方式比较常见,但是当异步代码嵌套层次较多时,代码会变得难以阅读和维护。

        Koa 框架使用 ES6 Generator 函数的方式来处理异步代码,这种方式使得异步代码的编写更加简单和直观。Koa 框架还提供了很多中间件来处理异步代码,例如 koa-async、koa-await 等等,使得异步代码的编写更加方便。

        4)错误处理方式不同

        Express 框架使用中间件的方式来处理错误

        Koa 框架使用 try...catch 的方式来处理错误

2、Koa 和 Express 的优缺点

Express 的优点

(1)使用简单,上手容易。

(2)社区活跃,文档齐全。

(3)提供了很多预定义的中间件和路由,使得开发者可以快速地搭建出一个 Web 应用程序。

(4)已经经过了很长时间的实践,稳定性和可靠性较高。

Express 的缺点
(1)设计理念不够灵活,代码容易变得臃肿。

(2)异步编程方式不够直观,代码可读性和可维护性较差。

(3)错误处理方式不够清晰,代码容易变得冗长。

Koa 的优点
(1)设计理念更加灵活和可控,使得开发者可以更加轻松地编写出高效、安全、可维护的 Web 应用程序。

(2)异步编程方式更加直观,使得开发者可以更加方便地编写异步代码。

(3)中间件机制更加简单、易用和可控,使得开发者可以更加灵活地控制 Web 应用程序的处理流程。

(4)错误处理方式更加清晰,使得开发者可以更加方便地处理错误。

Koa 的缺点
(1)相对于 Express,Koa 的学习曲线较陡峭,需要开发者具备一定的 JavaScript 基础和异步编程经验。

(2)由于 Koa 是一个相对较新的框架,因此社区和文档相对较少,可能会给开发者带来一些不便。

六十七、什么是jQuery 

        jQuery是JavaScript的一个工具库,封装了JavaScript常用的功能代码。jQuery设计的宗旨是“写更少的代码,做更多的事情”。

 jQuery 优点:
  • 占用空间少,只有30KB

  • 符合CSS3规范

  • 跨浏览器,兼容各种主流浏览器。

六十八、常见的浏览器内核有哪些?
  • 主要分成两部分:渲染引擎(layout engineer或Rendering Engine)和JS引擎。
    • 渲染引擎:负责取得网页的内容(HTML、XML、图像等等)、整理讯息(例如加入CSS等),以及计算网页的显示方式,然后会输出至显示器或打印机。浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。所有网页浏览器、电子邮件客户端以及其它需要编辑、显示网络内容的应用程序都需要内核。
    • JS引擎则:解析和执行javascript来实现网页的动态效果。
  • 最开始渲染引擎和JS引擎并没有区分的很明确,后来JS引擎越来越独立,内核就倾向于只指渲染引擎。
  • 常见内核
    • Trident 内核:IE, MaxThon, TT, The World, 360, 搜狗浏览器等。[又称 MSHTML]
    • Gecko 内核:Netscape6 及以上版本,FF, MozillaSuite / SeaMonkey 等
    • Presto 内核:Opera7 及以上。 [Opera内核原为:Presto,现为:Blink;]
    • Webkit 内核:Safari, Chrome等。 [ Chrome的:Blink(WebKit 的分支)]

六十九、视频播放 

使用flv.js+video的形式播放视频

七十、Vue中的的通信方式有几种?隔代组件的通信你用那种方式解决?

Vue 组件间通信只要指以下 3 类通信:父子组件通信、隔代组件通信、兄弟组件通信,下面我们分别介绍每种通信方式且会说明此种方法可适用于哪类组件间通信。
(1)props / $emit适用 父子组件通信

  • 这种方法是 Vue 组件的基础,相信大部分同学耳闻能详,所以此处就不举例展开介绍。

(2)ref与 $parent / $children适用 父子组件通信

  • ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
  • $parent / $children:访问父 / 子实例

(3)EventBus ($emit / $on)适用于 父子、隔代、兄弟组件通信

  • 这种方法通过一个空的 Vue 实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件。

(4)$attrs/$listeners适用于 隔代组件通信

  • $attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 ( class 和 style 除外 )。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外 ),并且可以通过 v-bind="$attrs"传入内部组件。通常配合 inheritAttrs 选项一起使用。
  • $listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners"传入内部组件

(5)provide / inject适用于 隔代组件通信

  • 祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。provide / inject API主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。

(6)Vuex适用于 父子、隔代、兄弟组件通信

  • Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。
  • Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
  • 改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。

七十一、v-show 和 v-if指令的共同点和不同点? 

 v-show是css切换,v-if是完整的销毁和重新创建,如果频繁切换时用v-show,运行时较少改变用v-if

七十二、 vue-router 有哪几种导航钩子?

1、全局导航钩子:router.beforeEach(to,from,next)作用:跳转前进行判断拦截、组件内的钩子、单独路由独享组件
2、路由独享钩子可以在路由配置上直接定义 beforeEnter
3、组件内的导航钩子有三种:
beforeRouteEnter 在进入当前组件对应的路由前调用
beforeRouteUpdate 在当前路由改变,但是该组件被复用时调用
beforeRouteLeave 在离开当前组件对应的路由前调用

七十三、v-bind和v-model的区别 

v-bind用来绑定数据和属性以及表达式
v-model使用在表单中,实现双向数据绑定的。 

七十四、Vue 是如何实现数据双向绑定的?

如果被问到 Vue 怎么实现数据双向绑定,大家肯定都会回答 通过 Object.defineProperty() 对数据进行劫持,但是 Object.defineProperty() 只能对属性进行数据劫持,不能对整个对象进行劫持。
同理无法对数组进行劫持,但是我们在使用 Vue 框架中都知道,Vue 能检测到对象和数组(部分方法的操作)的变化,那它是怎么实现的呢?我们查看相关代码如下:

  /**
   * Observe a list of Array items.
   */
  observeArray (items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])  // observe 功能为监测数据的变化
    }
  }

  /**
   * 对属性进行递归遍历
   */
  let childOb = !shallow && observe(val) // observe 功能为监测数据的变化

通过以上 Vue 源码部分查看,我们就能知道 Vue 框架是通过遍历数组 和递归遍历对象,从而达到利用 Object.defineProperty() 也能对对象和数组(部分方法的操作)进行监听。

七十五、Vue和React的区别是什么? 

一、核心思想不同

Vue是一个灵活易用的渐进式双向绑定的MVVM框架。

React的核心思想是声明式渲染和组件化、单向数据流,React既不属于MVC也不属于MVVM架构。 

二、组件写法上不同
Vue的组件写法是通过template的单文件组件格式。
React的组件写法是JSX+inline style,也就是吧HTML和CSS全部写进JavaScript中。
三、Diff算法不同
vue对比节点,如果节点元素类型相同,但是className不同,认为是不同类型的元素,会进行删除重建,vue的列表比对采用的是首尾指针法

react则会认为是同类型的节点,只会修改节点属性,react采用的是从左到右依次比对的方式

四、响应式原理不同

vue会遍历data数据对象,使用Object.definedProperty()将每个属性都转换为getter和setter,每个Vue组件实例都有一个对应的watcher实例,在组件初次渲染的时候会记录组件用到了那些数据,当数据发生改变的时候,会触发setter方法,并通知所有依赖这个数据的watcher实例调用update方法去触发组件的compile渲染方法,进行渲染数据。

React主要是通过setState()方法来更新状态,状态更新之后,组件也会重新渲染。

七十六、什么是Babel?它有什么作用?

Babel是一个 JavaScript 编译器,它主要用于将 ECMAScript 2015+ 代码转换为向后兼容的 JavaScript 版本,以便在各种环境中运行。它允许开发者使用最新的JavaScript特性,而无需担心兼容性问题。

作用:

转译新特性: Babel 可以将使用新版本 JavaScript 特性编写的代码,如箭头函数、模板字符串、解构赋值等,转译成 ES5 或其他旧版 JavaScript 的等效代码。这使得你可以使用最新的语言特性,而不必担心浏览器兼容性问题。

模块转译: Babel 也支持转译不同模块系统(如 ES6 模块、CommonJS、AMD 等)之间的代码,从而使你可以在不同环境中共享模块化的代码。

插件系统: Babel 具有丰富的插件生态系统,允许开发者根据需要自定义转译过程。你可以选择性地启用或禁用插件,以适应你的项目需求。

转译 JSX: Babel 可以将 JSX 语法(通常与 React 一同使用)转译为标准 JavaScript。这使得你可以在不同的项目中使用 JSX 而无需担心浏览器是否支持。

扩展语法: Babel 可以根据不同的需求扩展 JavaScript 语法,使其更适应特定领域或项目需求。

七十七、null、未定义或未声明的变量之间有什么区别? 

‘null’表示故意不存在任何对象值,‘undefined’表示声明的变量没有赋值,未声明的变量根本不声明。 

七十八、CORS 代表什么以及它解决什么问题? 

 CORS 代表跨源资源共享。它是一种机制,允许从资源来自源域之外的另一个域请求网页上的资源。CORS 解决了浏览器强制执行的同源策略,该策略可防止脚本出于安全原因向不同域发出请求。

七十九、Prop 和 State 有什么区别? 

props 和 state 都是 React 组件中使用的普通 JavaScript 对象。props 从父组件传递到子组件,用于组件内不会更改的数据。另一方面,状态在组件本身内进行管理,并且可以使用 setState 方法进行更新。props 是不可变的,而 state 可以更新。 

八十、React 组件中有哪些生命周期方法? 

React 组件中的一些常见生命周期方法包括 componentDidMount、componentWillMount、componentWillUpdate、componentDidUpdate、shouldComponentUpdate 和 componentWillReceiveProps。 

八十一、解释一下 function Person(){}、var person = Person() 和 var person = new Person() 之间的区别。 

function Person(){} 正在声明一个函数。

var person = Person() 将 Person 函数的引用分配给 person 变量。

var person = new Person() 使用“new”关键字创建 Person 类的新实例。

八十二、剩余运算符和扩展运算符有什么区别? 

剩余运算符(例如,…args)允许您将不定数量的参数表示为数组。当使用可变参数函数或处理可变数量的函数参数时,它非常有用。

扩展运算符(例如,...array)允许您将数组扩展为单个元素。当您想要将数组作为单独的参数传递给函数或基于现有数组创建新数组时,它会很方便。 

八十三、“重置”和“规范化”CSS 有什么区别?你会选择哪个,为什么? 

重置 CSS 会删除每个元素的所有样式,包括边距、填充和其他属性。它提供了一个干净的状态,并确保不同浏览器之间的启动样式一致。

规范化 CSS 的目的是通过应用一组预定义的样式,使元素在浏览器中呈现一致。它为样式提供了一致的基线,并有助于减少浏览器的不一致。

重置 CSS 还是规范化 CSS 之间的选择取决于项目的要求和偏好。重置使您可以完全控制样式,但需要重新设置每个元素的样式。

规范化提供了更一致的基础,但可能需要额外的自定义才能满足您的设计要求。

八十四、解释同步函数和异步函数之间的区别 

同步函数逐步执行,每一行都等待前一行完成。异步函数允许在上一步完成之前执行到下一步。异步函数通常用于非阻塞操作。  

八十五、什么是事件循环?调用堆栈和任务队列有什么区别? 

事件循环负责利用单个线程执行 JavaScript 中的操作。它使用调用堆栈来跟踪当前正在执行的操作,并使用任务队列来管理异步任务。调用堆栈按照后进先出的顺序处理函数,而任务队列则按照先进先出的顺序处理。 

八十六、函数式编程与面向对象编程相比有何优缺点? 

函数式编程避免共享状态和可变数据,使代码更易于阅读和调试。面向对象编程依赖于共享的主状态,这可能会导致复杂性。

函数式编程还促进不变性并支持高阶函数,而面向对象编程则强调封装和多态性。

八十七、PureComponent 是什么以及如何利用它? 

PureComponent 是 React.Component 的子类,它实现了带有浅层 prop 和状态比较的 shouldComponentUpdate 方法。

当 props 和 state 没有改变时,它可以防止不必要的组件重新渲染,从而帮助优化性能。

八十八、React Hooks 的优点和用途 

React Hooks 引入了一种在功能组件中编写可重用和有状态逻辑的新方法。它们简化了组件组合,减少了对类组件的需求,并通过允许在不编写类的情况下使用状态和其他 React 功能来提高代码的可读性和可维护性。 

八十九、如果要做优化,CSS提高性能的方法有哪些? 

内联首屏关键CSS
异步加载CSS
资源压缩
合理使用选择器
减少使用昂贵的属性
不要使用@import 

九十、DOCTYPE(⽂档类型) 的作⽤ 

        Doctype是HTML5的文档声明,通过它可以告诉浏览器,使用哪一个HTML版本标准解析文档。在浏览器发展的过程中,HTML出现过很多版本,不同的版本之间格式书写上略有差异。如果没有事先告诉浏览器,那么浏览器就不知道文档解析标准是什么?此时,大部分浏览器将开启最大兼容模式来解析网页,我们一般称为怪异模式,这不仅会降低解析效率,而且会在解析过程中产生一些难以预知的bug,所以文档声明是必须的。
浏览器渲染页面的两种模式(可通过document.compatMode获取):
CSS1Compat:标准模式(Strick mode),默认模式,浏览器使用W3C的标准解析渲染页面。在标准模式中,浏览器以其支持的最高标准呈现页面。
BackCompat:怪异模式(混杂模式)(Quick mode),浏览器使用自己的怪异模式解析渲染页面。在怪异模式中,页面以一种比较宽松的向后兼容的方式显示。

九十一、img标签的title属性与alt属性的区别是什么? 

alt是给搜索引擎识别,在图像无法显示时的替代文本

title属性是关于元素的注释信息,主要是给用户解读

九十二、src 和 href 的区别? 

  • href (Hypertext Reference)指定网络资源的位置,从而在当前元素或者当前文档和由当前属性定义的需要的锚点或资源之间定义一个链接或者关系。(目的不是为了引用资源,而是为了建立联系,让当前标签能够链接到目标地址。)
  • src source(缩写),指向外部资源的位置,指向的内容将会应用到文档中当前标签所在位置。
  • href与src的区别
    • 1、请求资源类型不同:href 指向网络资源所在位置,建立和当前元素(锚点)或当前文档(链接)之间的联系。在请求 src 资源时会将其指向的资源下载并应用到文档中,比如 JavaScript 脚本,img 图片;
    • 2、作用结果不同:href 用于在当前文档和引用资源之间确立联系;src 用于替换当前内容;
    • 3、浏览器解析方式不同:当浏览器解析到src ,会暂停其他资源的下载和处理,直到将该资源加载、编译、执行完毕,图片和框架等也如此,类似于将所指向资源应用到当前内容。这也是为什么建议把 js 脚本放在底部而不是头部的原因。

九十三、iframe的优点和缺点 ? 

优点:

  • 解决加载缓慢的第三方内容如图标和广告等的加载问题
  • 能够原封不动的把嵌入的网页展现出来
  • 并行加载脚本

缺点:

  • iframe会阻塞主页面的Onload事件
  • 即时内容为空,加载也需要时间
  • 没有语意

九十四、rgba和opacity的透明效果有什么不同? 

rgba设置的元素,该元素的后代不会继承该属性,只对该元素的背景色有改变。

opacity属性的值,给父级div设置opacity属性,那么所有子元素都会继承这个属性,并且,该元素及其继承该属性的所有子元素的所有内容透明度都会改变。 

九十五、如何用CSS3画一条0.5px的直线? 

height: 1px;

transform: scale(0.5);

九十六、keep-alive 的作用是什么? 

实现组件缓存,保持这些组件的状态,以避免反复渲染导致的性能问题。 

九十七、混入mixin的原理? 

mixin 项目变得复杂的时候,多个组件间有重复的逻辑就会用到mixin,多个组件有相同的逻辑,抽离出来 

劣势

1. 变量来源不明确,不利于阅读

2. 多mixin可能会造成命名冲突

3. mixin和组件可能出现多对多的关系,使得项目复杂度变高

九十八、Loader和Plugin的区别? 

1、Loader 本质就是一个函数,在该函数中对接收到的内容进行转换,返回转换后的结果。 因为 Webpack 只认识 JavaScript,所以 Loader 就成了翻译官,对其他类型的资源进行转译的预处理工作。

2、Plugin 就是插件,基于事件流框架 Tapable,插件可以扩展 Webpack 的功能,在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。 

3、Loader 在 module.rules 中配置,作为模块的解析规则,类型为数组。每一项都是一个 Object,内部包含了 test(类型文件)、loader、options (参数)等属性。

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

九十九、 如何优化 Webpack 的构建速度?

(1)使用高版本的 Webpack 和 Node.js
(2)压缩代码
通过 uglifyjs-webpack-plugin 压缩JS代码
通过 mini-css-extract-plugin 提取 chunk 中的 CSS 代码到单独文件,通过 css-loader 的 minimize 选项开启 cssnano 压缩 CSS。
(3)多线程/多进程构建:thread-loader, HappyPack
(4)压缩图片: image-webpack-loader
(5)缩小打包作用域

一百、Webpack构建过程? 

1、从entry里配置的module开始递归解析entry依赖的所有module

2、每找到一个module,就会根据配置的loader去找对应的转换规则

3、对module进行转换后,再解析出当前module依赖的module

4、这些模块会以entry为单位分组,一个entry和其所有依赖的module被分到一个Chunk组

5、最后Webpack会把所有Chunk转换成文件输出在整个流程中,Webpack会在恰当的时机执行plugin里定义的逻辑         

一百零一、说一下 Webpack 的热更新原理吧? 

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

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

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

一百零二、什么是bundle?什么是chunk?什么是module?

bundle:是webpack打包后的一个文件;

chunk:代码块,一个chunk 可能有很多的模块组成,用于合并和分割代码;

module:模块,在webpack中,一切都是模块,一个文件就是一个模块,她从入口开始查找webpack依赖的所有模块 

一百零三、webpack和grunt以及gulp有什么不同?

1、grunt和gulp是基于任务处理的工具,我们需要把我们要做的事分配成各种各样的任务,grunt和gulp会自动执行各种分配的任务,像流水线一样,把资源放上去通过不同的插件进行加工,他的插件非常丰富,能够为我们打造各种工作流;
2、webpack是模块化打包工具,把所有文件都当作模块进行处理,也就是说webpack和grunt和gulp是两种完全不一样的东西;

一百零四、JSON.stringify( ) 和 JSON.parse( ) 

JSON.stringify():将对象、数组转换成字符串

JSON.parse():将字符串转成json对象 

一百零五、简述一下你对 HTML 语义化的理解? 

用正确的标签做正确的事情。
html 语义化让页面的内容结构化,结构更清晰,便于对浏览器、搜索引擎解析;即使在没有样式 CSS 情况下也以一种文档格式显示,并且是容易阅读的;
搜索引擎的爬虫也依赖于 HTML 标记来确定上下文和各个关键字的权重,利于 SEO;
使阅读源代码的人对网站更容易将网站分块,便于阅读维护理解。 

一百零六、 介绍一下 CSS 的盒子模型?

有两种, IE 盒子模型、W3C 盒子模型;
盒模型: 内容(content)、填充(padding)、边界(margin)、 边框(border);
区别: IE 的 content 部分把 border 和 padding 计算了进去; 

一百零七、 css 选择器优先级?

!important > 行内样式(比重1000)> ID 选择器(比重100) > 类选择器(比重10) > 标签(比重1) > 通配符 > 继承 > 浏览器默认属性 

一百零八、 垂直居中几种方式?

单行文本: line-height = height
图片: vertical-align: middle;
absolute 定位: top: 50%;left: 50%;transform: translate(-50%, -50%);
flex: display:flex;margin:auto 

一百零九、简明说一下 CSS link 与 @import 的区别和用法? 

link 是 XHTML 标签,除了加载CSS外,还可以定义 RSS 等其他事务;@import 属于 CSS 范畴,只能加载 CSS。
link 引用 CSS 时,在页面载入时同时加载;@import 需要页面网页完全载入以后加载。
link 是 XHTML 标签,无兼容问题;@import 是在 CSS2.1 提出的,低版本的浏览器不支持。
link 支持使用 Javascript 控制 DOM 去改变样式;而@import不支持。 

一百一十、display:none和visibility:hidden的区别? 

display:none 隐藏对应的元素,在文档布局中不再给它分配空间,它各边的元素会合拢,就当他从来不存在。
visibility:hidden 隐藏对应的元素,但是在文档布局中仍保留原来的空间。 

一百一十一、 position的值, relative和absolute分别是相对于谁进行定位的?

relative:相对定位,相对于自己本身在正常文档流中的位置进行定位。
absolute:生成绝对定位,相对于最近一级定位不为static的父元素进行定位。
fixed: (老版本IE不支持)生成绝对定位,相对于浏览器窗口或者frame进行定位。
static:默认值,没有定位,元素出现在正常的文档流中。
sticky:生成粘性定位的元素,容器的位置根据正常文档流计算得出。 

一百一十二、HTML5、CSS3 里面都新增了那些新特性? 

HTML5

  • 新的语义标签
    • article 独立的内容。
    • aside 侧边栏。
    • header 头部。
    • nav 导航。
    • section 文档中的节。
    • footer 页脚。
  • 画布(Canvas) API
  • 地理(Geolocation) API
  • 本地离线存储 localStorage 长期存储数据,浏览器关闭后数据不丢失;
    sessionStorage 的数据在浏览器关闭后自动删除
  • 新的技术webworker, websocket, Geolocation
  • 拖拽释放(Drag and drop) API
  • 音频、视频API(audio,video)
  • 表单控件,calendar、date、time、email、url、searc

CSS3

  • 2d,3d变换
  • Transition, animation
  • 媒体查询
  • 新的单位(rem, vw,vh 等)
  • 圆角(border-radius),阴影(box-shadow),对文字加特效(text-shadow),线性渐变(gradient),旋转(transform)transform:rotate(9deg) scale(0.85,0.90) translate(0px,-30px) skew(-9deg,0deg);//旋转,缩放,定位,倾斜
  • rgba

一百一十三、BFC 是什么? 

BFC 即 Block Formatting Contexts (块级格式化上下文),它属于普通流,即:元素按照其在 HTML 中的先后位置至上而下布局,在这个过程中,行内元素水平排列,直到当行被占满然后换行,块级元素则会被渲染为完整的一个新行,除非另外指定,否则所有元素默认都是普通流定位,也可以说,普通流中元素的位置由该元素在 HTML 文档中的位置决定。
可以把 BFC 理解为一个封闭的大箱子,箱子内部的元素无论如何翻江倒海,都不会影响到外部。
只要元素满足下面任一条件即可触发 BFC 特性

  • body 根元素
  • 浮动元素:float 除 none 以外的值
  • 绝对定位元素:position (absolute、fixed)
  • display 为 inline-block、table-cells、flex
  • overflow 除了 visible 以外的值 (hidden、auto、scroll)

一百一十四、常见兼容性问题?

  • 浏览器默认的margin和padding不同。解决方案是加一个全局的*{margin:0;padding:0;}来统一。
  • Chrome 中文界面下默认会将小于 12px 的文本强制按照 12px 显示,
    可通过加入 CSS 属性 -webkit-text-size-adjust: none; 解决.

一百一十五、JS 数据类型 ? 

数据类型主要包括两部分:

  • 基本数据类型: Undefined、Null、Boolean、Number 和 String
  • 引用数据类型: Object (包括 Object 、Array 、Function)
  • ECMAScript 2015 新增:Symbol(创建后独一无二且不可变的数据类型 )

一百一十六、 判断一个值是什么类型有哪些方法?

  • typeof 运算符
  • instanceof 运算符
  • Object.prototype.toString 方法

一百一十七、怎么判断一个变量arr的话是否为数组(此题用 typeof 不行)? 

arr instanceof Array
arr.constructor == Array
Object.protype.toString.call(arr) == '[Object Array]' 

一百一十八、new操作符具体干了什么呢? 

1、创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
2、属性和方法被加入到 this 引用的对象中。
3、新创建的对象由 this 所引用,并且最后隐式的返回 this 。 

一百一十九、documen.write 和 innerHTML 的区别? 

document.write 只能重绘整个页面
innerHTML 可以重绘页面的一部分 

一百二十、请解释一下 JavaScript 的同源策略? 

概念:同源策略是客户端脚本(尤其是Netscape Navigator2.0,其目的是防止某个文档或脚本从多个不同源装载。
这里的同源策略指的是:协议,域名,端口相同,同源策略是一种安全协议。
指一段脚本只能读取来自同一来源的窗口和文档的属性。 

一百二十一、 JavaScript原型,原型链 ? 有什么特点? 

  • 每个对象都会在其内部初始化一个属性,就是prototype(原型),当我们访问一个对象的属性时,
    如果这个对象内部不存在这个属性,那么他就会去prototype里找这个属性,这个prototype又会有自己的prototype,
    于是就这样一直找下去,也就是我们平时所说的原型链的概念。
  • 关系:instance.constructor.prototype = instance.proto
  • 特点:
    JavaScript对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一改变。

一百二十二、说说你对 SPA 单页面的理解,它的优缺点分别是什么? 

SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。

  • 优点:
    用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染;
    基于上面一点,SPA 相对对服务器压力小;
    前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理;
  • 缺点:
    初次加载耗时多:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一> 加载,部分页面按需加载;
    前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所> 有的页面切换需要自己建立堆栈管理;
    SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。

一百二十三、Class 与 Style 如何动态绑定? 

Class 可以通过对象语法和数组语法进行动态绑定:

  • 对象语法:
<div v-bind:class="{ active: isActive, 'text-danger': hasError }"></div>
data: {
	isActive: true,
  	hasError: false
}
  • 数组语法:
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
data: {
  activeClass: 'active',
  errorClass: 'text-danger'
}

Style 也可以通过对象语法和数组语法进行动态绑定:

  • 对象语法:
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data: {
  activeColor: 'red',
  fontSize: 30
}
  • 数组语法:
<div v-bind:style="[styleColor, styleSize]"></div>
data: {
  styleColor: {
     color: 'red'
   },
  styleSize:{
     fontSize:'23px'
  }
}

一百二十四、怎样理解 Vue 的单向数据流? 

1、所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。
2、这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。
3、这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
4、子组件想修改时,只能通过 $emit 派发一个自定义事件,父组件接收到后,由父组件修改。 

一百二十五、父组件可以监听到子组件的生命周期吗? 

比如有父组件 Parent 和子组件 Child,如果父组件监听到子组件挂载 mounted 就做一些逻辑处理,可以通过以下写法实现:

// Parent.vue
<Child @mounted="doSomething"/>

// Child.vue
mounted() {
  this.$emit("mounted");
}

以上需要手动通过 $emit 触发父组件的事件,更简单的方式可以在父组件引用子组件时通过 @hook 来监听即可,如下所示:

//  Parent.vue
<Child @hook:mounted="doSomething" ></Child>

doSomething() {
   console.log('父组件监听到 mounted 钩子函数 ...');
},

//  Child.vue
mounted(){
   console.log('子组件触发 mounted 钩子函数 ...');
},    

// 以上输出顺序为:
// 子组件触发 mounted 钩子函数 ...
// 父组件监听到 mounted 钩子函数 ...

当然 @hook 方法不仅仅是可以监听 mounted,其它的生命周期事件,例如:created,updated 等都可以监听。

一百二十六、谈谈你对 keep-alive 的了解? 

keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染 ,其有以下特性:

  • 一般结合路由和动态组件一起使用,用于缓存组件;
  • 提供 include 和 exclude 属性,两者都支持字符串或正则表达式, include 表示只有名称匹配的组件会被缓存,exclude 表示任何名称匹配的组件都不会被缓存 ,其中 exclude 的优先级比 include 高;
  • 对应两个钩子函数 activated 和 deactivated ,当组件被激活时,触发钩子函数 activated,当组件被移除时,触发钩子函数 deactivated。

一百二十七、为什么组件中的 data 必须是一个函数,然后 return 一个对象,而 new Vue 实例里,data 可以直接是一个对象?

  • 因为组件是用来复用的,且 JS 里对象是引用关系,如果组件中 data 是一个对象,那么这样作用域没有隔离,子组件中的 data 属性值会相互影响,
  • 如果组件中 data 选项是一个函数,那么每个实例可以维护一份被返回对象的独立的拷贝,组件实例之间的 data 属性值不会互相影响;而 new Vue 的实例,是不会被复用的,因此不存在引用对象的问题。

 一百二十八、v-model 的原理?

我们在 vue 项目中主要使用 v-model 指令在表单 input、textarea、select 等元素上创建双向数据绑定,我们知道 v-model 本质上不过是语法糖,v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:

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

 一百二十九、使用过 Vue SSR 吗?说说 SSR? 

  • Vue.js 是构建客户端应用程序的框架。默认情况下,可以在浏览器中输出 Vue 组件,进行生成 DOM 和操作 DOM。然而,也可以将同一个组件渲染为服务端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上完全可交互的应用程序。
  • 即:SSR大致的意思就是vue在客户端将标签渲染成的整个 html 片段的工作在服务端完成,服务端形成的html 片段直接返回给客户端这个过程就叫做服务端渲染。

服务端渲染 SSR 的优缺点如下:

  • (1)服务端渲染的优点:
    • 更好的 SEO:因为 SPA 页面的内容是通过 Ajax 获取,而搜索引擎爬取工具并不会等待 Ajax 异步完成后再抓取页面内容,所以在 SPA 中是抓取不到页面通过 Ajax 获取到的内容;而 SSR 是直接由服务端返回已经渲染好的页面(数据已经包含在页面中),所以搜索引擎爬取工具可以抓取渲染好的页面;
    • 更快的内容到达时间(首屏加载更快):SPA 会等待所有 Vue 编译后的 js 文件都下载完成后,才开始进行页面的渲染,文件下载等需要一定的时间等,所以首屏渲染需要一定的时间;SSR 直接由服务端渲染好页面直接返回显示,无需等待下载 js 文件及再去渲染等,所以 SSR 有更快的内容到达时间;
  • (2) 服务端渲染的缺点:
    • 更多的开发条件限制:例如服务端渲染只支持 beforCreate 和 created 两个钩子函数,这会导致一些外部扩展库需要特殊处理,才能在服务端渲染应用程序中运行;并且与可以部署在任何静态文件服务器上的完全静态单页面应用程序 SPA 不同,服务端渲染应用程序,需要处于 Node.js server 运行环境;
    • 更多的服务器负载:在 Node.js 中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用CPU 资源 (CPU-intensive - CPU 密集),因此如果你预料在高流量环境 ( high traffic ) 下使用,请准备相应的服务器负载,并明智地采用缓存策略。

 一百三十、vue路由的几种模式  

1.hash模式
特点:在url地址上有#号
实现的原理:原生的hasChange事件来实现,来监听hash值的变化
window.onhaschange=function(){}
刷新页面的时候:不会去发送请求,页面不会有任何问题,不需要后端来配合

2.history模式
特点:在url地址上没有#号,比较与hash模式看起来好看一些
实现的原理:利用的是history的api 来实现的 popState() 来实现的
刷新页面的时候:会去发送请求然后会导致页面出现找不到的情况,需要后端来配合解决

3.abstract 模式

支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式.

一百三十一、能说下 vue-router 中常用的 hash 和 history 路由模式实现原理吗? 

(1)hash 模式的实现原理
早期的前端路由的实现就是基于 location.hash 来实现的。其实现原理很简单,location.hash 的值就是 URL 中 # 后面的内容。比如下面这个网站,它的 location.hash 的值为 '#search':
https://www.word.com#search
hash 路由模式的实现主要是基于下面几个特性:

  • URL 中 hash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 部分不会被发送;
  • hash 值的改变,都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制hash 的切换;
  • 可以通过 a 标签,并设置 href 属性,当用户点击这个标签后,URL 的 hash 值会发生改变;或者使用 JavaScript 来对 loaction.hash 进行赋值,改变 URL 的 hash 值;
  • 我们可以使用 hashchange 事件来监听 hash 值的变化,从而对页面进行跳转(渲染)。

(2)history 模式的实现原理
HTML5 提供了 History API 来实现 URL 的变化。其中做最主要的 API 有以下两个:history.pushState() 和 history.repalceState()。这两个 API 可以在不进行刷新的情况下,操作浏览器的历史纪录。
唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录,如下所示:
window.history.pushState(null, null, path);
window.history.replaceState(null, null, path);
history 路由模式的实现主要基于存在下面几个特性:

  • pushState 和 repalceState 两个 API 来操作实现 URL 的变化 ;
  • 我们可以使用 popstate 事件来监听 url 的变化,从而对页面进行跳转(渲染);
  • history.pushState() 或 history.replaceState() 不会触发 popstate 事件,这时我们需要手动触发页面跳转(渲染)。

一百三十二、Vue 怎么用 vm.$set() 解决对象新增属性不能响应的问题 ?

受现代 JavaScript 的限制 ,Vue 无法检测到对象属性的添加或删除。
由于 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。
但是 Vue 提供了 Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value)来实现为对象添加响应式属性,那框架本身是如何实现的呢?

我们查看对应的 Vue 源码:vue/src/core/instance/index.js

export function set (target: Array<any> | Object, key: any, val: any): any {
  // target 为数组
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    // 修改数组的长度, 避免索引>数组长度导致splcie()执行有误
    target.length = Math.max(target.length, key)
    // 利用数组的splice变异方法触发响应式
    target.splice(key, 1, val)
    return val
  }
  // key 已经存在,直接修改属性值
  if (key in target && !(key in Object.prototype)) {
    target[key] = val
    return val
  }
  const ob = (target: any).__ob__
  // target 本身就不是响应式数据, 直接赋值
  if (!ob) {
    target[key] = val
    return val
  }
  // 对属性进行响应式处理
  defineReactive(ob.value, key, val)
  ob.dep.notify()
  return val
}

 我们阅读以上源码可知,vm.$set 的实现原理是:
如果目标是数组,直接使用数组的 splice 方法触发相应式;
如果目标是对象,会先判读属性是否存在、对象是否是响应式,最终如果要对属性进行响应式处理,则是通过调用 defineReactive 方法进行响应式处理( defineReactive 方法就是 Vue 在初始化对象时,给对象属性采用 Object.defineProperty 动态添加 getter 和 setter 的功能所调用的方法)

一百三十三、 虚拟 DOM 实现原理?

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

  • 用 JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象;
  • diff 算法 — 比较两棵虚拟 DOM 树的差异;
  • pach 算法 — 将两个虚拟 DOM 对象的差异应用到真正的 DOM 树。

一百三十四、你有对 Vue 项目进行哪些优化? 

(1)代码层面的优化

  • v-if 和 v-show 区分使用场景
  • computed 和 watch 区分使用场景
  • v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
  • 长列表性能优化
  • 事件的销毁
  • 图片资源懒加载
  • 路由懒加载
  • 第三方插件的按需引入
  • 优化无限列表性能
  • 服务端渲染 SSR or 预渲染

(2)Webpack 层面的优化

  • Webpack 对图片进行压缩
  • 减少 ES6 转为 ES5 的冗余代码
  • 提取公共代码
  • 模板预编译
  • 提取组件的 CSS
  • 优化 SourceMap
  • 构建结果输出分析
  • Vue 项目的编译优化

(3)基础的 Web 技术的优化

  • 开启 gzip 压缩
  • 浏览器缓存
  • CDN 的使用
  • 使用 Chrome Performance 查找性能瓶颈

一百三十五、对于 vue3.0 特性你有什么了解的吗? 

Vue 3.0 的目标是让 Vue 核心变得更小、更快、更强大,因此 Vue 3.0 增加以下这些新特性:

1、监测机制的改变
3.0 将带来基于代理 Proxy 的 observer 实现,提供全语言覆盖的反应性跟踪。这消除了 Vue 2 当中基于 Object.defineProperty 的实现所存在的很多限制:

(1)只能监测属性,不能监测对象
(2)检测属性的添加和删除;
(3)检测数组索引和长度的变更;
(4)支持 Map、Set、WeakMap 和 WeakSet。
新的 observer 还提供了以下特性:

(1)用于创建 observable 的公开 API。这为中小规模场景提供了简单轻量级的跨组件状态管理解决方案。
(2)默认采用惰性观察。在 2.x 中,不管反应式数据有多大,都会在启动时被观察到。如果你的数据集很大,这可能会在应用启动时带来明显的开销。在 3.x 中,只观察用于渲染应用程序最初可见部分的数据。
(3)更精确的变更通知。在 2.x 中,通过 Vue.set 强制添加新属性将导致依赖于该对象的 watcher 收到变更通知。在 3.x 中,只有依赖于特定属性的 watcher 才会收到通知。
(4)不可变的 observable:我们可以创建值的“不可变”版本(即使是嵌套属性),除非系统在内部暂时将其“解禁”。这个机制可用于冻结 prop 传递或 Vuex 状态树以外的变化。
(5)更好的调试功能:我们可以使用新的 renderTracked 和 renderTriggered 钩子精确地跟踪组件在什么时候以及为什么重新渲染。
2、模板
模板方面没有大的变更,只改了作用域插槽,2.x 的机制导致作用域插槽变了,父组件会重新渲染,而 3.0 把作用域插槽改成了函数的方式,这样只会影响子组件的重新渲染,提升了渲染的性能。
同时,对于 render 函数的方面,vue3.0 也会进行一系列更改来方便习惯直接使用 api 来生成 vdom 。

3、对象式的组件声明方式
vue2.x 中的组件是通过声明的方式传入一系列 option,和 TypeScript 的结合需要通过一些装饰器的方式来做,虽然能实现功能,但是比较麻烦。
3.0 修改了组件的声明方式,改成了类式的写法,这样使得和 TypeScript 的结合变得很容易。
此外,vue 的源码也改用了 TypeScript 来写。其实当代码的功能复杂之后,必须有一个静态类型系统来做一些辅助管理。
现在 vue3.0 也全面改用 TypeScript 来重写了,更是使得对外暴露的 api 更容易结合 TypeScript。静态类型系统对于复杂代码的维护确实很有必要。

4、其它方面的更改
vue3.0 的改变是全面的,上面只涉及到主要的 3 个方面,还有一些其他的更改:

支持自定义渲染器,从而使得 weex 可以通过自定义渲染器的方式来扩展,而不是直接 fork 源码来改的方式。
支持 Fragment(多个根节点)和 Protal(在 dom 其他部分渲染组建内容)组件,针对一些特殊的场景做了处理。
基于 treeshaking 优化,提供了更多的内置功能。

  • 14
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值