当前位置: 首页 > article >正文

Nodejs基础

Nodejs.day1

01-nodejs入门


1.1-什么是nodejs
  • 1.Node.js官网地址:https://nodejs.org/en/

  • 中文网站:http://nodejs.cn/api/

  • 2.Node是一个构建Chrome V8引擎之上的一个Javascript 运行环境

    • Node一个运行环境,作用是让js拥有开发服务端的功能
  • 3…Node中的NPM是世界上最大的开源库生态系统(类似于github)

    • NMP官网:https://www.npmjs.com
1.2-Node.js环境安装
  • 1.确认电脑是否安装Node环境

    • 打开终端,输入 node -v,如果能看到版本号则说明当前电脑已经安装Node环境,如果提示Node不是内部或外部命令,则表示未安装

      一旦安装了node,则会自动一并安装`npm[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f3tyT12I-1599793552966)(C:/Users/86188/AppData/Roaming/Typora/typora-user-images/1599028279378.png)]

1.3-服务端js与客户端js区别
    1. 前端js三个部分组成
      • ECMAScript :确定js的语法规范
      • DOM :js动态操作网页内容
      • BOM :js动态操作浏览窗口
  • ​ 2.服务端nodejs 只有一个部分 ECMAScript
    • nodejs可以使用 : 比那里 数据类型 运算符 分支循环
    • nodejs不能使用 : document,window
    • 因为服务端是没有界面的
      • 在nodejs中使用dom和bom的api程序会报错

02.-ES6语法

2.1-变量声明let与const
/* 学习目标: 了解let与const声明变量特点ES6学习技巧:对比法学习路线:1. 介绍ES5声明变量var特点* a. 有变量提升--->编译器会将var声明提升到当前作业与最顶端* b. 没有块级作用域 (块级作用域:大括号里面的变量都是局部的)2. 介绍ES6声明变量let与const特点* a. 没有变量提升 : 一定要先声明后使用* b. 有块级作用域 : 只要是大括号里面声明的变量,都是局部3. 介绍let与const区别* a. let :变量。 可以重新赋值* b. const :常量。 不可以重新赋值,只能在声明的时候唯一赋值一次*//* 1.介绍ES5声明变量var特点 */// 1.1 有变量提升
// var num; 编译器会将var声明提升到当前作用域最顶端
console.log(num);
var num = 10;// 1.2 没有块级作用域 (块级作用域:大括号里面的变量都是局部的)
if(1){// var在大括号里面声明的还是全局的var num = 20;
}
console.log(num);/* 2.介绍ES6声明变量let与const特点 */// 2.1 没有变量提升
// console.log(num1);
// let num1 = 10;// 2.2 有块级作用域if(1){let num2 = 20;console.log(num2);
}
// console.log(num2);/* 3.介绍let与const区别 */
// 3.1 let :变量(可以重新赋值)  和var一样
let a = 10;
console.log(a);a = 100;
console.log(a);// 3.2 const : 常量(不可重新赋值) 只能声明的时候赋值一次
const b = 800;
console.log(b);
2.2-结构赋值
/* 1.学习目标: 对象的解构赋值解构赋值 : 变量赋值的简写形式2.对比法学习2.1 取出对象的属性  赋值给  变量let {name,age,sex} = obj;2.2 取出变量的值   赋值给 对象的属性let obj = {name,age,sex}2.3 解构赋值语法默认值let {name,age=18,sex} = obj;
*//* 1. 取出对象的属性  赋值给  变量 *///1.1 复习 以前ES5写法
let obj = {name:'张三',age:20,sex:'男'
};// let name = obj.name 
// let age = obj.age 
// let sex = obj.sex
console.log(name,age,sex);// 1.1 复习 一起ES6写法let obj1 = {name:'张三',age:20,sex:'男'
};// let name = obj.name 
// let age = obj.age 
// let sex = obj.sex
let {age,name,sex} = obj1
console.log(name,age,sex);/* 2.2 取出变量的值   赋值给 对象的属性 */let name = '李四';
let age = 22;
let sex = '女';// ES5 写法
let obj2 = {name:name,age:age,sex:sex
}console.log(obj2);// ES6 写法
let obj = {name, // === name:nameage,sex,satHi:function(){console.log(11111);},eat(){  // === eat:function(){}console.log(22222);}
}console.log(obj2);//3 解构赋值语法默认值
let obj3 = {name:'王五',sex:'男',age:0
};// let name = obj.name;
// let sex = obj.sex;/* (1)声明变量 let age(2)检查obj有没有属性要age,有则赋值。没有则赋默认值if(obj.age != undefined) { age = obj.age} else {age = 18}*/let {name,sex,age} = obj3;console.log(name,sex,age);
2.3-数组解构
/* 数组解构赋值 */
let arr = [10,20,30]/* 解构数组 */
// let n1 = arr[0]
// let n2 = arr[1]
// let n3 = arr[2]
// let n4 = arr[3]let [n1,n2,n3,n4=100] =arr;console.log(n1,n2,n3,n4);
2.4-函数解构与默认参数
/* 学习目标 :函数的结构赋值与函数默认参数学习路线 :1.复习函数传参本质: 实参给形参2.函数解构赋值3.函数默认参数
*/// 1. 复习函数传参本质:实参给形参赋值// ES5接收参数:let obj = {name:'张三',age:20}
// ES6接收参数:let{name,age,sex="男"} = {name:'张三',age:20}
// function fn ({name,age,sex="男"}){
//     //ES5 :
//     // console.log(obj); 
//     // let name = obj.name;
//     // let age = obj.age;
//     console.log(name,age,sex);
// }// let preson = { name:'张三',age:20};
// fn(preson); // 2.函数默认参数
function fn1(a=10,b=20){// ES5 : 逻辑或短路// a = a || 10;// b = b || 20;console.log(a,b);
}
fn1(100,200);
fn1();
fn1(5)
2.5-箭头函数
/* 1. 学习目标:箭头函数*就是function函数的间歇形式*(1)将function替换成箭头*(2)将形参小括号移到箭头的=>的左边* let fn = () => {}2. 学习目标2.1 一般用法2.2 其他用法* a. 如果函数只有一个形参,则可以省略形参小括号* b. 如果函数只有一行,则可以省略函数体的大括号(此时必须要省略return
*/// 1. ES5声明函数:function// 无参无返回
let fn = ()=>{console.log('5555');
}
fn();// 有参有返回
let fn1 = (a,b)=>{return a+b;
}
let res = fn1(10,20);
console.log(res);// 2.其他用法// 2.1 如果函数只有一个形参,则可以省略小括号
let fn2 = a=>{ return a*2};
let res2 = fn2(100);
console.log(res2);// 2.2 如果函数体只有一行代码,则可以省略大括号(此时必须省略return)
let fn3 = a =>a*2;
let res3 =fn3(500);
console.log(res3);
2.6-模板字符串
/* 学习目标: 模板字符串
学习技巧: 对比法1. 复习ES5学习的字符串特点:一切以 单引号 '' 或 双引号 "" 引起了的内容1.1 不能字符串里面取变量的值:需要使用连接符1.2 不能识别字符串格式 : 需要使用转义符2. ES6新增 模板字符串:一切以反引号引起来的内容 ` `2.1 可以去变量的值: ${变量名}1.2 可以识别字符串格式
*/// 1.复习ES5学习的字符串特点// 1.1 不能在字符串中去变量的值 :只能使用字符串连接符 + 
// 1.2 不能识别字符串本身格式 : 需要使用转义符
let name ="保健";let str1 = '我爱\n'
+ name;
console.log(str1);//2.ES6新增 模板字符串:一切以反引号引起来的内容 ` `
// 2.1 可以去变量的值:${变量名}
// 2.2 支持字符串格式let name2 = '学生';
let str2 = `我爱
${name2}`;console.log(str2);
2.7-拓展运算符
/* 1.拓展运算符(展开运算符) : ...2.作用 :变量对象一种简写形式(类似于for-in循环)3.应用场景:a. 连接数组b. 求最大/最小值c. 一行代码实现数组去重
*/// 1. 基本用法
let car = {pinpai:'特斯拉',price:888888
};let hourse = {address : '深圳湾一号'
}let person = {name: '维克多',age:26,...car,...hourse
}console.log(person);// 2.应用场景// 2.1 连接数组let arr1 = [10,20,30]
let arr2 = [40,50,60]// 之前:concat
let arr3 = arr1.concat(arr2);  //连接arr1 和 arr2
arr3.push(70,80,90);  //连接之后,继续添加元素
console.log(arr3);// ES6展开运算符
let arr4 = [...arr1,...arr2,70,80,90];
console.log(arr4);// 2.2 求数组最大值/最小值let arr = [23,45,11,23,45,88,66,11,88,66];// 之前:擂台思想、 Math.max.apply()
let max = Math.max.apply(Math,arr);
console.log(max); // ES6展开运算符
let min = Math.min(...arr);
console.log(min);
2.8-数据类型
/* 1.新增数据类型(集合): Set* 与数组几乎一致,惟一的区别是不支持重复元素(相当于是无法存储重复元素)*/// 声明Set : new Set(数组)
let set = new Set([10,20,30,66,77,30,20,77,10])
console.log(set);// set一般不会单独使用,只是为了去掉重复数组元素。将set转成数组
let arr =[...set];
console.log(arr);// 应用场景:数组去重
let arr1 = [21,54,67,88,54,21,67];
// 最简单的一行代码实现数组去重
let newArr = [...new Set(arr1)];
console.log(newArr);

03-babel网站:ES6转ES5

  • babel官网地址:https://www.babeljs.cn/repl
    • babel作用:把ES6语法转成ES5语法
    • 由于ES6是js的最新语法,所以有的老版本浏览器不支持。这个网站可以查询ES6支持哪些浏览器:https://www.caniuse.com/#search=es6

Nodejs.day2

01-fs文件读写

1.1-readFile读取文件

​ 1.前端框架使用流程

​ *导包 (下载) :得到一个对象,存储框架中的API

<script src=""></script>

​ *拥抱 : 调用对象方法

​ 2.后端nodejs模块使用流程

​ *导包 :得到一个对象,存储框架中的API

​ *用包 :调用对象方法

fs.readFile('./data/c.txt','utf-8',(err,data)=>{if(err){console.log(err);// 抛出异常,throw的作用就是让node程勋终止运行,方便调试throw err;}else{console.log(data);}
})console.log('111');
1.2-writefile写入文件
//1. 导入模块
const fs =  require('fs');
//2. 使用模块
/**
* @description: 写入文件
* @param {string} path : 文件路径
* @param {string} data : 要写入的数据
* @param {string} option : 文件编码 自动根据数据识别,一般不用传
* @param {function} callback : 写入回调 (err)=>{ err:错误信息 }
* @return: 
*/
fs.writeFile('./data/b.txt','临渊羡鱼不如退而结网',err=>{if( err ){throw err;}else{console.log('写入成功');}
});
1-3-同步异步
/* 学习目标:   同步与异步区别 1. 编译器执行代码流程1.1 从上往下解析代码1.2 判断代码是同步还是异步同步  :立即执行异步  :不执行,放入一个事件队列池 (EventLoop)1.3 当页面所有的同步执行完毕之后,才会执行事件队列池中的代码2. 同步与异步区别2.1 API不同 :异步有回调函数,同步一般没有* 前端有两个API是异步 : 定时器 和 ajax2.2 执行顺序不同 : 同步有序且优先执行, 异步是无序执行2.3 性能不同 : 异步性能高(不阻塞线程),同步阻塞线程
*/// nodejs中所有的api默认都是异步的,如果希望拥抱就会后面加上Sync即可
/*异步特点 1. 有回调函数2. 无序执行3. 不阻塞线程 : 性能高
*/
fs.readFile('./data/c.txt','utf8',(err,data)=>{console.log('11111');  
})/* 同步特点1. 没有回调2. 有序3. 阻塞线程:性能低
*/
let data = fs.readFileSync('./data/c.txt','utf8');
console.log('7');

02-path路径

2-1.相对路径与绝对路径
//1.导入文件模块
const fs = require('fs');
//2.读取文件
/*a.注意点: node中的相对路径: ./ 不是相对于当前文件所在路径,而是相对于执行node命名的文件夹路径在服务端开发中,一般不要使用相对路径,而使用绝对路径b.解决方案:在nodejs中,每一个js文件都有两个全局属性,它可以帮助我们获取到文件的绝对路径__filename:当前js文件所在目录的绝对路径__dirname:当前js文件的绝对路径
*/
console.log(_dirname);
console.log(_filename);
/* 2.如果想要获取当前文件夹下其他文件绝对路径,可以使用 __dirname属性来拼接 */
var path = __dirname + '/aaa.txt';
console.log(path);fs.readFile(path,'utf-8',(err,data)=>{if(err){console.log(err);//抛出异常,throw的作用就是让node程序终止运行,方便调试throw err;}else{console.log(data);};
});
2-2.path模块介绍
/* 1. 前端相对路径 ./ 与nodejs后端 相对路径  ./ 区别1.1 前端相对路径   ./ :   相对于当前文件 所在的文件夹   (固定的)1.2 nodejs相对路径 ./ :   相对于 执行node命令 所在的文件夹 (不固定)2. nodejs中文件路径一般不适应相对路径,使用绝对路径* 在node中,每一个js文件都有两个默认的全局变量* __dirname :当前js文件所在的文件夹绝对路径* __filename : 当前js文件本身的绝对路径3. path模块作用: 拼接文件路径作用和自己拼接差不多,但是容错率更高* 自动识别当前操作系统的磁盘分隔符,会自动添加* 接错功能 :如果磁盘疯符写错了,path模块会自动纠正
*/
//1.导入模块
//(1)参数: 模块路径  (2)返回值: 对象 , 存储模块所有的api
const fs = require('fs');//2.使用模块
fs.readFile( aPath ,'utf-8',function(err,data){if(err)throw err;console.log(data);
});

03-http服务器

3-1-搭建服务器
//1.导入模块
const http = require('http');//2.创建服务器
let server = http.createServer((req,res)=>{//req : 请求对象。 负责获取客户端的请求信息   res : 响应对象。 负责响应客户端数据的console.log(req.url);//了解即可: url中的中文会被进行urlencode转码console.log(decodeURI(req.url));
});//3.开启服务器
server.listen(3000,()=>{console.log('服务器开启成功');
});

Nodejs.day3

01-nodejs搭建服务器

01-1-搭建静态服务器
/* nodejs搭建静态资源服务器1. 浏览器中所有的外部资源路径,都会变成网络请求*/// 1. 导入模块
const http = request('http');
const fs = require('fs');
const path = require('path');//2.创建服务器
let server = http.createServer((req,res)=>{// (1)请求:reqconsole.log(req.url);// (2)处理if (req.url == '/') {// 读取html文件响应返回fs.readFile(path.join(__dirname,'www','index.html'),'utf8',(err,data)=>{if(err) throw err;// (3)响应:resres.end(data);});}else if (req.url == '/resource/css/index.css'){fs.readFile( path.join(__dirname,'www','resource','css','index.css'),(err,data)=>{if(err) throw err;//(3)响应:resres.end(data);});}else if( req.url == '/resource/images/01.gif'){fs.readFile( path.join(__dirname,'www','resource','images','01.gif'),(err,data)=>{if(err) throw err;//(3)响应:resres.end(data);});}else if( req.url == '/resource/images/01.jpg'){fs.readFile( path.join(__dirname,'www','resource','images','01.jpg'),(err,data)=>{if(err) throw err;//(3)响应:resres.end(data);});}else if( req.url == '/resource/video.mp4'){fs.readFile( path.join(__dirname,'www','resource','video.mp4'),(err,data)=>{if(err) throw err;//(3)响应:resres.end(data);});}else{//错误路径res.end('404 not found');}});//3.开启服务器
server.listen(3000,()=>{console.log('服务器开启成功');
});
/* nodejs搭建静态资源服务器1. 浏览器中的所有外部资源路径,都会变成网络请求2. 一般静态资源(css文件,图片,音视频)使用 url路径拼接的方式来处理*///1.导入模块
const http = require('http');
const fs = require('fs');
const path = require('path');//2.创建服务器
let server = http.createServer((req,res)=>{// (1)请求:redconsole.log(req.url);// (2)处理if( req.url == '/' ){//读取html文件响应返回fs.readFile( path.join(__dirname,'www','index.html'),(err,data)=>{if(err) throw err;//(3)响应:resres.end(data);});}else if( req.url.indexOf('/resource') == 0  ){fs.readFile( path.join(__dirname,'www',req.url),(err,data)=>{if(err) throw err;//(3)响应:resres.end(data);});}else{//错误路径res.end('404 not found');}
});//3.开启服务器
server.listen(3000,()=>{console.log('服务器开启成功');
});
  • 1.html中所有外部资源路径都会变成网络请求
    • 服务器需要根据请求路径响应返回对应的文件
  • 2.静态资源(图片、文件、音视频)一般使用路径拼接的方式来处理
    • 服务短板可以直接拼接url来响应对应资源,简化代码

02-nodejs接收get请求与post请求

02-1-nodejs接收get参数
/* 
学习目标:nodejs接受get请求参数1. 前端发送get请求:直接在url后面拼接 url?key1=value&key2=val22. nodejs接受get请求参数2.1 导入url模块2.2 调用parse()方法解析参数: url.parse( req.url,true)2.3 获取解析好的query属性: get参数
*///1.导入模块
const http = require('http');const url = require('url');//2.创建服务器
let server = http.createServer((req,res)=>{// (1)get请求采纳数直接在url后面拼接console.log(req.url);// (2)get参数解析原理是 字符串切割// 第一个参数:要切割的url字符串 // 第二个参数:布尔类型  默认false:query不处理   true:query处理成对象let obj = url.parse(req.url,true);console.log(obj);let {query} = obj;let query = obj.queryconsole.log(query);// 响应返回参数:告诉客户端解析成功 (js对象需要转json对象才可以响应返回)res.end(JSON.stringify(query));
});//3.开启服务器
server.listen(3000,()=>{console.log('服务器开启成功');
});
02-2-nodejs接收post参数
/* 
学习目标:nodejs接受post请求参数1. 前端发送post1.1 需要设置请求头1.2 请求头中发送: xhr.send(key=value)2. nodejs接受post参数2.1 给red注册data事件 (接收数据包)2.2 给red注册end事件  (数据包接受完毕)2.3 在end方法中使用querystring模块解析body
*/// 1. 导入模块
//1.导入模块
const http = require('http');// 解析get请求参数
const url = require('url');
// 解析post请求参数
const querystring = require('querystring');//2.创建服务器
let server = http.createServer((req,res)=>{console.log(req.url);/* (1)给req注册data事件客户点每发送一个数据包,这个事件就会车发一次 (触发多次)*/let postData = '';req.on('data',function (chunk){postData += chunk;})/* (2)给req注册end事件客户端每一次post所有的数据包发送完毕会执行一次*/req.on('end',function (){/* (3)处置post参数:使用querystring模块切割 请求体 */console.log(postData);let body = querystring.parse(postData);console.log(body);// 响应给客户端res.end(JSON.stringify(body))})
});//3.开启服务器
server.listen(3000,()=>{console.log('服务器开启成功');
});
02-3-get与post区别

get与post区别

  • 位置不同:
    • get是在url后面拼接(请求行)
    • post是在请求体中发送(请求体)
  • 大小不同: 一般文件上传接口都是post
    • get发送数据有大小限制(一般1MB)
    • post发送数据没有大小限制
  • 速度不同:
    • get速度比post快
  • 安全性不同:一般登录注册都是post
    • post比get安全

03-Express框架

03-1框架介绍
  • Express官网:
    • http://www.expressjs.com.cn/
    • http://expressjs.com/
    • 多去官网文档查看API,多进行尝试,熟能生巧
  • 三大核心功能
      1. 托管静态资源
        1. nodejs实现静态服务器功能在express中只需一行代码
      2. 路由
        1. Express自带路由功能,Node服务端开发变得极其简单
        2. Express支持链式语法,可以让代码看起来更加简洁
      3. 中间件
        1. Express最为核心的技术和思想,万物皆中间件
        2. 中间件虽然理解起来有点困难,但是使用起来非常方便,类似于jquery的插件一样,由于jquery库自身的功能无法满足开发需求,所以通过插件来拓展功能
03-2响应数据
/*学习目标: express响应客户端数据 (1)响应普通文本 :  res.send('字符串')(2)响应json对象 :  res.send( js对象 )(3)响应html文件 :  res.sendFile( 文件绝对路径 )(1)express框架本质不是修改原生代码,而是在原生的基础上添加方法 (给req和res原型添加成员)
(2)在express中你可以使用一切node原生语法
*///1.导入模块----相当于http
const express = require('express');//2.创建服务器
let app = express();//3.路由(接口文档) : 一个请求对应一个函数
app.get('/',(req,res)=>{//响应普通文本//原生node语法// res.end('这是首页');//express语法res.send('这是首页');});app.get('/login',(req,res)=>{//响应json : 底层会自动将js对象转成json格式字符串res.send({name:'班长',age:40});
});app.get('/list',(req,res)=>{//响应html文件: 文件绝对路径res.sendFile( `${__dirname}/list.html`);
});app.use((req,res)=>{//自定义404处理结果res.end('404');
})//4.开启服务器
app.listen(3000,()=>{console.log('服务器开启成功');   
});
03-3-静态资源
//1.导入模块
const express = require('express');//2.创建服务器
let app = express();//3.托管静态资源
/* 
(1)如果请求路径为/ , express会自动读取www下面index.html响应返回
(2)如果请求路径是以www中文件夹开头,express会自动根据url拼接路径返回
*/
app.use(express.static('www'));//4.写路由(接口文档)//5.开启服务器
app.listen(3000,()=>{console.log('服务器开启成功');})
03-4-get与post参数
//1.导入模块
const express = require('express');//2.创建服务器
let app = express();//3.托管静态资源
app.use(express.static('www'));//4.配置中间件(jq的插件) : 本质都是给req或者res原型添加成员//4.1 body-parser中间件 : 解析post参数
//(1)导入中间件
const bodyParser = require('body-parser');
//(2)使用中间件 : 给req添加body属性,存储解析好的post参数
app.use(bodyParser.urlencoded({ extended: false }));//5.写路由(接口文档)/*接收get请求参数 req.query
*/
app.get('/hero/list',(req,res)=>{console.log( req.url );//获取get请求参数 : express会自动帮我们解析get参数并且添加到req的原型query属性中console.log( req.query );res.send( req.query )});/*接收post请求参数 (1)使用body-parser插件(2)req.body
*/
app.post('/hero/add',(req,res)=>{console.log( req.url );console.log( req.body );res.send( req.body )
});//6.开启服务器
app.listen(3000,()=>{console.log('服务器开启成功');
})

04-第三方模块使用流程

适合所有第三方模块

  • 官网:https://www.npmjs.com/
  • 按照官方文档要求安装模块`npm install 模块名
  • 复制粘贴官网代码(别人作者提前写好的)
04-1-爬虫实战
  • crawler爬取网页
/* 
第三方模块使用流程1. 初始化项目 : npm init -y*** 注意点:文件夹不能有中文* 生成 package.json文件 : 记录整个项目使用了那些第三方模块以及对应的版本号2. 装包(下载): npm install 模块名* 生成 package-lock.json文件 :记录每一个包的下载地址 * 提高下一次更新速度,类似于浏览器收藏夹功能* 生成node_modules文件夹 :  第三方模块资源文件*  类似于前端的libs,专门存放第三方模块资源的3. 用包 : 官网文档CV
*///1.导入爬虫模块
var Crawler = require("crawler");
//2.创建爬虫对象 
var c = new Crawler({maxConnections : 10,// This will be called for each crawled pagecallback : function (error, res, done) {if(error){console.log(error);}else{//爬虫成功回调//将爬取到的网页DOM树赋值给jq的$ (目的:使用jq的语法来操作DOM树)var $ = res.$;// $ is Cheerio by default//a lean implementation of core jQuery designed specifically for the server// console.log($("html").text());// console.log(  $('#jSearchHeroDiv>li').html() );console.log( $('html').text() );}done();}
});//3.开始爬虫
// Queue just one URL, with default callback
c.queue('https://mc.163.com/index.html');
  • crawler爬取文件
ar Crawler = require("crawler");
var fs = require('fs');var c = new Crawler({encoding:null,jQuery:false,// set false to suppress warning message.callback:function(err, res, done){if(err){console.error(err.stack);}else{//将爬取好的文件通过fs模块写入文件fs.createWriteStream(res.options.filename).write(res.body);}done();}
});//绝大多数网站,都有反爬机制。只有小众网站没有
//浏览器可以下载,但是服务端爬虫无效。反爬:检测你这个请求是通过浏览器发出来,还是服务端(解决方案:让服务端伪装成浏览器来发这个请求)
c.queue({url:"http://upos-hz-mirrorks3u.acgvideo.com/upgcxcode/75/11/112251175/112251175-1-6.mp4?e=ig8euxZM2rNcNbdlhoNvNC8BqJIzNbfq99M=&uipk=5&nbs=1&deadline=1571396695&gen=playurl&os=ks3u&oi=2005998532&trid=a4c624adafe64ababb2a851334eaf87eh&platform=html5&upsig=2a29cd105837278e3b4c92181fe3eb59&uparams=e,uipk,nbs,deadline,gen,os,oi,trid,platform&mid=0",filename:"./video/11111.mp4",//写入文件名 默认在根目录headers:{'User-Agent': 'requests'}//让服务端伪装成客户端
});

Nodejs.day4

01-初始化npm

node指令作用
npm init -y初始化
npm i express搭建nodejs服务器
npm i body-parser解析post请求
nodemon + 当前文件名自动刷新
npm i xpress-fileupload处理文件上传
npm i mysql-ithm操作mysql数据库
  • express-fileupload文件上传中间件:npm i xpress-fileupload
    • 官网地址:https://github.com/richardgirges/express-fileupload
  • express-fileupload使用流程
    • (1)装包:npm i xpress-fileupload
    • (2)配置中间件:查阅文档example
      • 导包:const* fileUpload = require('express-fileupload');
      • 使用中间件:app.use(fileUpload());
    • (3)接受用户上传文件

02-项目思路、流程

02-1-初始化
  • 1.初始化npm:npm init -y
  • 2.安装模块:
    • express:npm i express
    • body-parser:npm i body-parser
  • 3.使用模块
02-2-项目流程
  • (1)导入模块
  • (2)创建服务器
  • (3)托管静态资源
    • 只要请求路径是以www文件夹里面的文件开头,express就会自动拼接路径返回
      • app.use(express.static('www'));
    • 只要请求路径是 / 。 express会自动读取(打开)www里面的首页index.html 返回
      • 托管服务器的static文件:存储用户图像
      • app.use(express.static(‘static’));
  • (4)配置中间件
  • (5)写路由(接口文档)
  • (6)开启服务器
02-3-nodejs处理流程
  • 请求
  • 处理
  • 响应

03-CORS跨域

  • 1.跨域 :是一个固定格式报错。(没有之一)

    • Access to XMLHttpRequest at ‘服务器url地址’ from origin ‘null’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QXsW9KHq-1599793552989)(Nodejs课程笔记/day04.assets/1571856567565.png)]
  • 2.什么是跨域?

    • ajax请求地址 与 当前页面地址 不同源 称为跨域
      • (1)ajax :浏览器只有是有ajax发送请求才会出现跨域。 href属性与scr属性不会出现跨域
      • (2)接口地址:ajax请求的url
      • (3)当前页面地址 : window.location.href
      • (4)不同源 :两个网址的 协议 || id || 端口号 不一致
  • 3.同源策略 :协议 和 id 和 端口号 一致

    • 协议:http,https,file
    • id:域名
    • 端口号:3000, 4399
  • 4.浏览器同源策略存在的理由

    • 保证电脑安全 : 当你在一个页面,使用ajax向不同源的 地址发送请求。浏览器会认为是向两个不同的服务器发送请求。浏览器为了了保证你的安全,就会拦截你的请求
  • 5.如何解决跨域限制

    第一种 :CORS

    ​ 目前的主流方案,也是最简单的方案

    ​ 简单快捷,后台只需一行代码,跟前端没有任何关系

    第二种:JSONP

    ​ 面试专用,前端需要写代码


    工作原理: 服务器 在返回响应报文的时候,在响应头中 设置一个允许的header

    res.setHeader('Access-Control-Allow-Origin', '*');

app.use((req, res, next) => {//任何请求都会进入这个use中间件res.setHeader('Access-Control-Allow-Origin', '*')//设置响应头next()//执行下一个中间件
})

express有一个自带的中间件cors,它的作用是自动给每一个res设置默认请求头

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ef59Tn3S-1599793552994)(Nodejs课程笔记/day04.assets/1571858796482.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XqgEG2b7-1599793553004)(Nodejs课程笔记/day04.assets/1571858867308.png)]今日易错点:

  • 图片路径写错 书写注意图片路径要和服务器地址一样
  • 单词书写有误,记得细心检查

Nodejs.day4

01-MySQL数据库使用

01-1-MySQL数据库介绍
  • 什么是数据库
    • 存储数据的文件夹
  • 数据库使用 ---- 增删改查
  • 安装mysql数据库软件
    • 安装phpstudy软件自带mysql数据库

02MySQL语法

  • 学习网址: https://www.w3school.com.cn/sql/index.asp
02-1增删查改语句
1. 增删改查的意思
  • 增:新增记录 insert
  • 删:删除记录 delete
  • 查:查询记录 select
  • 改:修改记录 update
02-2 增加—insert语句
  • 新增数据 用法

    **insert into 表名(字段名) value(值)**

02-3 删除—delete语句
  • 删除数据

    delete from 表名

02-4 查询—select语句
  • 查询数据

    select 字段 from 表名

  • 查询所有字段

    select * from 表名

02-5 修改—update语句
  • 修改数据

    update 表名 set 字段 = 新值

  • 指定条件修改

    update student set name = “黑马一哥’ where id = 3

03-扩展知识

03-1 通配符—模糊查询

%为通配符,一般搭配like关键字使用

03-2 以什么开头

查询所有字段以关键字开头的数据

select * from 表名 where 字段 like 关键字%

03-3 以什么结尾

查询所有字段以关键字结尾的数据

select * from 表名 where 字段 like %关键字

03-4 包含什么内容

查询所有字段包含关键字的数据

select * from 表名 where 字段 like %关键字%

03-5 或者条件

使用or关键字

-- 找到student表中age小于10,或者age大于100的数据
select *from student where age < 10 or age > 100;

今日易错点

  • 粗心,依旧会有单词错误
  • 文件创建错误,每次写代码前先坚持是否准备好

Nodejs.day5

01-客户端代码

首页—index
  • 注意点:

    • 编辑按钮:使用window.location.href给编辑页面传id

      window.location.href='./update.html?id={{ v.id }}'

    • 删除按钮:

      • 用自定义属性设置id存储英雄id
      • 删除按钮是动态生成,需要进行委托事件
<!-- 模板引擎一 :英雄列表 -->
<script id="hero_list" type="text/html">{{ each heros v }}<tr><td> {{ v.name }} </td><td> <img src="{{ v.skill }}" width="100px" height="100px"> </td><td><img src="{{ v.icon }}" width="72px" height="72px"></td><td class="manager"><button  class="btn btn-success" onclick="location.href='./update.html?id={{ v.id }}'">编辑🐷</button><button id={{ v.id }} class="btn btn-danger">删除👍</button></td></tr>{{ /each  }}</script><script>// 入口函数  : 等页面加载完毕后执行$(function () {/* 查询英雄详情 */$('#searchBtn').click(function (e) {e.preventDefault();$.ajax({url: 'http://127.0.0.1:3000/hero/list',type: 'get',dataType: 'json',data: { search: $('#search').val() },success: function (backData) {$('#heroListBody').html(template('hero_list', backData))}});})/* ajax查询列表 */$('#searchBtn').click();/* 删除英雄 */$('tbody').on('click', '.btn-danger', function () {$.ajax({url: 'http://127.0.0.1:3000/hero/delete',type: 'post',dataType: 'json',data: { id: $(this).attr('id') },success: function (backData) {if (backData.code == 200) {alert('删除成功');window.location.reload();} else {alert(backData.msg);}}});})})</script>
编辑—update
<script>// 入口函数$(function(){/* 1. 获取上个页面的信息 */let id = window.location.href.split('=')[1];console.log(id);/* 2. 查询英雄详情 */$.ajax({url:'http://127.0.0.1:3000/hero/info',type:'get',dataType:'json',data:{id},success: function(backData){console.log(backData);$('#name').val(backData.data.name);$('#skill').val(backData.data.skill);$('#iconImg').attr('src',backData.data.icon);console.log(backData.data.icon);}});/* 2. ajax发送请求 *///1.给file表单元素注册onchange事件$('#icon').change(function () {//1.2 获取用户选择的图片let file = this.files[0];//1.3 将文件转为src路径let url = URL.createObjectURL(file);//1.4 将url路径赋值给img标签的src$('#iconImg').attr('src', url);});/* 3. 文件上传 */$('.btn-success').on('click',function(e){//禁用表单默认提交事件e.preventDefault();//创建FormData对象:参数是表单dom对象let fd = new FormData($('form')[0])fd.append('id',id)$.ajax({url:'http://127.0.0.1:3000/hero/update',type:'post',dataType:'json',data:fd,contentType: false,processData: false,success: function(backData){console.log(backData);if (backData.code == 200) {alert('编辑成功');window.location.href = './index.html'}else{alert('编辑失败');}}});});})</script>
新增—insert
<script>//  入口函数$(function(){/* 2. ajax发送请求 *///1.给file表单元素注册onchange事件$('#icon').change(function () {//1.2 获取用户选择的图片let file = this.files[0];//1.3 将文件转为src路径let url = URL.createObjectURL(file);//1.4 将url路径赋值给img标签的src$('#iconImg').attr('src', url);});/* 3. 文件上传 */$('.btn-insert').on('click',function(e){//禁用表单默认提交事件e.preventDefault();//创建FormData对象:参数是表单dom对象let fd = new FormData($('form')[0])$.ajax({url:'http://127.0.0.1:3000/hero/add',type:'post',dataType:'json',data:fd,contentType: false,processData: false,success: function(backData){console.log(backData);if (backData.code == 200) {alert('新增成功');window.location.href = './index.html'}else{alert('新增失败');}}});});})
</script>

02-验证码功能

  • 验证码功能思路
    • 1.服务端生成一个二进制验证码 图片文件与对应的 验证码文本
    • 2.服务端声明全局变量存储验证码文本(文本答案)
    • 3.客服端img标签发起网络请求,服务端响应验证码图片
    • 4.客户端提交注册数据,服务端处理
  • svg-captcha第三方模块:https://www.npmjs.com/search?q=svg-captcha

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yZIr1AcW-1599793553007)(课程资料/上课资料–imgs/验证码node安装与使用.png)]

验证码工作流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vDe9HOgC-1599793553009)(课程资料/上课资料–imgs/验证码工作流程.png)]

验证码–服务端
/* svg 验证码模块 */
const svgCaptcha = require('svg-captcha');
let captchaText = '';  // 声明全局变量存储验证码文本/* 5.6 验证码 */
app.get('/captcha', (req, res) => {// 1.请求console.log(req.url);// 2.1 生成图片 + 文本var captcha = svgCaptcha.create({size: 4,noise: 2,color: true,// background:'#d1c'});// 2.2 文本 :服务器自己存起来 (答案)captchaText = captcha.text;// 2.3 图片:响应返回浏览器res.type('svg');res.status(200).send(captcha.data);
})
客户端—register.html
  • img标签src属性请求图片,服务器响应之后img标签会自动加载图片
<img class="code" src="http://127.0.0.1:3000/captcha" alt="">

03-加密功能(md5加密与加盐技术)

  • 客户端点击提交的时候对密码进行md5加密 (使用前端第三方包 md5.min.js)
  • 服务端接收密文保存到数据库
    • 密码明文只存在于用户填写的表单input中
    • 网络传输和服务器都只存储密文
      • 防止http请求被攻击导致密码泄露
      • 防止数据库被攻击导致密码泄露
    • 下次用户登录,使用相同加密方式对登录密码加密,然后与后台服务器进行匹配看是否一致
客户端—register.html
	<!-- 导入md5 -->
<script src="./libs/md5.min.js"></script>
  • 官方文档:http://jquery.cuishifeng.cn/serialize.html
  • $(form表单).serialize()new FormData(form表单区别)
    • serialize() : 用来传输文本数据的(自己一个个拼接麻烦,这是简写)
    • FormData() : 用来传输文件数据的(如果要上传文件,就用formdata

Nodejs.day7

01-Promise

1.1Promise介绍 :解决 回调地狱
  • ES6教程传送门:http://es6.ruanyifeng.com/#docs/promise

  • Promise 是一个构造函数,用于创建Promise对象

    • Promise对象:可以理解为一个处理异步操作的容器,针对异步操作
  • Promise作用:解决回调地狱

    • 回调地狱 :异步回调,层层嵌套
/*  为什么要学Promise? 1.Promise作用 :解决回调地狱问题回调地狱 :异步回调 层层嵌套*/// 需求: 一次读取文件 a.txt , b.txt , c.txt , d.txt 这三个文件内容
cons fs = require('fs');
// (1)不能直接按照顺序写,因为异步操作是无序的// (2)解决方案 :在回调函数中嵌套执行
// 弊端 :形参回调地狱(异步回调 层层嵌套,麻烦不方便维护)
//读取文件a
fs.readFile('/data/a.txt','utf8',(err,data)=>{if(err){throw err;}else{console.log(data);//读取文件bfs.readFile('/data/b.txt','utf8',(err,data)=>{if(err){throw err;}else{console.log(data)//读取文件cfs.readFile('/data/c.txt','utf8',(err,data)=>{if(err){throw err;}else{console.log(data);//读取文件dfs.readFile('/data/d.txt','utf8',(err,data)=>{if(err){throw err;}else{console.log(data)}})}})}})}
})
1.2Promise使用

使用流程 Promise是一个后遭函数,用于创建promise实例

  • 创建Promise实例对象
    • let p = new Promise((resolve,reject)=>{ //异步操作 })
  • 调用实例对象的then方法处理异步操作结果
    • p.then(data=>{ //处理成功数据 },err=>{ //处理失败数据 })
/*
1.Promise是一个构造函数,返回一个Promise对象
2.使用流程(1)实例化Promise对象 :将异步操作放入Promise中(2)调用then()方法:处理异步操作结果
*/const fs = require('fs');
/* 
参数 :回调函数(resolve,reject)=>{ //异步操作 }resolve : 完成回调reject :失败回调
*/let p = new Promise( (resolve,reject)=>{//异步操作:读取文件afs.readFile(`${__dirname}/data/a.txt`,'utf8',(err,data)=>{if(!err){ //成功 // (1)resolve: 执行then方法里面的第一个函数reslove(data);}else{ // 失败// (2)reject: 执行then方法里的第二个函数reject(err);}})
} )//调用promise实例的then方法
//第一个参数:成功的回调
//第二个参数:失败的回调
p.then(data=>{console.log(data);
},err=>{console.log(err)
})
1.3Promise原理

promise本质不是修改异步的顺序(异步永远是无序的),而是通过控制异步结果的顺序,从而实现异步代码有序执行

/* 
1.Promise注意点及原理:1.1 Promise对象有三种状态* pending(进行中)* fulfilled(已成功)* rejected(已失败)1.2 Promise状态改变只有两种情况* 从pending(进行中)变为fulfilled(成功)* 从pending(进行中)变为rejected(失败)1.3 注意 :Promise对象在创建的时候,里面的异步就会立即执行***** 不要在创建promise的时候回处理异步结果,应该调用resolve()或reject()交给then()方法来处理1.4 promise解决回调地狱 :在上一个promise的then方法中返回下一个promise实例对象1.5 坤哥个人总结 :promise本质不是修改异步的顺序(异步永远是无序的),而是通过控制异步结果的顺序,从而实现异步代码有序执行*/const fs = require('fs');
//(1)创建三个异步操作 promise//读取文件a
let p1 = new Promise((resolve,reject)=>{// 读文件fs.readFile('./data/a.txt','utf8',(err,data)=>{if(!err){// 成功resolve(data);}else{// 失败reject(err);}})
})let p2 = new Promise((resolve,reject)=>{if(!err){// 成功resolve(data);}else{// 失败reject(err);}
})let p3 = new Promise((resolve, reject) => {//异步操作: 读取文件afs.readFile(`${__dirname}/data/c.txt`, 'utf8', (err, data) => {if (!err) {//成功resolve(data);} else {//失败reject(err);}});
});let p4 = new Promise((resolve, reject) => {//异步操作: 读取文件afs.readFile(`${__dirname}/data/d.txt`, 'utf8', (err, data) => {if (!err) {//成功resolve(data);} else {//失败reject(err);}});
});//(2)调用promise实例的then方法
//第一个参数:成功的回调
//第二个参数:失败的回调
p1.then(data=>{console.log(data);return p2; //在第一个promise的then方法中返回第二个promise对象
}).then(data=>{ //p2的thenconsole.log(data);return p3;
}).then(data=>{console.loh(data);return p4;
}).then(data=>{console.log(data);
})
1.4Promise函数封装
const fs = require('fs);//1.封装一个函数 :根据文件名生成 文件读取的promise
function createPromise(filename){return new Promise((resolve,reject)=>{fs.readFile(`${__dirname}/data/${filename}.txt`,'utf8',(err,data)=>{if(!err){resolve(data);}else{reject(err);}})})
}let p1 = createPromise('a')
let p2 = createPromise('b')
let p3 = createPromise('c')
let p4 = createPromise('d')//2.解决希求:先读a,读完a后读b,一次读取
p1.then(data=>{console.log(data);return p2;
}) .then(data=>{console.log(data);return p3;
}).then(data=>{console.log(data)return p3;
}).then(data=>{console.log(data)
})
1.5Promise对象的catch方法
  • catch() : 捕捉then方法中的错误err
const fs = require("fs");/* promise实例对象的catch方法: 用于捕获异步操作的错误信息 *///1.封装一个函数 :根据文件名生成 文件读取的promise
function createPromise(filename){return new Promise((resolve,reject)=>{fs.readFile(`${__dirname}/data/${filename}.txt`,'utf8',(err,data)=>{if(!err){resolve(data);}else{reject(err);}})})
}//2.解决需求:要先读a,读完a后读b
p1.then(data=>{console.log(data);return p2;
}) .then(data=>{console.log(data);return p3;
}).then(data=>{console.log(data)return p3;
}).then(data=>{console.log(data)
}).catch(err=>{//catch :上面任何一个then出错了都会进入这个方法console.log(err);
})
1.6Promise对象的all()方法
  • all() : 将多个promise对象放入数组中合并成一个promise
  • 所有异步全部执行完毕才会执行then方法
const fs = require('fs');//Promise是一个构造函数,用于创建promise实例//封装一个创建promise的函数
function createPromise(filename){return new Promise((resolve,reject)=>{fs.readFile(`${__dirname}/data/${filename}.txt`, 'utf8', (err, data) => {if (!err) {//成功resolve(data);} else {//失败reject(err);}});});
};let p1 = createPromise('a');
let p2 = createPromise('b');
let p3 = createPromise('c');
let p4 = createPromise('d');// Promise.all([p1,p2,p3,p4]) : 多个promise合成一个
let pArr = Promise.all([p1,p2,p3,p4])
// (2)调用promise实例的then方法
pArr.then(data=>{//执行时机:pAll中所有的promise全部都完成才会执行then// data : 数组。 数组中每一个元素就是每一个promise的结果console.log(data)
})
1.7Promise对象的rece()方法
  • race() : 将多个Promise合并成一个Promise
  • 任何一个异步 执行完毕就会执行then方法
const fs = require('fs');// Promise是一个构造函数,用于创建promise实例// 封装一个创建promise的函数
function createPromise(filename){return new Promise((resolve,reject)=>{fs.readFile(`${__dirname}/data/${filename}.txt`, 'utf8', (err, data) => {if (!err) {//成功resolve(data);} else {//失败reject(err);}});});};let p1 = createPromise('a');let p2 = createPromise('b');let p3 = createPromise('c');let p4 = createPromise('d');//Promise.all([p1,p2,p3,p4]) : 多个promise合成一个let pArr = Promise.race([p1,p2,p3,p4]);//(2)调用promise实例的then方法
pArr.then(data =>{//执行时机 : pAll中任何一个promise完成就会执行then//data : 每一个执行的promise的结果console.log(data)
})

02-js难点面试题

2.1箭头函数this指向
  • 箭头函数中没有this:这意味着 call() apply() bind()无法修改箭头函数中的this

  • 箭头函数中的this指向:访问上一个作用域的this

    <!DOCTYPE html>
    <html lang="en"><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"><title>Document</title>
    </head>
    <body><script>/* ES6箭头函数中的this(1)箭头函数中没有this :这意味着call() apply() bind() 无法修改箭头函数中的this(2)箭头函数中的this指向 :访问上一个作用域的this2.学习路线 : 对比法2.1 function函数中的this : 谁 ‘调用’ 我, 我就指向谁* 跟你怎么声明没关系,取决于调用 (变化的)普通函数 : this指向window对象方法 : this指向对象构造函数 : this指向new创建的对象2.2 箭头函数中的this : 谁 '声明' 我,我就指向谁* 跟你怎么调用没关系,取决于声明 (不变的)*/// 1.全局函数:windowlet fn = () =>{console.log(this);}fn();//windowfn.call({ a:11 });//window 箭头函数中的this无法修改,因为箭头函数没有this// 2. 对象方法:window (因为对象无法开辟作用域,obj所在作用域还是window)let obj ={sayHi:()=>{console.log('学习使我快乐')console.log(this);}};obj.sayHi();//window// 3.局部函数var person ={play:function(){console.log('paly中的this');console.log(this);//person//在局部作用域声明一个箭头函数let fn2 = () =>{console.log('fn2在一级链中被声明,我的this就是一级链的this')console.log(this);//person}fn2();}};person.paly();</script>
    </body>
    </html>
    
2.2数组降维
  • 数组降维:把一个二维数组变成一维数组
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><script>/* 二维数组 :数组每一个元素都是一个数组*/let arr = [['a','b','c'],['d','e','f'],['g','h','i']];console.log(arr);// 降维// 方式一:数组的concat()拼接数组// let newArr = [];// for(let i = 0;i < arr.length;i++) {//      newArr = newArr.concat(arr[i]);// };// console.log(newArr);// 方式二:使用ES6的拓展运算符let newArr = [];for(let i = 0;i < arr.length;i++) {newArr.push(...arr[i]);};console.log(newArr);</script>
</body>
</html>
2.3数组升维
<!DOCTYPE html>
<html lang="en">
<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"><title>Document</title>
</head>
<body><script>/* 升维 :一维数组生成二维*/let arr = [{type: '电子产品',name: 'iPhone',price: 8888},{type: '家具',name: '桌子',price: 100},{type: '食品',name: '瓜子',price: 10},{type: '家具',name: '椅子',price: 380}, {type: '电子产品',name: '小米手机',price: 1380}, {type: '食品',name: '辣条',price: 5}, {type: '食品',name: '咖啡',price: 50}];let newArr = []; //升维之后的数组let obj = {}; //记录一维数组种类for(let i = 0; i<arr.length;i++){if(!obj[arr[i].type]){ //第一次出现obj[arr[i].type] = 1;newArr.push({type:arr[i].type;data:[arr[i]];})}else{ //第二次出现//将arr[i]添加到指定的二维数组data中for(let j=0;j<newArr.length;j++){if(arr[i].type == newArr[j].type){newArr[j].data.push(arr[i]);break;}}}}console.log(newArr);
http://www.lryc.cn/news/2413647.html

相关文章:

  • CVE-2015-0235
  • python心理学实验平台,python心理学实验程序(psychopy)
  • 一个不错的网站,颜色推荐 http://www.colorhexa.com/
  • [ Python 库调用和管理 ] __init__.py 的基本使用和运作机制
  • js常见特效
  • 了解遗传算法
  • Web.xml配置之context-param
  • 密码学 / PKI 体系概述
  • C++ 算法篇 深度优先搜索(DFS)
  • 《帝国时代3:决定版》dll丢失?修复x3daudio1_7.dll文件指南
  • Ubuntu 中 安装ulipad 发现无法更新软件库,无法安装python-wxgtk2.8
  • APIHOOK实例剖析
  • InstallSeield安装及破解
  • 胡立阳七招
  • 史上最详细的Linux使用手册(持续更新中)
  • 火狐下载 firefox免费高速下载 firefox又出新版本了
  • 博雅书社网上书店系统的设计与实现
  • 车载电脑(car pc)
  • 基于Java实现医院网上预约挂号管理系统-任务书参考
  • 腾讯qq2014官方正式版 v6.3.12390 免费版
  • SpringBoot单元测试详解
  • awk数组
  • fw150um无线网卡linux驱动,fw150um无线网卡驱动
  • CreateTextFile 文件的使用
  • Cloudflare设置流程 免费CDN加速你的网站【2024年最新】
  • maven 构建报错 This failure was cached in the local repository and resolution is not reattempted until t
  • pert计算公式期望值_PERT方法:用于计算各工序和工时的方法
  • Java基础总结(不断更新)
  • Windows 10 下修改 smb 连接的默认端口(445)
  • VBScript脚本语言基础