2024年Web前端最新【面试题】2024前端面试知识体系_pxtpc(1),阿里前端面经社招

最后

你要问前端开发难不难,我就得说计算机领域里常说的一句话,这句话就是『难的不会,会的不难』,对于不熟悉某领域技术的人来说,因为不了解所以产生神秘感,神秘感就会让人感觉很难,也就是『难的不会』;当学会这项技术之后,知道什么什么技术能做到什么做不到,只是做起来花多少时间的问题而已,没啥难的,所以就是『会的不难』。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

我特地针对初学者整理一套前端学习资料

前端路线图

vue.js的36个技巧

console.log©;
}

functionfn() {
for (let arg ofarguments) {
console.log(arg)
}
}
fn(100, 200, ‘aaa’)

const pList = document.getElementsByTagName(‘p’)
// querySelectorAll(‘p’)for (let p of pList) {
console.log§
}
复制代码


* 遍历对象: for ... in 可以,for ... of 不可以


* 遍历Map Set:for...of 可以,for...in 不可以


* 遍历generator:for...of 可以,for ... in 不可以



对象,数组,字符串可枚举的,就可以使用for … in 循环
const obj1 = { x: 100 }
Object.getOwnPropertyDescriptors(obj1)
x:
configurable: trueenumerable: truevalue: 100writeable: true复制代码


* 可枚举 vs 可迭代


for ... in 用于可枚举数据,如对象,数组,字符串,得到key


for ... of 用于可迭代数据,如数组,字符串,Map,Set,得到value


## for-await-of有什么作用


for await...of 用于遍历多个Promise



functioncreatePromise(val) {
returnnewPromise((resolve) => {
setTimeout(() => {
resolve(val)
}, 1000)
})
}

(asyncfunction () {
const p1 = createPromise(100)
const p2 = createPromise(200)
const p3 = createPromise(300)
// const res1 = await p1// console.log(res1)// const res2 = await p2// console.log(res2)// const res3 = await p3// console.log(res3)const list = [p1, p2, p3]
// Promise.all(list).then(res => console.log(res))forawait (let res of list) {
console.log(res)
}
})()
复制代码


## offsetHeight - scrollHeight - clientHeight区别


盒子模型: width, height, padding, border, margin, box-sizing


offsetHeight offsetWidth : border + padding + content


clientHeight clientWidth: padding + content


scrollHeight scrollWidth: padding + 实际内容尺寸


## HTMLCollection和NodeList有什么区


* DOM是一颗树,所有节点都是Node


* Node是Element的基类


* Element是其他HTML元素的基类,如HTMLDivElement



const p1 = document.getElementById(‘p1’)
classNode {}

// documentclassDocumentextendsNode {}
classDocumentFragmentextendsNode {}

// 文本和注释classCharacterDataextendsNode {}
classCommentextendsCharacterData {}
classTextextendsCharacterData {}

// elemclassElementextendsNode {}
classHTMLElementextendsElement {}
classHTMLDivElementextendsHTMLElement {}
classHTMLInputElementextendsHTMLElement {}
复制代码


* HTMLCollection 是 Element 的集合


* NodeList 是 Node 集合



node vs element

复制代码

划重点:


* 获取 Node 和 Element 的返回结果可能不一样


* 如 elem.childNodes 和 elem.children 不一样


* 前者包含Text和Comment节点,后者不会


类数组 变成 数组



const arr1 = Array.from(list)
const arr2 = Array.prototype.slice.call(list)
const arr3 = […list]
复制代码


## Vue中computed和watch有什么区别


* computed 用于计算产生新的数据


* watch 用于监听现有数据



watch: {
name(newValue, oldValue) {
console.log(‘watch name’, newValue, oldValue)
}
},
computed: {
userInfo() {
returnthis.name + this.city
}
}
复制代码


computed有缓存 watch没有缓存


## Vue组件通讯有几种方式



props和$emit

$parent

自定义事件

$refs

$attr

provide/inject

vuex


$attrs $listeners

vue3 移除 $listeners

上一级没有接收到的

props: [‘x’], // $attrsemits: [‘getX’], // l i s t e n e r s O b j e c t . k e y s ( t h i s . listenersObject.keys(this. listenersObject.keys(this.attrs)

dom结点: inheritAttrs: false


this. p a r e n t t h i s . parentthis. parentthis.refsprovide: {
info: ‘aaa’
}

provide() {
return {
info: computed(() =>this.name)
}
}


父子组件

上下级组件(跨多级)通讯

全局组件
复制代码


## Vuex中action和mutation有什么区别


mutation: 原子操作,必须同步代码


action: 可包含多个mutation;可包含异步代码


## JS严格模式有什么特点



‘use strict’// 全局开启functionfn() {
‘use strict’// 某个函数开启
}
复制代码


* 全局变量必须先声明


* 禁止使用 with


* 创建eval作用域


* 禁止this指向window


* 函数参数不能重名


## JS内存垃圾回收用什么算法


垃圾回收 GC



什么是垃圾回收?

functionfn1() {
const a = 'aa’console.log(a)

const obj = { x: 100 }
console.log(obj)
}
fn1()
复制代码



functionfn2() {
const obj = { x: 100 }
window.obj = obj
}
fn2()

functiongetDataFns() {
const data = {} // 闭包return {
get(key) {
return data[key]
},
set(key, value) {
data[key] = value
}
}
}
const { get, set } = getDataFns()
set(‘x’, 100)
get(‘x’)
复制代码



引用计数(之前)

// 对象被 a 引用let a = { x: 100 }
let a1 = a
a = 10
a1 = null// 循环引用functionfn3() {
const obj1 = {}
const obj2 = {}
obj1.a = obj2
obj2.a = obj1
}
fn3()

// ie6-7 内存泄漏的 bugvar div1 = document.getElementById(‘div1’)
div1.a = div1
div1.someBigData = {}

标记清除(现代)
// JS 根 window复制代码


## JS闭包是内存泄漏吗


闭包的数据是不可以被垃圾回收的


## 如何检测JS内存泄漏


检测内存变化



const arr = []
for (let i = 0; i < 10 * 10000; i++) {
arr.push(i)
}

functionbind() {
// 模拟一个比较大的数据const obj = {
str: JSON.stringify(arr) // 简单的拷贝
}
window.addEventListener(‘resize’, () => {
console.log(obj)
})
}

let n = 0functionstart() {
setTimeout(() => {
bind()
n++

// 执行 50 次if (n < 50) {
start()
} else {
alert(‘done’)
}
}, 200)
}

document.getElementById(‘btn1’).addEventListener(‘click’, () => {
start()
})
复制代码


## JS内存泄漏的场景有哪些


1. 被全局变量,函数引用,组件销毁时未清除


2. 被全局事件,定时器引用,组件销毁时未清除


3. 被自定义事件引用,组件销毁时未清除


## JS内存泄漏的场景有哪些-扩展-WeakMap和Weak



// 标记清除算法const data = {}
functionfn1() {
const obj = { x: 100 }
data.obj = obj
}
fn1()
复制代码



const map = newMap()
functionfn1() {
const obj = { x: 100 }
map.set(‘a’, obj)
}
fn1()
复制代码



// WeakMap WeakSet 弱引用

复制代码


## Ajax-Fetch-Axios三者有什么区别


1. Ajax(Asynchronous Javascript and XML),一种技术统称


2. Fetch,一个具体的原生API 浏览器原生API,用于网络请求 和XMLHttpRequest一个级别 Fetch语法更加简洁,易用,支持Promise


3. Axios,是一个第三方库 最常用的网络请求lib 内部可用XMLHttpRequest和Fetch来实现


4. lib(库)和API(原生的函数)的区别


5. fetch 和 XMLHttpRequest 全局的API


## 用XMLHttpRequest实现Ajax



functionajax1(url, sucessFn) {
const xhr = newXMLHttpRequest();
xhr.open(“GET”, url, false);
xhr.onreadystatechange = function () {
// 这里的函数异步执行if (xhr.readyState == 4) {
if (xhr.status == 200) {
successFn(xhr.responseText);
}
}
}
xhr.send(null);
}

functionajax2(url) {
returnfetch(url).then(res => res.json());
}
复制代码


## 请描述TPC三次握手和四次挥手


### 建立TCP连接




---


1. 先建立连接(确保双方都有收发消息的能力)


2. 再传输内容(如发送给一个get请求)


3. 网络连接是TCP协议,传输内容是HTTP协议


4. SYN SYN+ACK ACK


### 四次挥手-关闭连接




---



1.FIN ->
2.ACK <-
3.FIN <-
4.ACK ->
复制代码


## HTTP跨域时为何要发送options请求


### 跨域请求




---


1. 浏览器同源策略


2. 同源策略一般限制Ajax网络请求,不能跨域请求server


3. 不会限制<link> <img> <script> <iframe>加载第三方资源


### JSONP




---



// www.aaa.com网页

<scriptsrc=“https://www.bbb.com/api/getData”>// https://www.bbb.com… 返回了一段字符串’onSuccess({ errno: 0, data: {} })'复制代码



// CORS 配置允许跨域(服务端)
response.setHeader(“Access-Control-Allow-Origin”, “http://localhost:8011”) // 或者"*"
response.setHeader(“Access-Control-Allow-Headers”, “X-Requested-With”)
response.setHeader(“Access-Control-Allow-Methods”, “PUT,POST,GET,DELETE,OPTIONS”)
response.setHeader(“Access-Control-Allow-Credentials”, “true”) // 允许跨域接收 cookie复制代码


options请求,是跨域请求之前的预检查;浏览器自行发起的,无需我们干预,不会影响实际的功能


## 浏览器和nodejs事件循环(EventLoop)有什么


### 单线程和异步




---


1. JS是单线程的(无论在浏览器还是nodejs)


2. 浏览器中JS执行和DOM渲染共用一个线程


3. 异步 宏任务 和 微任务 宏任务,如 setTimeout setInterval 网络请求 微任务,如 promise async / await 微任务在下一轮DOM渲染之前执行,宏任务在之后执行 微任务: MutationObserver 监听DOM树的变化,Mutation observer 是用于代替 Mutation events 作为观察DOM树结构发生变化时,做出相应处理的API MutationObserver接口提供了监视对DOM树所做更改的能力。它被设计为旧的Mutation Events功能的替代品,该功能是DOM3 Events规范的一部分。



const p = document.createElement(‘p’)
p.innerHTML = 'new paragraph’document.body.appendChild§

const list = document.getElementsByTagName(‘p’)
console.log(‘length—’, listh.length)

console.log(‘start’)
// 渲染之后setTimeout(() => {
const list = document.getElementsByTagName(‘p’)
console.log(‘length on timeout—’, list.length) // 2alert(‘阻塞 timeout’)
})
// 渲染之前Promise.resolve().then(() => {
const list = document.getElementsByTagName(‘p’)
console.log(‘length on promise.then—’, list.length) // 2alert(‘阻塞 promise’)
})
console.log(‘end’)
复制代码



// 同步任务 -> 异步任务 -> 宏任务// 微任务要比宏任务要快// Event Loop

复制代码


### Nodejs异步




---


1. Nodejs同样使用ES语法,也是单线程,也需要异步


2. 异步任务也分:宏任务+微任务


3. 但是,它的宏任务和微任务,分不同类型,有不同优先级


### 虚拟DOM(vdom)真的很快吗




---


1. vdom: Virtual DOM,虚拟DOM 用JS对象模拟DOM节点数据


2. 组件化 数据视图分离,数据驱动视图 只关注业务数据,而不用再关心DOM变化


3. vdom并不快,js直接操作dom才是最快的 但”数据驱动视图“要有合适的技术方案,不能全部dom重建 vdom就是目前最合适的技术方案(并不是因为它快,而是合适)


### 遍历一个数组用for和forEach哪个更快




---


* for更快


* forEach每次都要创建一个函数来调用,而for不会创建函数


* 函数需要独立的作用域,会有额外的开销


### nodejs如何开启多进程,进程如何通讯-进程和线程的




---


进程 process vs 线程 thread 进程,OS 进行资源分配和调度的最小单位,有独立内存空间 线程,OS 进行运算调度的最小单位,共享进程内存空间 JS是单线程的,但可以开启多进程执行,如WebWorker js 不可以开启一个线程


为何需要多进程?


1. 多核CPU,更适合处理多进程


2. 内存较大,多个进程才能更好的利用(单进程有内存上限)


3. 总之,“压榨”机器资源,更快,更节省 单个进程内存2G左右



nodejs如何开启多进程

// console.info(process.pid)const http = require(‘http’)

const server = http.createServer()
server.listen(3000, () => {
console.log(‘localhost: 3000’)
})

console.info(process.pid)

// WebWorker 进程// forkconst http = require(‘http’)
const server = http.createServer((req, res) => {
if (req.url === ‘/get-sum’) {
console.info(‘主进程 id’, process.id)

res.end(‘hello’)
}
})
server.listen(3000, () => {
console.info(‘localhost: 3000’)
})

// cluster 进程复制代码



// 子进程,计算functiongetSum() {
let sum = 0for (let i = 0; i < 10000; i++) {
sum += i
}
return sum
}

process.on(‘message’, data => {
console.log(‘子进程 id’, process.pid)
console.log(‘子进程接受到的信息:', data)

const sum = getSum()
// 发送消息给主进程
process.send(sum)
})
复制代码



const http = require(‘http’)
const fork = require(‘child_process’).forkconst server = http.createServer((req, res) => {
if (req.url === ‘/get-sum’) {
console.info(‘主进程 id’, process.pid)

// 开启子进程const computeProcess = fork(‘./compute.js’)
computeProcess.send(‘开始计算’)

computeProcess.on(‘message’, data => {
console.info(‘主进程接受到的信息:’, data)
res.end(‘sum is’ + data)
})

computeProcess.on(‘close’, () => {
console.info(‘子进程因报错而退出’)
computeProcess.kill()
res.end(‘error’)
})
}
})

server.listen(3000, () => {
console.info(‘localhost: 3000’)
})
复制代码



const http = require(‘http’)
const cpuCoreLength = require(‘os’).cpus().lengthconst cluster = require(‘cluster’)

if (cluster.isMaster) {
for (let i = 0; i < cpuCoreLength; i++) {
cluster.fork() // 开启子进程
}
cluster.on(‘exit’, worker => {
console.log(‘子进程退出’)
cluster.fork() // 进程守护
})
} else {
// 多个子进程会共享一个 TCP 连接,提供一份网络服务const server = http.createServer((req, res) => {
res.writeHead(200)
res.end(‘done’)
})
server.listen(3000)
}
复制代码


开启子进程 child\_process.fork 和 cluster.fork 使用 send 和 on 传递消息


## 请描述js-bridge的实现原理


1. JS无法直接调用 native API


2. 需要通过一些特定的“格式”来调用


3. JS Bridge的常见实现方式


1. 注册全局API


2. URL Scheme



// 封装 JS-bridgeconst sdk = {
invoke(url, data = {}, onSuccess, onError) {
const iframe = document.createElement(‘iframe’)
iframe.style.visibility = 'hidden’document.body.appendChild(iframe)

iframe.onload = () => {
const content = iframe1.contentWindow.document.body.innerHTML
}
}
}
复制代码


## requestIdleCallback和request


由React fiber引起的关注


1. 组建树转换为链表,可分段渲染


2. 渲染时可以暂停,去执行其他高优任务,空闲时再继续渲染


3. 如何判断空闲? - requestIdleCallback


区别


4. requestAnimationFrame 每次渲染完都会执行,高优


5. requestIdleCallback 空闲时才执行,低优



requestAnimationFrame

start
end
timeout
requestAnimationFrame
requestIdleCallback

window.onload = () => {

 console.info('start')
 setTimeout(() => {
  console.info('timeout')
 })
 // 宏任务,顺序交换也一样// 高优window.requestAnimationFrame(() => {
  console.info('requestAnimationFrame')
 })
 // 低优window.requestIdleCallback(() => {
  console.info('requestIdleCallback')
 })

 console.info('end')
}
复制代码

Vue每个生命周期都做了什么

  • beforeCreate 创建一个空白的Vue实例 data method 尚未被初始化,不可使用

  • created vue实例初始化完成,完成响应式绑定 data method都已经初始化完成,可调用 尚未开始渲染模板

  • beforeMount 编译模版,调用render生成vdom 还没有开始渲染DOM

$el null element没有

  • mounted 完成DOM渲染 组件创建完成 开始由“创建阶段”进入“运行阶段”

  • beforeUpdate data发生变化之后 准备更新DOM(尚未更新DOM)

  • updated data发生变化,且DOM更新完成 (不要在updated中修改data,可能会导致死循环)

  • beforeUnmount 组件进入销毁阶段(尚未销毁,可正常使用) 可移除,解绑一些全局事件,自定义事件

  • unmounted 组件被销毁了 所有子组件也都被销毁了

  • keep-alive组件 onActivated 缓存组件被激活 onDeactivated 缓存组件被隐藏

<keep-alive>
 <Child1v-if="num === 1"></Child1><Child2v-else></Child2>
</keep-alive>

// Child1 2created() {
 console.log() // keep-alive 中只创建
}
activated() {}
deactivated() {}

// 创建一次被缓存复制代码
child1 created
child1 activated
child2 created
child1 deactivated
child2 activated
child2 deactivated
child1 activated
复制代码
  • vue什么时候操作DOM比较合适 mounted和updated都不能保证子组件全部挂载完成 使用$nextTick渲染DOM

只有nextTick操作DOM才是最安全的

$nextTick
mounted() {
 this.$nextTick(function () {
  // 仅在整个视图都被渲染之后才会运行的代码
 })
}
复制代码
  • ajax 应该在那个生命周期? 有两个选择:created 和 mounted 推荐:mounted

  • vue3 Composition API 生命周期有何区别? 用setup代替了beforeCreate和created 使用Hooks函数的形式,如mounted改为onMounted()

import { onUpdated, onMounted } from'vue'exportdefault {
 setup() {
  onMounted(() => {
 
  })
  onUpdated(() => {
  
  })
 }
}
复制代码

Vue2和Vue3和React三者的diff算法有什么

介绍diff算法 diff算法很早就有

tree diff优化 只比较同一层级,不跨级比较 tag 不同则删掉重建(不再去比较内部的细节) 子节点通过key区分(key的重要性)

vue3最长递增子序列 vue2 双端比较 React 仅右移

Vue-router的MemoryHistory是什么

Hash, WebHistory, MemoryHistory( v4 之前叫做 abstract history)

移动端H5点击有300ms延迟,该如何解决

FastClick原理 监听touchend事件(touchstart touchend会先于click触发) 使用自定义DOM事件模拟一个click事件 把默认的click事件(300ms之后触发)禁止掉

现代浏览器的改进

<head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <meta http-equiv="X-UA-Compatible" content="ie=edge">
</head>
复制代码

HTTP请求中token和cookie有什么区别-cookie

  1. cookie HTTP无状态,每次请求都要带cookie,以帮助识别身份 服务端也可以向客户端set-cookie,cookie大小限制4kb 默认有跨域限制:不可跨域共享、传递cookie

  2. withCredentials 前端设置跨域cookie共享 cookie本地存储 HTML5之前cookie常被用于本地存储 HTML5之后推荐使用localStorage和sessionStorage

  3. 现在浏览器开始禁止第三方cookie 和跨域限制不同。这里是:禁止网页引入的第三方JS设置cookie 打击第三方广告,保护用户隐私 新增属性 SameSite: Strict/Lax/None;值可自己选择

  4. 浏览器的Cookie新增加了一个SameSite属性,用来防止CSRF攻击和用户追踪

  5. cookie和session cookie用于登录验证,存储用户标识 session在服务端,存储用户详细信息,和cookie信息一一对应 cookie和session是常见登录验证解决方案

HTTP请求中token和cookie有什么区别-token

  1. token vs cookie cookie是HTTP规范,而token是自定义传递 cookie会默认被浏览器存储,而token需自己存储 token默认没有跨域限制

  2. jwt (json web token) 可以取代 cookie和session 前端发起登录,后端验证成功之后,返回一个加密的token 前端自行存储这个token(其中包含了用户信息,加密了) 以后访问服务端接口,都带着这个token,作为用户信息

  3. cookie:HTTP标准;跨域限制;配合session使用

  4. token:无标准;无跨域限制;用于JWT

session和JWT哪个更好

  1. session缺点 占用服务端内存,硬件成本高 多进程,多服务器时,不好同步,需使用第三方缓存,如redis 默认有跨域限制

  2. session优点 原理简单,易于学习 用户信息存储在服务端,可快速禁某个用户

  3. jwt 优点 不占用服务端内存 多进程,多服务器 不受影响 没有跨域限制

  4. jwt 缺点 用户信息存储在客户端,无法快速封禁某用户 万一服务端秘钥被泄漏,则用户信息全部丢失 token体积一般大于cookie,会增加请求的数据量

如有严格管理用户信息的需求(保密,快速封禁)推荐session 如没有特殊要求,则使用jwt

如何实现SSO单点登录

  1. 基于cookie cookie默认不可跨域共享,但有些情况下可设置为共享 主域名相同,如www.baidu.com image.baidu.com 设置cookie domain为主域名,即可共享cookie

  2. sso 主域名完全不同,则cookie无法共享 可使用sso技术方案

HTTP协议和UDP协议有什么区别

网络协议 HTT P协议在应用层 TCP UDP 协议再传输层 严格来说,应该拿TCP和UDP进行比较

OSI的体系结构

7. 应用层
6. 表示层
5. 会话层
4. 运输层
3. 网络层
2. 数据链路层
1. 物理层
复制代码

TCP/IP的体系结构

1. 应用层(各种应用层协议,如DNS,HTTP,SMTP等)


### 最后

![](https://img-blog.csdnimg.cn/img_convert/99c58ff6374dd01890c551a7805e10e0.webp?x-oss-process=image/format,png)

![](https://img-blog.csdnimg.cn/img_convert/90059f81c5b34d4e558d1a086e979214.webp?x-oss-process=image/format,png)

**资料过多,篇幅有限**

**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)**

>自古成功在尝试。不尝试永远都不会成功。勇敢的尝试是成功的一半。




4. token:无标准;无跨域限制;用于JWT


## session和JWT哪个更好


1. session缺点 占用服务端内存,硬件成本高 多进程,多服务器时,不好同步,需使用第三方缓存,如redis 默认有跨域限制


2. session优点 原理简单,易于学习 用户信息存储在服务端,可快速禁某个用户


3. jwt 优点 不占用服务端内存 多进程,多服务器 不受影响 没有跨域限制


4. jwt 缺点 用户信息存储在客户端,无法快速封禁某用户 万一服务端秘钥被泄漏,则用户信息全部丢失 token体积一般大于cookie,会增加请求的数据量


如有严格管理用户信息的需求(保密,快速封禁)推荐session 如没有特殊要求,则使用jwt


## 如何实现SSO单点登录


1. 基于cookie cookie默认不可跨域共享,但有些情况下可设置为共享 主域名相同,如[www.baidu.com](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0) [image.baidu.com](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0) 设置cookie domain为主域名,即可共享cookie


2. sso 主域名完全不同,则cookie无法共享 可使用sso技术方案


## HTTP协议和UDP协议有什么区别


网络协议 HTT P协议在应用层 TCP UDP 协议再传输层 严格来说,应该拿TCP和UDP进行比较


OSI的体系结构



  1. 应用层
  2. 表示层
  3. 会话层
  4. 运输层
  5. 网络层
  6. 数据链路层
  7. 物理层
    复制代码

TCP/IP的体系结构



  1. 应用层(各种应用层协议,如DNS,HTTP,SMTP等)

最后

[外链图片转存中…(img-EXDVKIso-1715447978664)]

[外链图片转存中…(img-s3lXCkOR-1715447978664)]

资料过多,篇幅有限

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

自古成功在尝试。不尝试永远都不会成功。勇敢的尝试是成功的一半。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值