nodejs&es6

Node.js & ES6

1. Nodejs概述

目标:Node.js用途;安装Node.js

小结

1) nodejs 安装
2) 验证
	>  node -v 
3) 什么是NodeJS(理解)

javaEE 可以使用Request,Respose **“接受参数处理业务逻辑,返回数据”**等操作,所以我们可以把我们写的java代码部署在tomcat 上运行,变成一个服务器

javascript 语言 是不是也能 接受参数,处理业务逻辑返回数据,如果能有一个软件能部署我们的 js 代码,那么我们是不是就可以使用js 写服务器端代码呢?

Nodejs 就是基于 Google 浏览器 的V8 引擎 开发的 平台(类似于tomcat ) 能够部署我们使用 js 写的服务器代码

有了这个技术,前端开发人员也可以写服务器端代码了.

Node.js是一个可以在js中接收和处理web请求的应用平台。

clipboard.png

左边是JAVA的运行环境,右边是NODE的运行环境

扩展阅读:

话说有个叫Ryan Dahl的外国人,他的工作是用C/C++写高性能Web服务。开发web服务器对于高性能,异步IO、事件驱动是最基本的要求,但是做到这一点用C/C++写就太痛苦了。于是这位仁兄开始设想用高级语言开发Web服务。他评估了很多种高级语言,发现很多语言虽然同时提供了同步IO和异步IO,但是开发人员一旦用了同步IO,他们就再也懒得写异步IO了,所以,最终,Ryan瞄向了JavaScript。(可能有童鞋会问为啥不选择java,因为java 公认的性能低,所以被抛弃了) 
	
	因为JavaScript是单线程执行,根本不能进行同步IO操作,所以,JavaScript的这一“缺陷”导致了它只能使用异步IO。

选定了开发语言,还要有运行时引擎。这位仁兄曾考虑过自己写一个,不过明智地放弃了,因为V8就是开源的JavaScript引擎。让Google投资去优化V8,咱只负责改造一下拿来用,还不用付钱,这个买卖很划算。

于是在2009年,Ryan正式推出了基于JavaScript语言和V8引擎的开源Web服务器项目,命名为Node.js。虽然名字很土,但是,Node第一次把JavaScript带入到后端服务器开发,加上世界上已经有无数的JavaScript开发人员,所以Node一下子就火了起来。

在Node上运行的JavaScript相比其他后端开发语言有何优势?

最大的优势是借助JavaScript天生的事件驱动机制加V8高性能引擎,使编写高性能Web服务轻而易举。

2. Nodejs模块化编程

目标**:编写模块文件使用require引入模块后使用node.js执行

小结1:

node demo1.js
证明 node 可以解析并运行我们的js文件就和 java First.Hello执行java文件一样

小结2

默认 每一个JS 文件中的 变量函数是私有的,为了能够像其他语言一样能够重复调用代码(类似java)
Node 引入了 模块的概念(类似于java 中的 "类")
1) 一个 JavaScript 文件就是一个模块
2) 模块天生就是一个私有的作用域,默认模块内定义的变量等成员只能被模块内部访问,(这里的意思是不写exports 外部文件无法访问)
3) 每一个模块中都有一个 module.exports 编程接口对象,默认是一个空对象,可以通过给 module.exports 编程接口对象添加成员向外暴露内部成员
add=function(a, b){
    return a+b;
}
//默认 module.exports={};
// 我们可以往  exports 属性中增加属性

module.exports.add=add;
// 也可以使用简写方式
exports.add=add;

---------
    var demo= require('./demo3_1'); 这里是文件名称

一句话总结:

​ 可以使用exports将js方法导出,并使用require引入对应的js模块,然后再引入之后可以使用对应的方法。

注意:

在 node js中 默认 有一行配置
var exports = module.exports;
于是我们可以直接在 exports 对象上添加方法,表示对外输出的接口,如同在module.exports上添加一样。
例如 exports.add=funciton(){…}

注意,不能直接将exports变量指向一个值,因为这样等于切断了exports与module.exports的联系。
例如: exports = {}
此时 exports 被覆盖 exports!= module.exports 了,和module.exports 没有关系了,之后对 exports 的所有操作都和 module.exports 没有关系了

3. 创建Nodejs Web服务器

目标:引入http模块监听8888端口实现输出字符

小结

可以利用node.js创建web服务器:

//引入node.js内置http模块
var http = require("http");

//创建并监听web服务器
http.createServer(function (request, response) {

    //发送http头部
    //参数1:响应状态码,200表示成功
    //参数2:响应头部信息,Content-Type内容类型:纯文本
    response.writeHead(200, {"Content-Type": "text/plain"});

    //发送响应数据
    response.end("Hello World \n");
}).listen(8888);
console.log("服务器运行在 http://127.0.0.1:8888 ");

写入JSON的做法:

    response.writeHead(200, {"Content-Type": "application/json"});
    var emp=new Object;
    emp.name="Atila";
    emp.age=39;
    //JSON.stringify用于将对象转成JSON文本,JSON.parse用于将JSON文本转成对象
    var retval=JSON.stringify(emp);

    response.write(retval)

4. 处理Nodejs Web请求参数

目标:引入http和url模块创建web容器并使用url解析请求路径中参数且输出

分析

需求:http://127.0.0.1:8888?id=123&name=heima 获取到请求路径中参数及值并输出

实现步骤:

  1. 创建web服务器;
  2. 引入url模块;
  3. 利用url解析请求地址中的参数和值并输出
  4. 启动测试;

小结

在node.js中可以引入url内置模块对请求地址进行处理:

//引入node.js内置http模块
var http = require("http");
var url = require("url");

//创建并监听web服务器
http.createServer(function (request, response) {

    //发送http头部
    //参数1:响应状态码,200表示成功
    //参数2:响应头部信息,Content-Type内容类型:纯文本
    response.writeHead(200, {"Content-Type": "text/plain"});

    //解析请求地址
    //参数1:请求地址
    //参数2:true的话使用query解析参数到一个对象,默认false
    var params = url.parse(request.url, true).query;
    for(var key in params){
        response.write( key + " = " + params[key]);
        response.write("\n");
    }

    //发送响应数据
    response.end("");
}).listen(8888);
console.log("服务器运行在 http://127.0.0.1:8888 ");

扩展:

使用express响应请求:

const express = require('express')
const app = express()

app.get('/', (req, res) => res.send('Hello World!'))

app.get('/user', (req, res) => {
    var user = new Object
    user.name = 'zhangsan'
    user.age = 18
    res.setHeader('Content-Type','application/json')
    res.send(JSON.stringify(user))
})

app.listen(3000, () => console.log('Example app listening on port 3000!'))

5. 包资源管理器NPM

目标:说出npm的作用;区别本地安装和全局安装

小结1:

 1) npm 类似于我们java maven  , 我们可以使用npm 安装前端框架需要的 工具包(其实是一堆js 文件)
 2) npm init命令用于生成node.js的包管理环境

小结2:

npm intstall 根据package.json下载对应的包到node_modules目录

npm intstall express 下载对应的包到node_modules目录,修改package.json在依赖项中添加express

npm uninstall express

版本号定义(了解):

版本由三部分组成:X,Y,Z,分别是主要版本,次要版本和补丁版本。

例如:1.2.3,主要版本1,次要版本2,补丁3。

补丁中的更改表示不会破坏任何内容的错误修复。 次要版本的更改表示不会破坏任何内容的新功能。 主要版本的更改代表了一个破坏兼容性的大变化。 如果用户不适应主要版本更改,则内容将无法正常工作。

指定版本:比如1.2.2,遵循“大版本.次要版本.小版本”的格式规定,安装时只安装指定版本。
波浪号(tilde)+指定版本:比如~1.2.2,表示安装1.2.x的最新版本(不低于1.2.2),但是不安装1.3.x,也就
是说安装时不改变大版本号和次要版本号。
插入号(caret)+指定版本:比如ˆ1.2.2,表示安装1.x.x的最新版本(不低于1.2.2),但是不安装2.x.x,也就
是说安装时不改变大版本号。需要注意的是,如果大版本号为0,则插入号的行为与波浪号相同,这是因为此时处于开发
阶段,即使是次要版本号变动,也可能带来程序的不兼容。
latest:安装最新版本。

package.json,package-lock.json 这两个文件相当于我们 maven 的pom 文件
如果我们把刚才下载依赖的删除,我们可以再次使用 "npm install" 命令下载 
此时的下载规则是:
	package.json文件记录你项目中所需要的所有模块。
	package-lock.json文件锁定所有模块的版本号,包括主模块和所有依赖子模块。
	当你执行npm install的时候,node从package.json文件读取模块名称,从package-lock.json文件中获取版本号,然后进行下载或者更新。 

小结3:

npm是一个node.js的管理和分发工具,可以根据配置package.json下载js库。

  • 本地安装:将下载的模块安装到当前目录(项目)./node_modules
  • 全局安装:将下载的模块安装到全局的目录( npm root -g 查看全局路径)

小结4:

全局安装和本地安装的区别:

全局安装完毕后 任意目录下都可以使用安装的命令
	全局安装一般只安装全局的配置文件例如 cnpm 等工具,安装完,任意地方都可以使用,像项目中全局安装jquery ,实际项目中并不能直接使用。一般安装在全局的都是有命令行需求的 package,比如前边说的 bower,你需要在命令行中使用 bower 这个命令。

局部安装只当前项目有效

查询npm包的网站:

https://www.npmjs.com/

6. 切换NPM镜像

目标:安装nrm组件将npm的镜像切换为淘宝镜像

小结

  • 使用nrm 切换镜像源
npm install nrm -g --registry=https://registry.npm.taobao.org

nrm use taobao
  • 使用cnpm下载
npm install -g cnpm --registry=https://registry.npm.taobao.org

cnpm install **

补充说明:

npm install 和 npm install --save 的区别 npm install --save-dev

https://www.jianshu.com/p/442cae168455

    1)npm install  默认是 npm install --save 
    2)执行 npm i express --save则表示我想在生产环境中使用express, 同时, express的版本信息会被写入package.json中的dependencies属性中.
    3) 而执行npm i express --save-dev 表示我想在开发和测试环境中使用. express的版本信息会被写入package.json中的devDependencies属性中.

npm 和cnpm 的区别

cnmp其实就是别名,在npm的命令后添加了–registry=https://registry.npm.taobao.org,指定从淘宝的镜像站上下载包

1) cnpm install  不支持  package_lock.json 文件 即,上一个小结中的  package_lock.json  文件不会被生成,即使手动创建了也不会被使用
2) cnpm install 默认是  npm install --no-save 
    npm install  默认是   npm install --save 

7. webpack概述

目标:webpack的作用是什么并安装webpack

为何需要webpack 打包

一个HTML中有时候我们需要引入很多的js 文件

为了未来方便写代码,我们可以将多个js 文件打包,

  1. 减少http请求的数量(文件多了请求建立就会很多)

  2. 文件打包后一般压缩,大小会变得更小,节约流量

  3. 对文件进行混淆,加大了别的开发人员山寨我们功能的难度

小结

webpack作用:可以将多个静态资源js、css等打包成一个js文件。

安装命令如下:

npm install webpack -g
npm install webpack-cli -g

“脚手架” ==== maven 的"骨架"

最近经常看到“脚手架”这个词,这个就是一个比喻,比喻项目开发的前期工作。每次开始一个项目,你得先把整个文件目录结构搭好把,把必要的文件先建好。这个过程每次新建项目都需要,其实这个就是脚手架 该干的活。再说明白点,这个脚手架就是个项目模板,有了他能给我省很多事。就像在后台创建Maven项目时,可以直接Create from archetype,这个 archetype就是个原型或者说是模板,跟这个脚手架差不多

"webpack 脚手架"及 webpack-cli ,这是一个帮助我们快速构建前端项目的 “骨架”

当然webpack 的另一个作用就是 把我们写好的项目中的静态资源统一打包成一个文件

8. webpack打包js

目标:创建2个js文件,使用webpack命令打包js文件到 dist/bundle.js 文件中并测试。

分析

实现步骤:

  1. 创建2个js文件;
  2. 创建入口文件main.js;
  3. 创建webpack的配置文件;
  4. 运行webpack命令;
  5. 创建index.html页面进行测试

小结

webpack打包js的配置文件:

var path = require("path");

module.exports = {
    //入口文件
    entry: "./src/main.js",
    output: {
        //路径,__dirname是nodejs中的全局变量,代表当前目录的路径
        //path.resolve可以拼接路径 
        //filename:生成文件的名称
        path: path.resolve(__dirname, "./dist"),
        filename: "bundle.js"
    }
}

webpack可以将多个js文件打包成一个js

path.join(__dirname, './02art-template.js');
// 'C:\Users\xiadong\Desktop\Node_study\Node\5.path\02.art-template.js'

9. webpack打包css

目标:安装style-loader css-loader组件,创建并使用css文件,使用webpack命令打包js文件到 dist/bundle.js 文件中并测试。

分析

实现步骤:

  1. 安装转换css的组件;

    这些组件只需要在开发环境使用,所以加上–save-dev参数

    cnpm install style-loader css-loader --save-dev
    cnpm install less less-loader --save-dev
    
  2. 修改配置文件;

  3. 创建css文件;

  4. 修改入口文件,加载css文件;

  5. 打包并测试

小结

webpack打包css文件需要安装转换的组件,并修改配置文件:

var path = require("path");

module.exports = {
    //入口文件
    entry: "./src/main.js",
    output: {
        //路径
        path: path.resolve(__dirname, "./dist"),
        filename: "bundle.js"
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ["style-loader", "css-loader"]
            }
        ]
    }
}

10. ES6概述&创建测试工程

目标:了解es6应用场景;创建测试工程

js 的发展历史
https://www.w3school.com.cn/js/pro_js_history.asp
ECMAScript是JavaScript的规范,规定了js 应该有哪些功能
ECMAScript 也和我们的java 语言一样,不断的在改进语法
2015年发布了第6 个重大版本,我们叫做 ES6, 其中出现了一些新的特性正在被开发人员大量使用

ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准。因为当前版本的ES6是在2015年发布的,所以又称ECMAScript 2015

img

小结

ECMAScript是前端js的语法规范;可以应用在各种js环境中。如:浏览器或者node.js环境。

它有很多版本:es1/2/3/5/6,很多新特性,可以在js环境中使用这些新特性。

目前各大浏览器基本上都支持 ES6 的新特性,其中 Chrome 和 Firefox 浏览器对 ES6 新特性最友好,IE7~11 基本不支持 ES6。

以下是各大浏览器支持情况及开始时间:

Chrome 58Edge 14Firefox 54Safari 10Opera 55
2017 年 1 月2016 年 8 月2017 年 3 月2016 年 7 月2018 年 8 月

ECMAScript是前端js的语法规范;可以应用在各种js环境中。如:浏览器或者node.js环境。

它有很多版本:es1/2/3/5/6,很多新特性,可以在js环境中使用这些新特性。

备注: js 没有 4.xx 的版本 ,因为4 的版本开发阶段提出了很多超前思想,然后官方开了个会决定放弃这个版本的发布
  实际上 4.x 的版本的很多特性被ES6 实现了

Idea 默认只支持ES5 语法,需要设置ES6
在这里插入图片描述

11. let和const命令及模板字符串

目标:使用let和const定义变量说出区别;模板字符串的好处以及对象初始化简写

小结

  • let所声明的变量只能在let命令所在的代码块中使用

     for (let i = 0; i < 5; i++) {
           console.log(i)
      }
       //console.log("let 循环外:" + i);
    
  • const声明的变量是常量,也就是值不可以改变

    const a = 1
    a = 2 //报错,const不可被修改
    
  • 模板字符串:可以不用通过反斜杠拼接多行字符,直接使用`

    //es5
    var name = 'itcast'
    console.log('hello ' + name)
    //es6
    const name = 'itcast' 
    console.log(`hello ${name}`) //hello itcast 漂号,在ESC下面,可以支持换行和注入
    
    // es5
    var msg = "Hi \
    man!"
    // es6
    const template = `<div>
    <span>hello world</span>
    </div>`
    
  • 对象初始化简化:可以在返回的对象时,不用指定属性名。


        function person5(name, age) {
            return {name: name, age: age};
        }

        console.log("es5 => " + JSON.stringify(person5("heima1", 13)));

        //es6
        function person6(name, age) {
            return {name, age};
        }
 
        console.log("es6 => " + JSON.stringify(person6("heima2", 13)));


12. 解构表达式

目标:将数组、对象中的值或属性使用解构表达式设置到对应变量

小结

解构表达式:可以对数组、对象的元素或者属性按顺序、名称的方式进行赋值到对应变量中。

作用 : 使我们基于一个对象创建新变量的操作更加简单;

let arr = [1,2,3]
const [x,y,z] = arr;// x,y,z将与arr中的每个位置对应来取值
// 然后打印
console.log(x,y,z);

const person = {
    name:"jack",
    age:21,
    language: ['java','js','css']
}

// 解构表达式获取值
const {name,age,language} = person;
var name = person.name
var age = person.age
...
// 打印
console.log(name);
console.log(age);
console.log(language);

//name是属性名,name2是别名
//var name2 = person.name
const {name:name2} = person
/**
var lan3 = person.language
var obj = {
    age : person.age,
    name : person.name
}
**/
const {language:lan3,...obj} = person

13. 函数优化

目标:箭头函数的好处;对象的函数属性简写,箭头函数与解构表达式的应用

小结

箭头函数:可以不用编写基础函数的解构(如:function),直接使用 =>

        var print = function (obj) {
            console.log(obj);
        };

        print("print");
        //箭头函数
        var print2 = obj => console.log(obj);
        print2("print2");

        var sum = function (a, b) {
            return a+b;
        };
        console.log(sum(1,2));

        //箭头函数
        var sum2 = (a,b) => a+b;
        console.log(sum2(1,2));

        var sum3 = (a,b) => {
            console.log(a+b);
            return a+b;
        };
        console.log(sum3(1,2));

        let person = {
            "name": "heima",
            //不给定参数默认为java
            learn1: function (course="java") {
                console.log(this.name + " 在学习 " + course);
            },
            //箭头函数;不能使用this
            learn2: (course) => console.log(person.name + " 在学习 " + course),
            //简写
            learn3(course){
                console.log(this.name + " 在学习 " + course);
            }
        };
        person.learn1("java");
        person.learn2("js");
        person.learn3("css");

        const person = {
            name:"heima",
            age:13,
            language:["java","js","css"]
        };
         function hello(person) {
             console.log("hello " + person.name);
         }
         hello(person);

         //箭头函数、解构表达式
//  此处的{name} 意思是  未来传递的对象必须有一个name 属性,否则无法使用
      // var {name} =person
         //箭头函数、解构表达式
        var hello2 = ({name}) => console.log("hello2 " + ps.name);
        hello2(person);

14. map方法使用

目标:数组中map方法的应用场景

小结

map方法可以将原数组中的所有元素通过一个函数进行处理并放入到一个新数组中并返回该新数组。

//已知一个数组,let arr =[3,2,1] ,求数组中每个数的三次方,并放入一个新的数组
// 答:
let arr =[3,2,1]
 var ar = arr.map(function(elem){
    return elem*elem*elem;
 });
console.log(ar); // [27,8,1]

[‘3’,‘2’,‘1’ ,‘0’] => map [3,2,1,0]

15. reduce方法使用

目标:数组中reduce方法的应用场景

小结

reduce方法会从左到右依次把数组中的元素用函数处理(reduce方法的第一个参数),会返回一个执行结果。

let arr = [1, 2, 3]

arr.reduce((a,b)=>a+b)

第1次:(1,2) => 1+2

第2次:(3, 3) => 3+3 --------> 6

16. promise应用示例

目标:promise的应用场景及示例

小结

我们知道的,在 JavaScript 中,所有代码都是单线程的,也就是同步执行的。而 Promise 就为异步编程提供了一种解决方案。

function callback() {
            try {
                console.log('Done');
                const i=0;
                i=100;
            }catch (e){
                console.log("exception");
            }

        }
console.log('before setTimeout()');
setTimeout(callback, 1000); // 1秒钟后调用callback函数
console.log('after setTimeout()');
/* 问题:
 如果此时我们想让主线程知道方法调用是否成功,如果想成功了 做点事情,失败了做点事情, 传统方式是做不到的
 ES6 提供了一个新的对象供我们使用解决上述问题 promise
 */

promise是一个对象,保存着预期事件执行的结果;可以应用在异步操作时候,指定异步操作的成功与失败的结果。

        const promise = new Promise(function (resolve, reject) {
            setTimeout(()=>{
                let num = Math.random();
                if(num < 0.5){
                    resolve("操作成功!num=" + num);
                } else {
                    reject("操作失败!num=" + num);
                }
            },300);
        });

        promise.then(function (msg) {
            console.log(msg);
        }).catch(function (msg) {
            console.log(msg);
        });

17. 对象的扩展方法使用

目标:Object对象中的keys/values/assign的作用

小结

Object的方法:

  • keys:获取对象的属性名

  • values:获取对象的属性值

  • entries:获取对象的属性和属性值

  • assign:可以将对象进行拷贝

      let p2 = {"name":"jack","age":21}
            console.log(Object.keys(p2))
            console.log(Object.values(p2))
            console.log(Object.entries(p2))
            //测试覆盖问题
            let obj = {"name":"rose"}
            console.log(Object.assign(obj, p2))
            console.log(Object.assign(obj, p2, {"address":"China"}))
    
            let obj2 = {"name":{"test":"123"},"age":16}
            let obj3 = {}
            Object.assign(obj3, obj2)
            console.log(obj3)
            obj2.age = 18
            console.log("age-obj2:" +  JSON.stringify(obj2))
            console.log("age-obj3:" + JSON.stringify(obj3))
            obj2.name.test = 'ttt'
            console.log("name.test-obj2:" + JSON.stringify(obj2))
            console.log("name.test-obj3:" + JSON.stringify(obj3))
    
var person ={
           age:13,
           order:{
                id:1
                test:{
               
           		}
           }
       }
        var obj ={}
       //  obj.age=person.age;// 赋值
       Object.assign(obj,person)

       console.log(obj.age);
       console.log(obj.order.id);

       person.age=14;
       person.order.id=3;
       console.log(obj.age);
       console.log(obj.order.id); // 浅 copy
/-------------------------------------------------------
var obj =  JSON.parse(JSON.stringify(person));// 深克隆 (copy)

浅拷贝:拷贝内容如果涉及到属性是对象,并不会重新生成一个对象,只是把属性做一个引用

深拷贝:如果是属性对象,会重新生成对象,与原来的对象完全没有关系 deep clone

18. 数组的扩展方法使用

目标:数组中的find/findIndex/includes的作用

小结

数组的扩展方法:

  • find:根据函数条件查找数组中符合条件的元素
  • findIndex:与find类似,返回的不是元素而是元素对应的索引号
  • includes:判断指定的元素是否存在
  let arr5 = [1, 20, -5, 3]
        //元素能整除2的
        console.log(arr5.find(i => i%2===0))
        //查询元素能整除2的索引号
        console.log(arr5.findIndex(i => i%2===0))
        console.log(arr5.includes(3))
        console.log(arr5.includes(4))

19. 安装babel

目标:安装babel及了解babel的作用

小结

babel是一个javaScript语法编译器,可以将es6的语法进行转换并执行。

主要用于将 ECMAScript 6+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。

需要安装babel:

cnpm install babel-preset-es2015 --save-dev 
cnpm install babel-cli -g

编写.babelrc文件,指定es的版本

编写demo.js

let a = 5;
const b = 10;
let input = [1,2,3];
input.map(item => item+1);

在package.json中添加

  "scripts":{
    "build":"babel demo.js --out-file bunder.js"
  }

执行npm run build命令打包bunder.js

"use strict";

var a = 5;
var b = 10;
var input = [1, 2, 3];
input.map(function (item) {
  return item + 1;
});

20. export和import联合应用

目标:export和import的联合应用场景;使用export导出模板文件,import引入模板文件

小结

可以通过export导出模块,通过import导入模块并使用其内容。

导出有两种方式:命名导出和默认导出。

因为某些浏览器默认不支持export和import命令,所以需要安装babel对export和import进行解析和执行。

//补充 ES6 的 export和import 方法是可以在当前版本的chrome浏览器中使用的

//export.js
export let add = function(a,b){
  return a+b;
}
// test.html

<script type="module">
	import {add} from "./export.js"
	alert("3+5="+(add(3,5)));
</script>

在这里插入图片描述

babel-node import1.js
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值