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

前言

写本章的初衷是因为目前网上的面试题层出不穷,各种类型的问题都有,不是很符合本公司的项目要求以及就面试者的水品也是层次不齐,所以很难有一个通用性面试题目。
本文章将根据笔者作为面试官以及面试者的一些经验进行以下几方面总结。

一、html方面

1、html语义化标签有哪些?以及为什么使用语义化标签?

<!--
 * @Descripttion: 
 * @version: 1.0.0
 * @Author: karl
 * @Date: 2022-02-24 08:35:12
 * @LastEditors: karl
 * @LastEditTime: 2022-03-02 16:15:59
-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>子元素水平垂直居中</title>
</head>
<body>
    <div class="container_box">
        <div class="box">居中盒子</div>
    </div>
    <div>
        <header>头部</header>
        <nav>导航</nav>
        <article>文章</article>
        <aside>边框</aside>
        <section>内容</section>
        <footer>底部</footer>
        语义化标签的优点:
        代码结构: 使页面没有css的情况下,也能够呈现出很好的内容结构
        有利于SEO: 爬虫依赖标签来确定关键字的权重,因此可以和搜索引擎建立良好的沟通,帮助爬虫抓取更多的有效信息
        提升用户体验: 例如title、alt可以用于解释名称或者解释图片信息,以及label标签的灵活运用。
        便于团队开发和维护: 语义化使得代码更具有可读性,让其他开发人员更加理解你的html结构,减少差异化。
        方便其他设备解析: 如屏幕阅读器、盲人阅读器、移动设备等,以有意义的方式来渲染网页。
    </div>
</body>

</html>

二、css方面

1、盒子模型有哪些?盒子的组成?

标准盒子模型 宽度 = (content)+ border + padding + margin
IE盒子模型 宽度 = (content + border + padding) + margin

盒子的组成从外到内:margin(外边距)border(边框)padding(内边距)contnet(内容)

盒子的宽度:width + padding-left + padding-right + border-left + border-right + margin-left + margin-right
盒子的高度:height + padding-top + padding-bottom + border-top + border-bottom + margin-top + margin-bottom

2、元素水平垂直居中方法有哪些?

<!--
 * @Descripttion: 
 * @version: 1.0.0
 * @Author: karl
 * @Date: 2022-02-24 08:35:12
 * @LastEditors: karl
 * @LastEditTime: 2022-03-02 16:15:59
-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>子元素水平垂直居中</title>
</head>
<body>
    <div class="container_box">
        <div class="box">居中盒子</div>
    </div>
</body>
<style>
    .container_box {
        width: 600px;
        height: 600px;
        background-color: aqua;
    }
    /* 在已知子元素高度的情况下使用下列方法 */

    /* 1 使用相对定位进行实际偏移 */
    .container_box {
        width: 600px;
        height: 600px;
        background-color: aqua;
    }
    .box {
        width: 200px;
        height: 200px;
        position: relative;
        left: 200px;
        top: 200px;
        background-color: blanchedalmond;
    }

    /* 2 使用margin进行偏移 */
    .container_box {
        width: 600px;
        height: 600px;
        background-color: aqua;
        position: relative;
    }
    .box {
        width: 200px;
        height: 200px;
        position: absolute;
        left: 50%;
        top: 50%;
        margin-top: -100px;
        margin-left: -100px;
        background-color: blanchedalmond;
    }

    /* 3 使用计算函数进行偏移 */
    .container_box {
        width: 600px;
        height: 600px;
        background-color: aqua;
        position: relative;
    }
    .box {
        width: 200px;
        height: 200px;
        position: absolute;
        left: calc(50% - 100px);
        top: calc(50% - 100px);
        background-color: blanchedalmond;
    }

    /* 4 子元素相对于父元素绝对定位,偏移量都为0,使用margin:auto */
    .container_box {
        width: 600px;
        height: 600px;
        background-color: aqua;
        position: relative;
    }
    .box {
        width: 200px;
        height: 200px;
        position: absolute;
        left: 0;
        top: 0;
        right: 0;
        bottom: 0;
        margin: auto;
        background-color: blanchedalmond;
    }


    /* 子元素高度未知和已知的情况都适用 */
    /* 1 父元素display: flex;子元素margin:auto */
    .container_box {
        display: flex;
    }
    .box {
        width: 200px;
        height: 200px;
        background-color: blanchedalmond;
        margin: auto;
    }

    /* 2 父元素使用 存在浏览器兼容性 */
    .container_box {
        display: flex;
        align-items: center;
        justify-content: center;
    }
    /* 3 使用css3 适用多行文本单行文本 特定元素 */
    .container_box {
        position: relative;
    }
    .box {
        position: absolute;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
    }
</style>
</html>

3、谈谈对 flex 布局以及grid 布局的理解

布局的传统方法是基于盒子模型,使用display position float 进行布局,但是要使一个盒子垂直居中,使用传统的方案就比较麻烦,2009年之后,w3c组织颁布了 弹性布局方法 也就是 flex布局。任何一个元素都是指定为弹性布局。
flex: 集合了 display position float 三个属性

三、js方面

(一)、说说你对原型,原型链,闭包,作用域,作用连的理解
1、原型的概念:

每一个javaScript对象除了null外,在创建的时候都会与之关联另外一个对象,这个对象就是原型,每一个对象都会冲原型中继承属性。
// 代码示例:
function Person(age) {
    this.age = age;
}
Person.prototype.name = 'Kaval';
var person1 = new Person();
var person2 = new Person();
person1和person2 原型指向相同属性也相同

(二)、访问原型的方法(3种)

var obj = {}; // 对象实例
// 方法一 通过原型链(IE不支持)
var prototype1 = obj._proto_; 
// 方法二 通过原型构造器访问
var prototype2 = obj.constructor.prototype;
// 方法三 原型的静态属性方法(推荐)
var prototype3 = Object.getPrototypeOf(obj);

(三)、设置原型的方法

var proto = { name: 'prototype' } // 原型对象
var obj1 = {}; // 目标对象
// 方法一 通过私有属性_proto_(IE不支持)
obj1._proto_ = proto;
// 方法二 原型静态属性方法(IE不支持)
Object.setPrototypeOf(obj1, proto);
// 方法三 创建对象并设置原型
var obj2 = Object.create(proto);

(四)、检测原型的方法 isPrototypeOf

var fn = function() {};
var obj = new fn();
var proto = Object.getPrototypeOf(obj);
var isTrue = proto.isPrototypeOf(obj); // true
console.log(proto.isPrototypeOf({})); // true
console.log(proto.isPrototypeOf([])); // true

(五)、原型属性以及私有属性相关问题

// 原型属性与私有属性
var fn = function() {};
fn.prototype.a = 1;
var fn1 = new Fn();
var fn2 = new Fn();
// 修改原型属性将会影响实例对象,修改任何原型属性的值,则该构造函数所有的实例都会变化,这样可以同意修改实例属性
fn.prototype.a = 2;
console.log(fn1.a); // 2
console.log(fn2.a); // 2

// 私有属性
function f() {
    this.a = 1; // 申明一个私有属性
    this.b = function () { // 申明一个私有方法
        return this.a;
    }
}
 var d = new F();
 var e = new F();
 d.a = 2;
 console.log(d.a); // 2
 console.log(e.a); // ? (私有属性被修改,不同实例之间不会相互干扰)

(六)、原型属性以及私有属性的实际项目中运用有哪些?

// 例一 利用原型属性设置实例的默认值,如果私有属性和原型属性同名的话,删除私有属性的,就会默认去找原型属性
function p(x) {
    // 如果参数存在就设私有属性的默认值
    if (x) {
        this.x = x;
    }
}
p.prototype.x = 0; // 利用原型属性设置私有属性的默认值
var p1 = new p(); // 实例化一个没有带参数的对象,返回值为原型属性 0;
var p2 = new p(1); // 实例化一个带参数的对象,返回值为私有属性 1;

// 例二 利用原型间接实现数据本地备份
function p(x) {
    this.x = x;
};
p.prototype.copydata = function() {
    for (let i in this) {
        p.prototype[i] = this[i]
    }
}
var p1 = new p(1); // 修改原型数据
p.copydata; // 备份数据
p1.x = 10; // 修改数据
p1 = p.prototype; // 恢复数据
p1.x; // 打印 1 

(七)、原型继承

// 原型继承是一种简化的继承机制,也就是javaScript原生继承模式,简单点来说等同于对象继承方式。
function A(x) {
    this.x1 = x;
    this.get1 = function() {
        return this.x1 * 1
    }
}
function B(x) {
    this.x2 = x2;
    this.get2 = function() {
        return this.x2 * 2
    }
}
function C(x) {
    this.x3 = x
    this.get3 = function() {
        return this.x3 * 3
    }
}
B.prototype = new A(1); // B原型对象继承A的实例
C.prototype = new B(2); // C原型对象继承B的实例
// 此时在C的实例中访问AB的属性
var c = new C(1);
console.log(c.x1, get1); // ?
console.log(c.x2, c.get2); // ?

3、闭包

闭包的个人理解:
在js的作用域环境中访问变量是由内而外的,也就是说内部作用域可以访问当前作用于下的变量,也可以访问当前作用域下的外层作用域内的变量,反之则不能。
因此才诞生了闭包的概念,一个函数的内部去访问另外一个函数内部的变量,就使用闭包去实现。
闭包的本质就是在一个函数的内部创建另一个函数。
function a () {
	var name = 'karl'
	return function() {
		return name;
	}
};
var b = a(); // 全局作用域访问函数内部的变量
在这段代码中,a()中的返回值是一个匿名函数,这个函数在a()作用域内部,所以它可以获取a()作用域下变量name的值,将这个值作为返回值赋给全局作用域下的变量b,实现了在全局变量下获取到局部变量中的变量的值

面试题1:下面函数 fn1 fn2 打印结果是?
function fn () {
	var a = 0;
	return function () {
		var b = 2;
		console.log(++a);
		console.log(++b);
	}
}
var fn1 = fn(); // 1 3
var fn2 = fn(); // 1 4
解释:匿名函数作为fn的返回值被赋值给了fn1 fn2,并且匿名函数内部引用着fn里的变量a,所以变量a无法被销毁,
而变量b是每次被调用时新创建的,所以每次fn1执行完后它就把属于自己的变量连同自己一起销毁,于是乎最后就剩下孤零零的a,于是这里就产生了内存消耗的问题.

面试题2:使用闭包函数每隔一秒打印1-10for(var i=1; i<10; i++){
	(function(i){
		setTimeout(()=>{
			console.log(i)
		},i*1000);
	})(i);
}
解释:使用自执行函数,会在循环体内部生成一个闭包环境,因为该闭包函数内部引用了循环体的i,定时器内部又引用了自执行函数传入的i,此时的会常驻内存,不会被垃圾机制回收。

4、作用域链
一般情况下,变量取值到 创建 这个变量 的函数的作用域中取值。但是如果在当前作用域中没有查到值,就会向上级作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链

变量以及函数在申明后就决定了其作用域
var x = 10;
function fn(){
	console.log(x) // ? 自由变量取值,在函数创建的当前作用域取值,也就是常说的静态作用域了
}
function fn2(f){
	var x = 20;
	(function(){
		f() // ? 
	})()
}
fn2(fn); ? 打印?

5、节流与防抖

**debounce 防抖**
理解:
触发高频事件N秒后只执行一次,会清除上次一个定时器,最终执行最后一次事件的定时器,不会稀释函数频率,
防抖重在清零,就是我要最后执行,我管上面有多少个定时器待执行,我都把你们干掉,我自己一个人走。
使用场景:
按钮点击、输入记录、窗口变化
function debounce(fn) {
	let timeout = null
	return function(){
		timeout && clearTimeout(timeout)
		timeout = setTimeout(()=>{
			fn.apply(this,arguments)
		},500)
	}
}

**throttle 节流**
理解:
高频事件触发,在N秒内只执行一次,不管你触发多少次,我在我的时间内我只执行一次,其它进都别想进来
function throttle(fn) {
	let canrun = true;
	return function() {
		if (!canrun) return;
		canrun = false;
		setTimeout(() => {
			fn.apply(this, arguments)
			canrun = true
		}, 500)
	}
}

7、作用域与执行上下文有什么区别?

执行上下文是在运行的时候确定,且根据调用对象的不同随时改变;作用域是函数声明的时候就确定了,且不会随时改变

四、网络请求方面

1、常见的状态码有哪些?

301 永久重定向,指请求的资源在别的地方,浏览器可以自动转到对应的地址
302 临时重定向,指所访问的资源已被修改到新的服务地址,需要手动更新请求地址
304 命中缓存,但未修改,表示客户端缓存的是最新的,可以继续使用
400 客户端请求语法错误,服务器无法理解
401 请求要求身份认证
403 服务器理解客户端请求,但拒绝执行请求(无权限)
404 服务器无法根据请求找到对应请求地址
500 服务器内部错误,无法完成请求
501 服务器不支持的请求,无法完成请求
502 作为网关或者代理服务器尝试请求时,从远程服务器返回了一个无效响应
503 服务器超负荷

2、http于https区别?

1 HTTPS 需要向CA(证书颁发机构申请证书)需要支付相应的费用;
2 HTTP是超文本传输协议,信息是明文传输,而HTTPS是通过SSL加密的传输协议;
3 两者的端口不一样,HTTP 默认是80HTTPS 默认是 4434 HTTP的连接很简单,是无状态,而HTTPS是基于SSL+HTTP的加密协议,加身份校验的传输方式;

3、cookie是否被覆盖?cookie跨域请求是否是可携带?

cookie是可以被覆盖的,如果重复写入同名的cookie,那么之前cookie就会被后者覆盖。
cookie跨域请求是可以被携带的,进行如下设置
1当发送跨域请求时,携带cookie信息(客户端设置)
xhr.withCredentials = true;
2、服务端设置
Access-Control-Allow-Origin: a.com //这里需要换成相应的发起请求的域名
Access-Control-Allow-Credentials: true

4、JSONP的原理?怎么读取一个script里面的数据?

    // 利用script标签src属性不受同源策略的影响,设置访问地址以及参数,最重要的是设置callback回调函数(函数命需要前后端统一约定),既然是链接访问的实则就是一个get请求,所以jsonp跨域仅限于get请求

    var script = document.createElement('script');
    script.src = 'https://www.test/detail?name=a&callback=getData'
    document.getElementsByTagName('body').appendChild(script)

    function getData(res) {
        console.log(res) // 服务器返回的数据
    }


    // 服务器实现
    router.get('/detail', (req, res) => {
    console.log(req.query, '123');
    let data = {
        message: 'success!',
        name: req.query.name,
    }
    data = JSON.stringify(data)
    res.end('getData(' + data + ')');
    });

5、什么是强缓存和协商缓存?
首先看看完整浏览器缓存过程,如下图所示:
在这里插入图片描述

(一)首先要理解缓存的作用是什么?
     1 可以减少冗余的数据传输,节省带宽,加快网页的加载速度;
     2 减少服务器的压力,从而得到服务器更快相应;
     3 优化用户体验

(二)缓存有什么类型?
     memory cache 内存缓存,将资源缓存在内存中,等下次请求访问的时候哦不需要从服务器中获取,直接从内存中获取。
     disk cache 硬盘缓存,将资源缓存在硬盘中,下次访问服务器的时候直接从硬盘中获取。
   两者的区别?
     内存缓存退出进程的时候会被删除,硬盘缓存不会
     内存缓存的读取速度比硬盘缓存更快
     内存缓存一般放一些图片 字体 脚本,硬盘缓存一般放一些静态样式css
   缓存读取的原理?
   	 先从内存中查找对应的缓存,如果内存中能找到就读取对应的缓存,否则的话就从硬盘中查找对应的缓存,如果有就读取,否则的话,就重新网络请求。
   	 
(三)协商缓存
      客户端向服务端发起请求时,服务端会检查请求是否携带对应标识,如果没有对应的标识,服务端在返回的时候的会带相应标识给客户端,然后客户端在下次请求的时候会携带这个标识,服务端会验证这个标识,如果验证通过了,会返回304,读取缓存,如果没有通过,则会返回新的请求资源。
      
(四)强缓存
      浏览器在访问服器的时候会根据请求头header里面的cache-control 来判断是否需要强制缓存,如果命中的话,就会读取缓存数据,否则,会继续向服务器发送请求获取最新的数据。
      
(五)强制缓存命中机制?
	response header 设置的Expires过期时间,浏览器再次加载资源时,如果在这个过期时间内,则命中强制缓存,
	Cache-Control max-age 如果超过这个时间,同样也会命中强制缓存,而Cache-Control的优先级高于Expires。
	
(六)协商缓存命中机制?
	协商缓存机制存在两种:
		一是在浏览器第一次请求时,由服务器在response header 里面返回一个标识Etag,该标识用来标记服务器资源是否更新,每当服务器资源发生更新都生成新的Etag,当浏览器再次发送请求的时候,会在请求头中 request header If-None-Match 中携带之前返回的Etag,服务器在接收后会进行对比,如果该Etag与服务器的没有变化,则命中协商缓存。
		二是Last-Modified和If-Modified-Since,浏览器在第一次请求后,服务器会在 response header 里返回 Last-Modified 服务器资源更新的最后时间,当浏览器再次发送请求时会在 request header If-Modified-Since 携带 Last-Modified 的值,服务器在接受请求后,会与当前服务器资源更新的最新时间进行对比,如果有变化则命中协商缓存。
		
	ETag和Last-Modified区别?
		1、在方式上,Etag是对资源的一种唯一标识,而Last-Modified是该资源文件最后一次更改时间。
		2、在精确度上,Etag要优于Last-Modified。Last-Modified的时间单位是秒,如果某个文件在1秒内改变了多次,那么他们的Last-Modified其实并没有体现出来修改,但是Etag每次都会改变确保了精度;如果是负载均衡的服务器,各个服务器生成的Last-Modified也有可能不一致。
		3、在性能上,Etag要逊于Last-Modified,毕竟Last-Modified只需要记录时间,而Etag需要服务器通过算法来计算出一个hash值。
		4、在优先级上,服务器校验优先考虑Etag。

6、TCP与UDP的区别以及应用场景?

TCP是什么?
	TCP是一种面向连接的、可靠的、基于字节流传输层通信协议。
UDP又是什么?
	internet 协议集支持一个无连接的传输协议,该协议称为**用户数据报协议**UDP,User Datagram Protocol)。UDP 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法。RFC 768 描述了 UDP。
区别:
	1TCP 面向连接,在使用TCP协议通信时,客户端与服务端需要先建立一对一的连接,并且这个连接是全双工的,双方可以在一个连接上完成数据读/写,完成数据交互后,双方必须断开连接以释放系统资源。
	2TCP 可靠传输,通过应答机制、超时重传、拥塞控制进行可靠传输。
	3TCP 字节流传输,通过字节流无限制传输,类似于管道传输。
	4UDP 实时性高,在遇到网络拥塞时,UDP协议可以在应用层控制重传策略,减少重传间隔,超时时间后无法发送的数据直			  接丢弃。
	5UDP 资源占用少,不需要保持长连接状态,不需要三次握手四次挥手,因此不会占用大量网络资源。
	6UDP 基于数据报,UDP是面向报文的,UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低。
应用场景:
		TCP:文件传输、邮件传输(安全性要求较高的)
        UDP:音视频通话,实时对战游戏,DNS解析过程(安全性较低的)

五、vue框架方面

(一)vue生命周期有哪些?

beforeCreate 在组件实例初始化完成之后立即调用,会在实例初始化完成、props 解析之后、data() 和 computed 等选项处理之前立即调用。
created 在组件实例处理完所有与状态相关的选项后调用,这个钩子被调用时,以下内容已经设置完成:响应式数据、计算属性、方法和侦听器。然而,此时挂载阶段还未开始,因此 $el 属性仍不可用。
beforeMount 当这个钩子被调用时,组件已经完成了其响应式状态的设置,但还没有创建 DOM 节点。它即将首次执行 DOM 渲染过程。
mounted 在组件被挂载之后调用,所有的同步的子组件都已完成挂载(异步组件除外),可以访问真实的Dom,$el 可以使用。
beforeUpdate 在组件即将因为一个响应式状态变更而更新其 DOM 树之前调用,这个钩子可以用来在 Vue 更新 DOM 之前访问 DOM 状态。在这个钩子中更改状态也是安全的。
updated 在组件因为一个响应式状态变更而更新其 DOM 树之后调用,父组件的更新钩子将在其子组件的更新钩子之后调用。
beforeUnmount 在组建卸载之前执行,依然保留完整的功能。
unmounted 在组件实例卸载之后执行。

(二)vue内置命令有哪些?至少5个以上

v-if、v-show、v-model、v-for、v-once、v-on、v-else、v-bind

(三)组件通信方式有哪些?

1、props 传递,父传子
2、通过 $emit 触发自定义事件
3、通过 ref 访问子组件实例
4、通过 EventBus 事件总线实现
5、使用 parent或root访问整个实例
6、使用 attrs 与 listeners
7、使用 vuex 或者 pina 全局状态管理
8、使用 provider 与 inject 注入全局注入,建议使用容易出现数据混乱现象

(四)路由跳转方式有哪些?各自的优缺点有哪些?

路由跳转方式分为两种 history 与 hash 模式
history 模式 
缺点:需要服务端支持,刷新后可能会出现404页面
优点:可以利用浏览器api进行页面的前进后退操作
hash 模式
缺点:只能改变#后面的来实现路由跳转
优点:使用URL的hash值来作为路由,支持所有浏览器

(五)怎么解决页面刷新数据丢失的问题?

使用keep-alive组件包裹对应的路由出口文件
<template v-for="item in routes">
	<div v-if="item.meta.keepAlive">
	<keep-alive>
		<router-view path="item.path"/>
	</keep-alive>
	</div>
	<div v-else>
		<router-view path="item.path"/>
	</div>
</template>

(六)computed 和 watch 谁先执行?有什么不同?使用场景?

1、如果 watch 设置了 immediate:true,那么watch就先执行
2、如果 watch 监听的属性值变化依赖computed 计算结果那么computed先执行
3、如果 watch 未设置 immediate,computed先执行

区别:
1、功能:computed是计算属性,watch是监听一个值的变化而执行对应的回调

2、是否调用缓存:computed函数所依赖的属性不变的时候会调用缓存;watch每次监听的值发生变化时候都会调用回调

3、是否调用return:computed必须有;watch可以没有

4、使用场景:computed当一个属性受多个属性影响的时候;例如购物车商品结算;watch当一条数据影响多条数据的时候,例如搜索框

5、是否支持异步:computed函数不能有异步;watch可以

(七)vue dom 更新的机制怎么样的?

vue 

(八)vuex 有哪些生命钩子函数并描述其作用?
(九)父子组件生命周期执行顺序是怎么样的?

父组件的beforeCreate、created、beforeMount --> 所有子组件的beforeCreate、created、beforeMount --> 所有子组件的mounted --> 父组件的mounted

(十)简单描述一下V2与V3的区别?
(十一)有没有自己实现过vue模块化,例如路由模块化,组件模块化,数据模块化

(十二)v-if与v-show有什么不同?

v-if 控制Dom元素是否挂载,条件区块内的事件监听器和子组件都会被销毁与重建,有更高的切换开销。
v-show 元素无论初始条件如何,始终会被渲染,只有 CSS display 属性会被切换,有更高的初始开销。不能用于template block等虚拟模版元素。

(十三)有没有实现过自定义指令?

六、react框架方面

(一)类组件与函数式组件的区别

1、类组件需要继承 class,函数组件不需要;
2、类组件可以访问生命周期方法,函数组件不能;
3、类组件中可以获取到实例化后的 this,并基于这个 this 做各种各样的事情,而函数组件不可以;
4、类组件中可以定义并维护 state(状态),而函数组件不可以;

(二)父子组件通信方式
(三)useCallback 与 useMemo 区别
(四)

七、小程序方面

八、webpack构建方面

九、前端性能优化策略

十、ES6语法

十一、开发中遇到过哪些兼容性问题以及处理方法

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值