我的node模块备忘录

作者: F菌fghpdf | 来源:发表于2016-04-24 00:48 被阅读402次

    最近工作中积累了不少模块的使用办法,特此备忘啦,哇咔咔


    TCP服务器

    TCP服务器需要用到net模块,建立TCP服务器的场景还是很多的,很多硬件发送帧都需要TCP服务器来接收,学会这个还是很实用,我用的方法也不多,大致为:

    • 创建服务器: createServer
    • socket连接建立
    • socket处理
    • socket连接关闭

    直接上代码

    var net = require('net');
    
    //这里需要注意,直接写回环地址,比如localhost,或者127.0.0.1,就只会监听本机发来的连接,
    //写成0.0.0.0 就可以处理发送到本机的所有连接了
    var HOST = '0.0.0.0';
    var PORT = 3030;
    
    //创建tcp服务器,注意末尾需要把host和port加上,表示监听本机3030端口
    net.createServer(function(sock) {
    
        // 输出我们获得的连接
        console.log('CONNECTED: ' +
            sock.remoteAddress + ':' + sock.remotePort);
    
        // 这是socket实例数据处理的一个事件,收到的数据会进入这里,然后交给回调函数的第一个参数
        sock.on('data', function(data) {
            //你可以在这里处理这个数据,做点什么事吧
        });
    
        // 这是socket实例关闭连接的一个事件
        sock.on('close', function(data) {
            console.log('CLOSED: ' +
                sock.remoteAddress + ' ' + sock.remotePort);
        });
    
    }).listen(PORT, HOST);//不要忘记这里
    
    //加个信息提示一下
    console.log('Server listening on ' + HOST +':'+ PORT);
    

    HTTP服务就不说了,做网站的开不了HTTP服务器,还是先去看看框架吧


    Request模拟报文发送

    Request我现在多用于测试自己写的api,大致使用:

    • get方法
    • post方法

    还是直接上代码

    var request = require('request');
    
    ///这里就是设置一下参数了
    var options = {
        //url不必说了,发向那个就写那个,这里是我的一个例子
        url: 'http://localhost:3000/commodityManage/purchaseAdd',
        //报文头,这里就是按需填写了,我因为希望发送和接收都是json格式,所以这么写
        //一般为了安全会在报文头加token,这里你也是可以模拟的,可以去浏览器抓取请求报文,能理解的更深一些
        headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/json'
        },
        //这里就是填写数据了,我这里使用的是post方法,get的话是不用加的,json格式大家应该看的懂吧
        form: {
            'commodityList': [
                {
                    'commodityName': '坦克杯',
                    'commodityId': '4',
                    'commodityPrice': 79999,
                    'commodityNumber': 1
                },
                {
                    'commodityName': '飞机杯',
                    'commodityId': '5',
                    'commodityPrice': 128,
                    'commodityNumber': 1
                }
            ],
            'purchasePrice': 79999,
            'userId': '2'
        }
    };
    
    //这里为了方便写了一个函数,大家也可以直接放在下面,但是不推荐这么做
    //在这里做了一个检测,当request抓取到error时,会传进回调函数的第一个参数,而第二个参数是返回报文
    //第三个就是返回的信息了。200是正常处理的状态,所以进行一个显示操作
    function callback(error, response, body) {
        if (!error && response.statusCode == 200) {
            var info = JSON.parse(body);
            console.log("info:", info);
        }
    }
    
    //这里也要注意,post需要这么写,使用get时,直接写request(options, callback);
    request.post(options, callback);
    

    当然,这是给http服务器发送报文,tcp发送报文的方式在net模块中有,非常简单,不再赘述


    Buffer处理

    Buffer处理也是我跟硬件打交道需要掌握的,硬件一般走tcp来传输数据,传输到的数据一般都是流式数据
    这次工作我主要接触的是16进制的buffer,所以我大致用到的方法:

    • buffer转json
    • json转字符串

    这次处理也是比较闹心,比较硬件给的buffer不是那么听话,所以先用JSON.stringify()来统一格式,然后转成字符串数组,但是这么做出了一个新问题,转出来的是10进制的,而我需要多16进制的字符串进行解析,所以,就多一个10进制转16进制的步骤,代码如下

    var dataPromise_1 = JSON.stringify(data);
    var dataPromise_2 = JSON.parse(dataPromise_1);
    var array = dataPromise_2.data;
    var str = '';
    for(var num = 0; num < array.length; num++) {
         //这里因为10进制中,01变成了1,为了还原,所以有了这个步骤
         if(array[num] < 16) {
              str += '0' + array[num].toString(16);
          } else {
              str +=  array[num].toString(16);
          }
    }
    

    也许有人说,为什么不用toString(),硬件传过来的buffer编码布吉岛方式呀,心里苦,只能傻傻的这么暴力解决了


    一些关于时间处理的函数

    这次也是大大刷新了我对时间函数的用法,js提供的date对象,真是太好用了,不需要其他的模块,已经非常强大了
    看api手册就能获得大部分信息,平时用chrome的控制台也能补全函数,不怕忘记,说几个这次遇到的问题

    1. 时区对齐
      服务器直接获取客户端的时间对象,可能会出现在原有基础上加上8小时的问题,这个情况出现的原因是,我们国家统一采用北京所在时区的时间,而我国幅员辽阔,横阔多个时区,避免时区带来的混乱,所以有了这8小时的误差,但是我们在开发中,很多时候也是使用北京时间,所以就得消除8小时误差带来的影响
    date.setHours(date.getHours() + date.getTimezoneOffset() / 60);//这样来消除
    
    1. 获得当前星期是一年中的第几个星期
      一年中的星期数是有限的,对他们一一编号,在做时间选择的时候,是非常方便的,方法也非常简单
    function getWeek(date, callback) {
        var time,week,checkDate = new Date(date);
        checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
        time = checkDate.getTime();
        checkDate.setMonth(0);
        checkDate.setDate(1);
        week=Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
        callback(week);
    }
    

    3.关于时间选择器
    大家应该都知道datetimepicker,我们可以通过这个选取两个时间点,来获取这两个时间点之间的数据,但是这里需要注意一点,我们在时间选择器上选择了年月日形式的两个时间点,比如2016年4月1日到2016年4月3日,我们想获取的是1号,2号和3号的数据,但是时间选择器的终止时间是2016年4月3日 00:00:00,这样就只能获取1号和2号的数据了,所以需要将时间拨快一天,这里就用到setDate这个方法

    date.setDate(date.getDate() + 1);
    

    登录相关的模块补充

    登录相关的模块在我的另外一篇文章中系统的讲过了
    点击这里查看
    这里做一个小小的补充,就是我用到的加密模块bcrypt-nodejs
    我们一般在数据库保存的是密文密码,不是明文密码,密码学是一个专门的学问,有兴趣的可以深入参考一下,这里我只说用法

    var bcrypt = require('bcrypt-nodejs');
    var password = user.password;//用户传入的明文密码
    var hash = bcrypt.hashSync(password);//保存这个就行了
    

    获取url的参数

    我现在一般使用两种方法来在url里添加参数,所以,后台获取参数也会有所不同

    1. 在url加入json字符串
      这应该是很常见的方法,前端js可以在请求的url里加上json字符串,格式还可以自己定义,只要前后端保持一致就行,node在后台的解析也很方便,不用自己写正则表达式了
    var url = require('url');
    var token = (url.parse(req.url, true).query.token;//假设我传入的参数名是token
    
    1. 路由参数
      使用过express的童鞋应该知道这个,在参数比较少的情况下,这个东西也是很好用的,它不需要额外的模块来解析
    var express = require('express');
    var router = express.Router();
    //路径后面加上:,再跟上的就是参数名
    router.get('/index/:id', function(req, res, next) {
       //参数名保持一致,就能从params里取到参数的值了
      var id = req.params.id;
    });
    

    生成唯一的短Id

    有的时候,做唯一性标识,而且不希望太长的时候,这是个很方便的模块

    我自己生成的短Id

    大家可以算算,你的数据量有多大时,会出现重复

    var shortid = require('shortid');
    var appId = shortid.generate();
    

    还有很多用法自行google


    文件上传

    由于我使用的是express,官方推荐的中间件就是multer,这个东西确实不错,你不用这个,直接在req里是取不到文件的
    使用它,有几个点要注意

    1. form表单必须有enctype="multipart/form-data" ,而且提交方式为post
    2. 多文件上传,input必须带上multiple="multiple",否则默认只能传一个文件
    3. 后台对于单文件和多文件的处理是不同的,单文件的路径保存在file里,多文件保存在files里
    var multer = require('multer');
    
    var appStorage = multer.diskStorage({
        destination: function(req, file, callback) {
            //存放的位置
            callback(null, 'public/images');
        },
        filename: function(req, file, callback) {
            var appId = req.params.appId;
            console.log('appId', appId);
            var fileFormat = (file.originalname).split(".");
            //这一步会将存放的文件重命名成你想要的名称
            callback(null, file.fieldname + '-' + appId + '.' + fileFormat[fileFormat.length - 1]);
        }
    });
    
    //单文件使用single,里面的参数必须和input里的name一致
    var startUpload = multer({ storage: appStorage}).single('startImages');
    
    //多文件使用array,里面的参数必须和input里的name一致
    var carouselUpload = multer({ storage: appStorageArray}).array('carouselImages');
    
    //单文件处理
    startUpload(req, res, function (error) {
            if (error) {
                //错误处理
            } else {
               //注意是file
                console.log(req.file.path);
            }
    });
    
    //多文件处理
    carouselUpload(req, res, function (error) {
            if (error) {
                //错误处理
            } else {
               //注意是files,这里保存的是一个数组
                console.log(req.files[0].path);
            }
    });
    
    

    异步编程

    讲真,node自带并发太折磨人了,写一个for循环,竟然每个循环体都是同步进行的,一旦有数据相关,for循环都不能用,一般方法是写回调,但是回调太多就成了大括号陷阱了,所以还是要借助模块来帮忙,所以,我目前是采用两种方法解决异步编程

    • 数量少写回调
    • 数量多用async

    简单说一下async我用的用法

    var async = require('async');
    var taskList = [task_1, task_2, task_3];
    async.eachSeries(taskList, function(item, callback_async) {
            //item里面有taskList的值,用它可以来取值
           //做点什么吧,接下来的回调会进入下一个任务
          callback_async(null, item);
        }, function(err) {
            //错误处理
            console.log(err);
            if(err) {
                callback({ success: false, errorMessage: err});
            } else {
                callback({ success: true});
            }
    });
    

    有的时候会发现async也比较麻烦,那么推荐另外一个库co
    co配合yield(es6),可以达到异步的目的

    const co = require('co');
    
    function* task_1() {
      // 你的函数
    }
    
    function* task_2() {
      // 你的函数
    }
    
    function* task_3() {
      // 你的函数
    }
    
    co(function *() {
      yield task_1;
      yield task_2;
      yield task_3;
    }).then().catch();
    

    闭包

    node本来就是js,说到js就得说说闭包呀,其实什么是闭包这个问题也是比较难理解的
    阮一峰的网络日志中有这么一个解释

    闭包就是能够读取其他函数内部变量的函数。

    那么,为什么要闭包?
    我们不可能把变量都设置为全局变量,在函数中的变量,我们也有取出来的需求,在java的类中,有get方法可以直接获取,而我们闭包所做的,也就类似于这个了。

    //Java
    public class init(){
         private String username;    
    }
    
    public String getUsername() {
       return username;
     }
    
    //js
    function init() {
      var name = "Mozilla"; 
      function displayName() { 
        alert(name);  
      }
      displayName();    
    }
    

    原型

    说到原型还是得把Java拿出来做对比,Java的类继承模型非常典型,而Js的则是饱受非议的原型继承
    Java的类继承不是重点,但是要理解原型最好还是参考一下Java,有差异才有比较
    对于Js,我还是习惯使用栗子,在Js的array对象中,是没有最大值,最小值的方法的,我们可以通过原型来精简我们的代码

    Array.prototype.max =
    function(){ 
      return Math.max.apply({},this) 
    } 
    
    Array.prototype.min = function(){ 
      return Math.min.apply({},this) 
    } 
    
    [1,2,3].max()// => 3 
    [1,2,3].min()// => 1
    

    这样数组就直接可以取最大值最小值了,这样相当于重写原型中的方法(虽然原本没有)

    原型的用法还很多,我们看看如何把js的对象做的和java的差不多
    方法一:

    var init = function(username) {
        this.username = username;
    };
    
    init.prototype = {
      getName: function() {
          return this.username;
      },
    
      setName: function(username) {
          this.username = username;
      }
    };
    
    var test = new init();
    init.setName("fghpdf");
    init.getName(); // => "fghpdf"
    

    方法二:

    var init = function(username) {
        this.username = username;
    };
    
    init.prototype = {
      getName = function() {
          return this.username;
      },
    
      setName = function(username) {
          this.username = username;
      },
      return {
           getName: getName,
           setName: setName
      }
    };
    
    var test = new init();
    init.setName("fghpdf");
    init.getName(); // => "fghpdf"
    
    

    proto) 属性

    该属性可以获取或设置一个对象的原型

    1. 创建一个以指定对象为原型的对象
    var obj = {
        __proto__: myProto,
        foo: 123,
        bar: "abc"
    };
    
    1. 为内置类型添加子类型
    var MyArrayProto = Object.create(Array.prototype);
    //还可以写成var MyArrayProto = {__proto__:Array.prototype};
    MyArrayProto.foo = function (...) { ... };
    function createMyArray() {
        var arr = Array.prototype.slice.call(arguments);
        arr.__proto__ = MyArrayProto;
        return arr;   
    }
    var myarr = createMyArray(1,2,3);    //myarr会有foo方法,也会有其他的数组方法
    

    函数序列化

    函数自带toString方法,可以把函数转成字符串

    function a() { 
      console.log("aaaa")
    };
    a.toString(); // => "function a(){console.log("aaaa")}"
    

    相关文章

      网友评论

        本文标题:我的node模块备忘录

        本文链接:https://www.haomeiwen.com/subject/yztfrttx.html