node.js服务器的应用、CommonJs模块化、url模块、querystring模块、path模块、util模块

目录

        学习⽂档NodeJS学习⽂档地址:Node.js 简介

环境搭建

REPL环境

 案例体验

Node.js 服务器应⽤

创建 Node.js 应⽤

步骤⼀、引⼊ required 模块

步骤⼆、创建服务器

 运⾏ Node.js 应⽤

 终⽌ Node.js 应⽤

Node.js 回调函数

阻塞代码示例

⾮阻塞代码实例

Node.js模块系统

CommonJS模块化

导出模块

导入模块

module对象

require方法

服务端的模块种类

 ECMAScript模块

异常处理

异常产生原因

解决方案try-catch

回调函数

EventEmitter接口的error事件

url模块

url.parse()

url.format()

url.resolve(from,to)

url.hash

url.host

url.hostname

url.origin

url.pathname

url.search

querystring模块

 querystring.parse(str[, sep[, eq[, options]]])

querystring.stringify(obj[, sep[, eq[, options]]])

querystring.escape

querystring.unescape

path模块

path.basename(str[, ext]))

path.dirname(path)

path.extname(path)

path.parse(path)

path.format(pathObject)

path.isAbsolute(path)

path.join([...paths])

path.normalize(path)

path.relative(from, to)

path.resolve([...paths])

path.sep

path.win32

path.posix

path.delimiter

util模块

util.callbackify


学习⽂档
NodeJS学习⽂档地址:Node.js 简介

 环境搭建

NodeJS语⾔需要运⾏在Nodejs软件中,Nodejs软件的下载地址

下载 | Node.js 中文网

 下载之后直接进⾏安装即可。安装之后使⽤ node -v 来查看nodejs的版本号。安装了nodejs软件后,会⾃动携带了npm包管理⼯具。因为npm包管理⼯具默认是从国外下载项⽬所需的依赖,⽐较慢,所以nodejs安装之后可以修改npm的仓库地址:

$ npm config set registry https://registry.npm.taobao.org

也可以全局安装cnpm, cnpm直接使⽤的就是国内淘宝镜像,使⽤cnpm也可快速下载项⽬所需的依赖:

$ npm install -g cnpm --registry=https://registry.npm.taobao.org

REPL环境

Node.js REPL(Read Eval Print Loop:交互式解释器)表示⼀个电脑的环境,类似 Windows 系统的终端或 Unix/Linux,shell,我们可以在终端中输⼊命令,并接收系统的响应。
使⽤ node 命令进⼊NodeJS软件的REPL环境, REPL环境中执⾏以下任务:
读取 - 读取⽤户输⼊,解析输⼊的 Javascript 数据结构并存储在内存中。
执⾏ - 执⾏输⼊的数据结构
打印 - 输出结果
循环 - 循环操作以上步骤直到⽤户两次按下 ctrl-c 按钮退出。

 使⽤两次 ctrl+C 来退出REPL环境

 案例体验

Node.js 服务器应⽤

Node.js 应⽤是由哪⼏部分组成的:
1.引⼊ required 模块: 我们可以使⽤ require 指令来载⼊ Node.js 模块。
2.创建服务器: 服务器可以监听客户端的请求,类似于 Apache 、 Nginx 等 HTTP 服务器。
3. 接收请求与响应请求 服务器很容易创建,客户端可以使⽤浏览器或终端发送 HTTP 请求,服务器接收请求后返回响应数据。


创建 Node.js 应⽤

新建demo⽂件夹作为项⽬根⽬录,在项⽬的根⽬录下创建⼀个叫 server.js 的⽂件。

步骤⼀、引⼊ required 模块

我们使⽤ require 指令来载⼊ http 模块,并将实例化的 HTTP 赋值给变量 http,实例如下:

var http = require("http");

步骤⼆、创建服务器

接下来我们使⽤ http.createServer() ⽅法创建服务器,并使⽤ listen ⽅法绑定 8888 端⼝。 函数通过 request,response 参数来接收和响应数据。
server.js 完整代码如下:
 

var http = require('http');
http.createServer(function (request, response) {
// 发送 HTTP 头部
// HTTP 状态值: 200 : OK
// 内容类型: text/plain
response.writeHead(200, {'Content-Type': 'text/plain'});
// 发送响应数据 "Hello World"
response.end('Hello World\n');
}).listen(8888);
// 终端打印如下信息
console.log('Server running at http://127.0.0.1:8888/');

以上代码我们完成了⼀个可以⼯作的 HTTP 服务器。
代码如下图:

 运⾏ Node.js 应⽤

⽽运⾏ Node.js 程序的常⽤⽅法是,运⾏全局可⽤的命令 node (安装 Node.js 之后)并且传⼊您要执⾏的⽂件的名称。
如果您的 Node.js 主应⽤程序⽂件是 server.js ,则您可以通过键⼊以下来调⽤它:

node server.js

以上,你显式地告诉 shell 使⽤ node 运⾏你的脚本,运⾏结果如下:

 接下来就可以在⻚⾯上访问http://127.0.0.1:8888/这个⽹址。访问之后获取到该服务器给的响应“Hello World”。

 终⽌ Node.js 应⽤

有多种⽅法可以终⽌ Node.js 应⽤程序。
当在控制台中运⾏程序时,可以⽤⼀次 ctrl+C 关闭它。

 我们这⾥要讨论的是以编程⽅式退出。 process 核⼼模块提供了⼀种⽅便的⽅法,允许您以编程⽅式退出
Node.js 程序: process.exit() 。当 Node.js 运⾏ process.exit() 时,进程⽴即被强制终⽌。
这意味着任何待处理的回调、任何仍在发送的⽹络请求、任何⽂件系统访问、或者正在写⼊ stdout 或 stderr的进程,所有这些都将⽴即被⾮正常地终⽌。

Node.js 回调函数

Node.js 异步编程的直接体现就是回调。
异步编程依托于回调来实现,但不能说使⽤了回调后程序就异步化了。
回调函数在完成任务后就会被调⽤, Node 使⽤了⼤量的回调函数, Node 所有 API 都⽀持回调函数。
例如,我们可以⼀边读取⽂件,⼀边执⾏其他命令,在⽂件读取完成后,我们将⽂件内容作为回调函数的参数返回。这样在执⾏代码时就没有阻塞或等待⽂件 I/O 操作。这就⼤⼤提⾼了 Node.js 的性能,可以处理⼤量的并发请求。
回调函数⼀般作为函数的最后⼀个参数出现:

function foo1(name, age, callback) { }
function foo2(value, callback1, callback2) { }

阻塞代码示例

创建⼀个⽂件 input.txt ,内容如下:

Hello 我是⽂件内的内容!

创建 main.js ⽂件, 代码如下:
 

var fs = require("fs");
var data = fs.readFileSync('input.txt');
console.log(data.toString());
console.log("程序执⾏结束!");

代码如下图:

 运⾏结果如图:

node main.js

⾮阻塞代码实例

保持input.txt不变,将main.js的内容设置为如下内容:

var fs = require("fs");
fs.readFile('input.txt', function (err, data) {
if (err) return console.error(err);
console.log(data.toString());
});
console.log("程序执⾏结束!");

代码如下图:

 运⾏结果如下:

node main.js

 以上两个实例我们了解了阻塞与⾮阻塞调⽤的不同。第⼀个实例在⽂件读取完后才执⾏程序。 第⼆个实例我们不需要等待⽂件读取完,这样就可以在读取⽂件时同时执⾏接下来的代码,⼤⼤提⾼了程序的性能。

因此,阻塞是按顺序执⾏的,⽽⾮阻塞是不需要按顺序的,所以如果需要处理回调函数的参数,我们就需要写在回调函数内。


Node.js模块系统

package.json 是模块的清单文件,记录了当前模块的基本信息、依赖信息等

属性                                      解释
version                              模块版本
name                                模块名称
description                        模块描述
main                                 入口文件
private                       如果设置为 true,则可以防止应用程序/软件包被意外地发布到 npm。
scripts                                  脚本
dependencies                  产品依赖
devDependencies           开发依赖
engines nodejs                  版本号
browserslist                  支持的浏览器


CommonJS模块化

早期的Javascript(ECMAScript5)中是没有模块化的,NodeJS推出后,使用的是便是 CommonJS 模块规范。后来ES6出现后才出现了ECMAScript模块化,在node-v12后可以使用 ECMAScript模块化。
CommonJS规定,每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可⻅。每个模块内部, module 变量代表当前模块。这个变量是一个对象,它的 exports 属性(即module.exports )是对外的接口。加载某个模块,其实是加载该模块的 module.exports 属性。 require 方法用于加载模块。


导出模块

新建modules1.js文件,在该文件中内容如下:

module.exports = {
name: 'tom',
sayHello() {
console.log("hello");
},
};

exports 和 module.exports 的使用
默认情况下: module.exports===exports 为true,相当于如下内容:

module.exports = {}
exports = module.exports;

require 引入的对象本质上是 module.exports 。这就产生了一个问题,当 module.exports 和 exports 指向的不是同一块内存时, exports 的内容就会失效。
如果要对外暴露属性或方法,就用 exports 就行,要暴露对象(包含了很多属性和方法),就用 module.exports。


导入模块

在 Node.js 中,引入一个模块非常简单,如下我们创建一个 module1Test.js 文件并引入 module 模块,代码如下:

let obj = require('./modules1')
console.log(obj);
obj.sayHello()

代码如下图:

 运行结果如下:

node module1Test.js

也可以在导入的同时做解构

let { name, sayHello } = require('./modules1')
console.log(name);
sayHello()

 运行结果如下:

node module1Test.js

1.所有代码都运行在模块作用域,不会污染全局作用域。
2. 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后 再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
3. 模块加载的顺序,按照其在代码中出现的顺序。

module对象

每个模块内部,都有一个 module 对象,代表当前模块。

属性名                                  解释
id                           模块的识别符,通常是带有绝对路径的模块文件名。
filename                模块的文件名,带有绝对路径。
loaded                   返回一个布尔值,表示模块是否已经完成加载。
parent                   返回一个对象,表示调用该模块的模块。
children                返回一个数组,表示该模块要用到的其他模块。
exports                 表示模块对外输出的值。

require方法

require 引入的对象本质上是 module.exports
NodeJS使用CommonJS模块规范,内置的 require 命令用于加载模块文件。 require 命令的基本功能是,读入并执行一个JavaScript文件,然后返回该模块的exports对象。如果没有发现指定模块,会报错。 需要注意的是:
1. 如果参数字符串以“/”开头,则表示加载的是一个位于绝对路径的模块文件。比如,
require('/home/marco/foo.js') 将加载 /home/marco/foo.js 。

2. 如果参数字符串以“./”开头,则表示加载的是一个位于相对路径(跟当前执行脚本的位置相比)的模块文件。比如, require('./circle') 将加载当前脚本同一目录的circle.js 。
3. 如果参数字符串不以“./“或”/“开头,则表示加载的是一个默认提供的核心模块(位于Node的系统安装目录中),或者一个位于各级node_modules目录的已安装模块(全局安装或局部安装)。
4. require发现参数字符串指向一个目录以后,会自动查看该目录的文件, 然后加载 main 字段指定的入口文件。如果 package.json 文件没有 main 字段,或者根本就没有 package.json 文件,则会加载该目录下的index.js 文件或 index.node 文件。

服务端的模块种类

也许你已经注意到,我们已经在之前代码中使用了模块了。像这样:

var http = require("http");
http.createServer(...);

Node.js 中自带了一个叫做 http 的模块,我们在我们的代码中请求它并把返回值赋给一个本地变量。这把我们的本地变量变成了一个拥有所有 http 模块所提供的公共方法的对象。

Node.js 中自带了一个叫做 http 的模块,我们在我们的代码中请求它并把返回值赋给一个本地变量。这把我们的本地变量变成了一个拥有所有 http 模块所提供的公共方法的对象。

模块种类:
内置模块/核心模块,比如上方的http模块或者fs模块。使用 require('内置模块名') 来引入
用户自定义模块,比如上方的modules1.js文件,在该文件中有导出,就可以当做一个模块来使用。使用
require('自定义模块路径') 来引入。一般是以 ./ 或者 ../ 开始的路径所代表的的模块就是用户自定义模块。
第三方模块,第三方模块首先需要下载,然后再引入。比如 require('moment')

内置模块: http://nodejs.cn/api/path.html
http 提供HTTP服务器功能。
fs 与文件系统交互。
url 解析url字符串
querystring 解析URL的查询字符串。
util 提供一系列实用小工具。
path 处理文件路径。
crypto提供加密和解密功能,基本上是对OpenSSL的包装。

 ECMAScript模块

在ES6中每一个模块即是一个文件,在文件中定义的变量,函数,对象在外部是无法获取的。如果你希望外部可以读取模块当中的内容,就必须使用export来对其进行暴露(输出)。
export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系,也就是说外部接口需要用这个接口名来引用。

var firstName = 'Michael';
var lastName = 'vicky';
export { firstName, lastName }; //列表导出
export { firstName as first, lastName as last}; //重命名导出
export var a = 3; //导出单个属性
export function multiply(x, y) { return x * y; }; //导出单个属性
//默认导出,一个模块只能有一个默认导出,不能使用 var、let 或 const 用于导出默认值
export default {}
export default function foo(){}
var a = 1;
export a; //报错,因为没有提供对外的接口。应该export var a = 1; 或者export {a}

静态的import 语句用于导入由另一个模块导出的绑定。

import * as person from './person.js' //导入整个模块内容
import {firstName,lastName} from './person.js' //导入多个接口
import {firstName as name} from './person.js' //重命名
import '/modules/my-module.js'; //运行整个模块而不导入任何值
import myDefault from './my-module.js'; // 导入使用export default导出的

异常处理

Node是单线程运行环境,一旦抛出的异常没有被捕获,就会引起整个进程的崩溃。所以, Node的异常处理对于保证系统的稳定运行非常重要。

异常产生原因

一般来说,Node有如下三种方式,会产生异常。
使用throw语句抛出一个错误对象,即抛出异常。
将错误对象传递给回调函数,由回调函数负责发出错误。
通过EventEmitter接口,发出一个error事件

解决方案
try-catch

一般来说,Node只在很少场合才用try/catch语句,比如使用 JSON.parse 解析JSON文 本。这个结构无法捕获异步运行的代码抛出的异常。

// 可以捕获
try {
console.log(a);
} catch (err) {
console.log("捕获异常:",err);
}
// 无法捕获
try {
setTimeout(()=>{ console.log(a);},0)
} catch (err) {
console.log("捕获异常:",err);
}

回调函数

Node采用的方法,是将错误对象作为第一个参数,传入回调函数。这样就避免了捕获代码与发生错误的代码不在同一个时间段的问题。

let fs = require('fs');
fs.readFile('/foo.txt', function (err, data) {
if (err !== null) throw err;
console.log(data);
});

EventEmitter接口的error事件

发生错误的时候,也可以用EventEmitter接口抛出error事件。

var EventEmitter = require('events').EventEmitter;
var emitter = new EventEmitter();
//触发错误
emitter.emit('error', new Error('something bad happened'));
//绑定错误监听事件处理程序
emitter.on('error', function(err) {
console.error('出错:' + err.message);
});

注意和如下代码的区别:

var EventEmitter = require('events').EventEmitter;
var emitter = new EventEmitter();
//绑定错误监听事件处理程序
emitter.on('error', function (err) {
console.error('出错:' + err.message);
});
//触发错误
emitter.emit('error', new Error('something bad happened'));

url模块

提供解析url的工具,一个完整的href结构如下:

url.parse()

将url字符串地址转换为URL对象

let url = require('url')
const myURL =url.parse('https://user:pass@sub.example.com:8080/p/a/t/h?que
ry=string#hash');
console.log(myURL);

url.format()

构建一个URL字符串

let url = require('url');
let result = url.format({
protocol: 'https',
hostname: 'example.com',
pathname: '/some/path',
query: {
page: 1,
format: 'json'
}
});
console.log(result);
// => 'https://example.com/some/path?page=1&format=json'

url.resolve(from,to)

合并url字符串

let url = require('url');
url.resolve('/one/two/three', 'four'); // '/one/two/four'
url.resolve('http://example.com/', '/one'); // 'http://example.com/one'
url.resolve('http://example.com/one', '/two'); // 'http://example.com/two

url.hash

获取或设置URL中hash值

let url = require('url');
const myURL = new URL('https://example.org/foo#bar');
console.log(myURL.hash);// Prints #bar
myURL.hash = 'baz';
console.log(myURL.href);// Prints https://example.org/foo#baz

url.host

获取或者设置URL中的host

let url = require('url');
const myURL = new URL('https://example.org:81/foo');
console.log(myURL.host);// Prints example.org:81
myURL.host = 'example.com:82';
console.log(myURL.href);// Prints https://example.com:82/foo

url.hostname

设置或获取URL中的hostname,与host不同的是,hostname不包含端口

let url = require('url');
const myURL = new URL('https://example.org:81/foo');
console.log(myURL.hostname);// Prints example.org

url.origin

获取URL中的origin

let url = require('url');
const myURL = new URL('https://example.org/foo/bar?baz');
console.log(myURL.origin);// Prints https://example.org

url.pathname

获取或设置URL中的路径

let url = require('url');
const myURL = new URL('https://example.org/abc/xyz?123');
console.log(myURL.pathname);// Prints /abc/xyz

url.port

获取或设置URL中的端口号

let url = require('url');
const myURL = new URL('https://example.org:8888');
console.log(myURL.port);// Prints 8888

url.protocol

获取或设置URL的协议

const myURL = new URL('https://example.org');
console.log(myURL.protocol);// Prints https:
myURL.protocol = 'ftp';
console.log(myURL.href);// Prints ftp://example.org/

url.search

获取或设置URL的查询字符串

const myURL = new URL('https://example.org/abc?123');
console.log(myURL.search);
// Prints ?123
myURL.search = 'abc=xyz';
console.log(myURL.href);
// Prints https://example.org/abc?abc=xyz

querystring模块

 querystring.parse(str[, sep[, eq[, options]]])

将查询字符串解析为一个对象

let querystring = require('querystring');
let qs = "name=terry&age=12&gender=male"
console.log(querystring.parse(qs));
// { name: 'terry', age: '12', gender: 'male' }

querystring.stringify(obj[, sep[, eq[, options]]])

将一个对象序列化为查询字符串

let querystring = require('querystring');
let qs_obj = { name: 'terry', age: '12', gender: 'male' };
console.log(querystring.stringify(qs_obj))
// name=terry&age=12&gender=male

querystring.escape

对查询字符串进行编码

et querystring = require('querystring');
console.log(querystring.escape('name=张三&age=12'));
//name%3D%E5%BC%A0%E4%B8%89%26age%3D12

querystring.unescape

对查询字符串进行解码

et querystring = require('querystring');
console.log(querystring.unescape('name%3D%E5%BC%A0%E4%B8%89%26age%3D12'));
//name=张三&age=12

path模块

提供了一系列解析路径的工具

path.basename(str[, ext]))

返回路径中最后一部分

let path = require('path')
let name = '/Users/briup/Desktop/2021/web2102/nodejs/prepare/3-path.js'
console.log(path.basename(name));//3-path.js
console.log(path.basename(name, '.js'));//3-path

path.dirname(path)

返回路径中代表文件夹的部分

let path = require('path')
let name = '/Users/briup/Desktop/2021/web2102/nodejs/prepare/3-path.js'
console.log(path.dirname(name));
// /Users/briup/Desktop/2021/web2102/nodejs/prepare

path.extname(path)

返回路径中文件的后缀名,即路径中最后一个'.'之后的部分。如果一个路径中并不包含'.'或 该路径只包含一个'.' 且这个'.'为路径的第一个字符,则此命令返回空字符串。

let path = require('path')
let name = '/Users/briup/Desktop/2021/web2102/nodejs/prepare/3-path.js'
console.log(path.extname(name));
// .js
let name3 = './Users/briup/Desktop/2021/web2102/nodejs/prepare/3-path.js'
console.log(path.extname(name2));
//.js
let name2 = './Users/briup/Desktop/2021/web2102/nodejs/prepare/3-path'
console.log(path.extname(name3));
//空字符串

path.parse(path)

返回路径字符串的对象。

et path = require('path')
let name = '/Users/briup/Desktop/2021/web2102/nodejs/prepare/3-path.js'
console.log(path.parse(name));
/*
{
root: '/',
dir: '/Users/briup/Desktop/2021/web2102/nodejs/prepare', base: '3-path.js',
ext: '.js',
name: '3-path'
} */

path.format(pathObject)

从对象中返回路径字符串,和 path.parse 相反。

let path = require('path')
console.log(path.format({ root: '/', name: 'file', ext: '.txt' }));
// Returns: '/file.txt'
console.log(path.format({ root: '/', base: 'file.txt', ext: 'ignored' }));
// Returns: '/file.txt'
console.log(path.format({ root: '/ignored', dir: '/home/user/dir', base: 'file.txt' }));
// Returns: '/home/user/dir/file.txt'
console.log(path.format({ dir: './path/dir', base: 'file.txt' }));
// Returns: './path/dir/file.txt'

path.isAbsolute(path)

判断参数 path 是否是绝对路径。

let path = require('path')
let name = '/Users/briup/Desktop/2021/web2102/nodejs/prepare/3-path.js'
console.log(path.isAbsolute(name));
// true

path.join([...paths])

连接多个地址

let path = require('path')
console.log(path.join('/foo', 'bar', 'baz/asdf', 'quux', '..'));
//'/foo/bar/baz/asdf'

path.normalize(path)

标准化路径,可以识别"." ".."

let path = require('path')
console.log(path.normalize('/foo/bar//baz/asdf/quux/..'));
// Returns: '/foo/bar/baz/asdf'

path.relative(from, to)

用于将绝对路径转为相对路径,返回从 from 到 to 的相对路径(基于当前工作目录)

let path = require('path')
path.relative('/data/orandea/test/aaa', '/data/orandea/impl/bbb');
// Returns: '../../impl/bbb

path.resolve([...paths])

解析为绝对路径,给定的路径的序列是从右往左被处理的,后面每个 path 被依次解析,直到构造完成一个绝对路径。

let path = require('path')
console.log(path.resolve('/foo/bar', './baz'));
// Returns: '/foo/bar/baz'
console.log(path.resolve('/foo/bar', '/tmp/file/'));
// Returns: '/tmp/file'
console.log(path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif'));
// If the current working directory is /home/myself/node,
// this returns '/home/myself/node/wwwroot/static_files/gif/image.gif'

path.sep

平台的文件路径分隔符,'\' 或 '/'。

let path = require('path')
console.log('foo/bar/baz'.split(path.sep));
//linux中 Returns: ['foo', 'bar', 'baz']

path.win32

提供包含上述 path 的方法的对象,不过总是以 win32 兼容的方式交互。

let path = require('path')
console.log(path.win32)
console.log(path.win32===path) //mac电脑返回false

path.posix

提供包含上述 path 的方法的对象

let path = require('path')
console.log(path.posix)
console.log(path.posix===path) //mac系统返回true

path.delimiter

路径分隔符,linux操作系统分隔符为":", windows操作系统分隔符为";"

let path = require('path')
console.log(path.delimiter);
//mac系统输出 

util模块

util模块提供了很多工具函数,具体可参照 https://nodejs.org/dist/latestv14.x/docs/api/util.html
或者 http://nodejs.cn/api/util.html#utilcallbackifyoriginal

util.callbackify

将 async 异步函数(或者一个返回值为 Promise 的函数)转换成遵循异常优先的回调⻛格的函数,例如将 (err, value)=> ... 回调作为最后一个参数。 在回调函数中,第一个参数为拒绝的原因(如果 Promise 解决,则为 null ),第二个参数则是解决的值。

const util = require('util');
async function fn() {
return 'hello world';
}
const callbackFunction = util.callbackify(fn);
callbackFunction((err, value) => {
if (err) throw err;
console.log(value);
});

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值