• 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 + "
      ");   // /admin/aa/bb/cc/dd
          res.write(req.baseUrl + "
      ");    //  /admin
          res.write(req.path + "
      ");     //    /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模块。
  • 相关阅读:
    如何使标签a处于不可用状态
    document.referrer的使用和window.opener 跟 window.parent 的区别
    纯CSS让overflow:auto页面滚动条出现时不跳动
    闭包的使用实例
    VMware workstation使用小技巧
    个人命令简记
    中国剩余定理
    UVA 10603 倒水问题
    Haybale Stacking(差分数组 + 求中位数的一些方法 + nth_element)
    POJ 1511 Invitation Cards (最短路的两种方法spfa, Dij)
  • 原文地址:https://www.cnblogs.com/topyang/p/7834063.html
Copyright © 2020-2023  润新知