nodeCZBK-笔记1

时间:2023-12-21 11:51:38


day01

node简介

  1. Node.js是一个让JavaScript运行在服务器端的开发平台。

    node就是一个js的执行环境

  2. node 与其它后台语言的不同

    1. Node.js不是一种独立的语言,与PHP、JSP、Python、Perl、Ruby的“既是语言,也是平台”不同,Node.js的1. 使用JavaScript进行编程,运行在JavaScript引擎上(V8)。
    2. 与PHP、JSP等相比(PHP、JSP、.net都需要运行在服务器程序上,Apache、Naginx、Tomcat、IIS。),Node.js跳过了Apache、Naginx、IIS等HTTP服务器,它自己不用建设在任何服务器软件之上。
    3. Node.js没有web容器。

      URL是通过了Node的顶层路由设计,呈递某一个静态文件的。
  3. node特点:

    --所谓的特点,就是Node.js是如何解决服务器高性能瓶颈问题的

    1. 单线程

      在Java、PHP或者.net等服务器端语言中,会为每一个客户端连接创建一个新的线程。而每个线程需要耗费大约2MB内存。也就是说,理论上,一个8GB内存的服务器可以同时连接的最大用户数为4000个左右。

      Node.js不为每个客户连接创建一个新的线程,而仅仅使用一个线程。当有用户连接了,就触发一个内部事件,通过非阻塞I/O、事件驱动机制,让Node.js程序宏观上也是并行的。使用Node.js,一个8GB内存的服务器,可以同时处理超过4万用户的连接。

      另外,单线程的带来的好处,还有操作系统完全不再有线程创建、销毁的时间开销。

      坏处,就是一个用户造成了线程的崩溃,整个服务都崩溃了,其他人也崩溃了。
    2. 非阻塞I/O ( non-blocking I/O )

      阻塞模式下,一个线程只能处理一项任务,要想提高吞吐量必须通过多线程。而非阻塞模式下,一个线程永远在执行计算操作,这个线程的CPU核心利用率永远是100%。
    3. 事件驱动( event-driven )

      在Node中,客户端请求建立连接,提交数据等行为,会触发相应的事件。

      Node.js中所有的I/O都是异步的,回调函数,套回调函数。
    4. 适合开发什么?
      1. 善于I/O,不善于计算。

        因为Node.js最擅长的就是任务调度,如果你的业务有很多的CPU计算,实际上也相当于这个计算阻塞了这个单线程,就不适合Node开发。
      2. 当应用程序需要处理大量并发的I/O,而在向客户端发出响应之前,应用程序内部并不需要进行非常复杂的处理的时候,Node.js非常适合。
      3. Node.js也非常适合与web socket配合,开发长连接的实时交互应用程序。
      4. 比如:

        ● 用户表单收集

        ● 考试系统

        ● 聊天室

        ● 图文直播

http模块

  1. //require表示引包,引包就是引用自己的一个特殊功能
    //require的时候会执行一遍此文件,而且不管require几次只会执行一次。
    //后面有用户再访问不会再次执行、引用
    var http = require("http"); //创建服务器,参数是一个回调函数,表示如果有请求进来,要做什么
    var server = http.createServer(function(req,res){
    //req表示请求,request; res表示响应,response //设置HTTP头部,状态码是200,文件类型是html,字符集是utf8
    res.writeHead(200,{"Content-type":"text/html;charset=UTF-8"});
    res.end("哈哈哈哈,我买了一个iPhone" + (1+2+3) + "s");
    }); //运行服务器,监听3000端口(端口号可以任改)
    server.listen(3000,"127.0.0.1");

url模块

  1. req

    req.url 属性,表示用户的请求URL地址。所有的路由设计,都是通过req.url来实现的。
  2. 识别URL

    用到两个新模块,第一个就是querystring模块,第二个就是url模块
    querystring.parse('foo=bar&baz=qux&baz=quux&corge')
    // returns
    { foo: 'bar', baz: ['qux', 'quux'], corge: '' } // it can decode `gbk` encoding string
    querystring.parse('w=%D6%D0%CE%C4&foo=bar', null, null,
    { decodeURIComponent: gbkDecodeURIComponent })
    // returns
    { w: '中文', foo: 'bar' }
    //url.parse()可以将一个完整的URL地址,分为很多部分:
    //host、port、pathname、path、query var pathname = url.parse(req.url).pathname;
    //url.parse()如果第二个参数是true,则 query 属性会通过 querystring 模块的 parse() 方法生成一个对象
    //就可以直接打点得到这个参数
    var query = url.parse(req.url,true).query;
    var age = query.age; //直接打点得到这个参数

fs模块

  1. 事件循环机制
    var server = http.createServer(function(req,res){
    //不处理小图标
    if(req.url == "/favicon.ico"){return;} //给用户加一个五位数的id
    var userid = parseInt(Math.random() * 89999) + 10000; console.log("欢迎" + userid); res.writeHead(200,{"Content-Type":"text/html;charset=UTF8"});
    //两个参数,第一个是完整路径,当前目录写./
    //第二个参数,就是回调函数,表示文件读取成功之后,做的事情
    fs.readFile("./test/1.txt",function(err,data){ //异步执行,不会阻塞前后的代码
    if(err){ throw err; } console.log(userid + "文件读取完毕");
    res.end(data);
    });
    });
    // 并不一定是打印每一次 “欢迎..”间隔打印一次 “..文件读取完毕”
  2. API
    1. //创建文件夹
    fs.mkdir("./album/aaa"); 2. //stat检测状态,*异步执行*
    fs.stat("./album/bbb",function(err,stats){
    //检测这个路径,是不是一个文件夹
    console.log(stats.isDirectory());
    });
  3. 异步导致的错误
    失败案例,列出album文件夹中的所有子文件夹 
    
    3. //读取文件夹
    fs.readdir("./album",function(err,files){
    //files是个文件名的数组,表示./album这个文件夹里的所有东西(文件、文件夹) for(var i = 0 ; i < files.length ;i++){
    var thefilename = files[i];
    //进行一次检测
    fs.stat("./album/" + thefilename,function(err,stats){
    //由于异步,本次使用的 thefilename,并不一定是本次循环所赋的值 if(stats.isDirectory()){ //如果他是一个文件夹,那么输出它
    wenjianjia.push(thefilename);
    }
    console.log(wenjianjia); //由于异步不会真的打印所出有文件夹
    });
    }
    }); <!--循环里面放异步就容易出现这种错误-->

异步变成同步(迭代器)

  1. 遍历album里面的所有文件、文件夹

    fs.readdir("./album/",function(err,files){
    //files是一个存放文件(夹)名的数组
    var wenjianjia = []; //存放文件夹的数组 //迭代器就是强行把异步的函数,变成同步的函数
    // 1做完了,再做2;2做完了,再做3
    (function iterator(i){
    //结束遍历
    if(i == files.length){
    console.log(wenjianjia);
    return; //一定不能丢,否则结束不了会报错
    } var thefilename = files[i];
    fs.stat("./album/" + thefilename,function(err,stats){
    if(stats.isDirectory()){
    //如果是文件夹,那么放入数组。不是,什么也不做
    wenjianjia.push(thefilename);
    }
    iterator(i+1);
    });
    })(0);
    });

path

  1. 1. path.extname('index.html');
    // 返回: '.html'
    path.extname('index.coffee.md');
    // 返回: '.md' 2. path.normalize() 方法会规范化给定的 path,并解析 '..' 和 '.' 片段
    path.normalize('/foo/bar//baz/asdf/quux/..');
    // 返回: '/foo/bar/baz/asdf'

静态资源文件管理

  1. var http = require("http");
    var fs = require("fs");
    var url = require("url");
    var path = require("path"); var server = http.createServer(function(req,res){ //得到地址
    var pathname = url.parse(req.url).pathname;
    //判断此时用户输入的地址是文件夹地址还是文件地址
    //如果是文件夹地址,那么自动请求这个文件夹中的index.html
    if(pathname.indexOf(".") == -1){
    pathname += "/index.html";
    }
    //输入的网址是127.0.0.1/images/logo.png
    //实际请求的是./static/images/logo.png
    var fileURL = "./" + path.normalize("./static/" + pathname);
    //得到拓展名
    var extname = path.extname(pathname); //把文件读出来
    fs.readFile(fileURL,function(err,data){
    //读完之后做的事情
    if(err){
    //文件不存在
    res.writeHead(404,{"Content-Type":"text/html;charset=UTF8"})
    res.end("404,页面没有找到");
    }
    //读完之后做的事情
    getMime(extname,function(mime){
    res.writeHead(200,{"Content-Type":mime})
    res.end(data);
    });
    });
    }); server.listen(80,"127.0.0.1"); function getMime(extname,callback){
    //读取mime.json文件,得到JSON,根据extname key ,返回对应的value
    //读取文件,异步,异步,异步
    fs.readFile("./mime.json",function(err,data){
    if(err){
    throw Error("找不到mime.json文件!");
    return;
    }
    //转成JSON
    var mimeJSON = JSON.parse(data);
    var mime = mimeJSON[extname] || "text/plain";
    //执行回调函数,mime类型字符串,就是它的参数
    callback(mime);
    });
    }

day02

模块化

  1. -- 狭义的说,每一个JavaScript文件都是一个模块;而多个JavaScript文件之间可以相互require,他们共同实现了一个功能,他们整体对外,又称为一个广义上的模块。

  2. Node.js中,一个JavaScript文件中定义的变量、函数,都只在这个文件内部有效。

    当需要从此JS文件外部引用这些变量、函数时,必须使用exports对象进行暴露。使用者要用require()命令引用这个JS文件。

  3. 一个JavaScript文件,可以向外exports无数个变量、函数。但是require的时候,仅仅需要require这个JS文件一次。使用的它的变量、函数的时候,用点语法即可。所以,无形之中,增加了一个顶层命名空间。

  4. Node中,js文件和js文件,就是被一个个exportsrequire构建成为网状的。而不是靠html文件统一在一起的。

  5. 可以将一个JavaScript文件中,描述一个类。用

    module.export = 构造函数名的方式向外暴露一个类。

  6. 也就是说,js文件和js文件之间有两种合作的模式:

    1. 某一个js文件中,提供了函数,供别人使用。 只需要暴露函数就行了: exports.msg=msg;
    2. 某一个js文件,描述了一个类: module.exports = People;, 此时,People就被视为构造函数,可以用new来实例化了。

文件夹模块

  1. var foo = require("foo.js"); 没有写./, 所以不是一个相对路径。是一个特殊的路径那么Node.js将该文件视为node_modules目录下的一个文件。

  2. node_modules文件夹并不一定在同级目录里面,在任何直接祖先级目录中,都可以。甚至可以放到NODE_PATH环境变量的文件夹中。

    var bar = require("bar");
    //那么Node.js将会去寻找node_modules目录下的bar文件夹中的index.js去执行。
  3. 每一个模块文件夹中,推荐都写一个package.json文件,这个文件的名字不能改,必须放在模块的根目录里。node将自动读取里面的配置。有一个main项,就是入口文件:

    {
    "name": "kaoladebar",
    "version": "1.0.1",
    "main" : "app.js"
    }
  4. package.json

    "dependencies": {
    "silly-datetime": "^0.^1.0" //^ 表示固定
    },

路径

1. require()别的js文件的时候,将执行那个js文件。

require()中的路径,是从当前这个js文件出发,找到别人。而fs是从命令提示符找到别人。

所以,桌面上有一个a.js,test文件夹中有b.js、c.js、1.txt

    // a要引用b:
var b = require(“./test/b.js”);
// b要引用c:
var b = require(“./c.js”);
但是,fs等其他的模块用到路径的时候,都是相对于cmd命令光标所在位置。

所以,在b.js中想读1.txt文件,推荐用绝对路径:

```
fs.readFile(__dirname + "/1.txt",function(err,data){
if(err) { throw err; }
console.log(data.toString());
});
``` ```
var b = require(“./test/b.js”); // './' 指当前文件路径 fs.readFile( "./uploads",function(err,data){ // './' 指根目录文件路径
...
});
```

body-parser、formidable

```
var bodyParser = require('body-parser'); app.use(bodyParser.urlencoded({ extended: false }));
app.post("/",function(req,res){
console.log(req.body);
});
```
```
var formidable = require('formidable'); var server = http.createServer(function(req,res){
if(req.url == "/dopost" && req.method.toLowerCase() == "post"){
//Creates a new incoming form.
var form = new formidable.IncomingForm();
//设置文件上传存放地址
form.uploadDir = "./uploads"; //执行里面的回调函数的时候,表单已经全部接收完毕了。
form.parse(req, function(err, fields, files) {
if(err){throw err;} console.log(fields); //所有的文本域、单选框,都在fields存放;
console.log(files); //所有的文件域,files res.writeHead(200, {'content-type': 'text/plain'});
res.end("成功");
});
}
});
```

day03

express路由能力

var express = require("express");
var app = express(); //当用get请求访问一个网址的时候,做什么事情:
//所有的GET参数,?后面的都已经被忽略。锚点#也被忽略;你路由到/a,实际/a?id=2&sex=nan 也能被处理
app.get("/haha",function(req,res){
res.send("这是haha页面,哈哈哈哈哈哈");
});
。 //当用post访问一个网址的时候,做什么事情:
app.post("网址",function(req,res){ //互联网的网址,不分大小写 });
//如果想处理这个网址的任何method的请求,那么写all
app.all("/",function(){ }); //正则表达式可以被使用。正则表达式中,未知部分用圆括号分组,然后可以用req.params[0]、[1]得到。req.params类数组对象。
app.get(/^\/student\/([\d]{10})$/,function(req,res){
res.send("学生信息,学号" + req.params[0]); //req.params[0]指第一个小括号匹配到的内容
}); app.get("/student/:id/:name",function(req,res){
var id = req.params["id"]; // :后的内容自动加入到req.params中(类数组对象)
var name = req.params["name"]; var reg= /^[\d]{6}$/; //正则验证
if(reg.test(id)){
res.send(id);
}else{
res.send("请检查格式");
}
}); app.listen(3000);

express静态文件

var express = require("express");
var app = express(); app.use(express.static("./public")); app.get("/haha",function(req,res){
res.send("haha ");
}); app.listen(3000);

express与模版引擎配合

var express = require("express");
var app = express(); app.set("view engine","ejs"); //不需要再require('ejs')
app.set('views', './views'); //设置存放模版文件路径,默认就是views app.get("/",function(req,res){
res.render("haha",{
"news" :["我是小新闻啊","我也是啊","哈哈哈哈"]
});
}); app.listen(3000);

express中间件

  1. 如果我的get、post回调函数中,没有next参数,那么匹配上第一个路由,就不会往下匹配了。

    如果想往下匹配的话,那么需要写next();
    app.get("/",function(req,res,next){
    console.log("1");
    next();
    }); app.get("/",function(req,res){
    console.log("2");
    });
    //不会打印 2
  2. 下面两个路由,感觉没有关系,但是实际上冲突了,因为admin可以当做用户名 login可以当做id。
    app.get("/:username/:id",function(req,res){
    console.log("1");
    res.send("用户信息" + req.params.username);
    }); app.get("/admin/login",function(req,res){
    console.log("2");
    res.send("管理员登录");
    });

    解决1: 交换位置。也就是说,express中所有的路由(中间件)的顺序至关重要。匹配上第一个,就不会往下匹配了。所以具体的往上写,抽象的往下写。

    app.get("/admin/login",function(req,res){
    console.log("2");
    res.send("管理员登录");
    }); app.get("/:username/:id",function(req,res){
    console.log("1");
    res.send("用户信息" + req.params.username);
    });

    解决2: 路由get、post、use这些东西,就是中间件,中间件讲究顺序,匹配上第一个之后,就不会往后匹配了。next函数才能够继续往后匹配。

    app.get("/:username/:id",function(req,res,next){
    var username = req.params.username;
    //检索数据库,如果username不存在,那么next()
    if(检索数据库){
    console.log("1");
    res.send("用户信息");
    }else{
    next();
    }
    }); app.get("/admin/login",function(req,res){
    console.log("2");
    res.send("管理员登录");
    });

express - use

  1. app.use() 也是一个中间件。与get、post不同的是,他的网址不是精确匹配的,而是能够有小文件夹拓展的(子路径)。

    比如网址: http://127.0.0.1:3000/admin/aa/bb/cc/dd
    app.use("/admin",function(req,res){ 
    
        res.write(req.originalUrl + "\n");   // /admin/aa/bb/cc/dd
    res.write(req.baseUrl + "\n"); // /admin
    res.write(req.path + "\n"); // /aa/bb/cc/dd res.end("你好");
    });

    如果写一个 /

    //当你不写路径的时候,实际上就相当于"/",就是所有网址
    app.use(function(req,res,next){
    console.log(new Date());
    next();
    });

    app.use()就给了我们增加一些特定功能的便利场所。

    实际上app.use()的东西,基本上都从第三方能得到。

    app.use(haha);
    
    function haha(req,res,next){
    var filePath = req.originalUrl;
    //根据当前的网址,读取public文件夹中的文件
    fs.readFile("./public/" + filePath,function(err,data){
    if(err){ //文件不存在
    next();
    return;
    }
    res.send(data.toString());
    });
    } //静态服务
    app.use(express.static("./public"));
    // jingtai路由 下才会访问静态文件
    app.use("/jingtai",express.static("./public"));

ejs - render()、express - send()、原生 - end()

  1. 大多数情况下,渲染内容用res.render(),将会根据views中的模板文件进行渲染。

    如果不使用views文件夹,那么app.set("views","aaaa");

  2. 如果想写一个快速测试页,可以使用res.send()

    这个函数将根据内容,自动帮我们设置了Content-Type头部和200状态码。

  3. send()end()一样,只能用一次。

    和end不一样send()能够自动设置MIME类型。

  4. 如果想使用不同的状态码,可以:

    res.status(404).send('Sorry, we cannot find that!');

  5. 如果想使用不同的Content-Type,可以:

    res.set('Content-Type', 'text/html');

GET请求和POST请求的参数

  1. GET请求的参数在URL中,在原生Node中,需要使用url模块来识别参数字符串。在Express中,不需要使用url模块了。可以直接使用req.query对象。
  2. POST请求在express中不能直接获得,必须使用body-parser模块。使用后,将可以用req.body得到参数。但是如果表单中含有文件上传,那么还是需要使用formidable模块。