• nodejs学习随笔


    <一> 简述nodejs
    (社区:www.npmjs.com)可查找一些第三方模块。node.js的包管理系统已经成为世界上最大的开源库生态系统。JavaScript已经成为github上使用最多的语言。

      
      node提供大量的工具库,使得JavaScript可以调用操作系统级别的API(读取文件、开机、关机等),这些功能都是都是单纯的js不具备的。
      nodejs是可以让js运行在浏览器之外的服务器端的平台,是JavaScript语言的服务器运行环境,实现了文件系统、模块、包、操作系统API、网络通信等JS没有的功能,但是没有DOM和BOM。nodejs使用了来自Google ChromeV8引擎,V8引擎是世界上最快的js引擎(chrome浏览器也是使用的v8引擎,引擎就相当于是个解析器,将我们的代码解析为计算机可以识别的语言)。nodejs摒弃了传统平台依赖多线程来实现高并发的设计思路,采用的单线程、异步式I/O,事件驱动式的程序设计模型。(下面以实际生活中的餐厅举例说明)

      nodejs的主线程是单线程的(后台I/O是多线程的),所有阻塞的部分交给一个线程池处理,主线程通过一个队列跟线程池协作。所以nodejs内部实现是单线程非阻塞I/O异步编程
    先说几个概念
      ·多线程:就像是多个服务员同时工作,一个服务员负责一个顾客(开销大)。
      ·单线程:就像是一个餐厅只有一个服务员。
      ·回调:异步变成基本的方法。
      ·同步:就像一个服务员负责一位顾客,直到该顾客的餐全部上齐才会去负责下一位顾客。
      ·异步:就像一个服务员负责一位顾客点餐,然后菜单给了厨房之后就可以给下一位顾客点餐了,当第一个顾客的餐做好之后,厨师呼叫这个唯一的服务员,该服务员再给第一位顾客送餐。因此可知,nodejs是单线程异步编程。
      ·非阻塞:针对内核来说,向内核发起请求的时候不会阻塞主线程的执行,是实现异步的前置条件。如果厨师比作内核,那么非阻塞就是不让唯一的服务员等待,阻塞就是让唯一的服务员等待。一般来说,阻塞了就是同步了。
      ·I/O:Input从文件系统中读取文件(输入),Output向文件系统输入文件(输出)。输入输出的过程就像是做饭的过程。
      ·事件环:主线程按时间执行事件队列,由普通函数与回调函数组成。
    <二> 全局对象
    nodejs中的全局对象global,相当于js中的window。其中global也是global的属性,并且可以无限循环下去(global.global.global...)。
      __filename 表示当前模块的文件绝对路径
      __dirname 表示模块所在目录的绝对路径
      setImmediate 把参数函数放在下一个事件环中的顶部执行,相当于setTimeOut(fn,0),但效率更高一些。(见图1)

      Process(global属性)
        1,cwd:process.cwd() //输出当前的工作目录
        2,chdir:process.chdir("..") //切换到上级工作目录
        3,memoryUsage:process.memoryUsage() //内存的使用量,其中rss表示常驻内存(栈),heapTotal表示堆的总内存,heapUsed表示堆使用内存。nodejs的内存限制为1.4G左右,V8最多能用1.4G内存。
        4,nextTick:process.nextTick(fn)放在当前任务列表的尾部(见图1)

      global属性可以直接使用。其他不是global的属性,需要用require进行引用之后才可以使用。


    Tips:
    console.time("cost");
    这段代码执行的时间
    console.timeEnd("cost");

     

                      图1

    <三>发布一个包
    模块:在node中,每个js就是一个模块,module代表当前模块即当前js。用require加载同一个模块时,属于“===”的关系,因为require有缓存,即require.cache(存放着所有的模块缓存),用delete require.cache[某个模块],就可以从缓存中删除该模块。require是同步的,模块内部的变量是私有的。模块有三种:node自带模块、第三方模块和自定义模块。
      初始化一个项目,包名不能含大写,例如:
      1,创建并进入目录 mkdir fengmin \保证名字的唯一
      2,初始化项目 npm init fengmin
      3,编写命令行工具 app.js
      4,在package.json中添加 "bin":{"fengmin":"./app.js"}
      5,npm publish发布项目
      6,npm install fengmin -g \全局安装自己发布的模块
    全局安装:
      1,查看全局安装的根路径 npm root -g
      2,把模块安装到全局node_modules下面
      3,配置可执行文件
    package.json相关概念:
      一个包下面如果有package.json,则可在package.json文件中用main指定入口文件,如果没有该文件,则默认是该包下面的index.js为入口文件。

    <四>Buffer
    全局对象,暂时存放输入输出数据的一段内存。
    js没有二进制数据类型,但在处理TCP和文件流(图片、音视频等),必须要处理二进制数据,所以服务器端需要用二进制数据。node提供的Buffer对象对二进制进行操作,是一个全局对象,要提前确定放到缓存区中的字节数。
    1Byte = 8bit,一个中文用两个字节保存。
    parseInt可以把任意进制转换为十进制,如:parseInt("111111",2)和parseInt("0xff",16)
    把十进制转成任意进制用{}.toString,如:(255).toString(16)。
    定义Buffer的三种方式:
      1,var buffer = new Buffer(size),size表示指定的Buffer的长度,每次实例的新Buffer,输出可能不一样。buffer像是一个字节数组,可以直接赋值,如buffer[0]=0,也可以buffer.fill(0),把buffer中所有元素置为0。
      2,传个数组,buffer的长度就是数组的长度。如:var buffer = new Buffer([1,2,3,4,5,6]),其中传入的数组一定是数字数组,且数字的范围为0~255,如果不符合传入规则,则默认为0。
      3,字符串创建,var buffer = new Buffer(str,[encoding]),其中可选编码参数encoding的默认值为utf-8。如:var buffer = new Buffer("abc"),可得到其分别对应ASC码对应的buffer值,为[97,98,99]。
    buffer的常用方法
      1,合并buffer:Buffer.concat([buf1,buf2],length);
      2,复制Buffer:Buffer.copy(targetBuffer,targetstart,sourcestart,sourceend)
      3,判断一个对象是否Buffer用isBuffer方法,如:Buffer.isBuffer(buffer);

    <五>Util模块
    node自带的模块,并不是global的属性,所以需要用require引用。
      var util = require("util");
      1,util.inherits //子类可以继承父类原型上的方法
      2,util.inspect //
      3,isArray、isRegExp、isDate、isError

     

    <六>文件和路径
    node中用fs实现文件的读写,fs模块中,所有的异步方法的回调函数的第一个参数都是error对象,所以异步回调方法中的err有值,则说明出错了。

        var fs = require("fs")。
      fs.readFile	//异步方法,在回调方法处理数据
      fs.readFileSync("./1.txt")	//同步方法读取,有返回值,所有的异步方法一定会晚于同步方法的执行
      fs.writeFileSync("./2.txt","fengmin",{encoding:"utf8"})	//同步方法写入
      fs.writeFile	//异步方法写入,有回调函数        

    1,目录操作
    创建一个目录,与linux下的创建相似
      fs.mkdirSync("a");
      fs.mkdir("a",function(err){})
    读取目录下的文件,结果是一个数组

    fs.readdir("./a",function(err,files){
        files.forEach(function(file){
            //此处如果file是目录会报错,因此需要用isFile判断一下
            fs.readFile("./a/"+file,function(err,data){
                console.log(data.toString());
            })
        })
    })            

    判断一个文件是否存在
      fs.exist("./a",function(exist){//node中回调的特殊例子,第一个参数不是err,而是一个布尔值})
    2,路径操作
      var path = require("path");
      路径的“/”,在windows和linux下不一致,windows下是“”,linux下是“/”。
      常用方法:
        1,path.join将多个参数值字符串合并为一个路径字符串 //把多个路径合并为一个路径,如:path.join(__dirname,"a","b","c");
        2,path.resolve以应用程序根目录为起点,根据参数解析出一个绝对路径。普通字符串的参数表示当前目录的下级目录;“..”参数表示回到上一级目录;“/”开头表示一个绝对的根目录
        3,path.basename获取路径中的文件名
          path.basename(__filename) //path.js
          path.basename(__filename,".js") //path
        4,extname获取路径中的扩展名
          path.extname(__filename) //.js
        5,delimiter操作系统的分隔符,如环境变量中用到的,windows中用的是“;”,linux中用的是“:”
        6,sep路径的分隔符,如“”与"/"
        7,normalize 将非标准的路径字符串转为标准的。如:path.normalize("a/../b////c/d//")可以解析为bcd
        8,relative用于获取两个路径之间的相对关系。

    <七>事件

    发布/订阅模式
    EventEmitter方法
    on相当于addListener,可给某一个事件绑定多个回调函数
    once 事件只绑定一次,执行一次就不再执行了
    removeListener("事件名",callback),移除某事件监听的某一个回调函数
    removeAllListener
    emit("事件名","回调的参数")

    var EventEmitter = require("events")
    var events = new EventEmitter;
    events.addListener("click",function(){console.log("click")})
    events.emit("click");//click就是个名字,可随意换
    /*========*/
    var util = require("util");
    function Girl(){}
    util.inherit(Girl,EventEmitter);
    var girl = new Girl();
    girl.on("hungry",function(){console.log("吃饭")});
    girl.emit("hungry");

    <八>服务器

    根据请求的路径返回相应的结果,IP定了,一般地址也就确定了。可以使用netstat -anto查看网络状态,一个普通网站的访问过程是:
    1,浏览器向服务器发出一个http请求
    2,先把域名解析为IP地址(先找浏览器缓存:chrome://net-internal/#dns,如果找不到就会搜索操作系统缓存,再找不到就会读取本地的host文件,再找不到就会发起DNS系统调用、运营商DNS缓存、根域、com域.....)
    3,客户端通过随机端口向服务器发起TCP三次握手,建立TCP连接
    4,连接建立后,浏览器就可以发送http请求了
    webpack可以把所有用到的资源打包为一个,减少http请求
    5,服务器接收请求后,解析请求的路径和参数,后台处理

    <九>nodejs中的流

    流:一组有序的、有起点和终点的字节数据传输手段。是nodejs各种对象实现的抽象接口。所有的stream对象都是EventEmitter的实例,可以发射事件。上一个的输出是下一个的输入,就像是人与人之间传递东西。如:request、response、stdout(标准输出流)、console也是一种流。流是EventEmitter的子类,
    stream.Readable 可读流
      可读流触发的事件有:data、end、error
      可读流的方法有:setEncoding、pause(停止触发data事件)、resume(恢复触发data事件)、pipe
      创建一个可读流
      fs.createReadStream(path,[options]),其中,path是读取文件的路径;opations中,hightWaterMark,默认为64kb
    stream.Writable 可写流
      可写流的方法有:write、end、drain
      创建一个可写流,fs.createWriteStream(path,[options]),其中,options中的hightWaterMark,默认为16kb

     <十>模块

    每个js文件都是一个模块,模块内部声明的变量都是私有变量,外部无法访问

      ·创建模块就是创建一个js文件 math.js

      ·导出模块使用exports对象,在该js文件中exports.add=function(a,b){return a+b}

      ·加载模块 var math = require("./math.js")

      ·调用模块 var sum = math.add(1,2)

    模块可以分为三类

      ·核心模块 http fs path等

      ·文件模块 var math = require('./math.js')

      ·第三方模块  var async = require('async')

      三种类型的模块,都是通过require加载的。

    <十一>包和npm

    多个模块可以封装成一个包,npm是nodejs默认的包管理器,用来安装和管理node模块,网址为http://npmjs.org,可以以包的方式通过npm安装、卸载、发布包

    如何初始化一个项目:

      mkdir studynode 创建目录

      cd studynode 进入目录

      npm init 初始化项目描述文件

      注意项目的名称不能是别人已经注册的名称

    安装第三方模块

      ·全局安装 直接下载到node的安装目录中,各个项目都可以调用,适合工具模块,比如gulp。

      npm install -g [package name]

      ·本地安装 将一个模块下载到当前目录的node_modules子目录中,然后只有在当前目录和它的子目录之中,才能调用这个模块

      npm install [package name]

    <十二>HTTP

    服务器:可以是专业服务器,也可以是个人电脑,与硬件无关,与功能有关。能在特定IP服务器的特定端口上监听客户端的请求,并根据请求的路径返回相应结果都叫服务器。

    客户端:只要能向特定IP服务器的特定端口发起请求并接受响应的都叫客户端,比如mac、iPhone、ipad等。

    数据在服务器和客户端之间传递,比如文档、图片、音视频等。可以把服务器硬盘上已经有的静态文件发送给客户端,也可以由服务器经过逻辑处理生成的动态内容返回给客户端,比如计算出来的字符串或者json串。

    要让这些形形色色的机器能够通过网络进行交互,我们就需要指明一种协议(比如http/https)和一种数据封装格式(比如HTML/JSON)。http指的就是这种协议+数据格式的交流体系。

    一个http请求一般包括方法、url、协议版本和请求首部字段几部分

    一个http响应一般包括协议版本、状态码头、状态码的原因短语和响应首部字段等

    一个普通网站访问的过程:

      ·浏览器(或其他客户端如微信)向服务器发出一个HTTP请求

      ·先把域名解析为IP地址(chrome缓存一分钟(chrome://net-internals/#dns)->搜索操作系统缓存->读取本地host文件->发起DNS系统调用->运营商DNS缓存->找根域->com域)

      ·客户端通过随机端口向服务器发起TCP三次握手,建立了TCP连接

      ·连接建立后浏览器就可以发送HTTP请求了

        请求的url为http://user:pass@服务器地址:端口号/node/index.html?type=1#top,其中user:pass表示用户认证的登录信息

      ·服务器接收HTTP请求,解析请求的路径和参数,经过后台的一些处理之后生成完整的相应页面

      ·服务器将生成的页面作为HTTP响应体,根据不同的处理结果生成响应头,发回给客户端

      ·客户端(浏览器)接收到HTTP响应,从响应中得到的HTTP响应体里的是HTML代码,于是对HTML代码开始解析

      ·解析过程中遇到引用的服务器上的资源(额外的css、js、图片、音视频、附件等),再向服务器发送请求

      ·浏览器解析HTML包含的内容,用得到的css代码进行外观上的进一步渲染,js代码也可能会对外观进行一定的处理

    创建一个http.js:

    var http = require('http')
    var server = http.createServer(serve);//每当有请求来的时候,调用serve函数对客户端进行响应
    server.listen(8080,'localhost');//监听本机的8080端口
    function serve(request,response){
      //获取请求流里的相关信息:request.method(请求方法),request.url(请求里的url),request.headers(请求头)
      //设置响应信息:response.statusCode=200(状态码),
      //response.setHeader('Content-Type','text/html;charset=utf-8')(设置响应头,设置响应的类型,编码为utf-8)
      //response.setHeader('name','nodejs')
      //response.write(new Date().toString())(设置响应体)
      //response.end()(结束http响应)
    }

     解析查询字符串

    var http = require('http');
    var fs = require('fs');
    var mime = require('mime');
    var url = require('url');//对URL进行处理,把字符串转成对象
    /**
     *
     * @param request 请求
     * @param response 响应
     */
    function serve(request,response){
     //true 表示query转成对象
     var urlObj = url.parse(request.url,true);
     console.log(request.url,urlObj.query.name,urlObj.query.age);
     var pathname = urlObj.pathname;
    
     if(pathname == '/'){
        //设置响应的类型,编码为utf-8
        response.setHeader('Content-Type','text/html;charset=utf-8');
        //读取文件的内容并且将读到的内容写入响应体
        fs.readFile('index.html',function(err,data){
          response.write(data);//写响应体
          response.end();
        })
      }else if(pathname == '/clock'){
         var counter =0;
        var int = setInterval(function(){
            response.write(new Date().toString());
            counter++;
            if(counter==5){
                clearInterval(int);
                response.end();
            }
        },1000);
     }else{
        static(pathname,response)
      }
    
      function static(pathname,response){
        //设置响应的类型,编码为utf-8
        response.setHeader('Content-Type',mime.lookup(pathname)+';charset=utf-8');
        //读取文件的内容并且将读到的内容写入响应体
        fs.readFile(pathname.slice(1),function(err,data){
          response.write(data);//写响应体
          response.end();
        })
      }
    
    
    }
    //每当有请求来的时候调用serve函数对客户端进行响应
    var server = http.createServer(serve);
    
    server.listen(8080,'localhost');
  • 相关阅读:
    Oracle面试题及答案整理
    Oracle问题总结
    Dubbo(四) -- telnet命令
    Dubbo(三) -- 多协议支持与多注册中心
    每天一算法 -- (冒泡排序)
    Dubbo(二) -- Simple Monitor
    数据库优化
    ActiveMQ内存配置和密码设置
    Dubbo源码导入Eclipse遇到的问题
    Dubbo(一) -- 初体验
  • 原文地址:https://www.cnblogs.com/web-fengmin/p/6245072.html
Copyright © 2020-2023  润新知