前端面试题汇总(实时更新ing)

前端面试题汇总

https://www.one-tab.com/page/DUzvPkoFTy67kYevpvS2WQ?utm_source=wechat_session&utm_medium=social&utm_oi=775824004362543104 (面试汇总)

HTTP/HTML/浏览器

http和https

基本概念:

  • http
    超文本传输协议,是一个客户端和服务端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议。

  • https
    是以安全为目标的HTTP通道,简单来说,是HTTP的安全版,即HTTP下加入SSL层。

区别:

  • http传输数据没有加密,是明文传输。
  • https需要ca证书,费用高。

tcp三次握手

1. 第一次握手,服务器确认自己可以收到客户端的信息。
2. 客户端确定服务器收到了发送的信息,并确定自己可以收到服务器的信息。
3. 服务器确定客户端收到了自己的信息

TCP和UDP区别

1. TCP是面向链接的,UDP是无连接的(即发送数据前不需要建立链接)。
2. TCP服务可靠,UDP不可靠,可能丢失信息。
3. TCP面向字节流,UDP面向报文。
4. TCP是一对一,UDP可以一对多。

WebSocket

  • 支持持久性链接
  • 链接完成,服务器端可以主动推送数据到客户端

web Quality(无障碍)

  • 因为个别浏览器功能不全面,从而导致内容缺失。
  • 解决方法:
    1. 优雅降级
    2. 渐次增强
    3. 使用alt属性。(图片加载不出来时显示文字)

Cookie、sessionStorage、localStorage

  • 相同点: 都是保存在浏览器端,并且都是同源的。
  • 不同点:
    1. cookie:会在http请求时自动携带,在浏览器和服务器间来回传递。存储大小很小(4K左右)
    2. sessionStorage:保存在本地,当关闭浏览器,sessionStorage会失效(销毁)。
    3. localStorage: 保存在本地,一直存在。

web worker

  • 在html之中,如果执行脚本,在加载引入的js时会造成页面阻塞(即页面内容不加载,专心加载js,从而导致白屏)。
  • web worker试运行在后台的js,独立于其他脚本,不会影响页面性能。

HTML语义化

  • 语义化标签没有实际含义
  • 语义化标签提高代码结构的阅读性
  • 比如:nav表示导航条,header,footer等。

iframe

  • iframe时包含一个文档的内敛框架,有些类似与组件化思想。
  • 可以用iframe解决跨域问题
  • 缺点:
    1. 会阻塞主页面的onload事件
    2. 搜索引擎无法解读,不利于SEO(优化)
    3. 影响性能

Doctype严格模式和混杂模式

  • 严格模式: 排版和JS 运作模式是 以该浏览器支持的最高标准运行。
  • 混杂模式:标准比较松懈。向后兼容。

http返回的状态码

  • 100 continue 继续。客户端继续其请求。
  • 101 switching Protocols 切换协议。服务端根据客户端的请求切换协议(只能切换到更高级协议)
  • 200 ok 成功。请求成功
  • 201 created 已创建。成功请求并创建了新的资源
  • 202 accepted 已接受。已经接受请求,但未处理完成。
  • 203 Non-Authoritative Information 非授权信息。请求成功,但返回的meta信息不在原始的服务器,而是一个副本。
  • 204 no content 无内容。服务器处理成功,但是没有返回内容。
  • 205 reset content 重置内容。用户终端应重置文档视图。
  • 206 partial content 部分内容。服务器成功处理了部分GET请求。
  • 300 Multiple Choices 多种选择。请求的资源包括多个位置。
  • 301 Moved Permanently 永久移动。请求的资源已经被永久移动到新的URL。
  • 302 found 临时移动。与301相同,但资源只是临时被移动。
  • 303 See Other 查看其它地址。和301,302一样。使用GET或者POST请求查看。
  • 304 Not Modified 未修改。所请求的资源未修改。
  • 305 use proxy 使用代理。所使用的资源必须通过代理访问。
  • 306 unused 已经被废弃了。
  • 307 Temporary Redirect 临时重定向。和302类似,使用GET请求重定向。
  • 400 Bad Request 客户端请求的语法错误,服务器无法理解。
    产生原因:1. 语义有误,2.请求参数有误。
  • 401 Unauthorized 请求要求用户的身份验证
  • 402 Payment Required 保留,将来使用
  • 403 Forbidden 服务器理解请求,但是拒绝执行。
    产生原因:权限不够
  • 404 Not Found 找不到地址
    产生原因:请求的地址出错。
  • 405 Method Not Allowed 客户端请求的方法被禁止。
    产生原因:请求方法不对(应该用GET,你用POST)
  • 406 Not Acceptable 服务器无法根据客户端请求的内容特性完成请求.
    产生原因:服务器返回的数据客户端无法接受。
  • 407 Proxy Authentication Required 请求要求代理的身份认证
  • 408 Request Time-out 服务器等待客户端发送的请求时间过长,超时
  • 409 ~ 417 不常用,有意可以自行了解
  • 500 Internal Server Error 服务器内部错误
  • 501 Not Implemented 服务器不支持请求的功能
  • 502Bad Gateway 从远程服务器接收到了一个无效的响应
  • 503 Service Unavailable 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中
  • 504 Gateway Time-out 充当网关或代理的服务器,未及时从远端服务器获取请求
  • 505 HTTP Version not supported 服务器不支持请求的HTTP协议的版本,无法完成处理

http常用请求头

协议头说明
accept可接受的相应内容类型(content-Type)
accept-Charset可接受的字符集
accept-encoding可接受的响应内容的编码方式
accept-Language可接受的响应内容语言列表
accept-Datetime可接受的按照时间来响应内容版本
Authorization用于表示HTTP协议中需要认证资源的认证信息
Cache-Control用于指定请求/回复中是否使用缓存机制
connection客户端想要优先使用的链接类型
Cookie由之前服务器通过Set-Cookie(见下文)设置的一个HTTP协议Cookie
content-Length以8进制表示的请求体长度
Content-MD5请求体内容的二进制MD5散列值,以Base64编码的结果
Content-Type请求体的MIME类型(用于POST和PUT)
Date发送该消息时的日期和时间
Expect表示客户端要求服务器做出特定的行为
From发起此请求的用户的邮件地址
Host表示服务器的域名以及服务器所监听的端口号
Max-Forwards限制该消息可以被代理以及网关转发的次数
Origin发起一个针对跨域资源共享的请求
Proxy-Authorization用于向代理进行认证的认证信息
Referer表示浏览器访问的前一个页面
TE浏览器与其接受的传输时的编码方式
User-Agent浏览器的身份标识字符串
Upgrade要求服务器升级到一个高版本协议
Via告诉服务器,这个请求是由哪些代理发出的
Warning一个一般性的警告,表示实体内容可能存在错误

强缓存,协商缓存

  • 缓存分为两种:强缓存和协商缓存,根据相应的header内容决定。
获取资源方式状态码发送请求到服务器
强缓存从缓存中获取内容200(from cache)否,直接从缓存中获取
协商缓存从缓存中获取304(not modified)先访问服务器,进行资源比对,如果本地缓存和服务器内容一样,则调用本地缓存

前端优化

方法具体解析
降低请求量合并资源精灵图
减少HTTP请求强缓存,协商缓存
minify/zip压缩压缩图片,压缩代码
webP压缩图片
lazyLoad懒加载
加快请求速度预解析DNS浏览网页时,浏览器在加载网页时对网页中的域名进行解析缓存,这样在单击当前网页中的连接时就无需进行DNS的解析,减少用户等待时间,提高用户体验。
减少域名数
并行加载并发加载,谷歌最多支持6个。
CDN分发CDN全称是Content Delivery Network,即内容分发网络
缓存HTTP协议缓存请求通过请求头设置cookie。
离线缓存mainfest协商缓存,请求mainfest内容时,本地缓存先和网上内容进行比对,如果不同,请求网上内容,如果相同,则直接使用本地。
离线数据缓存localStorage强缓存,将信息保存再本地,需要时不和网上进行比对直接进行使用。
渲染JS/CSS优化优化代码。
加载顺序js写在底部,css写在头部,先外链,再本页。
服务端渲染vue,react等MVVM。
pipelinepipeline是一套jenkins官方提供的插件,它可以用来在jenkins中实现和集成连续交付。

GET和POST的区别

GETPOST
传递方式通过url(?id=1)放在request body中
传递长度有长度限制没有长度限制
安全性参数暴露在url不暴露
编码方式只能进行url编码多种编码方式
请求参数保留在浏览历史记录里不会被保存
TCP数据包产生一个TCP数据包产生两个TCP数据包

从输入URL到页面加载的过程

在地址栏输入一个URL,到这个页面呈现,中间的过程(简略)

  1. DNS解析

    1. TCP连接
    2. 发送HTTP请求
    3. 服务器处理HTTP请求并返回报文
    4. 浏览器解析渲染页面
    5. 连接结束

具体过程(将依次拓展)

1.从浏览器接受url到开启网络请求线程(可展开浏览器的机制以及进程与线程之间的关系)
2.开启网络线程到发出一个完整的http请求(设计dns查询,tcp/ip请求,五层因特网协议栈等)
3.从服务器接收到请求到对应后台接收到请求(负载均衡吗,安全拦截以及后台内部的处理)
4.后台和前台的http交互(包括http头部、响应码、报文结构、cookie等)
5.单独拎出来的缓存问题,http的缓存(http缓存头部,etag,catch-control)
6.浏览器接收到http数据包后的解析流程(解析html词法分析然后解析成dom树、解析css生成css规则书、合并成render树,然后layout、painting渲染,、复合图层、GPU绘制、外联资源的处理、loaded和domcontentload等)
7.css的可视化格式模型(元素的渲染规则,如包含块,BFC,IFC等概念)
8.JS引擎解析过程(JS的解释阶段,预处理阶段,执行阶段生成执行上下文,VO,作用域链,回收机制等等)
9.其他(可以拓展不同的知识模块,如跨域,web安全,hybrid模式等)

从浏览器接收url到开启网络请求线程

这一部分展开的内容是:浏览器进程/线程模型,js的运行机制

多进程的浏览器

浏览器是多进程的,有一个主控进程,以及每一个tab页面都会新开一个进程(某些情况下多个tab会合并进程)

进程跨域包括主控进程,插件进程,GPU,tab页(浏览器内核)等等

  • Browser进程:浏览器的主进程(负责协调、主控),只有一个
  • 第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时才创建
  • GPU进程:最多一个,用于3D绘制
  • 浏览器渲染进程(内核):默认每一个tab页一个进程,互不影响,控制页面渲染,脚本执行,事件处理等

多线程的浏览器内核

每一个tab页面可以看作是浏览器内核进程,然后这个进程是多线程的,他有几大类子线程

  • GUI线程
  • JS引擎线程
  • 事件触发线程
  • 定时器线程
  • 网络请求线程

解析URL

输入URL后,会进行解析(URL的本质就是统一资源定位符)

URL一版包括几大部分:

  • protocol,协议头,譬如http,ftp等
  • host,主机域名或ip地址
  • prot,端口号
  • path,目录路径
  • query,即查询参数
  • fragment,即#
  • 后面的hash值,一般用来定位到某个位置

网络请求都是单独的线程

每次网络请求时都需要开辟单独的线程进行,譬如如果URL解析到http协议,就会新建一个网络线程去处理资源下载

因此,浏览器会根据解析出的协议,开辟一个网络线程,前往请求资源(这里,暂时理解为时浏览器内核开辟)

开启网络线程到发出一个完整的http请求

这一部分主要内容包括:dns查询,tcp/ip请求构建,五层因特网协议栈等

DNS查询得到IP

如果输入的是域名,需要进行dns解析成IP,大致流程:

  • 如果浏览器有缓存,直接使用浏览器缓存,否则使用本机缓存,再没有的话就是用host
  • 如果本地没有的话,就向dns域名服务器查询,查询到对应的ip

注意,域名查询是有可能是经过了cdn调度器的(如果有cdn存储功能的话)

而且,需要知道dns解析是很耗时的,因此如果解析域名过多,会让收评加载变的过慢,可以考虑dns-prefetch优化

dns-prefetch

  • 什么是DNS Prefetch

    • DNS Prefetch 是一种DNS预解析技术。当你浏览网页时,浏览器会在加载网页时对网页中的域名进行解析缓存,这样在你单击当前网页中的链接时就无需进行DNS的解析,减少用户等待时间,提高用户体验。
    • 目前每次DNS解析,通常在200ms以下。针对DNS解析耗时问题,一些浏览器通过DNS Prefetch来提高访问的流畅性。
  • 如何设置dns-prefetch

    • DNS Prefetch应该尽量放在网页的前面,推荐放在后面。

    • <meta http-equiv="x-dns-prefetch-control" content="on">
      <link rel="dns-prefetch" href="//www.zhix.net">
      <link rel="dns-prefetch" href="//api.share.zhix.net">
      <link rel="dns-prefetch" href="//bdimg.share.zhix.net">
      
    • <!--如果不确定是http还是https连接的话建议如下写法 -->
      <link rel="dns-prefetch" href="//renpengpeng.com" />
      

tcp/ip请求

http的本质就是tcp/ip请求

tcp将http长报文划分为短报文,通过三次握手与服务端建立连接,进行可靠传输

三次握手的步骤:(抽象派)

客户端:hello,你是server嘛?
服务端:heool,我是server,你是client嘛?
客户端:yes,我时client

建立链接成功后,接下来就正式传输数据

然后,待断开链接时,需要进行四次挥手(因为时全双工的,所以需要四次挥手)

四次挥手的步骤:(抽象派)

主动方:我以及关闭了向你那边的主动通道,只能被动接收了
被动方:收到通道关闭的信息
被动方:那我也告诉你,我这边向你的主动通道也关闭了
主动方:最后收到数据,双方无法通信

tcp/ip的并发限制

  • 浏览器对同一域名下并发的tcp链接时有限制的(2-10个不等)
  • 而且在http1.0中往往一个资源下载就对应一个tcp/ip请求
  • 所以针对这个平静,又出现了很多资源优化方案

GET和POST的区别

get和post虽然本质都是tcp/ip,但两者除了在http层面外,在tcp/ip层面也有区别。

get会产生一个tcp数据包,post会产生俩个。

  • get请求时,浏览器会把headers和data一起发送过去,服务其响应200
  • post请求时,浏览器会先发送headers,服务其响应100 continue,浏览器再发送data,服务器响应200(返回数据)。

五层因特网协议栈

从客户端发出http请求到浏览器接收,中间会经过一系列的流程。

简括就是:

  • 从应用层的发送http请求,到传输层通过三次握手简历tcp/ip链接,再到网络层的ip寻址,再到数据链路层的封装成帧,最后到物理层的利用物理介质传输。

从服务器接收到请求到对应后台接收到请求

负载均衡

  • 对于大型项目,由于并发访问量很大,所以往往一台服务其是吃不消的,所以一版会有若干台服务其组成一个集群,然后配合反向代理实现负载均衡。

  • 用户发起的请求都指向调度服务其(反向代理服务其,譬如安装了nginx控制负载均衡),然后调度服务器根据实际的调度算法,分配不同的请求给对应集群中的服务器执行,然后调度器等待实际服务器的HTTP响应,并将它反馈给用户

后台的处理

  • 一般后台都是部署到容器里,所以一般为:
    • 先是容器接收到请求(如tomcat)
    • 然后对应容器中的后台程序接收到请求(如java程序)
    • 然后就是后台会有自己的统一处理,处理完后响应结果
  • 概括下:
    • 一般有的后端是有统一的验证的,如安全拦截,跨域验证
    • 如果这一步不符合规则,就直接返回了响应的http报文(如拒绝请求等)
    • 当验证通过后,才会进入实际的后台代码,此时是程序接收到请求,然后执行
    • 等程序执行完毕后,就会返回一个http相应包
    • 将这个包从后端发送到前端,完成交互

后台和前台的http交互

http报文结构

  • 报文结构一般包括了:通用头部,请求/响应头部,请求/响应体

通用头部

  • 这也是开发人员见过的最多的信息,包括如下:

    Request Url:请求的web服务器地址
    Request Method:请求方式(Get,POST,OPTIONS,PUT,HEAD,DELETE,CONNECT,TRACE)
    Status Code:请求的返回状态码
    Remote Address:请求的远程服务器地址(会转为IP)
    
  • 其中,Method的话分为两批次:

    http1.0定义了三种请求方法:GET,POST和HEAD方法
    http1.1定义了八种请求方法:GET,POST,HEAD,OPTIONS,PUT,DELETE,TRACE和CONNECT
    

gzip压缩

  • 首先,明确gzip是一种压缩格式,需要浏览器支持才有效(一般浏览器都支持),而且gzip压缩效率很好(高达70%)
  • gzip一般是由apache、tomcat等web服务器开启

长连接与短连接

首先看tcp/ip层面的定义:

  • 长连接:一个tcp/ip连接上可以连续发送多个数据包,再在tcp链接保持期间,如果没有数据包发送,需要双方发检测包来维持此链接,一般需要自己做在线维持(类似于心跳包)
  • 短链接:通信双方有数据交互时,就建立一个tcp链接,数据发送完成后,就断开此tcp链接

在http层面:

  • http1.0中,默认使用的是短链接,也就是说,浏览器每进行一次

浏览器多进程到JS答线程

浏览器内核

  • 浏览器的渲染是多线程的:
  • GUI渲染线程
    • 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。
    • 当界面需要重绘或者某种操作引发回流的时候,该线程就会执行。
    • GUI渲染线程和js引起线程互斥。
  • JS引擎线程
    • 也成为JS内核,负责处理js脚本程序
    • js引擎线程负责解析js脚本,运行代码
    • js引擎一直等待着任务队列中人物的到来,然后加以处理,一个tab页中无论什么时候都只有一个js线程。
    • GUI渲染和js引擎线程互斥。
  • 事件触发线程
    • 归属于浏览器而不是js引擎,用来控制事件循环(可以理解,js引擎自己都忙不过来,需要浏览器另开线程协助)
    • 当js引擎执行代码块如setTimeOut时,(火来自浏览器内核的其他线程,如鼠标点击,AJAX异步请求等),会将对应任务添加到事件线程中
    • 当对应的事件符合触发条件被触发的时候,该线程会把事件添加到待处理队列的队尾,等到js引擎的处理。
    • 由于js的单线程关系,所以这些待处理队列中的事件都需要排队等待js引擎处理。
  • 定时触发器线程
    • 传说中setInterval与setTimeout所在线程
    • 浏览器定时器并不是由javaScript引擎计数(js引擎单线程,如果处于阻塞状态就会影响计时器的准确)
    • 因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待js引擎空闲后执行)
    • 注意,W3c在HTML标准中规定,要求setTimeout中低于4ms的时间间隔算为4ms
  • 异步http请求线程
    • 在XML HttpRequest在连接后是通过浏览器新开一个线程请求
    • 在检测到状态变更时,如果设置有回调函数,异步引擎就会产生状态变更事件,将这个回调再放入事件队列中,再由javaScript引擎执行。

Browser(渲染)进程和浏览器内核进程(Renderer进程)的通信进程

  • browser进程收到用户请求,首先需要获取页面内容,随后将该任务通过RendererHost接口传递给Render进程。
  • Renderer进程的Renderer接口收到信息,简单解释后,交给渲染线程,开始渲染。
    • 渲染线程接收到请求,加载网页并渲染网页,这其中可能需要Browser进程获取资源和需要GPU进程来帮助渲染
    • 当然可能会有JS线程操作DOM(这样可能会造成回流和重绘)
    • 最好Render进程将结果传递给Browser进程
  • Browser进程接收到结果并将结果绘制出来

梳理浏览器内核中线程之间的关系

GUI线程和JS引擎线程互斥

  • 因为js是可以操作dom的,如果在修改这些元素属性的同时渲染界面(即js和ui线程同时运行),那么渲染线程前后获得的元素数据可能就不一致了。
  • 因此为了防止渲染出现不可预期的结果,浏览器设置GUI渲染线程和JS引擎为互斥的关系,当JS引擎执行时GUI线程会被挂起
  • GUI更新则会保存在一个队列中等到JS引擎线程空闲时立即执行

JS阻塞页面加载

  • 从上述互斥可以推导出,如果JS执行时间过长,就会阻塞页面。

WebWork,JS的多线程

  • 创建Woker时,JS引擎向浏览器申请开一个子线程(子线程是浏览器开的,完全受主线程控制,而且不能操作DOM)
  • JS引擎线程与worker线程间通过特定的方式通信(postMessage线程 API,需要通过序列化对象来与线程交互特定的数据)
  • 注意,JS是单线程的,这一点的本质并未改变

浏览器渲染流程

  1. 解析html建立dom树
  2. 解析css构建render树
  3. 布局render树(layout/reflow),负责各元素尺寸,位置的计算
  4. 绘制render树
  5. 进行合成场景并显示

load事件和DOMContentLoaded事件的先后

  • 渲染完成之后会触发load事件,这里包括两部分,load和DOMContentLoaded。
  • 当DOMContentLoaded事件触发时,仅当DOM加载完成,不包括样式表,图片。(比如如果有async加载的脚本就不一定完成)
  • 当onload事件触发时,页面上所有的DOM,样式表,脚本,图片都已经加载完成了
  • 所以,顺序是:DOMContentLoaded -> load

css加载是否会阻塞dom树渲染

  • 这里说的是头部引入css的情况
  • css是由单独的下载线程异步下载的
    • css加载不会阻塞DOM树解析(异步加载时DOM照常构建)
    • 但会阻塞render树渲染(渲染时需等css加载完毕,因为render树需要css信息)

普通图层和复合图层

  • 浏览器渲染的图层一版分为两大类:普通图层和符合图层
  • 首先,普通文档流内可以理解为一个复合图层(默认符合层,里面不管添加多少元素,其实都在同一个复合图层中)
  • absolute布局等,虽然会脱离普通文档流,但仍属于默认复合层
  • 可以通过硬件加速的方式,声明一个新的复合图层,他会单独分配资源
  • GPU中,各个复合层是单独绘制的,所以互不影响,这也是为什么某些场景硬件加速效果一级棒。

如何变成复合图层

  • 把该元素变成一个复合图层,就是传说中的硬件加速技术
    • 常用方法为:translate3d,translzteZ
    • opacity属性/过渡动画(需要动画执行的过程才会创建合成层,动画没有开始或结束后元素还会回到之前的状态)
    • will-change属性(比较偏僻),一般配合opacity和translate使用(而且经测试,除了上述可以引发硬件加速的属性外,其他属性不会变成复合层)作用时提前告诉浏览器要变化,这样浏览器会开始做一些优化工作
    • video,iframe,canvas,webgl等元素
    • 其他,比如以前的flash插件

absolute和硬件加速的区别

  • 可以看到,absolute虽然可以脱离普通文档流,但是无法脱离默认复合层
  • 所以,就算absolute信息改变的时候不会改变普通文档流的render树,答案,浏览器最终绘制的时候,是整个复合层绘制的,所以absolute中信息的改变,仍然会影响整个复合层的绘制(浏览器会重绘它,如果复合层中内容比较多,absolute带来的绘制信息变化过大,资源消耗就会很严重)
  • 硬件加速直接在另一个复合层打开,所以它的信息改变不会影响默认复合层,仅仅是影响最后的合成

复合图层的作用

  • 一般一个元素开启硬件加速后会变成复合图层,可以独立于普通文档流中,改动后可以避免整个页面重绘,提升性能
  • 尽量不要大量的使用符合图层,否则资源消耗过度,页面反而更卡

从Event Loop谈JS的运行机制

  • 执行一个宏任务
  • 执行过程中遇到微任务,把它加到微任务的任务队列中
  • 宏任务执行完毕,立即开始执行当前微任务队列中的所有微任务
  • 当前宏任务执行完毕,开始检查渲染,任何GUI线程接管渲染
  • 渲染完毕后,js线程继续接管,开始下一个宏任务
  • PS:Promise的polyfill于官方版本的区别在于,官方版本中promise是微任务,polyfill是宏任务

CSS

####css盒模型

  • 简介:装页面上元素的矩形区域,包括IE盒子模型标准的W3C盒子模型
  • box-sizing:border-box,padding-box,content-box
  • 标准盒子模型(content-box):width = content + padding + border
  • IE盒子模型(border-box): width = content
  • padding-box: width = content + padding

画一条0.5px的线

  • 原因:0.5px的线在不同浏览器会有不同的体现。
  • 方法实现:
    1 meta viewport进行缩放
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />

2 border-image的方式
将图片规定为包围 div 元素的边框

3 transform: scale()
同样是使用缩放

link标签和impot标签的区别

  • link属于html标签,@import是css提供的
  • 页面加载时,link会同时被加载,@import引用的css要等到页面加载结束后加载。
  • link是html标签,没有兼容性。@import要IE5以上才能识别
  • link方式权重高于@import

transition和animation的区别

他们大部分属性相同,都是随着时间改变元素的属性值。

  • 区别:
    1 transition需要触发一个事件才能改变属性(比如hover)。而animation不需要。
    2 transition为2帧,animation是1帧。

Flex布局

flex是‘弹性布局’,用来为盒模型提供最大的灵活性。

  • 使用方法
    1 在父元素定义 display:flex。
    2 在父元素可定义如下属性
    1. flex-direction: 决定主轴方向。
    2. flex-wrap: 决定换行规则。
    3. flex-flow : 是前两个的缩写。
    4. justify-content: 对齐方式
    5. align-items: 交叉轴上如何对齐。
    6. align-content: 多根轴线的对齐方式,如果项目只有一根轴线,则不起作用。

3 在子元素可定义如下属性
7. order: 定义项目的排序顺序,数值越小,排列越前。
8. flex-grow: 定义项目的放大比例,默认为0(即不放大)。(如果所有项目的此属性都是1,则等分剩余空间,如果有一个为2,则其占据的空间是其他项目占据的两倍)
9. flex-shrink: 定义项目的缩小比例,默认为1(即如果空间不足,该项目会缩小)。(如果所有项目都为1,则空间不足会等比例缩小。如果其中一个为0,则空间不足时,为0的项目不缩小。)
10. flex-basis: 定义在分配多余空间之前,项目占据的主轴空间。默认为auto(项目本来大小)。
11. flex: 是上三项的简写。
12. align-self: 允许当个项目与其他项目不一样的对齐方式。默认为auto。

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

  • 块级格式化上下文,是一个独立的渲染区域,并且有一定的布局规则。
  • 直白的来说:BFC区域就是将区域内的浮动元素计入布局之中(比如解决高度坍塌)。
  • 生成BFC的元素:
    1 根元素或者其他包含它的元素
    2 浮动元素(元素的float不为none)
    3 绝对定位元素(元素position为absolute或fixed)
    4 内联块(display:inline-block)
    5 表格单元格(diaplay:table-cell,HTML表格单元格默认属性)
    6 表格标题(diaplay: table-caption,HTML表格标题默认属性)
    7 具有overflow且值不为visible的块元素
    8 display: flow-root
    9 column-span:all 应当总会创建一个格式化上下文,即便具有column-span:all 的元素并不被包裹在一个多列容器中。
    10 一个块格式化上下文包括创建它的元素内部所有内容,除了被包换与创建新的块级格式化上下文的后代元素内的元素。

垂直居中的方法

  • margin: auto
  • display:table-cell;vertical-align:middle;
  • flex布局
	div{
		display:flex;
		justify-content:center;
		align-items: center;
	}

关于JS动画和css3动画的差异性

  • 功能涵盖面上,js比css大
  • 实现重构的难度,css3更简单。
  • css3可以做到自然降级。
  • css3动画有天然事件支持
  • csss3有兼容问题

优雅降级和渐进增强

  • 优雅降级
    先适配高版本浏览器。而后针对低版本浏览器逐次降低配置需求,使其可以实现基本功能。

  • 渐进增强
    一开始针对低版本浏览器满足基本功能。而后根据浏览器版本的提升依次提高页面交互,效果等新功能。

块元素和行内元素

  • 块元素
    独占一行,并且有自动填满父元素,可以设置margin和padding以及高度和宽度

  • 行元素
    不会独占以上,weight和height会失效,垂直方向的padding和margin会失效。

  • img元素例外,他是行元素,但是它具有weight和height等属性。

多行元素的文本内容省略

	display: -webkit-box;
	-webkit-box-orient:vertical;
	-webkit-line-clamp:3;
	overflow:hidden;

visivility = hidden, opacity = 0, display:none区别

  • opacity = 0
    设置透明度为全透明,隐身状态,但是依然存在,不会改变页面布局,可以触发绑定的事件。

  • visibility = hidden
    把该元素隐藏起来,不改变页面布局,但是绑定的事件不会触发

  • display : none
    相当于直接删掉该元素。改变页面布局,绑定事件失效。

外边距折叠问题

  • 问题描述:
    相邻元素的margin属性重叠。

  • 示例:
    margin:20px;margin:30px;
    如上,两个元素的margin分别是20和30.但是两个元素之间距离不是50(20+30);而是30(外边距重叠)。

  • 折叠结果:
    1 都为正数时,是他们之间较大的值。
    2 都为负数时,是他们绝对值较大的。
    3 一正一负,是他们相加的和。

position属性

  • fixed(固定定位)
    脱离文本流,定位位置是浏览器窗口固定位置(左上角)
  • relative(相对定位)
    不脱离文本流,定位位置是元素本来位置。
  • absolute(绝对定位)
    脱离文本流,定位位置为最近的设置定位的父元素。
  • static(默认定位)
    默认值,没有定位。
  • inherit(继承)
    继承父元素的position属性。

浮动消除

1. 使用带clear属性的空元素
在浮动元素后面使用一个空元素(div),在css之中赋予 `clear : both` 属性。

2. 使用css的overflow属性
  将overflow的值设为hidden或者auto。

3. 给浮动元素的容器设置浮动
  不推荐,这会使整体浮动。

css选择器和优先级

css优先级高低排序:!important > 行内样式>ID选择器 > class选择器 > 标签 > 通配符 > 继承 > 浏览器默认属性

重绘和重排

  • 重排
    dom属性的变化(width,height等)导致浏览器需要重新计算盒子模型的排布。
  • 重绘
    浏览器将受到影响的部分重新绘制在屏幕上。
  • 引起重排重绘的原因:
    1. 添加删除可见的dom
    2. 元素尺寸位置的改变
    3. 浏览器页面初始化
    4. 浏览器窗口大小发生改变
  • 减少重绘重排的方法:
    1. 使用css一次性修改属性
    2. 使用fragment

fragment(碎片)

所谓farment,类似于组件化。
在项目中,我们可以出现要重复添加子元素(比如ul里面要添加很多个li),如果for循环添加会不停的导致重排(重绘),所以使用fragment,先全部添加到这里,而后将fragment一次性添加到ul里。

JS

前端的事件流

  • 含义:从页面中接收时间的顺序。
  • 三个阶段
    1. 事件捕获阶段
    2. 处于目标阶段
    3. 事件冒泡阶段

添加事件的方法

  • 直接在html里面通过"onclick = ‘functionName’"绑定

    • 通过此方法只能添加一个事件,如果连续添加两个事件的话,第二个事件会覆盖第一个事件。
  • addEventListener(event,function,useCapture)(ie8之前使用 attachEvent()

    • event:事件名称
    • function:要执行的方法
    • useCapture(选填):指定事件是否在冒泡或者捕获阶段执行,true——捕获,false(默认)——冒泡
  • 注意事项

    • 通过 addEventListener 可以添加多个事件,并且不会被覆盖。
    • 如果在捕获,冒泡都添加了事件,需要删除两次。
    • 如果样式中 onclick事件 和 js中的 addEventListener 都进行了使用,那么这两个都会绑定成功,且执行顺序为,先onclick 再 js绑定事件

删除事件的方法

  • 如果是在html直接添加οnclick=functionName

    • document.getElementById(‘button’).οnclick=function(){};

    • 如果是给方法定义名字的话,将方法名指向空方法即可即可。如下:

      var textfunction = function(){
      			alert('123');
      		}
      textfunction = function(){}
      
    • 如果是jquery的话,$(’#button’).removeAttr(‘onclick’);

  • removeEventListener(event,function,useCapture)

    • 参数信息同上面。
    • 如果要移除事件句柄,addEventListener() 的执行函数必须使用外部函数

防止事件冒泡或者捕获

  • 防止事件冒泡: w3c的方法是e.stopPropagation(),IE则是使用e.cancelBubble = true。
    • 冒泡的终点是windows
  • 阻止默认事件: w3c的方法是e.preventDefault(),IE则是使用e.returnValue = false。
  • jquery的 return false 既可以防止事件冒泡,也可以阻止默认事件。

如何让事件先冒泡后捕获

1. 对于同一事件,监听捕获和冒泡。
2. 监听到捕获事件,暂缓执行。
3. 直到冒泡事件被捕获(冒泡完毕),执行暂缓的捕获事件

事件委托

  • 含义:不在事件的发生地(比如ui下的li)上设置监听函数,而是在其父元素上设置监听函数。当点击子元素时,通过事件冒泡,父元素监听到子元素,从而进行子元素事件的触发。
  • 好处:比较适合动态元素的绑定,新添加的子元素也会有监听函数和事件触发机制。

图片的懒加载和预加载

  • 预加载
    提前加载图片,当用户需要查看时直接在本地进行渲染。

  • 懒加载
    做服务端前端你的优化,减少请求书或者延迟请求数。

js的new操作符做了哪些事情

1. 新建一个空对象
2. 这个对象的原型指向构造函数的prototype。
3. 执行构造函数后返回这个对象

改变函数内部this指针的指向函数

  • 通过apply和call都可以改变。
  • 区别在于,apply获取的第二个参数时数组,call则是...Array
  • 通过bind改变this做用户会返回一个新的函数,这个函数不会立即执行。

Ajax解决浏览器缓存问题

1. 在ajax请求前加上 anyAjaxObj.setRequestHeader("If-Modified-Since","0")。
2. 在ajax发送请求前加上 anyAjaxObj.setRequestHeader("Cache-Control","no-cache")。
3. 在URL后面加上一个随机数: "fresh=" + Math.random()。
4. 在URL后面加上时间搓:"nowtime=" + new Date().getTime()。
5. 如果是使用jQuery,直接这样就可以了 $.ajaxSetup({cache:false})。这样页面的所有ajax都会执行这条语句就是不需要保存缓存记录。

浏览器渲染原理

1. js引入
2. style样式计算
3. layout布局计算
4. paint绘制填充样式
5. composite渲染层合并

js的节流和防抖(高性能滚动csroll以及页面渲染优化)

  • 含义:再绑定scroll,resize这类事件时,当他发生,触发的频率非常高。间隔很近。如果此类函数涉及大量计算,会造成浏览器的掉帧。cpu使用率增加,影响用户体验。所以出现了节流和防抖。
  • 防抖
    在一定时间内,规定事件被触发的次数。
  • 节流
    节流保证函数在规定时间内,必定会触发一次。

js中的垃圾回收机制

1. 标记清除
	var a="hello world";
	var b="world";
	var a=b;

如上,这时,系统会释放 “hello world” 的内存。
2. 引用计数
给每个值一个引用的次数,当声明一个变量,并将一个引用类型的值赋值给该变量,这个值的引用次数+1,反之-1.当引用次数为0,系统会清理该变量。(会出现内存泄漏)

eval

将对应的字符串解析成js并执行。(很少使用,消耗性能)

js判断类型

1. typeof()
  typeof()只能判断基本类型,array和object都会返回object。
2. instanceof() 
3. object.prototype.tostring()
4. object.is()
  ES6新出

数组去重

1. indexof循环去重
2. ES6 SET去重
  Array.from(new Set(Array))

闭包

  • 简单来说,闭包 = 函数 + 函数能够访问的自由变量。

  • 含义:有权访问另外一个函数作用域中的变量。
    闭包时函数的局部变量集合,在一个函数内定义另一个函数就会产生闭包。

  • 用途

    1. 匿名自执行函数
      2.结果缓存
      3.封装

性能优化

  • 减少HTTP请求
  • 使用内容发布网络(扔到服务器上,使用的时候进行加载)
  • 添加本地缓存
  • css样式表放顶部,js放底部(或者defer)
  • 避免使用css表达式
  • 使用外部js和css
  • 图片懒加载
  • 避免重定向

继承实现的几种方式

1. 原型链继承
  将父类的实例作为子类的原型。
    优点: 易于实现
    缺点: 无法多继承,无法向父类够惨函数传参

  2. 构造继承
 使用父类的构造函数来增强子类实例,即复制父类的实例对象给子类。
   优点: 可以实现多继承,可以向父类传参
   缺点: 影响性能

   3. 实例继承
为父类实例添加新特性,作为子类实例返回。
优点:实例继承的特点时不限制调用方法,不管是new子类()还是子类() 返回的对象具有相同的想过。
缺点: 不支持多继承

  4. 拷贝继承
 优点: 支持多继承
   缺点: 效率低,内存占用高

  5. 组合继承:

  6. 寄生组合继承

symbol

sumbol是ES6新增的属性,为对象加标识,使其唯一。

promise

promise是一个对象,解决异步状态的时候使用。

  • 特点:
    1. 对象状态不受外界影响,有三种状态,pending(进行中),fulfilled(成功),rejected(失败)
    2. 一旦状态改变,就不会再变。

promise+Generator+Async的对比

  • 他们三者都是解决异步现象的。
  • promise上面有介绍。
  • generator函数,是可以分段运行的函数体,相比较于promise,解决连续异步时减少了代码冗余。
  • Async 时generator的语法糖。ES7语法。

setTimeout和Promise的执行顺序

如下问题:

	setTimeout(function() {
		console.log(1)
	}, 0);
	new Promise(function(resolve, reject) {
		console.log(2)
	for (var i = 0; i < 10000; i++) {
		if(i === 10) {console.log(10)}
		i == 9999 && resolve();
	}
		console.log(3)
	}).then(function() {
		console.log(4)
	})
		console.log(5);

输出答案为2 10 3 5 4 1
原因:

  • promise新建之后立即执行,
  • then在promise函数同级任务执行完执行,
  • setTimeout 不是真的立即执行,所以没有then执行快。

如何获得对象上的属性

1. for(let i in obj)依次访问对象中可枚举的内容
2. object.keys():返回数组,包含所有可枚举内容
3. object.getOwnPropetryNames:返回一个数组,包含不可枚举属性。

箭头函数和function的区别

  • 箭头函数没有绑定自己的this,在箭头函数中调用this,沿作用域链向上寻找,找到最近的一个this来使用。

this关键字

  • this指向的永远是一个对象
  • 在方法中,this表示该方法所属的对象
  • 如果单独使用,this表示全局对象
  • 在函数中,this表示全局对象
  • 在事件中,this表示接受事件的元素
  • 箭头函数不会生成新的this,箭头函数里的this会自动寻找最近的this。
  • 类似call(),apply()可以将this引入到任何对象

箭头函数(注意点)

  • 函数体内的this,是定义时所在的对象,而不是使用时所在的对象
  • 不可以当成构造函数,也就是说,不可以使用new命令,不然会报错
  • 不可以使用arguments都西昂,该对象在函数体内不存在。如果要用,用rest参数代替。
  • 不可以使用yield命令,因此箭头函数不能用作Generator函数

解构赋值

  • 解构赋值时浅拷贝,即如果一个键的值时复合类型的值(数组,对象,函数),那么解构赋值拷贝的是这个值的引用,而不是副本。

GET和POST区别

  • GET产生一个TCP数据包;POST产生两个TCP数据包。并不是所有浏览器都会在POST中发送两次包,firfox只发送一次。
  • GET在浏览器回退时是无害的,而POST会再次发送请求。
  • GET是表单提交的默认方法。
  • GET请求参数会被完整保留在浏览器历史记录里面,POST不会。
  • GET传递没有POST安全,而且GET传输数据的大小远比POST小的多。

说一声SEO(搜索引擎优化)

  • 对自身网页而言:
    • 网页头部添加TDK,即title,description,keyword这三个标签
    • 使用语义化的标签。如main,article,header,footer,nav,aside等。语义化代码会让搜索引擎容易理解网页。
    • 重要的内容不要放在js代码里,搜索引擎不会爬取js代码
    • 提高网站性能
  • 其他的一些方面
    • 使用https协议
    • 网址静态化,短网址,伪静态
    • 全站地图sitemap,即告诉搜索引擎怎么爬取你的网址
    • 提交百度收录,添加tobots机器人(一个txt文件,告诉八度引起想让他爬什么,不能爬什么)

提高网站性能

  • 减少http请求(使用websockets,减少重定向等)
  • 使用压缩,webpack打包等
  • css放最上面,js放最下面(js放下面防止出现空白网页,js可能阻碍渲染)
  • 懒加载(vue的路由懒加载,图片懒加载等)
  • nginx反向代理
  • 使用cdn

Let 和 Const对比

  • let生命的变量具有块级作用域
  • let声明的变量不能在通过window.变量名进行访问
  • 形如for(let x)的循环每次迭代都会为x创建新的绑定

如何实现跨域

  • jsonp
    和ajax不同,请求script,动态插入。
  • CORS
    服务器端设置Access-Control-Allow-Origin
  • iframe
    iframe解决跨域,不推荐
  • proxy代理
    使用代理服务器,实现数据转发

VUE

vue生命周期

1. boefocreated (创建之前)
   在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
2. created(创建,生成data,可以进行data的修改,一般在这里发起ajax请求获取数据)
   在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。
3. beforemount(挂载之前)
   在挂载开始之前被调用:相关的 render 函数首次被调用。
4. mounted(挂载,挂载DOM,可以进行DOM操作,一般在这里进行DOM相关操作)
   el 被新创建的 vm.$替换,并挂载到实例上去之后调用该钩子。如果实例挂载了一个文档内元素,当被调用时el 替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内。
5. beforeupdate (数据更新之前)
   数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。
6. update(数据更新)
   由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
   当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。
7. beforeDestory(销毁之前)
   实例销毁之前调用。在这一步,实例仍然完全可用。
8. destroyed(销毁)
   Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

v-for的时候为什么需要绑定key值

  • key的作用主要是为了高效的更新虚拟DOM。
  • VUE的虚拟DOM的Diff算法有关系。(比如在 ABCDE 中的B和C之间插入E)
    • 如果没有key,系统会将C更新为F,D更新为C,E更新为D,然后在最好插入E。浪费效率。
    • 如果由key,Diff算法可以正确的识别此节点,找到正确的位置进行插入。

VUE虚拟DOM

  • 虚拟DOM就是为了解决浏览器性能问题而被设计出来的。

  • 案例解析:

    • 若一次操作中有10次更新DOM的动作,操作真实DOM,会造成连续多次的更新DOM元素,从而影响性能。
    • 虚拟DOM不会立即操作DOM,而是将这10次更新的diff内容保存到本地一个JS对象中,最终将这个JS对象一次性attch到DOM树上,再进行后续操作,避免大量无谓的计算量。
  • 好处:页面的更新可以先全部反映在JS对象(虚拟DOM)上,操作内存中的JS对象的速度显然要更快,等更新完成后,再将最终的JS对象映射成真实的DOM,交由浏览器去绘制。

VUE获取DOM元素

  • 在vue之中, 原生html获取DOM(不推荐)
mounted(){//这里必须是mouted钩子
  //document.querySelector('#footer-box-title') 原生html获取DOM
  this.title = document.querySelector('#footer-box-title');
  this.title.style.color = "#ff0000";
}
  • 通过ref获取
//给标签设置ref值,
<button ref="btn">获取ref</button>
//通过refs获取
this.$refs.btn.style.backgroundColor="#ff0000"

VUE之中method属性方法可以实现computed属性的功能,为什么还要用computed?

  • computed是有缓存的,当页面多次调用时,会进行对比,如果数据没有发生改变,将直接渲染。
  • method方法没有缓存,调用一次便会执行一次,影响性能。

v-on详解

事件句柄(可绑定事件)

属性监听事件
onabort图像加载被中断
onblur元素失去焦点
onchange用户改变域的内容
onclick鼠标点击对象
ondblclick鼠标双击对象
onerror当加载文档或图片时出现错误
onfocus元素获得焦点
onkeydown某个键盘的键被按下
onkeypress某个键盘的键被按下或按住
onkeyup某个键盘的键被松开
onload某个页面或图像被加载完成
onmousedown某个鼠标按键被按下
onmousemove鼠标被移动
onmouseout鼠标从某元素移开
onmouseover鼠标被移到某元素之上
onmouseup某个鼠标按键被松开
onreset重置按钮被点击
onresize窗口或框架被调整尺寸
onselect文本被选定
onsubmit提交按钮被点击
onunload用户退出页面

v-on 修饰符

修饰符说明
.stop调用event.stopPropagetion()
.prevent调用event.perventDefault()
.capture添加事件监听器时使用capture(捕获)模式
.self只当事件从监听器绑定的元素本身触发时才触发回调
.{keyCode | keyAlias}当事件从特定键触发的时候才会触发回调
.native监听组件根元素的原生事件
.once只触发一次回调
.left只当点击鼠标左键的时候触发
.right只当点击鼠标右键的时候才触发
.middle只当点击鼠标中键的时候才触发
.passive以{passive:true}模式添加监听器,(进行默认事件),该方法和pervent冲突。(如果一起使用,编译器会直接报错)

keyCode值和对应按键

按键keyCode
0-948-57
a-z/A-Z65-90
F1-F24112-135
BackSpace(退格)8
Tab9
Enter(回车)13
Caps_Lock(大写锁定)20
Space(空格键)32
Left(左箭头)37
up(上箭头)38
Right(右箭头)39
Down(下箭头)40

VUE为常用按键设置了别名

别名按键
.deletedelete(删除)/BackSpace(退格)
.tabTab
.enterEnter(回车)
.escEsc(退出)
.spaceSpace(空格键)
.leftLeft(左箭头)
.upUp(上箭头)
.rightRight(右箭头)
.downDown(下箭头)
.ctrlCtrl
.altAlt
.shiftShift
.metawindows中为window键,mac中为command键
  • 使用Element-UI时,需要使用.native修饰符,如:@keyup.enter.native="dosth"

  • Vue中支持组合写法,如:@keyup.alt.67=”dosth” 为 Alt + C

通过v-if切换input等组件时会出现组件复用问题

  • vue会尽可能的优化性能,所以进行了组件的服用,不过需要变换的id,class都会进行更改,只是input里面的内容不会被清空。
  • 防止复用的方法:为不被复用的组件设置不同的key。

在前端JS之中,没有重载,如果定义一样的名字,会进行覆盖,但是我们可以通过方法实现类似重载的效果。

父子组件之间的传值调用等方法

传值调用方法
父传子prop使用ref获取子组件并调用方法
子传父1.vuex
2.this.parents.data
1.使用emit来调用方法
2.使用this.parents.methods调用
兄弟组件VUEX,BUS
跨级组件VUEX,attrs,listeners,provide,inject

关于双向绑定

vue是响应式的,如果vue中data上有一个obj对象,对一个属性没有进行声明,那么给这个属性赋值的时候,是非响应式的,不会引起重新渲染。html不会发生变化,forceUpdate可以帮助我们处理

 	this.$forceUpdate()

但是不建议这么做,因为对性能会有影响。

建议使用的方法是set方法。

	this.$set(this.data,'show',true)

ps:this.$nextTick()对于这类情况不产生作用。因为双向绑定依然是没有进行绑定的。

路由相关

hashhistory
样式https://www.word.com#idhttps://www.word.com/id
特性1.客户端的状态(向服务端发送请求的时候,hash部分不被发送)
2.会在浏览器访问历史上增加一个记录,可以使用浏览器的前进后退
3.hashchange事件来监听
1.需要后台进行对应配置,不然刷新会404
2提供了对应的api来加入到路由
  • 1
    点赞
  • 11
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论

打赏作者

XAIhan

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值