一步一步学习TypeScript(19.Modules_模块)

本文介绍如何使用TypeScript实现模块化编程,包括模块定义、使用及组织方式,并演示了从简单验证器到复杂验证系统的模块化过程。

学习自:http://www.typescriptlang.org/Handbook#modules
首先定义一个文本格式检查器,可以查看输入的内容是否为字母或者数字

不使用模块

Validators.ts

interface StringValidator{
    isAcceptable(s:string):boolean;
}

class ZipCodeValidator implements StringValidator{
    zipRegexp:RegExp = /^\d{6}$/;

    isAcceptable(zip:string){
        return this.zipRegexp.test(zip);
    }
}

class LettersValidator implements StringValidator{
    lettersRegexp:RegExp = /^[a-zA-Z]+$/;

    isAcceptable(letters:string){
        return this.lettersRegexp.test(letters);
    }
}

//待验证的字符串
var str = ['150000', 'hello', '一二三'];

var zipCodeValid:StringValidator = new ZipCodeValidator();
var lettersValid:StringValidator = new LettersValidator();

str.forEach(s=>{
    if(zipCodeValid.isAcceptable(s)){
        console.log(s+' 是邮政编码');
    }else if(lettersValid.isAcceptable(s)){
        console.log(s+' 是字母');
    }else{
        console.log(s+' 不知道是什么');
    }
});
使用模块

上面代码看起来没有什么问题, 但是随着功能的增加, 我们可能需要加入更多的验证, 比如验证邮箱, 手机号, 身份信息等… 我们的代码会越来越大, 为了防止命名上的冲突, 变量或类的命名会越来越长,越来越复杂, 导致了维护的困难,全局变量的混乱.这时我们就会想到是否可以有效的组织我们的代码到不同的模块中,用模块划分功能.下面我们来改造一下上面的例子.

module Validator{
    interface StringValidator{
        isAcceptable(s:string):boolean;
    }

    export class ZipCodeValidator implements StringValidator{
        zipRegexp:RegExp = /^\d{6}$/;

        isAcceptable(zip:string){
            return this.zipRegexp.test(zip);
        }
    }

    export class LettersValidator implements StringValidator{
        lettersRegexp:RegExp = /^[a-zA-Z]+$/;

        isAcceptable(letters:string){
            return this.lettersRegexp.test(letters);
        }
    }
}

var str = ['150000', 'hello', '一二三'];

var zipCodeVali = new Validator.ZipCodeValidator();
var lettersVali = new Validator.LettersValidator();

str.forEach(s=>{
    if(zipCodeVali.isAcceptable(s)){
        console.log(s+' 是邮政编码');
    }else if(lettersVali.isAcceptable(s)){
        console.log(s+' 是字母');
    }else{
        console.log(s+' 不知道是什么');
    }
});

使用export暴露想要对外提供的类或方法, StringValidator因为是一个内部接口没必要让外部使用.
我们看使用模块改造后的例子,重点看module代码块,与前一个例子相比,全局变量减少到只有一个Validator, 把StringValidator,ZipCodeValidator,LettersValidator封装到模块内部, 这样有效的减少了对全局变量的污染,之后在加新的验证方法时只要放到模块内部即可.

划分模块

把所有逻辑放到一个页面中显然是不好的.我们来看一下如何组织我们的模块到不同的页面中.
validator.ts

module Validation {
    export interface StringValidator{
        isAcceptable(s:string):boolean;
    }
}

zipCodeValidator.ts

/// <reference path="validator.ts" />
module Validation {
    export class ZipCodeValidator implements StringValidator{
        zipRegexp:RegExp = /^\d{6}$/;

        isAcceptable(zip:string){
            return this.zipRegexp.test(zip);
        }
    }
}

lettersValidator.ts

/// <reference path="validator.ts" />
module Validation{
    export class LettersValidator implements StringValidator{
        lettersRegexp:RegExp = /^[a-zA-Z]+$/;

        isAcceptable(letters:string){
            return this.lettersRegexp.test(letters);
        }
    }
}

test.ts

/// <reference path="validator.ts" />
/// <reference path="zipCodeValidator.ts" />
/// <reference path="lettersValidator.ts" />


var str = ['150000', 'hello', '一二三'];

var zipCodeVali = new Validation.ZipCodeValidator();
var lettersVali = new Validation.LettersValidator();

str.forEach(s=>{
    if(zipCodeVali.isAcceptable(s)){
        console.log(s+' 是邮政编码');
    }else if(lettersVali.isAcceptable(s)){
        console.log(s+' 是字母');
    }else{
        console.log(s+' 不知道是什么');
    }
});

编译上诉代码

tsc test.ts

这时会发生编译器会同时帮助我们编译与之相关的其它文件.这就是/// <reference path="xxx" />这段代码的作用了.它告诉typescript编译器,代码与引用之间的关系.
这时在页面中页用时需要通过四个script脚本引入编译好的js文件.
当尝试中nodejs中执行node test.js命令时会得到一个错误,因为我们没有把相关联的文件合并到test.js中,.需要通过下面语句整合三部分到一个文件中,在运行.

tsc --out sample.js test.ts
node sample.js

tsc --out会自动检查reference注释关联的代码,并按顺序输出到一个文件中.我们也可以手动指定

tsc --out sample.js validation.ts lettersValidator.ts zipCodeValidator.ts test.ts

在网页中可以通过script标签<script src="sample.js" type="text/javascript" />引用.

外部模块

外部模块是与上面相对的,上面所说的不论怎么分文件,最好使用时还是合并成一个文件.像java c#等语言想要引入其它类文件时,都是使用import进行引入.这种方式在typescript中也是支持的.如nodejs中使用commonjs规范加载模块,js中有amd规范等.

在TypeScript中,只要在顶层代码中上包含有一个exportimport,都被认为是一个外部模块.

引用一个外部模块将使用var moduleName = import('modulePath')来代替/// <reference path="xxx" />标签,

要想编译一个外部模块,在编译时需要加入--module参数,如:

tsc --module commonjs Test.ts

TypeScript支持导出为’commonjs’,’amd’,’system’,’umd’等模块规范,使用时需时用import导入模块.

validator.ts

interface StringValidator{
    isAcceptable(s:string):boolean;
}

export {StringValidator}; 
/* //也可以这么导出
export interface StringValidator{
    isAcceptable(s:string):boolean;
}
*/

zipCodeValidator.ts

import { StringValidator } from './validator';
export class ZipCodeValidator implements StringValidator{
    zipRegexp:RegExp = /^\d{6}$/;

    isAcceptable(zip:string){
        return this.zipRegexp.test(zip);
    }
}

/*
//也可以这么导入
import valid = require('./validator');
export class ZipCodeValidator implements valid.StringValidator{
    zipRegexp:RegExp = /^\d{6}$/;

    isAcceptable(zip:string){
        return this.zipRegexp.test(zip);
    }
}
*/

lettersValidator.ts

import { StringValidator } from './validator';
import valid = require('./validator');
export class LettersValidator implements StringValidator{
    lettersRegexp:RegExp = /^[a-zA-Z]+$/;

    isAcceptable(letters:string){
        return this.lettersRegexp.test(letters);
    }
}

test.ts

import { ZipCodeValidator } from './zipCodeValidator';
import { LettersValidator } from './lettersValidator';


var str = ['150000', 'hello', '一二三'];

var zipCodeVali = new ZipCodeValidator();
var lettersVali = new LettersValidator();

str.forEach(s=>{
    if(zipCodeVali.isAcceptable(s)){
        console.log(s+' 是邮政编码');
    }else if(lettersVali.isAcceptable(s)){
        console.log(s+' 是字母');
    }else{
        console.log(s+' 不知道是什么');
    }
});
tsc --module commonjs test.ts
node test.js
Failed to load 'F:\吴亮\TypeScript\build\webpack.config.js' config [webpack-cli] Error: Cannot find module 'clean-webpack-plugin' Require stack: - F:\吴亮\TypeScript\build\webpack.pro.config.js - F:\吴亮\TypeScript\build\webpack.config.js - F:\吴亮\TypeScript\node_modules\webpack-cli\lib\webpack-cli.js - F:\吴亮\TypeScript\node_modules\webpack-cli\lib\bootstrap.js - F:\吴亮\TypeScript\node_modules\webpack-cli\bin\cli.js - C:\Program Files\nodejs\node_global\node_modules\webpack-cli\node_modules\import-local\index.js - C:\Program Files\nodejs\node_global\node_modules\webpack-cli\bin\cli.js - C:\Program Files\nodejs\node_global\node_modules\webpack-dev-server\bin\webpack-dev-server.js at Function.Module._resolveFilename (internal/modules/cjs/loader.js:902:15) at Function.Module._load (internal/modules/cjs/loader.js:746:27) at Module.require (internal/modules/cjs/loader.js:974:19) at require (internal/modules/cjs/helpers.js:93:18) at Object.<anonymous> (F:\吴亮\TypeScript\build\webpack.pro.config.js:1:32) at Module._compile (internal/modules/cjs/loader.js:1085:14) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10) at Module.load (internal/modules/cjs/loader.js:950:32) at Function.Module._load (internal/modules/cjs/loader.js:790:12) at Module.require (internal/modules/cjs/loader.js:974:19) { code: 'MODULE_NOT_FOUND', requireStack: [ 'F:\\吴亮\\TypeScript\\build\\webpack.pro.config.js', 'F:\\吴亮\\TypeScript\\build\\webpack.config.js', 'F:\\吴亮\\TypeScript\\node_modules\\webpack-cli\\lib\\webpack-cli.js', 'F:\\吴亮\\TypeScript\\node_modules\\webpack-cli\\lib\\bootstrap.js', 'F:\\吴亮\\TypeScript\\node_modules\\webpack-cli\\bin\\cli.js', 'C:\\Program Files\\nodejs\\node_global\\node_modules\\webpack-cli\\node_modules\\import-local\\index.js', 'C:\\Program Files\\nodejs\\node_global\\node_modules\\webpack-cli\\bin\\cli.js', 'C:\\Program Files\\nodejs\\node_global\\node_modules\\webpack-dev-server\\bin\\webpack-dev-server.js' ] }
07-20
正在执行任务: npm run test:jest > origin@0.1.1 test:jest > VUE_APP_SSO_ENV=test jest Browserslist: caniuse-lite is outdated. Please run: npx update-browserslist-db@latest Why you should do it regularly: https://github.com/browserslist/update-db#readme Browserslist: caniuse-lite is outdated. Please run: npx update-browserslist-db@latest Why you should do it regularly: https://github.com/browserslist/update-db#readme Browserslist: caniuse-lite is outdated. Please run: npx update-browserslist-db@latest Why you should do it regularly: https://github.com/browserslist/update-db#readme Browserslist: caniuse-lite is outdated. Please run: npx update-browserslist-db@latest Why you should do it regularly: https://github.com/browserslist/update-db#readme FAIL tests/integration/kpiManage/editKpi/EditStandard.spec.ts ● Test suite failed to run Jest encountered an unexpected token Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax. Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration. By default "node_modules" folder is ignored by transformers. Here's what you can do: • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it. • If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config. • If you need a custom transformation specify a "transform" option in your config. • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option. You'll find more details and examples of these config options in the docs: https://jestjs.io/docs/configuration For information about custom transformations, see: https://jestjs.io/docs/code-transformation Details: SyntaxError: Unexpected token (25:23) at Parser.pp$4.raise (node_modules/vue-template-es2015-compiler/buble.js:2757:13) at Parser.pp.unexpected (node_modules/vue-template-es2015-compiler/buble.js:647:8) at Parser.pp$3.parseExprAtom (node_modules/vue-template-es2015-compiler/buble.js:2196:10) at Parser.<anonymous> (node_modules/vue-template-es2015-compiler/buble.js:6003:24) at Parser.parseExprAtom (node_modules/vue-template-es2015-compiler/buble.js:6129:31) at Parser.pp$3.parseExprSubscripts (node_modules/vue-template-es2015-compiler/buble.js:2047:19) at Parser.pp$3.parseMaybeUnary (node_modules/vue-template-es2015-compiler/buble.js:2024:17) at Parser.pp$3.parseExprOps (node_modules/vue-template-es2015-compiler/buble.js:1966:19) at Parser.pp$3.parseMaybeConditional (node_modules/vue-template-es2015-compiler/buble.js:1949:19) at Parser.pp$3.parseMaybeAssign (node_modules/vue-template-es2015-compiler/buble.js:1925:19) at Parser.pp$3.parseMaybeConditional (node_modules/vue-template-es2015-compiler/buble.js:1954:28) at Parser.pp$3.parseMaybeAssign (node_modules/vue-template-es2015-compiler/buble.js:1925:19) at Parser.pp$3.parseExprList (node_modules/vue-template-es2015-compiler/buble.js:2663:20) at Parser.pp$3.parseSubscripts (node_modules/vue-template-es2015-compiler/buble.js:2075:29) at Parser.pp$3.parseExprSubscripts (node_modules/vue-template-es2015-compiler/buble.js:2050:21) at Parser.pp$3.parseMaybeUnary (node_modules/vue-template-es2015-compiler/buble.js:2024:17) at Parser.pp$3.parseExprOp (node_modules/vue-template-es2015-compiler/buble.js:1985:41) at Parser.pp$3.parseExprOps (node_modules/vue-template-es2015-compiler/buble.js:1968:91) at Parser.pp$3.parseMaybeConditional (node_modules/vue-template-es2015-compiler/buble.js:1949:19) at Parser.pp$3.parseMaybeAssign (node_modules/vue-template-es2015-compiler/buble.js:1925:19) at Parser.pp$3.parseExprList (node_modules/vue-template-es2015-compiler/buble.js:2663:20) at Parser.pp$3.parseSubscripts (node_modules/vue-template-es2015-compiler/buble.js:2075:29) at Parser.pp$3.parseExprSubscripts (node_modules/vue-template-es2015-compiler/buble.js:2050:21) at Parser.pp$3.parseMaybeUnary (node_modules/vue-template-es2015-compiler/buble.js:2024:17) at Parser.pp$3.parseExprOps (node_modules/vue-template-es2015-compiler/buble.js:1966:19) at Parser.pp$3.parseMaybeConditional (node_modules/vue-template-es2015-compiler/buble.js:1949:19) at Parser.pp$3.parseMaybeAssign (node_modules/vue-template-es2015-compiler/buble.js:1925:19) at Parser.pp$3.parseExprList (node_modules/vue-template-es2015-compiler/buble.js:2663:20) at Parser.pp$3.parseExprAtom (node_modules/vue-template-es2015-compiler/buble.js:2175:26) at Parser.<anonymous> (node_modules/vue-template-es2015-compiler/buble.js:6003:24) at Parser.parseExprAtom (node_modules/vue-template-es2015-compiler/buble.js:6129:31) at Parser.pp$3.parseExprSubscripts (node_modules/vue-template-es2015-compiler/buble.js:2047:19) at Parser.pp$3.parseMaybeUnary (node_modules/vue-template-es2015-compiler/buble.js:2024:17) at Parser.pp$3.parseExprOps (node_modules/vue-template-es2015-compiler/buble.js:1966:19) at Parser.pp$3.parseMaybeConditional (node_modules/vue-template-es2015-compiler/buble.js:1949:19) at Parser.pp$3.parseMaybeAssign (node_modules/vue-template-es2015-compiler/buble.js:1925:19) at Parser.pp$3.parseExpression (node_modules/vue-template-es2015-compiler/buble.js:1896:19) at Parser.pp$1.parseReturnStatement (node_modules/vue-template-es2015-compiler/buble.js:946:31) at Parser.pp$1.parseStatement (node_modules/vue-template-es2015-compiler/buble.js:781:35) at Parser.parseStatement (node_modules/vue-template-es2015-compiler/buble.js:6116:31) at Parser.pp$1.parseBlock (node_modules/vue-template-es2015-compiler/buble.js:1112:23) at Parser.pp$3.parseFunctionBody (node_modules/vue-template-es2015-compiler/buble.js:2600:22) at Parser.pp$1.parseFunction (node_modules/vue-template-es2015-compiler/buble.js:1219:8) at Parser.pp$3.parseExprAtom (node_modules/vue-template-es2015-compiler/buble.js:2184:17) at Parser.<anonymous> (node_modules/vue-template-es2015-compiler/buble.js:6003:24) at Parser.parseExprAtom (node_modules/vue-template-es2015-compiler/buble.js:6129:31) at Parser.pp$3.parseExprSubscripts (node_modules/vue-template-es2015-compiler/buble.js:2047:19) at Parser.pp$3.parseMaybeUnary (node_modules/vue-template-es2015-compiler/buble.js:2024:17) at Parser.pp$3.parseExprOps (node_modules/vue-template-es2015-compiler/buble.js:1966:19) at Parser.pp$3.parseMaybeConditional (node_modules/vue-template-es2015-compiler/buble.js:1949:19) FAIL tests/integration/kpiManage/createKpi/CreateStandard.spec.ts ● Test suite failed to run Jest encountered an unexpected token Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax. Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration. By default "node_modules" folder is ignored by transformers. Here's what you can do: • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it. • If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config. • If you need a custom transformation specify a "transform" option in your config. • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option. You'll find more details and examples of these config options in the docs: https://jestjs.io/docs/configuration For information about custom transformations, see: https://jestjs.io/docs/code-transformation Details: SyntaxError: Unexpected token (25:23) at Parser.pp$4.raise (node_modules/vue-template-es2015-compiler/buble.js:2757:13) at Parser.pp.unexpected (node_modules/vue-template-es2015-compiler/buble.js:647:8) at Parser.pp$3.parseExprAtom (node_modules/vue-template-es2015-compiler/buble.js:2196:10) at Parser.<anonymous> (node_modules/vue-template-es2015-compiler/buble.js:6003:24) at Parser.parseExprAtom (node_modules/vue-template-es2015-compiler/buble.js:6129:31) at Parser.pp$3.parseExprSubscripts (node_modules/vue-template-es2015-compiler/buble.js:2047:19) at Parser.pp$3.parseMaybeUnary (node_modules/vue-template-es2015-compiler/buble.js:2024:17) at Parser.pp$3.parseExprOps (node_modules/vue-template-es2015-compiler/buble.js:1966:19) at Parser.pp$3.parseMaybeConditional (node_modules/vue-template-es2015-compiler/buble.js:1949:19) at Parser.pp$3.parseMaybeAssign (node_modules/vue-template-es2015-compiler/buble.js:1925:19) at Parser.pp$3.parseMaybeConditional (node_modules/vue-template-es2015-compiler/buble.js:1954:28) at Parser.pp$3.parseMaybeAssign (node_modules/vue-template-es2015-compiler/buble.js:1925:19) at Parser.pp$3.parseExprList (node_modules/vue-template-es2015-compiler/buble.js:2663:20) at Parser.pp$3.parseSubscripts (node_modules/vue-template-es2015-compiler/buble.js:2075:29) at Parser.pp$3.parseExprSubscripts (node_modules/vue-template-es2015-compiler/buble.js:2050:21) at Parser.pp$3.parseMaybeUnary (node_modules/vue-template-es2015-compiler/buble.js:2024:17) at Parser.pp$3.parseExprOp (node_modules/vue-template-es2015-compiler/buble.js:1985:41) at Parser.pp$3.parseExprOps (node_modules/vue-template-es2015-compiler/buble.js:1968:91) at Parser.pp$3.parseMaybeConditional (node_modules/vue-template-es2015-compiler/buble.js:1949:19) at Parser.pp$3.parseMaybeAssign (node_modules/vue-template-es2015-compiler/buble.js:1925:19) at Parser.pp$3.parseExprList (node_modules/vue-template-es2015-compiler/buble.js:2663:20) at Parser.pp$3.parseSubscripts (node_modules/vue-template-es2015-compiler/buble.js:2075:29) at Parser.pp$3.parseExprSubscripts (node_modules/vue-template-es2015-compiler/buble.js:2050:21) at Parser.pp$3.parseMaybeUnary (node_modules/vue-template-es2015-compiler/buble.js:2024:17) at Parser.pp$3.parseExprOps (node_modules/vue-template-es2015-compiler/buble.js:1966:19) at Parser.pp$3.parseMaybeConditional (node_modules/vue-template-es2015-compiler/buble.js:1949:19) at Parser.pp$3.parseMaybeAssign (node_modules/vue-template-es2015-compiler/buble.js:1925:19) at Parser.pp$3.parseExprList (node_modules/vue-template-es2015-compiler/buble.js:2663:20) at Parser.pp$3.parseExprAtom (node_modules/vue-template-es2015-compiler/buble.js:2175:26) at Parser.<anonymous> (node_modules/vue-template-es2015-compiler/buble.js:6003:24) at Parser.parseExprAtom (node_modules/vue-template-es2015-compiler/buble.js:6129:31) at Parser.pp$3.parseExprSubscripts (node_modules/vue-template-es2015-compiler/buble.js:2047:19) at Parser.pp$3.parseMaybeUnary (node_modules/vue-template-es2015-compiler/buble.js:2024:17) at Parser.pp$3.parseExprOps (node_modules/vue-template-es2015-compiler/buble.js:1966:19) at Parser.pp$3.parseMaybeConditional (node_modules/vue-template-es2015-compiler/buble.js:1949:19) at Parser.pp$3.parseMaybeAssign (node_modules/vue-template-es2015-compiler/buble.js:1925:19) at Parser.pp$3.parseExpression (node_modules/vue-template-es2015-compiler/buble.js:1896:19) at Parser.pp$1.parseReturnStatement (node_modules/vue-template-es2015-compiler/buble.js:946:31) at Parser.pp$1.parseStatement (node_modules/vue-template-es2015-compiler/buble.js:781:35) at Parser.parseStatement (node_modules/vue-template-es2015-compiler/buble.js:6116:31) at Parser.pp$1.parseBlock (node_modules/vue-template-es2015-compiler/buble.js:1112:23) at Parser.pp$3.parseFunctionBody (node_modules/vue-template-es2015-compiler/buble.js:2600:22) at Parser.pp$1.parseFunction (node_modules/vue-template-es2015-compiler/buble.js:1219:8) at Parser.pp$3.parseExprAtom (node_modules/vue-template-es2015-compiler/buble.js:2184:17) at Parser.<anonymous> (node_modules/vue-template-es2015-compiler/buble.js:6003:24) at Parser.parseExprAtom (node_modules/vue-template-es2015-compiler/buble.js:6129:31) at Parser.pp$3.parseExprSubscripts (node_modules/vue-template-es2015-compiler/buble.js:2047:19) at Parser.pp$3.parseMaybeUnary (node_modules/vue-template-es2015-compiler/buble.js:2024:17) at Parser.pp$3.parseExprOps (node_modules/vue-template-es2015-compiler/buble.js:1966:19) at Parser.pp$3.parseMaybeConditional (node_modules/vue-template-es2015-compiler/buble.js:1949:19) Test Suites: 2 failed, 2 total Tests: 0 total Snapshots: 0 total Time: 9.502 s Ran all test suites. npm error Lifecycle script `test:jest` failed with error: npm error code 1 npm error path /Users/tianyinfeng/Desktop/自动化测试/data_metrics_store/packages/origin npm error workspace origin@0.1.1 npm error location /Users/tianyinfeng/Desktop/自动化测试/data_metrics_store/packages/origin npm error command failed npm error command sh -c VUE_APP_SSO_ENV=test jest * 终端进程“/bin/zsh '-l', '-c', 'npm run test:jest'”已终止,退出代码: 1。 * 终端将被任务重用,按任意键关闭。 执行test时,这是什么问题,怎么解决
08-07
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值