• Node基础


    第一天

    Node简介

    -什么是Javascript

    +脚本语言

    +运行在浏览器中

    +一般用来做客户端页面的交互(Interactive)

    -JaveScript的运行环境?

      +是不是运行在浏览器呢?

       ,不够严谨

      +是运行在浏览器内核中的JS引擎(Engine)(而不是浏览器,浏览器是一个大的概念)

        

    浏览器的作用

    -浏览器中的JavaScript可以做什么?

    +操作DOM(对DOM的增删改、注册事件)

    +AJAX/跨域

    +BOM(页面跳转、历史记录、console.log()、alert())

      +ECMAScript

    -浏览器中的Javascript不可以做什么?

      +文件操作(文件和文件夹的CRUD)

      +没有办法操作系统信息

      +由于运行环境特殊,并不是在服务器执行,而是到不认识的人的浏览器客户端中执行

    -在开发人员能力相同的情况下编程语言的能力取决于什么

      +语言本身?

      +语言本身只是提供定义变量,定义函数,定义类型,流程控制,循环结构之类的操作

      +取决于运行该语言的平台(环境)

      +对于JS来说,我们常说JS实际是ES,大部分能力都是由浏览器的执行引擎决定

      +BOM和DOM可以说是浏览器开发出来的接口

      +比如:Cordova中提供JS调用摄像头,操作本地文件的API

      +Java既是语言也是平台

      +Java运行在Java虚拟机(跨操作系统)

    +PHP 即使语言也是平台

      +C#语言 平台: .net framework(Windows)

      +C#可以运行在Mono平台

      +因为有人需要将C#运行在Linux平台,所以出现了MONO

    -JavaScript只可以运行在浏览器中吗?

      +不是

      +能运行在哪取决于,这个环境有没有特定的平台

    ### 什么是Node

    Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. Node.js' package ecosystem, npm, is the largest ecosystem of open source libraries in the world.

    ·Node就是服务器端的一个Javascript运行环境

     

    Node在Web中的用途

    ·分发数据请求,渲染HTML

    (因为NodeJS并发数相比传统平台很优秀)

    常见命令行操作

    Path变量

    添加到Path变量后可以直接在CMD中打开

    *Windows默认使用反斜线

    *在Linux标准中一般使用/正斜线(除法的那个斜线)

    Node REPL环境

     

    浏览器中 全局对象是Window对象 在node中是没有window对象的。

    process.argv

    打印一个数组,第一个是node的路径,第二个是当前运行的程序的路径,后面的内容是数组中的参数。

    process.stdout

    process.stdout(msg) = console.log(`${msg} `);

    Debug

     

    process.stdin.on(‘data’,(input)=>{

      …

    })

    //输入的字符最后肯定是一个回车符

    所以要trim掉

    process.stdin.readline() //用户的操作是无状态的,所以一般都不用这个

    process.stdout.write();

    异步操作

    console.time(‘’)

    console.timeEnd(‘’)

    事件队列:

    var fs = require(‘fs’);

    fs.readFile(‘./typings/node/aa.ts’,’utf8’,(err,data)=>{

      if(err) throw err;

    console.log(data);

    })

     

    第二天

    错误优先的回调函数

    因为操作大多数都是异步形式,没法通过trycatch来捕获异常

    所以写错误优先 的回掉函数

    进程和线程

    进程:进行中的程序

     

    创建线程,就像当项目经理招人一样。需要时间,并不是那么容易。

    不要太较真,没有任何一个人是那么完美。

    多线程都是假的,因为只有一个CPU(单核)

    线程之间共享某些数据,同步某个状态都很麻烦

    更致命的是:

    -创建线程耗费时间

    -线程数量有限

    -CPU

    单线程产品nginx  Redis

    事实证明这些单线程产品比多线程产品性能要好。

    Node.js的优势

    事件驱动,非阻塞

    如果使用Java或者PHP需要创建新线程,分配资源,需要的代码和其他资源也比较多而复杂,而Node实现起来非常简单。在每一个异步操作后都会有一个回调。

    非阻塞I/O

    Node的核心特性

    const fs = require(‘fs’);

    事件驱动:

    const fs = require(‘fs’)

    //判断是否存在list文件

    fs.stat(‘./list.md’,(err,stats)=>{

      if(err) throw err;

      //存在删除

    /* 

    fs.unlink(‘./list.md’,(err,)=>{

    if(err) throw err;});

    */ console.log(stats);

    })

    const fs = require('fs');

     

    console.time('timer');

     

     fs.stat('./1.txt',(err,stats)=>{

        if(err) console.error(err);

        //创建

        

          fs.writeFile('./list.md',new Date(),(err)=>{

            if(err) console.error(err);

            console.log('创建成功');

           

            console.timeEnd('timer');

          })

     

     });

    事件队列:  Event Queue

    主线程执行,遇到异步事件(阻塞操作)会塞到事件队列里,

    主线程执行完开始到事件队列中拿第一个异步事件进行执行

    如果在这个异步事件的callback中有异步操作,那么把他放到事件队列中,

    阻塞操作是交给内部线程池来完成的

    Node在底层维护了一个线程池(里面有很多线程)如果有需要耗时的操作,就会交给线程来操作。

    不用的线程再次关回小黑屋(图片最下面的部分)

    Node为什么能实现非阻塞,

    原因是他在实现调度的工作,本身并没有实质的读文件 读网络的工作。

    调度。

     

    非阻塞的优势

    ·提高代码的相应效率

    ·充分利用单核CPU的优势(但是目前市场上大多数是多核CPU)

    ·改善I/O的不可预测带来的问题

    ·如何提高一个人的工作效率

    web中的单线程

    //Node 开发服务器的阻塞情况

    const http = require('http');

     

    let count = 0;

     

    const server = http.createServer((req,res)=>{

    //此回调回在有任何用户请求时触发 

    res.writeHead(200,{'Content-Type':'text/html;charset=utf-8'});

    res.write(`你是第${count++}个访问用户`,);

    res.end();

     

    });

     

    server.listen(2080,(err)=>{

       if(err) throw err;

       console.log('成功启动web服务,端口:2080');

    });

     

    下面把代码改成这样,

    因为事件队列对阻塞事件的处理,

    在第十个请求的时候会一直卡死。

    如果是PHP就没事儿,因为那个是不同的线程去执行的。

    注意:node开发时一定要谨慎防止这种情况的出现。

    模块化的开始

    //实现命令行计算器

     

    //1.接收参数

    const args = process.argv.slice(2);

    //['node 执行程序所在路径 ','当前脚本所在路径',...参数]

     

    //2.分析参数

    if(args.length != 3){

      console.log('参数不合法');

      throw new Error('err'); //到这里跳出这个文件

    }

     

    //parameter

    let p1 = args[0];

    let operator = args[1];

    let p2 = args[2];

     

    let result;

     

    switch (operator) {

     

      case '+':

        result = parseFloat(p1) + parseFloat(p2);

        break;

      case '-':

        result = parseFloat(p1) - parseFloat(p2);

        break;

      case 'x':

      case '*':

        result = parseFloat(p1) * parseFloat(p2);

        break;

      case '÷':

      case '/':

        result = parseFloat(p1) / parseFloat(p2);

        break;

      default:

        throw new Error('不被支持的操作符' + operator);

        break;

    }

     

    console.log(result);

     

    其他的社区规范.. CMD规范

    CommonJS规范

    在Node中实现的是CommonJS规范

     

    CommonJS与CMD规范的区别就是不需要用define了。

    文件模块 require(‘./../’)

    核心模块 require(‘fs’)   //并没有写什么目录

    所有的文件操作必须是绝对路径(物理路径)



    module1.js

    //获取当前脚本所在路径

    console.log(__dirname);

    //文件路径

    console.log(__filename);

     

    const fs = require('fs');

     

    //所有的文件操作必须是绝对路径(物理路径)

    fs.readFile(__dirname+'/../list.md','utf8',(err,content)=>{

      if (err) throw err;

        console.log(content);

     

    });

    2.js

    //模块中的全局成员

     

    const modul = require('./module/module1.js');

     

    为什么文件操作必须是绝对路径?

    答:因为require以后执行的话module1.js中的相对路径就会产生错误进而报错。

    //module对象

     

    console.log(module);

    Module {

      id: '.',

      exports: {},

      parent: null,

      filename: 'C:\Users\BDSOFT\Desktop\nodelearn\4.js',  loaded: false,

      children: [],

      paths:

       [ 'C:\Users\BDSOFT\Desktop\nodelearn\node_modules',

         'C:\Users\BDSOFT\Desktop\node_modules',

         'C:\Users\BDSOFT\node_modules',

         'C:\Users\node_modules',

         'C:\node_modules' ]

    }

    Node.js 全局对象

    JavaScript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。

    在浏览器 JavaScript 中,通常 window 是全局对象, 而 Node.js 中的全局对象是 global,所有全局变量(除了 global 本身以外)都是 global 对象的属性。

    在 Node.js 我们可以直接访问到 global 的属性,而不需要在应用中包含它。

     

    __filename

    __dirname

    setTimeout(cb, ms)

    clearTimeout(t)

    setInterval(cb, ms)

    console

    process

    为什么说是伪全局变量呢?

    答:因为在Node REPL里输__dirname是没有的。__dirname存在于node模块(.js文件)内部。

    模块本身就是一个封闭的作用域,没有必要写自执行函数。

    自己实现一个require

    module4.js:

    function say(msg){

        console.log(msg);

    }

    module.exports = {say};

     

    1.js:

    //自己写一个require函数

     

    function $require(id){

     

    // 1.先找到文件 如果文件不存在 Error: Cannot find module  'xxx.js'

    // 2.读取文件内容 内容是JS代码

    const fs = require('fs');

    const path = require('path');

     

    //要加载的js文件路径(完整路径)

    const filename = path.join(__dirname,id);

    const dirname = path.dirname(filename);

     

    let code = fs.readFileSync(filename,'utf8');

     

    // 3.执行代码,所要执行的代码 需要营造一个私有空间

    // 定义一个数据容器,用容器去装模块导出的成员

    let module = {id:filename,exports:{}};

    let exports = module.exports;

    code = `

    (function($require,module,exports,__dirname,__filename){

         ${code}

    })($require,module,exports,dirname,filename)`;

     

    eval(code);

     

    // 4.返回值

    return module.exports;

     

    }

     

     

    var m4 = $require('./module/module4.js');

     

    m4.say('hello');

    (然而没有讲export是怎么实现的.)

     

    require扩展名

    先加载js,如果没有按顺序往下面加在。

     

    //require不仅仅可以载入js模块,也可以用来读取配置信息(JSON)

    如果require了一个文件夹,会默认加载这个文件夹下的

    package.json中main指向的文件,

    如果没有定义就会载入index.js

    require模块加载机制

    从当前目录向上搜索node_modules目录中的文件(就近加载)

     

    模块的require缓存

     

    date.js:

    module.exports =  new Date();

     

     

    1.js:

    //模块的缓存

     

    setInterval(()=>{

     

      var date = require('./date.js');

      console.log(date.getTime());

     

    },1000);

    运行结果:

    说明模块有缓存机制。

    console.log(module.require)

    打印出一个对象,被引入的模块将被缓存在这个对象中。

    缓存结构示例:

    { 'C:\Users\BDSOFT\Desktop\nodelearn\require.js':

        Module {

          id: '.',

          exports: {},

          parent: null,

          filename: 'C:\Users\BDSOFT\Desktop\nodelearn\require.js',

          loaded: true,

          children: [

                [Module

                ]

            ],

          paths: [ 'C:\Users\BDSOFT\Desktop\nodelearn\node_modules',

             'C:\Users\BDSOFT\Desktop\node_modules',

             'C:\Users\BDSOFT\node_modules',

             'C:\Users\node_modules',

             'C:\node_modules'

            ]

        },

       'C:\Users\BDSOFT\Desktop\nodelearn\date.js':

        Module {

          id: 'C:\Users\BDSOFT\Desktop\nodelearn\date.js',

          exports: 2018-04-27T09: 31: 32.836Z,

          parent:

           Module {

             id: '.',

             exports: {},

             parent: null,

             filename: 'C:\Users\BDSOFT\Desktop\nodelearn\require.js',

             loaded: true,

             children: [Array

                ],

             paths: [Array

                ]

            },

          filename: 'C:\Users\BDSOFT\Desktop\nodelearn\date.js',

          loaded: true,

          children: [],

          paths: [ 'C:\Users\BDSOFT\Desktop\nodelearn\node_modules',

             'C:\Users\BDSOFT\Desktop\node_modules',

             'C:\Users\BDSOFT\node_modules',

             'C:\Users\node_modules',

             'C:\node_modules'

            ]

        }

    }

    清空一个缓存试试:

        Object.keys(require.cache).forEach((key)=>{

            delete require.cache[key];

        });

    *一般情况下不会手动清空它(闲得蛋疼),这里就是示例一下

    加缓存:

     

    function $require(id) {

        // 1.先找到文件 如果文件不存在 Error: Cannot find module  'xxx.js'

        // 2.读取文件内容 内容是JS代码

        const fs = require('fs');

        const path = require('path');

        //要加载的js文件路径(完整路径)

        const filename = path.join(__dirname, id);

       

        $require.cache  =$require.cache || {};

       

       

        if($require.cache[filename]){

            return $require.cache[filename].exports;

        }

        const dirname = path.dirname(filename);

        let code = fs.readFileSync(filename, 'utf8');

        // 3.执行代码,所要执行的代码 需要营造一个私有空间

        // 定义一个数据容器,用容器去装模块导出的成员

        let module = { id: filename, exports: {} };

        let exports = module.exports;

        code = `

        (function($require,module,exports,__dirname,__filename){

             ${code}

        })($require,module,exports,dirname,filename)`;

        eval(code);

        $require.cache[filename] = module;

       

        // 4.返回值

        return module.exports;

    }

    第三天

    学编程要学会看API文档

    NPM包管理工具

    where node

    npm config ls

    全局配置文件 npmrc

    prefix前缀

    设置npm install –g安装到哪里…

    npm config set prefix C:Develop vm pm

    npm config get prefix

    NodeResourceManager介绍

    npm install nrm –g

    rm ls

    这个星球所有的源!

    nrm use cnpm

    再看npm config ls

    文件系统操作

    文件操作必须使用绝对路径

    fs:

    path:

    readline:

    Windows默认编码 GBK

    笔记本保存的话显示ANSI,其实就是GBK

    path模块操作

    详见文档

    path.relative(from,to)

    path.resolve

    同步调用和异步调用

    fs.readFile

    fs.readFileSync

    读文件没有指定编码默认读取的是一个buffer(缓冲区)

    buffer

    这个”瓢”只能装4个字节

    LE、BE

    little-endian 小字节序 符合人的思维,地址低位存储值的低位,地址高位存储值的高位。

    big-endian 大字节序 最直观的字节序,地址低位存储值的高位,地址高位存储值的低位。

    转换成Base64

    通过Data:URL协议来打开看

    DATA-URI 是指可以在Web 页面中包含图片但无需任何额外的HTTP 请求的一类URI.

    一般的图片都会单独请求

    (这里是一个简单的介绍)

    文件编码的问题

    滚动显示歌词

    // 动态显示歌词

    const fs = require('fs')

    const path = require('path')

     

    fs.readFile(path.join(__dirname, './nineteen.lrc'), (err, data) => {

     

        if (err) throw err;

     

        var lines = data.toString('utf8').split(' ');

     

        // [00:17.78]走过初识你的地点

        // 第一组,第二组,第三组,第四组

        var regex = /[(d{2}):(d{2}).(d{2})](.+)/;

     

        var begin = new Date().getTime();

     

        //遍历  

        lines.forEach((line) => {

     

            var matches = regex.exec(line);

     

            // 如果匹配上了

            if (matches) {

                var m = parseFloat(matches[1]);

                var s = parseFloat(matches[2]);

                var f = parseFloat(matches[3]);

                var lyric = matches[4]; //当前行歌词其实不是立即执行

     

     

                //由于下达输出任务的时刻不同,设置定时器其实有一些几毫秒的误差

                var offset = new Date().getTime() - begin;

     

                setTimeout(() => {

                    console.log(lyric);

                }, m * 60 * 1000 + s * 1000 + f - offset);

     

            } else {

                // 不是一行歌词 

                console.log(line);

            }

     

        });

     

    });

    流的方式读取

     

    node会根据系统资源情况自动生成buffer大小 先把数据写到缓冲区,然后再从缓冲区写入磁盘中。

    const fs = require('fs')

    const path = require('path')

    const readline = require('readline')

     

    var filename = path.join(__dirname, './nineteen.lrc');

     

    var streamReader = fs.createReadStream(filename);

     

    var data = '';

     

    streamReader.on('data', (chunk) => {

     

        //chunk只是文档的一个片段 不完整

        data += chunk.toString();

     

    });

     

    streamReader.on('end',()=>{

          //通知你已经结束了 此时data是完整的

       

        console.log(data);

     

    });

    利用ReadLine读取:

    var streamReader = fs.createReadStream(filename);

     

    //利用readline读取

    var rl = readline.createInterface({input:streamReader})

     

    var begin = new Date().getTime();

     

    rl.on('line',(line)=>{

     

        //function()...

     

    });

     

    //经过console.log(typeof line)来看,

    readline读出来的已经是string了,不是buffer。不是buffer

    文件写入

    //文件写入

     

    const fs = require('fs');

    const path = require('path');

     

    //fs.writeFile();

     

    //JSON.stringify 序列化

    //JSON.parse 反序列化

     

    /*

    //默认覆盖文件

    fs.writeFile(path.join(__dirname, './temp.txt'), { id: 10 }, (err) => {

        //要是写入对象,其实就是toString()了

     

        if (err) {

     

            //读文件是不存在报错

            //写文件可能出现的..意外错误

            //..文件权限问题

            //..文件夹找不到(不会自动创建文件夹)

     

            console.log('error');

     

        } else {

     

            console.log('success');

     

        }

     

    });

     

    */

     

    //fs.writeFileSync();

     

    //java c

    // try{

     

    // } catch(error){

     

    // }

     

    //fs.createWriteStream();

     

    // var streamWriter = fs.createWriteStream(path.join(__dirname,'./temp.txt'));

     

    // setInterval(()=>{

     

    // 不断的往里面写,代码操作的是内存中的数据,(不是磁盘中的数据),然后这个副本会写到磁盘中

     

    //     streamWriter.write('hello',()=>{

    //         console.log('+1');

    //       });

         

       

    // },1000);



    /*追加

    fs.appendFile(path.join(__dirname, './temp.txt'), JSON.stringify({ id: 10 }), (err) => {

        //要是写入对象,其实就是toString()了

     

        if (err) {

     

            //读文件是不存在报错

            //写文件可能出现的..意外错误

            //..文件权限问题

            //..文件夹找不到(不会自动创建文件夹)

     

            console.log('error');

     

        } else {

     

            console.log('success');

     

        }

     

    });

     

    */

    其他文件操作

    fs.stat.isFile()…

    .isDirectory…

    重命名

    fs.rename

    fs.renameSync

    删除文件

    fs.unlink

    fs.unlinkSync

    //移动文件和重命名

    //移动文件和重命名

     

    const fs = require('fs');

    const path = require('path');

     

    var currentPath = path.join(__dirname,'./temp1.txt');

     

    var targetPath = path.join(__dirname,'./temp2.txt');

     

    fs.rename(currentPath,targetPath,(err)=>{

        if(err) {console.log(err)}

    });

     

    //和命令CMD中的mv操作一样。

     

    //删除文件和rm操作差不多

    打印当前目录文件列表

    //打印当前目录所有文件

     

    const fs = require('fs');

    const path = require('path');

     

    //获取当前有没有传入目标路径

     

    var target = path.join(__dirname,process.argv[2] || './');





    fs.readdir(target,(err,files)=>{

     

        files.forEach(file=>{

            //console.log(path.join(target,file));

       

            fs.statSync(path.join(target,file),(err,stats)=>{

             

     

              console.log(`${stats.mtime} ${stats.size} ${file})`);

     

            });

       

        });

     

    });

     

    递归目录树

    //递归目录树

     

    //1.先写一层的情况

    //2.抽象出递归参数

    //3.找到突破点(避免死循环)

    //自己调自己, 某种情况(肯定会存在的)不调用

     

    const fs = require('fs');

    const path = require('path');

     

    var target = path.join(__dirname, process.argv[2] || './');

     

    var level = 0;

     

    load(target,0);

     

    function load(target,depth) {

        //depth 0 = ''

        //depth 1 = '| '

        //depth 2 = '| | '

        var prefix = new Array(depth+1).join('| ');

     

        var dirinfos = fs.readdirSync(target);

     

        var dirs = [];

        var files = [];

     

        dirinfos.forEach(info => {

     

            var stats = fs.statSync(path.join(target, info));

            if (stats.isFile()) {

                files.push(info);

            } else {

                dirs.push(info);

            }

     

        });

     

        //│└

        dirs.forEach(dir => {

            console.log(`${prefix}├─${dir}`);

     

            // 当前是一个目录 需要深入进去

            load(path.join(target,dir),depth+1);

     

        });

     

        var count = files.length;

        files.forEach(file => {

           

            console.log(`${prefix}${--count ? '├' : '└'}─${file}`);

        });

     

    }


    总结一下:

    这部分最骚的操作就是递归,和打印目录列表

     

     

    循环创建文件夹

    node中的文件创建并不能像CMD中的mkdir a/b这样连续创建文件夹,一次只能创建一个,所以这里写了一个函数,目的是能实现连续创建文件夹的效果。

    1.js

    const fs = require('fs');

    const path = require('path');

     

    const mkdirs = require('./mkdirs');

     

    mkdirs('demo2/demo3');

     

     

    mkdirs.js

    //创建层及目录

     

    const fs = require('fs');

    const path = require('path');

     

    //创建文件,定义模块成员,导出模块成员,载入模块,使用模块

     

    function mkdirs(pathname, callback) {

     

        // 1.判断传入的是否是一个绝对路径

        // D:ademo2demo3

        pathname = path.isAbsolute(pathname) ? pathname : path.join(__dirname, pathname);

     

        //获取要创建的部分

        // pathname = pathname.replace(__dirname,'');

        var relativepath = path.relative(__dirname,pathname);

       

        // ['demo2','demo3']

        var folders = relativepath.split(path.sep);

       

     

        try{

            var pre = '';

            folders.forEach(folder =>{

                fs.mkdirSync(path.join(__dirname,pre,folder));

                pre = path.join(pre,folder); //demo2

            });

            callback&&callback(null);

        }catch(error){

            callback && callback(error);

        }

     

    }

     

    module.exports = mkdirs;

     

     

    为啥node_modules里面有那么多文件夹?

    顺带一提:windows下如果有好多文件夹嵌套会产生好多奇怪的bug

    node里面使用的是平行依赖。

    平行依赖与递归依赖

     

    __dirname不能乱用

    如果创立了一个module文件夹,然后把写好的mkdir放到里面,那么引用的时候当解析到mkdir.js的__dirname字段时会被认为是module文件夹,所以__dirname不能乱用,这里我们使用了path.dirname(module.parent.filename)。

     

    最终的mkdirs.js

    //创建层及目录

     

    const fs = require('fs');

    const path = require('path');

     

    //创建文件,定义模块成员,导出模块成员,载入模块,使用模块

     

    function mkdirs(pathname, callback) {

     

        //module.parent 拿到的是调用我的对象 02.js

       // console.log(module.parent);

       

         var root = path.dirname(module.parent.filename);

     

        //  console.log(root);

     

        // 1.判断传入的是否是一个绝对路径

        // D:ademo2demo3

        pathname = path.isAbsolute(pathname) ? pathname : path.join(root, pathname);

     

        //获取要创建的部分

        // pathname = pathname.replace(root,'');

        var relativepath = path.relative(root,pathname);

       

        // ['demo2','demo3']

        var folders = relativepath.split(path.sep);

       

     

        try{

            var pre = '';

            folders.forEach(folder =>{

              

               try{

                //如果不存在则报错

                fs.statSync(path.join(root,pre,folder));

               

                } catch(error){

     

                    fs.mkdirSync(path.join(root,pre,folder));

     

                }

              

              

                pre = path.join(pre,folder); //demo2

            });

            callback&&callback(null);

        }catch(error){

            callback && callback(error);

        }

     

    }

     

    module.exports = mkdirs;

     

     

    监视文件变化变为HTML文件

    利用文件监视实现markdown文件转换

    4.js

    //Markdown文件自动转换

     

    /*

    思路

    1.利用fs模块的文件监视功能监视指定MD文件

    2.当文件发生变化后,借助marked包提供的markdown to html功能改变后的MD文件转换为HTML

    3.再将得到的HTML替换到模板中

    4.最后利用BrowserSync模块实现浏览器自动刷新

    */

     

    const fs = require('fs');

    const path = require('path');

    const marked = require('marked');

     

    //接收需要转换的文件路径

    const target = path.join(__dirname,process.argv[2]||'./readme.md');

     

    //监视文件变化

    fs.watchFile(target,(curr,prev)=>{

     

       // console.log(`current:${curr.size};previous:${prev.size}`)

      

       // 判断文件到底有没有变化

       if(curr.mtime === prev.mtime){

           return false;

       }

      

       // 读取文件 转换为新的HTML

       fs.readFile(target,'utf8',(err,content)=>{

          if(err){throw err;}

     

          var html = marked(content);

        

          console.log(html);

          html = template.replace('{{{content}}}',html);

          fs.writeFile(target.replace('.md','.html'),html,'utf8');

     

       });

     

    var template = `

    <!DOCTYPE html>

    <html lang="en">

    <head>

        <meta charset="UTF-8">

        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <meta http-equiv="X-UA-Compatible" content="ie=edge">

        <title>Document</title>

    </head>

    <body>

         <div>

            {{{content}}}

         </div>

    </body>

    </html>

    `;

     

    });

     

     

    文件流的概念

    装着字节的大水流

    node中没有copy的方法,我们可以用流来复制。

     

    写文件及进度条功能的实现

     

    // 文件流的方式复制

     

    const fs = require('fs');

    const path = require('path');

     

    // 创建文件的读取流,并没有读出真实的数据,开始了读取文件的任务

     

    console.time('start');

    //执行到这里的时候已经从线程池中调度线程,往缓冲区中塞数据了

    var reader = fs.createReadStream('D:\1.txt');

    console.timeEnd('start');

     

    // 都文件内容与读文件信息不一样,都文件信息很快fsstat读取的是文件的元信息,所以很快

    fs.stat('D:\1.txt', (err, stats) => {

     

        if (err) throw err;

     

        if (stats) {

     

            var total = 0;

            //当缓冲区中塞了数据,就开始处理

            reader.on('data', (chunk) => {

                //chunk是一个buffer(字节数组)

     

                total += chunk.length;

                console.log('读了一点 进度:' + total / stats.size *100 + '%');

          

            });

        }

     

    })

    文件流写入

    在内存中再建立一个缓冲区,是写入的缓冲区。

     

     

    代码:

    // 文件流的方式复制

     

    const fs = require('fs');

    const path = require('path');

     

    // 创建文件的读取流,并没有读出真实的数据,开始了读取文件的任务

     

    //执行到这里的时候已经从线程池中调度线程,往缓冲区中塞数据了

    var reader = fs.createReadStream('D:\1.txt');

     

    var writer = fs.createWriteStream('D:\2.txt');

     

    // 都文件内容与读文件信息不一样,都文件信息很快fsstat读取的是文件的元信息,所以很快

    fs.stat('D:\1.txt', (err, stats) => {

     

        if (err) throw err;

     

        if (stats) {

     

            var totalRead = 0;

            var totalWrite = 0;

            //当缓冲区中塞了数据,就开始处理

            reader.on('data', (chunk) => {

                //chunk是一个buffer(字节数组)

     

     

                writer.write(chunk,(err)=>{

                     totalWrite += chunk.length;

                    console.log('写了一点 进度:' +  totalWrite/ stats.size *100 + '%');

          

                });

     

                totalRead += chunk.length;

                console.log('读了一点 进度:' + totalRead / stats.size *100 + '%');

          

            });

        }

     

    })



    pipe方法
    之前的方式就像是拿一个水瓢(缓冲区),舀出来一点,到出去一点,现在介绍一种方法叫pipe,管道,直接拿个管子把俩连起来。

     

     

    node progress 进度条

     

    这里讲了一下node progress包

    https://www.npmjs.com/package/progress

    回顾一下npm命令

    npm init –y

    npm ls展示当前目录所有依赖包

    npm ls –depth 0 展示第0层目录

     

     

    学个新单词

    Token令牌 标识

     

     

     

     

     

     

     

     

     

     

     

     

    Socket基础

     

    Socket

    网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。

    建立网络通信连接至少要一对端口号(socket)。socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。

    Socket可以理解为两个插孔,中间连上一根线就可以打电话啦。

    (在这个教程中视频讲师说socket是这个双向的连接,这个双向连接叫socket,和百度百科定义不太一样)

     

    *介绍一下nodemon 会根据文件变化自动运行,而不用每次都用node命令运行。

    npm install nodemon –g

    nodemon 4.js

    (然而并没有什么卵用,后面还是用的node命令来运行)

     

    一个简单的server和client利用socket通讯示例:

    server.js:

    // 建立一个Socket服务端

    const net = require('net');

     

    var server = net.createServer((socket)=> {

     

        console.log(socket); console.log(socket); console.log(socket);

       

        //有客户端连接时触发

        var clientIp = socket.address();

        console.log(`${clientIp.address} connectioned`);

     

        //监听数据

        socket.on('data', (chunk) => {

            process.stdout.write(' client> ');       

            console.log(chunk.toString());

            socket.write('你说啥?');

        });

    });

     

    var port = 2080;

     

    server.listen(port, (err) => {

        if (err) {

            console.log('端口被占用了');

            return false;

        }

        console.log(`服务器正常启动监听【${port}】端口`);

    });

     

     

    client.js:

    //建立socket客户端

    const net = require('net');

     

    const socket = net.createConnection({ port: 2080 }, () => {

        //'connect' listener

        console.log('Already connected to server');

     

        process.stdout.write(' client> ');

        process.stdin.on('data', (chunk) => {

            // 控制台输入回车

            var msg = chunk.toString().trim();

            socket.write(msg);

        });

    });

     

    socket.on('data', (data) => {

      process.stdout.write(' server> ');

      console.log(data.toString());

      process.stdout.write(' client> ');

      //socket.end();

    });

     

    // socket.on('end', () => {

    //   console.log('disconnected from server');

    // });

     

     

    Usage:

    node server.js

    node client.js

     

    因为只有Socket服务端启动了监听。Socket客户端没有启动监听,所以客户端之间不能互相通信。 上面例子完成了与服务器通信的简单功能,还可以通过指定一些特定的数据格式(协议),来实现更复杂的功能。

    通过制定一个协议实现聊天室服务端

     sever.js

    // 建立一个Socket服务端

    const net = require('net');

     

    // 用于存储所有的连接

    var clients = [];

     

    var server = net.createServer((socket) => {

       

        //socket对象push进去

        clients.push(socket);

     

        //广播方法

        function broadcast(signal){

              console.log(signal);

              // 肯定有用户名和消息

              var username = signal.from;

              var message = signal.message;

              // 我们要发给客户端的东西

              var send = {

                  protocal:signal.protocal,

                  from:username,

                  message:signal.message

                };

     

              // 广播消息

            clients.forEach(client => {

              client.write(JSON.stringify(send));       

            });

     

        }

     

        socket.on('data', (chunk) => {

     

            // chunk:broadcast|张三|弄啥咧!

            //        协议     用户名 消息

            // chunk:{'protocal':'broadcast','from':'张三','message':''}

            // chunk:{'protocal':'p2p',from:'张三',to:'李四',message':''}

     

            try {

                var signal = JSON.parse(chunk.toString().trim());

                var protocal = signal.protocal;

     

                switch (protocal) {

                    case 'broadcast':

                        broadcast(signal);

                        break;

                    case 'p2p':

                        p2p(signal);

                        break;

                    case 'shake':

                        shake(signal);

                        break;

                    default:

                        socket.write('协议的protocal字段有问题!');

                        break;

                }

     

                // var username = signal.from;

                // var message = signal.message;

     

            }

            catch (err) {

                socket.write('数据格式有问题!');

                throw err;

            }

     

            // 有任何客户端发消息都会触发

            // var username = chunk.username;

            // var message = chunk.messge;

            // broadcast(username.message)

        });

     

    });

     

    var port = 2080;

     

    server.listen(port, (err) => {

        if (err) {

            console.log('端口被占用了');

            return false;

        }

        console.log(`服务器正常启动监听【${port}】端口`);

    });

     

     

    client.js

    //客户端

    const net = require('net');

    const readline = require('readline');

     

     

    const rl = readline.createInterface({

        input: process.stdin,

        output: process.stdout

    });

     

    rl.question('What is your name? ', (name) => {

     

        name = name.trim();

        if (!name) throw new Error('姓名有问题!');

        //创建与服务端的连接

     

        var socket = net.createConnection({ port: 2080 }, () => {

            console.log(`Welcome ${socket.remoteAddress} ${name} to 2080 chatroom`);

          

            //监听服务端发过来的数据

            socket.on('data', (chunk) => {

                try {

                    var signal = JSON.parse(chunk.toString().trim());

                    var protocal = signal.protocal;

                    switch (protocal) {

                        case 'broadcast':

                            console.log(`[broadcast]"${signal.from}"说了:${signal.message}`);

                            rl.prompt();

                            break;

                        default:

                            server.write('数据协议字段有问题');

                            break;

                    }

                }

                catch (err) {

                    socket.write('数据格式有问题!');

                    throw err;

                }

     

            });





            rl.setPrompt(`${name}> `);

     

            rl.prompt();

     

            rl.on('line', (line) => {

     

                // chunk:{'protocal':'broadcast','from':'张三','message':''}

                var send = {

                    protocal: 'broadcast',

                    from: name,

                    message: line.toString().trim()

                };

     

                socket.write(JSON.stringify(send));

     

                rl.prompt();

     

            }).on('close', () => {

                //console.log('Have a great day!');

                //process.exit(0);

            });

     

        });

     

    });



    HTTP是超文本传输协议

    上面的例子也是一种协议。

     

     

    第五天

    CMD的netstat命令

     Netstat是在内核中访问网络连接状态及其相关信息的程序,它能提供TCP连接,TCP和UDP监听,进程内存管理的相关报告。

     

    处理服务器异常

    之前写的代码中,如果有客户机取消,那么所有都取消了,这个怎么处理呢?

     

    答:很简单,只要在socket on data的监听事件中添加error监听事件,就不会全部取消了。

    socket.on('error',(err)=>{

     

           //console.log(err) 这个错误也可以不log出来,只要捕获就没事儿

        });

     

     

    最终写好的聊天程序,支持p2p协议。

    server.js:

    // 建立一个Socket服务端

    const net = require('net');

     

    // 用于存储所有的连接

    var clients = [];

     

    // var clients = [

    //      {

    //       name:..

    //       socket:..

    //      }

    // ]

     

    var signin = false;

     

    var server = net.createServer((socket) => {



        //建立了连接,代表登录

        signin = true;

     

        var logName;

     

        //socket对象push进去

        clients.push({ socket: socket });

     

        socket.on('data', clientData).on('error', (err) => {

     

            for (let i = 0; i < clients.length; i++) {

                if (clients[i].socket == socket) {

                    logName = clients[i].name;

                    clients.splice(i, 1);

                }

            }

     

            console.log(`${socket.remoteAddress} ${logName} 下线了 当前在线${clients.length}人`);

     

        });



        //广播方法

        function broadcast(signal) {

            console.log(signal);

            // 肯定有用户名和消息

            var username = signal.from;

            var message = signal.message;

            // 我们要发给客户端的东西

            var send = {

                protocal: signal.protocal,

                from: username,

                message: message

            };

     

            // 广播消息

            clients.forEach( client  => {

                client.socket.write(JSON.stringify(send));

            });

     

        }

     

        //点对点消息

        function p2p(signal) {

            console.log(signal);

            // 肯定有用户名和消息

            var username = signal.from;

            var target = signal.to;

            var message = signal.message;

     

            // 我们要发给客户端的东西

            var send = {

                protocal: signal.protocal,

                from: username,

                message: message

            };

     

           // console.log(`${username}要发给${target}的内容是${message}`);



            clients.forEach(client=>{

     

            

                if(client.name == target)

                {

                    client.socket.write(JSON.stringify(send));

                }

     

            })

            // clients[target].write(JSON.stringify(send));

     

        }



        function clientData(chunk) {

     

            //如果是第一次连接,就是在登录,在传入用户名

            if (signin == true) {

                //第一次回传入用户名

                var JSONname = chunk.toString();

                var Objname = JSON.parse(JSONname);

                logName = Objname.name;

     

                clients[clients.length - 1]['name'] = logName;

                signin = false;

     

                console.log(`${socket.remoteAddress} ${logName} 上线了,当前在线${clients.length}人`);

     

            }

     

           

     

            // chunk:broadcast|张三|弄啥咧!

            //        协议     用户名 消息

            // chunk:{'protocal':'broadcast','from':'张三','message':''}

            // chunk:{'protocal':'p2p',from:'张三',to:'李四',message':''}

     

            try {

                var signal = JSON.parse(chunk.toString().trim());

                var protocal = signal.protocal;

     

               // console.log(chunk.toString());

     

                switch (protocal) {

                    case 'broadcast':

                        //console.log('要broadcast了');

                        broadcast(signal);

                        break;

                    case 'p2p':

                        // console.log('要p2p了!');

                        p2p(signal);

                        break;

                    // case 'shake':

                    //     shake(signal);

                    //     break;

                    // default:

                    //     socket.write('协议的protocal字段有问题!');

                    //     break;

                }

     

                // var username = signal.from;

                // var message = signal.message;

     

            }

            catch (err) {

                socket.write('数据格式有问题!');

                throw err;

            }

     

            // 有任何客户端发消息都会触发

            // var username = chunk.username;

            // var message = chunk.messge;

            // broadcast(username.message)

        };

     

    });

     

    var port = 2080;

     

    server.listen(port, (err) => {

        if (err) {

            console.log('端口被占用了');

            return false;

        }

        console.log(`服务器正常启动监听【${port}】端口`);

    });

     

    client.js:

    //客户端

    const net = require('net');

    const readline = require('readline');

     

    const rl = readline.createInterface({

        input: process.stdin,

        output: process.stdout

    });

     

    rl.question('What is your name? ', (name) => {

     

        name = name.trim();

        if (!name) throw new Error('姓名有问题!');

        //创建与服务端的连接

     

        //还可以传入一个参数host:192.xx...

        var socket = net.createConnection({ port: 2080 }, () => {

     

     

     

            console.log(`Welcome ${socket.remoteAddress} ${name} to 2080 chatroom`);

     

            //登录

            socket.write(JSON.stringify({name:name}));

     

        

     

            //监听服务端发过来的数据

            socket.on('data', (chunk) => {

                try {

                    var signal = JSON.parse(chunk.toString().trim());

                    var protocal = signal.protocal;

                    switch (protocal) {

                        case 'broadcast':

                            console.log(`[broadcast]"${signal.from}"说了:${signal.message}`);

                            rl.prompt();

                            break;

                        case 'p2p':

                            console.log(`[p2p]${signal.from}说了:${signal.message}`);

                            rl.prompt();

                            break;

                        default:

                            server.write('数据协议字段有问题');

                            break;

                    }

                }

                catch (err) {

                    socket.write('数据格式有问题!');

                    throw err;

                }

     

            });





            rl.setPrompt(`${name}> `);

     

            rl.prompt();

     

            rl.on('line', (line) => {

     

                line = line.toString().trim();

                var temp = line.split(':');

                var send;

     

                if (temp.length === 2) {

                    //点对点消息

                    //console.log('这是一个点对点消息');

     

                    send = {

                        protocal: 'p2p',

                        from: name,

                        to: temp[0],

                        message: temp[1]

                    };

     

                } else {

                    //广播消息

     

                    // chunk:{'protocal':'broadcast','from':'张三','message':''}

                    send = {

                        protocal: 'broadcast',

                        from: name,

                        message: line.toString().trim()

                    };

     

                }

     

                socket.write(JSON.stringify(send));

               

     

                rl.prompt();

     

            }).on('close', () => {

                console.log('Have a great day!');

                process.exit(0);

            });

     

        });

     

    });

    usage

    node server.js

    node client.js

     

     

    总结一下,复习,自己写一下简单的net模块使用流程:

    server.js:

    const net = require('net');

     

    var server = net.createServer((socket) => {

     

        console.log(`${socket.remoteAddress}连接上了我的服务器`);

     

        socket.on('data', (chunk) => {

     

            console.log(chunk.toString());

     

        }).on('err', (err) => { console.log(err) });

     

    });

     

    server.listen({ port: 2888 }, (err) => {

     

        if (err) throw err;

     

        console.log('在2888端口创立了服务器');

     

    })

     

     

    client.js:

    const net = require('net');

    const readline = require('readline');

    const rl = readline.createInterface({

            input:process.stdin,

            output:process.stdout

    });

     

    rl.setPrompt('> ');

    rl.prompt();

     

    rl.question('what is your name?',(name)=>{

     

       name = name.toString().trim();

     

       var socket =  net.createConnection({port:2888},(err)=>{

     

        if(err) throw err;   

     

        console.log('连接上服务器了');

     

        rl.prompt();

     

        socket.write(name);

     

                    rl.on('line',(line)=>{

     

                        socket.write(line.toString());

                        rl.prompt();              

                   

                    });

       });  

     

    });




    浏览器的作用

    HTTP也是一种协议,

     

    浏览器的本质作用:

    将用户输入的URL封装为一个请求报文。

    建立与服务端的Socket链接

    将刚刚封装好的请求报文通过socket发送到服务端。

    socket.write(JSON.stringify(send));

    。。。。。。。。

    接收到服务端返回的响应报文。

    解析响应报文(类似于我们写的那个JSON.parse…)

    渲染内容到页面中(类似于我们的console.log(msg))

     

     

    浏览器就是一个socket客户端

     

     

    CR+LF = 有一个回车符,有一个换行符

    URI(统一资源标识符)是URL(统一资源定位符)的超集

    统一资源标志符URI就是在某一规则下能把一个资源独一无二地标识出来。

    那统一资源定位符URL是什么呢。也拿人做例子然后跟HTTP的URL做类比,就可以有:

    动物住址协议://地球/中国/浙江省/杭州市/西湖区/某大学/14号宿舍楼/525号寝/张三.人

    可以看到,这个字符串同样标识出了唯一的一个人,起到了URI的作用,所以URL是URI的子集。URL是以描述人的位置来唯一确定一个人的。
    在上文我们用身份证号也可以唯一确定一个人。对于这个在杭州的张三,我们也可以用:

    身份证号:1234567

    http模块发送

    const http = require('http');

    const fs = require('fs');

     

    //利用node做一个客户端 请求m.baidu.com

     

    http.get('http://m.baidu.com',(response)=>{

     

    var rawContent = '';

     

    response.on('data',(chunk)=>{

     

     rawContent += chunk.toString();

     

    });

     

    response.on('end',()=>{

     

        //后面可以做爬虫哟

        fs.writeFile('./1.txt',rawContent,(err)=>{

     

            if(err) throw err;

       

            console.log('出来了');

        })



        console.log(response.headers['content-length']);

        //下面是不包含头的长度

        console.log(rawContent.length);

       

    });




    });

    看一下get方法:

    返回值是一个叫IncomingMessage的类

     

    IncomingMessage继承于流。

    所以会有on(‘data’)和on(‘end’)事件。

  • 相关阅读:
    Hibernate框架简介
    [leecode]Evaluate Reverse Polish Notation
    linux 服务器之间配置免密登录
    大数据学习系列之一 ----- Hadoop环境搭建(单机)
    Hadoop hbase集群断电数据块被破坏无法启动
    CentOS 6 上安装 pip、setuptools
    CentOs6.7 python2.6升级到2.7.11
    安装phantomjs(Ubuntu版本 MacOS版本)
    Linux/Centos下安装部署phantomjs 及使用
    linux 查看系统磁盘、内存大小
  • 原文地址:https://www.cnblogs.com/eret9616/p/9110543.html
Copyright © 2020-2023  润新知