深传互动前端面经

一、什么是H5

参考:https://baike.baidu.com/item/html5/4234903?fr=aladdin

  • H5是HTML的第五个版本,是构建web内容的一种语言描述方式。
  • HTML5 的语法特征更加明显,并且结合了 SVG 的内容。这些内容在网页中使用可以更加便捷地处理多媒体内容;
  • 视频、音频、图像、动画以及与设备的交互都进行了规范。

二、H5的新特性

参考:H5新特性总结H5新特性H5和CSS3属性一览
1、新语义:有利于代码的可读性和SEO(搜索引擎优化)。

  • header:包含引导和导航等,通常包括h1-h6、搜索框、logo等
  • footer:显示地址、作者信息等相关链接
  • article:包含文章
  • nav:一般包含多个a标签组成导航栏
  • aside:主要是侧边栏、广告
  • section:划分组件
  • Audio:插入音频
  • Video:插入视频

2、本地存储
主要有sessionstorage、localstorage和indexDB。

  • sessionstorage:基于会话,关闭浏览器后就消失。应用场景:一次性登录
  • localstorage:是存储在本地,因为各浏览器的存储上限不同,最低是2.6MB,所以一般以2.6MB为开发上限,最高5M。应用场景:长期登录、判断客户是否登录
  • indexDB:是locastorage不够用时可以可以借助它进行存储,上限是250MB.

补充
-cookie的存储大小只有4k,个数一般不超过20个。且始终在同源的HTTP请求中的请求头携带。且cookie是有有效期的。可在服务器中设置。应用场景:自动登录

  • localStorage没有过期时间,但是可以在loacalStorage中加一个时间字段轻松解决这个问题。但是它数据不能跨浏览器公用。
  • sessionStorage不同页面不同标签页无法共享,如果是页面包含多个iframe标签,且他们属于同源,那么他们之间可以共享。
  • session和cookie与服务器通信的区别:
    (1)session只有请求的时候使用数据,不参与通信,不会自动把数据发给服务器。(web storage支持事件通知机制,可以将数据更新的通知发送给监听者:window.addEventListener(‘storage’, showStorageEvent, true) )
    (2)cookie:每次会携带在HTTP头中
    (3)storage存储数据可以使用原生接口,setItem(key, value)、getItem(key)、removeItem(key)、clear()、key(index);
    cookie的原生API只有document.cookie(),所以需要自己封装setCookie、getCookie。
  • 其他区别请看参考链接
    -关于session会占用服务器内存问题(参考链接):
    sessionStorage是基于页面协议的,就是说,每一个页面都会开一个session,不管是重新加载还是恢复页面都会保持原来的页面会话。
  • 关于cookie的封装函数
// cookie函数封装
function setCookie(key,value,n){
   let date = new Date()
   date.setDate(date.getDate+7)
   document.cookie = key + "=" + value + ";exprise="+ date
}

function getCookie(key){
   let str = document.cookie
   let arr = str.slice("; ")
   for(let i=0;i<arr.length;i++){
       let newArr = arr[i].slice("=")
       if(newArr[0]==key){
           return newArr[1]
       }
   }
}

function removeCookie(key){
   setCookie(key,0,-1)
}

3、 离线web应用

  • 页面缓存还是在有网络的状态,而离线web应用是指在没有网络的状态也可以运行
 if(window.applicationCache){
        //支持离线应用
    } 
    ```
- 只需要在html标签设置manifest
 ```bash
<html manifest="cache.manifest">

4、表单新增功能

  • H5前<input>必须放在<form>标签里面才能和表单一起提交,现在只要在<input>标签上加入form属性就行;
<form id="testform">
    <input type="text" />
       </form>
<input form=testform />

-<input>标签上还增加了一些属性:

  • placeholder属性:获得焦点时提示文字消失,失去焦点若value为空,则再度提示
  • autofocus属性:页面加载完自动获得焦点
  • required :必须输入
  • pattern:正则验证
    <input type="text" placeholder="请输入密码" />
    <input type="text" autofocus />
    手机号:<input type="tel" pattern="^(\+86)?1\d{10}$"> <br>

4、地理定位

  • h5提供了Geolocation API访问地理位置,即window.navigation.geolocation来实现访问。这个对象有3个方法:getCurrentPosition()、watchPosition()、clearWatch()

5、Web Workers
web worker 是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能。您可以继续做任何愿意做的事情:点击、选取内容等等,而此时 web worker 在后台运行。(相当于实现多线程并发)
6、WebSocket
提供的一种在单个 TCP 连接上进行全双工通讯的协议。浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。以下 API 用于创建 WebSocket 对象。

三、css3

参考:css3新特性css3新增css3新增特性
1、媒体查询
参考:媒体查询使用方法@media
2、新增选择器:

  • 子类选择器:ele1>ele2(必须是父子关系才能被选中)
  • 兄弟选择器:ele1+ele2(匹配同一个父子紧跟在ele1后面的ele2)、ele1~ele2(ele1后面的所以兄弟ele2元素)
  • 伪元素选择器:
    (1)ele::before | after:在ele元素前面 | 后面插入一个行内元素
    (2)ele::fist-letter | fist-line:选择到了ele容器的第一个字母 | 第一行文本;
    :伪元素里面必须写上属性content:“ ”;
  • 伪类选择器:
    (1)ele: fist-child | last-child | nth-child(2n | 2n+1):匹配即是父元素的第一个(最后一个 | 第2n | 2n+1个)元素类型又是e的元素。
    (2)ele: fist-of-type | last-of-type | nth-of-type(2n | 2n+1):匹配指定类型e同级的第一个 | 最后一个 | 第2n | 2n+1个。
  • 属性选择器:用来选择包含指定属性的标签。
    (1)ele[attr]:选择具有attr属性的ele元素
    (2)ele[attr = “val”]:选择attr属性且属性值等于val的ele元素
    (3)ele[attr ^= “val” ]:选择attr属性,且值以val开头的ele元素
    (4)ele[attr$=“val”]:选择attr属性,且值以val结尾的ele元素
    (5)ele[attr*=“val”]:选择attr属性,且值含有val的ele元素
  • 选择器的权重
    (1)基础选择器:id>类>标签>通配符选择器(*)
    (2)伪类选择器、属性选择器的权重= 类选择器
    (3)伪元素选择器的权重 = 标签选择器

3、盒模型

  • box-sizing:content-box | border-box

4、边框圆角

  • boder-radius:x-radius / y-radius (用”/“分割水平半径和垂直半径的赋值,可得到椭圆角)
  • boder-radius:px | % (水平垂直都设置同一个值,得到圆角)\

5、阴影

  • 文字阴影:text-shadow:h-shadow(水平阴影值,允许负值) v-shadow(垂直阴影值,允许负值) blur(可选。模糊距离) color(可选。阴影颜色)【可添加多个阴影,逗号分隔多个阴影的属性值,最先写的阴影显示在最上面】
  • 盒子阴影:box-shadow:h-shadow( 水平阴影值,允许负值)| v-shadow(垂直阴影值,允许负值)| blur(可选。模糊距离)| spread(可选。模糊尺寸)| color(可选。阴影颜色)| inset(可选。将外部阴影改为内部阴影)【可添加多个阴影,逗号分隔多个阴影的属性值,最先写的阴影显示在最上面】

6、过渡元素
transition:transition-property(应用过度的css属性名称。all表示所有变化的属性) transition-duration(过渡效果的时间.默认是0) transition-timing-function【过渡效果的时间曲线。默认是ease(慢快慢)。值有:linear(匀速)、ease-in(慢开始)、ease-out(慢结束)、ease-in-out(以慢速开始和结束)、cubic-bezier(x1,y1,x2,y2) (x取0~1,y任意;表示两个点的坐标)】 transition-delay(过渡效果的延迟时间。默认是0)
7、转换:transfrom
参考:CSS3 transform 属性transform演示
对元素进行移动、缩放、转动、拉伸。
它的值有:

  • none
  • matrix(n,n,n,n,n,n):将原图进行6个值的矩阵的2D转换
  • matrix(n,n,n,n,n,n,n,n,n,n,n,n,n,n,n,n):将原图进行16个值的矩阵的3D转换
  • translate(x, y):定义2D平面上的移动
  • translate3d(x,y,z):定义3D平面上的移动
  • translateX(npx)| translateY(npx)| translateZ(npx)
  • scale(x,y):缩放。x,y分别为改变元素的宽度和高度的倍数
  • scale3d(x,y,z)| scaleX(npx)| scaleY(npx)| scaleZ(npx)
  • rotate(angle):旋转angle的角度【1deg(1度)、1grad(1百分度,一个完整的圆是400百分度)、1rad(1弧度,一个圆是2∏弧度)、1turn(1圈)】
  • rotate3d(x,y,z,angle)| rotateX(angle)| rotateY(angle)| rotateZ(angle)
  • skew(x-angle,y-angle):倾斜
  • skewX(angle)| skewY(angle)
  • perspective(npx):为3D转换定义透视图即z轴的视线焦点的观察位置;数值越大,观察点离z轴越远,图形效果越接近原始尺寸。

其他属性:

  • transform-origin:调整元素基准点位置(即原生位置)
  • transform-style:preserve-3d(保留3D空间)| flat(在2D平面呈现)
    一般会将该属性设置给父级元素,使其真正变成一个3D图形

8、动画
一般用@keyframe创建动画,用animation属性绑定动画
语法:

@keyframes 动画名称 {
   动画过程,可以添加任意百分比处的动画细节
    }
    
    div {
 		animation:动画名称 过渡时间 速度曲线 动画次数 延时时间。
 }

四、css都有哪些单位

参考:该链接第十点

五、css标签的等级

参考:CSS 样式优先级

  • 继承的属性(祖先样式),按照就近原则
  • 直接样式比祖先样式优先级高
  • !important>内联样式>ID选择器(#)>类选择器(.)= 属性选择器(a[ color : red ]{})= 伪类选择器(:hover)> 标签选择器 = 伪元素选择器(::before)> 通配符选择器(*{})
  • 在组合选择器中,单个选择器的由优先级高到底进行个数比较,比如两个组合选择器,先比较ID选择器的个数,个数多的优先级越高,如果相等再进行下一级的比较。

六、js的数据类型

  • 基础类型:String、Number、undefined、Boolean、null、Symbol
  • 引用类型:object(array、function、date)

七、判断数据类型的方法

参考链接:判断js数据类型的四种方法和原理

  • typeof(6种):undefined、number、string、object、Boolean、function
    一般用于判断基础数据类型
    对于typeof(null)== object,是因为特殊值null被认为是一个空的对象的引用
  • instanceof:object、function、array、Date、RegExp
    一般用于引用数据类型的判断,判断一个实例是否属于某种类型
    语法:arr instanceof Array // 返回true
    原理:判断instanceof右边的protptype(原型)是否在左边的_prop_原型链上。
    let rightProto = rightVaule.prototype; // 取右表达式的 prototype 值
    leftVaule = leftVaule.__proto__; // 取左表达式的__proto__值
    while (true) {
    	if (leftVaule === null) {
            return false;	
        }
        if (leftVaule === rightProto) {
            return true;	
        } 
        leftVaule = leftVaule.__proto__ 
    } } 
  • constructor:一个对象的prototype上由constructor属性,指向该原型的对象。
    语法:f.constructor == F // true
    一般用于确定某对象的构造函数
  • Object.prototype.toString():toString是Oject原型上的一个方法,该方法默认返回器调用者的具体类型。
    原理:toString运行时this指向的对象类型
    返回[ object xxx];它的值有String、Number、Boolean、Undefined、Null、Function、Array、RegExp、Error、Date、HTMLDocument、global
Object.prototype.toString.call('') ;   // [object String] 
Object.prototype.toString.call(1) ;    // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; //[object global]
window是全局对象global的引用 

八、es6

参考:前端ES6面试题ES6入门教程书

1、babel:是ES6的一个转码器,将ES6代码转为ES5代码。
2、新增let、const

  • 有块级作用域:由{}包括
    解决的问题:内层变量可能覆盖外层变量; 用来计数的循环变量泄露为全局变量。
  • 变量不能提升:即变量只能在声明之后使用,否则会报语法错误
  • 不可重复声明
  • 暂时性死区:在声明变量之前,该变量都是不可用的。
  • let 声明变量时,可以不设置初始值,但const必须设置初始值。
  • let变量可以重新赋值,但const不能重新赋值

3、模板字符串
用``可以直接拼接变量,,不需要用+

 let white = '小白'; let cat = `<ul>
            <li>你好,${white}</li>
          </ul>`; console.log(cat);

直接输出: <ul>
    <li>你好,${white}</li>
     </ul> 

4、解构赋值

const CAT = {   a: 10,   b: 20,   c: 'nihao' }

const {a, b, c} = CAT

5、rest参数
语法...变量名
作用:跟argument差不多,但是argument是一个对象,只是可以用下标访问而已,但是无法用数组的一些函数。而rest是真正的数组。

function date(...args) {   console.log(args);  // [1,2,3,4]} date(1, 2, 3, 4);

6、扩展运算符(...)
可以将数组转化为 逗号 隔开的参数序列
7、新增基础数据类型(Symbol)
参考:Symbol 的作用
特点:

  • 值是唯一的,用来解决命名冲突
  • 不能与其他数据类型进行运算
  • 定义的对象属性,不能使用for…in遍历,但是可以用Reflect.ownKeys来获取对象的所有键名

7、迭代器(Iterator)
它是一种接口,为不同数据结构提供统一的访问机制。任何数据只要部署iterator几口,就可以进行遍历。
主要供for...of使用。在array中,可以直接用fo…of遍历,但是对象中无法实现。

const arr = ['red', 'black', 'blue']

for(let i of arr) { 	console.log(i)  // 'red' 'black' 'blue' }

for(let i in arr) {
    console.log(i)  // 1, 2, 3 }
 // 直接使用 for...of 遍历对象 const obj = {   arr: ['red', 'blue', 'green'] }

for(let i of obj) {   console.log(i) // Uncaught TypeError: obj is not
iterable }

// 给对象添加一个迭代器 const obj = {   color: ['red', 'blue', 'green'],

  [Symbol.iterator]() {
    let index = 0
    return {
      next: () => {
        if (index < this.color.length) {
          const res = { value: this.color[index], done: false }
          index++
          return res
        }else {
          return { value: undefined, done: true }
        }
      }
    }   } }

for (let i of obj) {   console.log(i)  // 'red' 'blue', 'green' }

8、生成器(generator)
参考:Generator详解
是一种特殊的函数,声明时要加个*号。当我们实例化后,这个实例就是一个迭代器(iterator)。可以通过next()方法七点生成器以及控制生成器继续往下走。一般搭配yield使用。

// 声明方式一(个人比较偏向这种风格啦) function *main() {
    // do something…… }

// 声明方式二 function* main() {
    // do something }
 // 首先声明一个生成器函数 function *main() {
    console.log('starting *main()');
    yiled; // 打住,不许往下走了
    console.log('continue yield 1');
    yield; // 打住,又不许往下走了
    console.log('continue yield 2'); } // 构造处一个迭代器it let it = main(); 

// 调用next()启动*main生成器,表示从当前位置开始运行,停在下一个yield处 it.next(); // 输出
starting *main()

// 继续往下走 it.next(); // 输出 continue yield 1

// 再继续往下走 it.next(); // 输出 continue yield 2
 function *main() {
    let x = yield "starting";
    let y = yield (x * 2);

    console.log(x, y);
    return x + y; }

let it = main();

let res = it.next(); // 第一个next()用于启动生成器 console.log(res.value);  //
输出"starting" (yield语句后跟的值传给了next()的对象)

res = it.next(5); // 向等待的第一个yield传入值5,*main()中的 x 被赋值为5
console.log(res.value); // 输出10 (x * 2得到了10传给next(5)运行后的对象)

res = it.next(20); // 向等待的第二个yield传入值20, *main()中的x被赋值为20
                   // 输出5 20    (执行后面的console.log(x, y)语句分别输出x,y的值) console.log(res.value); // 输出25  (return ...的值传给了next(20)运行后的对象)

注:

  • 第一个next()仅仅是用于启动生成器用的,并不会传入任何东西,如果传入了参数也会被自动忽略掉。
  • yield …在值传递方面的作用相当于return …,你也可以把它当做一个return语句来看待,如果yield后面不加参数,则默认yield undefined;
  • 最后一个next()执行完毕之后,得到的值是*main()函数return出来的值,如果函数没有自己加return语句,一样也会默认return undefined;
  • next()执行完毕后会返回一个对象,属性值有两个,分别为value(从yield或return处拿到的值)和done(boolean值,标识生成器是否执行完毕)。

9、Promise
是es6引入的异步编程解决方案。语法上Promise是一个构造函数,用来封装异步操作的。并且可以获取成功或失败的结果。
10、set集合
类似于数组,但是成员的每一个值都是唯一的。集合实现了迭代器(iterator)接口,所以可以使用扩展运算符(…)和for…of进行遍历。
(1)集合的属性和方法:

  • size:返回集合的元素个数
  • add:增加一个新元素,返回当前集合
  • delete:删除元素,返回一个Boolean值
  • has:检测集合中是否包含某个元素,返回Boolean值
  • clear:清空集合
const set = new Set(['red', 'blue', 'green'])

const res1 = set.has('red') console.log(res1)  // true

const r2 = set.size console.log(r2)  // 3

set.add('white') console.log(set) // Set(4) {"red", "blue", "green",
"white"}

set.delete('red') console.log(set) // Set(3) {"blue", "green",
"white"}

set.clear() console.log(set) // Set(0) {}

(2)set集合的应用

  • 数组去重
const arr1 = [1, 2, 3, 3, 4, 2, 5]

const res1 = [...new Set(arr1)] console.log(res1)  // [1, 2, 3, 4, 5]
  • 两个数组取交集
const arr1 = [1, 2, 3, 3, 4, 2, 5] const arr2 = [4, 5, 6, 7, 5, 6]

const res2 = [...new Set(arr1)].filter(item => {  
		 const s = new  Set(arr2)
     	 if(s.has(item)) {
    		return true
    	   }else {
   			 return false   }
    })
     console.log(res2) // [4, 5] 
     // 或者 const res3 = [...new Set(arr1)].filter(item => new Set(arr2).has(item))
console.log(res3) // [4, 5]
  • 两个数组并集(数组合并)
const arr1 = [1, 2, 3, 3, 4, 2, 5] 
const arr2 = [4, 5, 6, 7, 5, 6]
const res4 = [...new Set(arr1), ...new Set(arr2)]
console.log(res4) // [1, 2, 3, 4, 5, 4, 5, 6, 7]
  • 数组差集
const arr1 = [1, 2, 3, 3, 4, 2, 5] 
const arr2 = [4, 5, 6, 7, 5, 6]
const res5 = [...new Set(arr1)].filter(item =>!new Set(arr2).has(item))
console.log(res5) // [1, 2, 3]

11、Map
类似于对象,键值对的集合。但是‘键’的范围不限于字符串。各种类型的值(包括对象)都可以当做键。Map 也实现了 iterator 接口,可以使用扩展运算符(…)和for…of遍历。
Map 的属性和结构

  • size:返回Map元素的个数
  • set:增加一个新元素,返回当前 Map
  • get:返回键名对象的键值
  • has:检测 Map 中是否含某个元素,返回Boolean值
  • clear:清空集合
 const map = new Map()

map.set('name', 'zhangsan') map.set('age', 18) map.set('say',
function() {   console.log('hello') })

console.log(map)  // {"name" => "zhangsan", "age" => 18, "say" => ƒ}

console.log(map.size) // 3

const res1 = map.get('name') console.log(res1)  // 'zhangshan'

const res2 = map.has('age') console.log(res2)  // true

const res3 = map.delete('say') console.log(res3)  // true

12、Object方法的扩展(自行查看参考链接

  • Object.is:判断两个值是否相等,相当于全等于(===),但是不能判断 NaN 和 (-0, 0)
  • Object.assign:对象合并,后一个对象中的属性值会覆盖前一个对象上的属性值,如果前一个对象中不存在后一个对象中的某个属性,就会在对象中添加这个属性。
  • Object.getPrototypeOf 和 Object.setPrototypeOf:
 const obj = {   name: 'xiaobai' } const obj1 = {   arr: [1, 2, 3] }

Object.setPrototypeOf(obj, obj1) console.log(obj)  //
obj.__proto__.obj1 = arr console.log(Object.getPrototypeOf(obj, obj1))
// {arr: [1, 2, 3]}
  • Object.values:Object.values 返回的是一个对象所有可枚举的属性值的数组
  • Object.entries:Object.entries 返回一个给定对象自身可遍历的属性 [key, value] 的数组,还可将对象转化成一个二维数组
  • Object.getOwnPrototyDescriptors:返回指定对象所有自身属性的描述对象
  • Object.fromEntries:创建对象,接收的是一个二维数组,数组里面传键值对形式数组,可以接收 Map 数据格式

13、Array方法扩展

  • includes:ncludes 方法用来检测数组中是否包含某个元素,返回 Boolean 值
  • flat:flat 是将多维数组转换成低维数组
  • flatMap:遍历数组之后,如果返回结果是多维数组,转换成低维数组
  • async:
    async 函数返回值为 promise 对象;
    promise 对象结果由 async 函数执行的返回值决定
  • await
    await 函数必须写在 async 函数中;
    await 右侧的表达式一般为 promise 对象;
    await 返回的是 promise 的成功值;
    await 的 promise 失败了,就会抛出异常,需要通过 try…catch 捕获处理;

14、可选链操作符
?.当对象层数比较深,使用可选链操作符,可以免去层级判断。如果不使用可选链操作符,一般使用 && 来连接判断
?.约等于&&

九、路由器有几种模式

参考:Vue的三种路由模式

1、hash模式
使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载,其显示的网路路径中会有 “#” 号。这是最安全的模式,因为他兼容所有的浏览器和服务器。
2、history模式
美化后的hash模式,会去掉路径中的 “#”。赖于Html5 的history,pushState API,并且还包括back、forward、go三个方法,对应浏览器的前进,后退,跳转操作。
3、abstract模式
适用于所有JavaScript环境,例如服务器端使用Node.js。如果没有浏览器API,路由器将自动被强制进入此模式。

十、vue的生命周期

在这里插入图片描述
分为八个阶段:

  • beforeCreate(创建前):此阶段为实例初始化之后,此时的数据观察和事件机制都未形成,不能获得DOM节点。
  • created(创建后):在这个阶段vue实例已经创建,仍然不能获取DOM元素。
  • beforeMount(载入前):在这一阶段,我们虽然依然得不到具体的DOM元素,但vue挂载的根节点已经创建,下面vue对DOM的操作将围绕这个根元素继续进行;beforeMount这个阶段是过渡性的,一般一个项目只能用到一两次。
  • mounted(载入后):mounted是平时我们使用最多的函数了,一般我们的异步请求都写在这里。在这个阶段,数据和DOM都已被渲染出来。
  • beforeUpdate(更新前):在这一阶段,vue遵循数据驱动DOM的原则。beforeUpdate函数在数据更新后虽然没立即更新数据,但是DOM中的数据会改变,这是Vue双向数据绑定的作用。
  • updated(更新后):在这一阶段DOM会和更改过的内容同步。
  • beforeDestroy(销毁前):在上一阶段Vue已经成功的通过数据驱动DOM更新,当我们不再需要vue操纵DOM时,就要销毁Vue,也就是清除vue实例与DOM的关联,调用destroy方法可以销毁当前组件。在销毁前,会触发beforeDestroy钩子函数。
  • destroyed(销毁后):在销毁后,会触发destroyed钩子函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值