nodeCZBK-笔记1


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模块。
posted @ 2017-11-14 19:05  杨旺  阅读(171)  评论(0编辑  收藏  举报