[TOC]
https://github.com/coreybutler/nvm-windows/releases/
以非安装版压缩包为例:
管理员运行install.cmd, 直接回车, 然后修改配置如下, 将其保存到nvm目录下
root: C:\node\nvm # nvm目录
path: C:\node\nodejs # nodejs版本快捷方式
arch: 64
proxy: none
node_mirror: http://npm.taobao.org/mirrors/node/
npm_mirror: https://npm.taobao.org/mirrors/npm/
修改环境变量:
NVM_HOME: C:\node\nvm
NVM_SYMLINK : C:\node\nodejs
PATH: ......;%NVM_HOME%;%NVM_SYMLINK%
控制台查看nvm version
即可
使用命令: nvm install [node版本号]
即可
// 全局成员概述
console.log(__filename);
console.log(__dirname);
console.log(process.argv); // 是一个数组, 分别为node.exe路径, js文件路径, 后面还会有命令行参数
console.log(process.arch);
var sum = function (a,b) {
return a+b;
}
// 导出模块方法一, 原本每个js都是相互独立的, 这里相当于暴露接口给外部使用
exports.sum = sum;
导入模块调用:
// 引入模块方法一
var module = require('./sum_module');
console.log(module.sum(3,4));
var sum = function (a,b) {
return a+b;
}
// 导出模块方法二(注意对应)
module.exports = sum;
// module.exports = {a:12,b:5}; // 可批量导出
导入模块调用
var sum = require('./sum_module'); // 此时的sum就是上面的module.exports
console.log(sum(3,4));
var sum = function (a,b) {
return a+b;
}
globals.sum = sum;
// =========================
require("./sum_module");
console.log(globals.sum(2,3));
node.js支持的模块格式有.js, .json(解析为对象), .node(C编译得到)
因为Node.js是在ES6语法基础上建立起来的, 因此需要了解ES6语法
新的语法let
和const
// let不会预解析
console.log(flag);
let flag = 1;
// let声明变量在同一作用域内不能重复
let flag = 1;
let flag = 2;
// ES6引入块级作用域
if(1){
var flag = 1;
}
console.log(flag);
if(1){
let flag1 = 1;
}
console.log(flag1); // 访问不到块级作用域下的let声明变量
{
//这也是一个块级作用域
let flag2 = 'hello'
}
const
除了上面的特性, 使用它声明的是常量, 无法被修改, 并且在声明时就要初始化
块级作用域的好处体现:
// 假如页面上有4个按钮, 要求每个按钮按下弹出alert:0,1,2,3
// 之前我们使用var, 需要配合闭包才能实现
// 使用let, 直接方便快捷实现
for(let i=0, length=btns.length;i<length;i++){
btns[i].onclick = function(){
alert(i);
}
}
数组的解构赋值
// 数组的解构赋值
let [a,b,c] = [1,2,3];
console.log(a);
console.log(b);
console.log(c);
let [e=33,f,g] = [,123,];
console.log(e,f,g); // 33,123,undefined
对象的解构赋值(与顺序无关)
// 对象的解构赋值(与顺序无关)
let {foo,bar} = {foo:"hello",bar:"hi"};
console.log(foo,bar);
// 对象别名:
let {foo:abc,bar1} = {foo:"hello",bar1:"hi"}; //foo有了别名abc, 原来的名字foo则失效
console.log(abc,bar1);
let {cos,sin} = Math;
console.log(typeof cos);
字符串的解构赋值:
let {a,b,c} = "hello";
console.log(a,b,c); // 'h','e','l';
let {length} = 'hello';
console.log(length); // length关键字为Number类型, 所以这里解构为string.length;
str.includes(searchingStr,index)
: 判断str中是否存在searchStr, 从index处开始搜索
startsWith()/endsWith()
: 判断开头和结尾.
模板字符串:
// 模板字符串
let person = {
name: "Samuel",
age: "24",
};
// 类似前端的js模板拼接结构
let tpl = `<div><span>${person.name}</span><span>${person.age}</span></div>`;
console.log(tpl);
在${}
中可以做一些简单的数值运算和函数调用.
arr.map(callback(value,index,arr));
arr.forEach(callback(value,index)); // 遍历时无法终止
arr.filter(callback(value,index,arr));
arr.some(callback(value, index, arr)); // 遍历数组, 若有一个值符合条件则返回true.可提前终止遍历
arr = [1,2,3];
arr.some(value=>{
if(value>1){
return true; // 此时遍历会终止.
}
})
arr.every(callback(value,index,arr)); // 遍历数组, 若有一个值满足条件则返回false. 可提前终止遍历
arr = [1,2,3];
arr.every(value=>{
if(value>1){
return false; // 此时遍历会终止.
}
})
arr.reduce(callback(tmp,value,index,arr));
reduce (让数组中的前项和后项做某种计算,并累计最终值)
callbackFunction 包含4个参数
prev:第一项的值或者上一次叠加的结果值 cur: 当前会参与叠加的项 index: 当前索引 arr: 数组本身
// 求平均数
let arr = [1, 2, 3, 4, 5, 6, 7];
result = arr.reduce((tmp, value, index) => {
if (index < arr.length - 1) {
//console.log(tmp, value);
return tmp + value;
} else {
return (tmp+value) / arr.length;
}
});
console.log(result);
参数默认值:
function foo(param = 'nihao'){
console.log(param);
}
rest参数(剩余参数)
function foo(a,b,...param){
console.log(param);
}
foo(1,2,3,4,5,6); // [3,4,5,6]
扩展运算符...
function foo3(a,b,c,d,e){
console.log(a+b+c+d+e);
}
let arr = [1,2,3,4,5];
foo3(...arr);
箭头函数
let foo = (a, b) => {
let c = 1;
console.log(a + b + c);
}
foo(1,2);
let arr1 = [123,456,789];
arr1.forEach((value,index)=>{
console.log(value,index);
});
箭头函数注意点:
function test() {
setTimeout(() => {
console.log(this);
}, 100);
}
test.call({num: 1}); // 此时箭头函数定义在test函数内部, this指向test函数的第一个参数
arguments
获取参数列表, 但是可以使用rest参数代替let test1 = (...param) => {
console.log(param);
}
test1(123, 3345);
// class关键字
class Animal {
// 构造函数
constructor(name) {
this.name = name;
}
// 方法
showName() {
console.log(this.name);
}
// 静态方法
static eat(a) {
console.log(a + "吃东西");
}
}
let a = new Animal('dog');
a.showName();
// 静态方法
Animal.eat("dog");
// 类的继承
class Dog extends Animal {
constructor(name, color) {
super(name);
this.color = color;
}
bark(){
console.log('a ' + this.color + ' dog is barking');
}
}
let d = new Dog('hehe','black');
d.bark();
传统的JS都是单线程执行, 通过回调函数来实现异步, 但是这种写法很恶心, 每次都要写回调, 就不能创建一个任务
, 然后判断任务.isSucess
? 这时候就要引入Promise了
// 创建Promise对象
let p=new Promise(function (resolve, reject){
$.ajax({
url: 'data/1.json',
dataType: 'json',
success(data){
resolve(data); // 参数传递到resolve中
},
error(res){
reject(res); // 异常传递到reject中
}
});
});
// then中传递连个函数, 分别对应上面的resolve和reject
p.then(function (data){
alert('成功');
console.log(data);
}, function (res){
alert('失败');
console.log(res);
});
同时Promise还有一个all接口, 负责封装多个异步任务, 统一获取数据
Promise.all([
$.ajax({url: 'data/1.json', dataType: 'json'}),
$.ajax({url: 'data/2.json', dataType: 'json'}),
$.ajax({url: 'data/3.json', dataType: 'json'}),
]).then((arr)=>{
let [data1, data2, data3]=arr;
console.log(data1, data2, data3);
}, (res)=>{
alert('错了');
});
但是, 上述Promise封装的任务是互不相关的, 实际开发中遇到的任务其实是相互联系的, 比如获取用户的登录状态,获取商品列表, 根据用户是否登录推荐不同的商品.
为了解决Promise的局限性问题, es6产生了async和await语法糖(实际还是会编译成回调形式的代码).使用方式如下:
async function show(){
let data1=await $.ajax({url: 'data/1.json', dataType: 'json'}); // await修饰的是一个Promise对象.它最主要的意图是用来等待 Promise 对象的状态被 resolved
let data2=await $.ajax({url: 'data/2.json', dataType: 'json'});
let data3=await $.ajax({url: 'data/3.json', dataType: 'json'});
console.log(data1, data2, data3);
}
show();
示例: 根据上一个异步请求的状态执行不同的异步操作
async function show(){
let data1=await $.ajax({url: 'data/1.json', dataType: 'json'});
if(data1.a<10){
let data2=await $.ajax({url: 'data/2.json', dataType: 'json'});
alert('a');
}else{
let data3=await $.ajax({url: 'data/3.json', dataType: 'json'});
alert('b');
}
}
str.padStart(length, padStr)/padEnd(length, padStr): 填充padStr至长度length
用于替代XMLHTTPRequest而产生的js api, https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch
let oImg=document.getElementById('img1');
let oBtn=document.getElementById('btn1');
oBtn.onclick=async function (){
//1.请求
let res=await fetch('data/1.png'); // Promise对象 可以请求txt json 二进制数据
//2.解析
let data=await res.blob(); // blob, json, text
let url=URL.createObjectURL(data);
oImg.src=url;
};
本质上就是在本地写好回调函数, 然后通过<script>
引入外部js调用回调函数
function show({s}){
console.log(s);
}
window.onload=function (){
let oTxt=document.getElementById('txt1');
oTxt.oninput=function (){
let url=`https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=${this.value}&cb=show`; // 百度搜索词联想
let oS=document.createElement('script');
oS.src=url;
document.head.appendChild(oS);
};
};
jQuery写法:
$('#txt1').on('input', function (){
$.ajax({
url: 'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su',
data: {wd: $(this).val()},
dataType: 'jsonp',
jsonp: 'cb'
}).then(({s})=>{
console.log(s);
}, res=>{
alert('失败');
});
}
配合<form>
可以更加轻松的完成表单提交, 处理文件上传也更加方便
let oForm=document.querySelector('#form1'); // form表单
oForm.onsubmit=function (){
let formdata=new FormData(oForm); // 通过form表单创建FormData对象
let xhr=new XMLHttpRequest(); // 原生ajax发送数据
xhr.open(oForm.method, oForm.action, true); // 通过form获取url和method, async=true.
xhr.send(formdata); // 发送formdata
xhr.onreadystatechange=function (){
if(xhr.readyState==4){
if(xhr.status==200){
alert('成功');
}else{
alert('失败');
}
}
};
return false; // 阻止默认提交
};
jQuery写法
$('#form1').on('submit', function (){
let formdata=new FormData(this);
$.ajax({
url: this.action,
type: this.method,
data: formdata,
processData: false, // 防止jquery处理数据
contentType: false // 防止jquery添加contenttype
}).then(res=>{
alert('成功');
}, res=>{
alert('失败');
});
return false;
});
手动构建:
let formdata=new FormData(); // 创建空的formdata
formdata.append('username', document.querySelector('#user').value); // 手动添加数据
formdata.append('password', document.querySelector('#pass').value);
formdata.append('f1', document.querySelector('#f1').files[0]); // 手动添加文件
//
let xhr=new XMLHttpRequest();
xhr.open('post', 'http://localhost:8080/', true);
xhr.send(formdata);
常用命令:
npm install -g cnpm --registry=https://registry.npm.taobao.org
之后把所有的npm命名换成cnpm即可, 推荐使用cnpm, 可以解决下载速度慢的问题
Buffer对象是Node处理二进制数据的一个接口**(操作字节)**。它是Node原生提供的全局对象,可以直接使用.
实例化Buffer:
功能方法:
实例方法:
utf8编码中, 英文字母占用1个字节, 汉字占用3个字节
https://nodejs.org/dist/latest-v10.x/docs/api/path.html
const path = require('path');
path.basename(path[, ext]): 返回路径的最后一部分(文件名)
path.dirname(path): dirname+basename = 完整的absolute path;
path.delimiter: 环境变量分隔符
path.extname(path): 文件后缀
path.parse(path): 从路径string解析为pathObject
path.format(pathObject): 从pathObject格式化为路径string
path.isAbsolute(path)
path.join([...paths]): 路径拼接
path.normalize(path): 规范化路径
path.relative(from, to): from到to的相对路径
path.resolve([...paths]): 类似cd命令, 返回cd完所有path之后的最终相对路径(相对工作目录)
path.sep: 路径分隔符
https://nodejs.org/dist/latest-v10.x/docs/api/fs.html
const fs = require('fs');
Node.js的异步操作是基于事件队列和回调实现的, 因此一般异步操作函数的写法如下:
const fs = require('fs');
const path = require('path');
// 读取文件
let strpath = path.join(__dirname, 'data.txt');
fs.readFile(strpath, (err, data) => {
if (err) return;
// console.log(data); // data是一个Buffer对象
console.log(data.toString())
});
// 第二个参数可以是编码, 此时data则是字符串
fs.readFile(strpath, 'utf8', (err, data) => {
if (err) return;
console.log(data)
})
// 同步写入文件
fs.writeFileSync(strpath, ' this is my life', {encoding: 'utf8', flag: 'a'});
文件的流操作
针对大文件, 无法一次全部读入内存, 因此采用流的操作方式, 降低开销
// 大文件的流式操作
const fs = require('fs');
const path = require('path');
let file_path = 'F:\\Download\\前端\\【05】WebAPI.rar';
let dest_path = path.join('e:/','test.rar');
// 创建读取流和写入流
let readStream = fs.createReadStream(file_path);
let writeStream = fs.createWriteStream(dest_path);
// 基于事件的处理方式
readStream.on("data",(chunk)=>{
// 读取文件块, 然后写入
writeStream.write(chunk);
})
readStream.on("end",()=>{
writeStream.close();
console.log("复制完成");
})
除了上述完整写法, 可以简化为一句: readStream.pipe(writeStream);
fs.mkdir();
fs.readdir(); 读取目录
fs.rmdir(); 删除目录
原生node.js封装的创建http服务器的模块, 使用方法:
// 初步实现服务器的功能
const http = require('http');
// 创建服务器实例
let server = http.createServer();
// 绑定请求事件
server.on('request',(req,resp)=>{
resp.end("hello");
})
// 监听端口
server.listen(3000);
简写版:
const http = require('http');
let server = http.createServer(function (req, resp) {
resp.write("hello"); // 写入前端页面数据, 可以分多次写入
resp.end();
});
server.listen(3000);
const http = require('http');
const url = require('url');
let server = http.createServer((req, resp) => {
//console.log(req);
let {pathname, query} = url.parse(req.url, true); // true表示自动使用queryString解析
console.log(pathname); // 请求的url地址
console.log(query); // 所有的get参数
});
server.listen(3000);
POST提交的数据-body(<=2GB), 无法一次性全部提交因此是分多次的提交的, 每次提交一个chunk(Buffer类型)习惯做法是建立一个数组, 把所有的buffer都接受, 然后再合并到一个Buffer中.
const http = require('http');
let server = http.createServer((req, resp) => {
pathname = req.url; // 请求的路径
let arr = [];
req.on('data',buffer=>{
arr.push(buffer);
});
req.on('end',()=>{
let result = Buffer.concat(arr);
console.log(pathname,result.toString()); // 只有字符串是可以用toString的, 如果是文件就不要使用.
});
});
server.listen(3000);
const http = require('http');
const url = require('url');
const querystring = require('querystring');
let server = http.createServer((req, resp) => {
let path="",get_param={},post_param={}; // 定义需要的参数容器
if(req.method=="GET"){
let {path, get_param} = url.parse(req.url, true);
complete();
}else if(req.method=="POST"){
path = req.url;
let arr = [];
req.on('data',buffer=>{
arr.push(buffer);
});
req.on('end',()=>{
let result = Buffer.concat(arr);
post_param = querystring.parse(result.toString());
complete();
});
}
function complete(){
console.log(path, get_param, post_param);
// 处理请求信息...
}
});
是一种h5下的新的的通信协议, 实现了浏览器与服务器之间的全双工通信.可以节省服务器带宽和资源(不用像http那样频繁的连接,通信,断开), 本质是基于TCP的通信.
该包封装websocket的node.js实现, 方便使用
cnpm i socket.io -D
websocket建立方法:
const io = require('socket.io');
const http = require('http');
//1.建立普通http(前端页面首先访问socket.io.js)
let server=http.createServer((req, res)=>{});
server.listen(8080);
//2.建立ws
let wsServer=io.listen(server);
wsServer.on('connection', sock=>{
//sock.emit('name', 数据)
//sock.on('name', function (数据){});
sock.on('aaa', function (a, b){
console.log(a, b, a+b);
});
});
前端页面:
<!-- 请求后端的socket.io.js -->
<script src="http://localhost:8080/socket.io/socket.io.js" charset="utf-8"></script>
<script>
let sock = io.connect('ws://localhost:8080/'); // socket连接ws
sock.on('timer',time=>{
console.log(time);
})
</script>
sock.on('name',(data)=>{})
用于接受新的数据sock.emit('name',data)
用于发送数据cnpm i mysql -D
const mysql = require("mysql");
let db = mysql.createPool({ // 创建爱你连接池
host: '192.168.40.20',
port: 3306,
user: 'root',
password:'sys963-+',
database: 'forNodeJs'
});
db.query("SELECT * from tb_user", (err,results,fields)=>{
if(err) throw err;
console.log(results);
// console.log(fields); 每一列的信息.
})
cnpm i co co-mysql -D
const mysql = require('mysql');
const co = require('co');
const co_mysql = require('co-mysql');
let pool = mysql.createPool({
host: '192.168.40.20',
port: 3306,
user: 'root',
password:'sys963-+',
database: 'forNodeJs'
});
let db = co_mysql(pool); // 使用co_mysql包装pool
(async ()=>{
let data = await db.query("select * from tb_user"); // 此时db.query是一个promise对象
// 使用await可以异步获取数据. 但是await必须在一个asysn函数中.因此声明在一个async function
console.log(data);
})();
对应fs的await版本, 无需写大量的回调https://www.npmjs.com/package/await-fs
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
ctx.body = 'Hello World';
});
app.listen(3000);
也可以使用app.callback()
, 配合http.createServer
处理请求
const http = require('http');
const https = require('https');
const Koa = require('koa');
const app = new Koa();
http.createServer(app.callback()).listen(3000);
https.createServer(app.callback()).listen(3001);
常用的方法和属性:
app.keys
=: 用于给cookie签名用的密钥app.listen(port)
Koa Context 将 node 的 request
和 response
对象封装到单个对象中,为编写 Web 应用程序和 API 提供了许多有用的方法.
每个请求都将创建一个 Context
,并在中间件中作为接收器引用,或者 ctx
标识符
app.use(async ctx => {
ctx; // 这是 Context
ctx.request; // 这是 koa Request
ctx.response; // 这是 koa Response
});
常用属性:
ctx.state
: 推荐的命名空间,用于通过中间件传递信息和你的前端视图(例如:ctx.state.user = await User.find(id);
)ctx.throw([status], [msg], [properties])
: 抛出错误信息, status是状态码, msg是消息, properties是对象(一般好像没啥用)ctx.cookies.set(name, value, [options])
: options常用设置: maxAge(毫秒), signed(true), expires(过期的Date), path(路径, 默认"/")和domain(域名), secure(https就是true), overwrite(默认false)ctx.cookies.get(name, [options])
常用属性:
属性(=表示可写) | 说明 |
---|---|
request.header(=) | 请求头 |
request.method(=) | 请求方法 |
request.url(=) | 请求的url |
request.originalUrl | 请求原始URL |
request.origin | 获取URL的来源,包括 protocol 和 host
|
request.href | 获取完整的请求URL,包括 protocol ,host 和 url
|
request.path(=) | 请求路径名 |
request.querystring(=) | GET参数 |
request.type | 请求 Content-Type 不含参数 "charset" |
request.charset | |
request.query(=) | GET请求查询字符串 |
request.ip | 发起请求的客户端的IP |
属性(基本都是可写)和方法 | 说明 |
---|---|
response.header | 响应头 |
response.status | 响应http状态码 |
response.message | 一般和status关联 |
response.body | 响应体 |
response.set(field, value) | 设置响应标头 field 到 value
|
response.append(field, value) | 追加响应头 |
response.type | 响应的Content-Type |
response.redirect(url, [alt]) | 重定向 |
const koa = require('koa');
const Router = require('koa-router');
let server = new koa();
let router = new Router(); // 创建路由
router.get("/a", async ctx => {
ctx.body = 'aaa';
ctx.body += "你好";
});
server.use(router.routes()); // 服务器使用路由
// ==============================================
// 路由嵌套
let userRouter = new Router(); // 用户根Router
let adminRouter = new Router(); // 管理员Router
adminRouter.get('/a', async ctx => {
ctx.body = "这是管理员的a";
});
let staffRouter = new Router(); // 员工Router
staffRouter.get("/a", async ctx => {
ctx.body = "这是员工的a";
});
userRouter.use('/admin', adminRouter.routes()); // 添加路由URL前缀给admin
userRouter.use('/staff', staffRouter.routes()); // 添加路由URL前缀给staff
路由还可以做权限控制的中间件和处理异常的中间件, route支持正则路由, 以及参数匹配('/users/:id'
)
let adminRouter = new Router(); // 管理员router
adminRouter.get("/login", async ctx=>{
// .. 渲染登录页面
});
adminRouter.post("/post", async ctx=>{
// .. 处理管理员登录逻辑
});
// 访问其他需要管理员登录权限的页面的中间件
adminRouter.use("*", async (ctx, next)=>{
if(!ctx.session['user']){
//用户没有登录
ctx.redirect("/login"); // 跳转到登录, 可以添加一些提示信息
}else{
try{
await next(); // 放行
}catch(e){
// 之前可以先用日志文件记录e
ctx.throw(500,'Internal Server Error');
}
}
});
// ... 需要管理员登录权限访问路由 (koa路由匹配的规则是先遇到哪个合适的就走哪个)
const session = require('koa-session');
app.use(session({
maxage: 20 * 60 * 1000, // 毫秒
renew: true // 当session过期时自动更新session
}, app));
app.use(ctx=>{
ctx.session.XXX = XXX; // 即可设置session
})
const static = require("koa-static");
router.all(/((\.jpg)|(\.png)|(\.gif))$/i, static('./static', {
maxage: 30 * 86400 * 1000
}));
参数:
maxage
缓存时间(毫秒)gzip
defaults to true自动解析post body, 支持form-data, json, 和文件上传的包
https://www.npmjs.com/package/koa-better-body
文档里面说了, 所有的参数都是直接传递给formidable的, 因此formidable的参数也是支持的. 可以限制文件的大小什么..
http://www.ptbird.cn/koa-body.html#menu_index_7
const Koa = require('koa');
const Router = require('koa-router');
const betterbody = require('koa-better-body');
const path = require("path");
let app = new Koa();
let router = new Router();
app.use(betterbody({
uploadDir: path.resolve(__dirname,'./upload'),
}));
router.post("/upload", async (ctx, next) => {
console.log(ctx.request.fields);
console.log(ctx.request.files);
ctx.body = "OK";
});
app.use(router.routes());
app.listen(8080);
https://aui.github.io/art-template/zh-cn/docs/
npm install --save art-template
npm install --save koa-art-template
配置:
const Koa = require('koa');
const render = require('koa-art-template');
const app = new Koa();
render(app, {
root: path.join(__dirname, 'view'), // 模板路径
extname: '.art', // 模板后缀
debug: process.env.NODE_ENV !== 'production' // debug模式, 开发时直接写true
});
配合路由使用:
router.get("/login", async ctx => {
await ctx.render('admin',{
user:user // 携带数据
})
});
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。