NodeJs分为四个阶段 第一阶段~
nodejs的简介以及基本语法教程
begin enjoy👇

什么是nodeJS

node.js是一个基于Chrome V8引擎的Javascript运行环境,node.js由ECMAScript和核心模块组成,没有浏览器环境的API,即DOM和BOM。

Node.js使用了一个事件驱动,非阻塞的I/O模型,使其轻量又高效 - Node.js诞生的初衷。

在此之前像C/PHP等语言平台 都是同步线程的业务模型,很多时候会产生等待和死锁。因此 有人希望能够创建一个异步多线程的业务模型 来提高服务端的执行效率 以应对更多的并发业务。如果要实现异步业务流 就必须借助回调函数 类似于客户端Ajax的线程模型 - 采用了异步回调的机制。

//在浏览器解析时 给DOM对象绑定事件处理函数 - 回调函数
xhr.onloadend = function () {
    //ajax执行完毕回调该事件
    
}
xhr.send();

node.js的包管理器npm(node package manager),是一个在线的包托管平台。很多语言平台都有自己的开发生态。例如PHP中的composer依赖管理平台,npm就是nodejs语言平台包依赖管理工具,也是全球最大的开源生态系统。

node.js的包管理器npm,是全球最大的开源库生态系统。

nodeJS环境安装

进入官网nodejs.cn,点击下载菜单,或者直接进入 @link http://nodejs.cn/download/

node.exe是node环境启动文件,内置了编译号的node核心模块,所有的node代码都要使用node.exe文件来执行。类似于PHP代码执行时需要安装php.exe的PHP引擎。

安装时,会将node.exe可执行程序自动写入环境变量。也就说接下来我们可以在任意目录中,使用node指令:

node -v用来查看node的安装版本:

> node -v

开始使用nodeJS

node平台是ECMAScript核心语法实现,因此所有的代码都要写在.js的文件中。在浏览器环境中,js文件需要被引入HTML中才能运行。而node中,js文件是直接运行的。

解释执行node代码

  • 创建js脚本文件,使用控制台输出

    //index.js
    console.log("hello world");//内置的console模块
    
  • 打开命令行 进入脚本文件位置

    • 系统cmd(comand)中 使用cd(change directory)命令切换到脚本所在目录

    • 在文件系统中进入文件所在目录位置,按住shift键 + 右键 --> 在此处打开PS窗口

    • 在编辑器中安装Terminal(终端)扩展,进入应用商店中搜索Terminal即可。例如:vs code中安装Termimal插件。如果是NetBeans IDE等 需要安装cgywin插件。

      接下来 在文件工作区中 鼠标右键 open in integrated Terminal 则会在当前目录打开终端

  • 使用node 指令运行指定文件 node index.js

    • 注意文件名不能是node.js 否则不执行。node.js文件导致其他文件无法正常执行和输出

    • node是服务器环境,它虽然采用ECMAScript核心语法,但是没有浏览器宿主环境的接口

      //index.js
      console.log(window);	//window is not defined
      

使用node读写文件

作为客户端环境的Javascript无法操作文件;但是Node中,通过内置的核心API,使其具备访问文件的能力;

node的核心模块fs(file system)专门用来读写文件内容,我们只需在用到的时候使用require载入即可。

const fs = require('fs');	//require用于引入系统核心API文件
							//参数fs是固定标识 不可更改,
							//接收变量可根据个人习惯修改 但通常和核心文件同名

读取文件的内容

  • 加载文件系统的核心API,fs(file-system)

    const fs = require('fs')

  • 使用fs.readFile()读取一个文件的内容

    fs.readFile(filename, [option], callback) callback有两个参数:data, error

    • 读取成功

      • data为文件数据。文件以二进制数据存储,这里返回的是经过转化的十六进制数据;无论二进制还是十六进制,它们都是机器码,如果要转化成人类可识别的语言 则使用data.toString()方法转化。
      • error则为null
    • 读取失败

      • data为null
      • error为一个错误对象 存储了错误信息 当读取一个不存在的文件时会产生错误
  • 完整的代码示例:

    //载入系统核心类库
    const fs = require("fs");
    fs.readFile("foo.txt", (err, buffer) => {//服务端通常不涉及this绑定 推荐使用箭头函数
        //检测是否有错误
        if (err) console.dir(err);
       else {
           //返回二进制数据 因为内容太长 转化为十六进制
           console.log(buffer); //<Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64>
           //使用toString()转化为字符串
           console.log(buffer.toString());
           //或者设置第二参数,指定返回内容的编码类型'utf-8'
       }
    })
    

    同样 需要注意的是使用node编写的js代码 只能运行于node环境中 而不能运行于浏览器环境。从本质上来说浏览器中javascript引擎和node引擎加载了不同的es扩展。

    <!--在HTML中引入node代码-->
    <script src="fs.js"></script>
    

向文件写入内容

  • 载入核心的文件系统API,fs

  • 使用fs.writeFile()写入一个文件内容

    • fs.writeFile(filename, content, callback) filename为写入文件名,content为文件内容 callback是执行结果的回调函数

    • callback回调函数拥有一个参数error,当执行成功 error为null,失败则为一个错误对象

    • 给已有文件写入内容会覆盖,新文件则创建之;如果文件名包含有特殊字符:*/\^&等会产生错误

  • 完整的示例代码:

    const fs = require("fs");
    //如果已有文件则覆盖,否则创建新的文件
    fs.writeFile("*.txt", "hello nodeJS", (err) => {
        
    });
    

创建http服务器

  1. 引入核心的系统API - http模块

    const http = require('http');
    
  2. 使用createServer()方法创建一个服务器

    const server = http.createServer();
    
  3. 启动服务器并设置http服务的监听端口

    server.listen(8000, (err) => {
        //发生错误返回一个错误对象
        if (err) console.dir(err);
        console.log('服务器已经启动');
    });
    

    现在,使用cmd 进入文件所在位置,执行node 文件名 即可实现类似Apache Web服务的启动。

    类似于Apache中进入http.conf中修改listen 8000 ,然后命令行中运行httpd -k start来启动服务

    注意端口号的设置应避免重复,当端口号被占用时服务无法启动。一个简单的示例 可以在另一个cmd窗口中运行该服务,由于使用了同样的端口号,则再次启动会失败。

    尽管服务已经启动,但还未添加任何对请求的处理,当在地址栏输入127.0.0.1:8000时,客户端未收到任何来自服务端的响应 始终处于等待状态

  4. 设置请求的响应处理

    node.js是异步非阻塞的I/O处理,几乎所有的操作都伴随异步行为;而异步的编码风格需要在异步行为之前使用回调函数来完成处理。

    接下来注册一个request事件的回调函数监听来自浏览器的请求并响应结果。

    //回调函数中的request为请求对象 response为响应对象
    server.on('request', (request, response) => {
        //一个HTTP请求 为一次客户端和服务器的会话
        //所谓的会话就是对话 需要你来我往 相互传送
        //对于客户端请求内容 可通过request对象查询
        //服务端的响应 需要使用response对象来返回
        console.log("收到客户端请求了");
    });
    

    完整的代码示意

    //引入系统核心模块http
    const http = require("http");
    
    //创建http访问的服务器
    const server = http.createServer();
    
    //启动前添加监听请求的回调函数 处理浏览器请求 给予响应
    server.on("request", (request, response) => {
        console.log("收到客户端请求了");
        //使用response设置响应正文
        response.write("hello world");
        response.write("and more...");
        //结束响应否则客户端会一直等待
        response.end();
    });
    
    //启动并设置监听端口
    server.listen(8000, (err) => {
        if (err) console.dir(err);
        else {
            console.log("服务已启动...");
        }
    })
    
    

    request对象

    request对象的url属性用来获取http请求中的路径,也就是端口号后面以/开头的那部分,形如

    127.0.0.1:8000 路径为/

    127.0.0.1:8000/a.html 路径为/a.html

    127.0.0.1:8000/foo/a.html 路径为/foo/a.html

    127.0.0.1:8000/foo/a.html?id=9527 路径为/foo/a.html?id=9527

    ​ 在Apache中 有初始化的DocumentRoot配置 指定站点根目录,接下来会由请求路径对应到一个具体的资源文件中;而node.js则是自己定制的服务器,这都需要我们手动处理。

    ​ 这里 我们首先解决一个基本的问题 就是如何响应一个文件给浏览器。无论是网页文件还是图片、视频等媒体资源 文件本质上都是二进制数据,还是返回字符串内容给浏览器,该内容是从文件中读取出来的。

    //引入系统核心模块
    const http = require("http"),
        fs = require("fs");
    
    //创建一个服务器
    const server = http.createServer();
    
    const DOCUMENT_ROOT = "D:/Wamp/WWW";
    
    //监听客户端请求
    server.on("request", (request, response) => {
        let filename;
        //request为请求对象存放请求信息
        // console.dir(request);
        //请求方式 解析参数
        console.log(request.method); //GET
        //请求地址 处理响应
        console.log(request.url); // '/'
        //请求头信息 检测请求
        console.dir(request.headers); //{...}
    
        //判断请求路径 响应站点下的文件
        //如果为根地址 则默认请求index
        if (request.url == "/") {
            filename = "index.html";
        } else {//路径中的特殊字符在http传输时经过编码 需要解码
            filename = decodeURIComponent(request.url);
        }
    
        fs.readFile(DOCUMENT_ROOT + "/" + filename, "utf-8", (err, data) => {
            if (err) console.dir(err);
            response.write(data);
            response.end();
        });
        
    });
    
    //启动并设置端口
    server.listen(8000, (err) => {
        if (err) console.dir(err);
        else {
            console.log("服务已经启动...");
        }
    })
    

    当在浏览器地址栏输入127.0.0.1:8000时,你会发现控制台打印的请求路径中还包括有/favicon.ico。或者我们在浏览器开发者工具的网络查看,你也会找到该请求。这是一个浏览器默认行为 目的是找到该页面的图标提供给title收藏夹使用。

    response对象

    response对象用来返回响应的结果,它有几个基本的方法:

    • response.setHeader()

      用来设置响应头信息,用来告知浏览器返回结果的文本类型、内容编码、缓存控制、跨域资源共享

      不同的资源文件,对应不同的Content-Type值,在Content-Type中一般设置两项:

      • 文档类型

        即常说的mime-type类型值,用来区别不同的网络资源,简单来说 就是指定文件数据格式,以便浏览器更好的去识别和解析它们。 @link http://tool.oschina.net/commons

      • 编码类型

        一般为字符数据才指定编码,如果是图片等二进制数据 则不需要指定编码类型。当响应内容包含中文时最好指定该值。

    • response.write()

      写入响应正文的方法,必须是二进制或者字符串数据。复合类型数据 必须转化成字符串才能用于http传输

    • response.end()

      write()方法可以写入多次,但最终必须以end结束 否则客户端会一直等待;end()方法 也可结合write()一起使用 简化代码的输入。

      //引入系统核心模块http
      const http = require("http");
      
      //创建http访问的服务器
      const server = http.createServer();
      
      server.on("request", (req, res) => {
      
          res.writeHead(200, {
              "Content-Type" : "text/html;charset=utf-8",
          });
          
          res.write("<h4>NodeJS服务返回的结果</h4>");
          
          //end方法结束响应并返回结果
          res.end();
          
      });
      
      //启动并设置监听端口
      server.listen(8000, (err) => {
          if (err) console.dir(err);
          else {
              console.log("服务已启动...");
          }
      });
      

模块化编程简介

Node中的模块遵循es6模块定义的规范,但是不支持export和import的使用,转而使用exports和require.

  • 在ES6中 模块文件使用export语法关键字导出接口,然后使用import导入模块文件并接收模块方法;

    而Node.js中 使用exports对象导出模块接口,使用require引入模块文件并接收exports对象内容;

  • es6模块中 import模块文件路径必须有./,且.js后缀名不可省略,而node中则不太一样,后面介绍。

    但无论es6模块还是node模块,都是具有封装性,外部无法访问模块中的成员。

系统核心模块

系统核心模块是一个具名的模块文件,使用require(模块名)直接引入即可,并定义变量接收模块对象接口。

用户定义模块

  • 在node中使用CommonJS规范定义模块文件,也叫CMD模块加载,它使用module.exports导出模块接口

    //demo.js
    //使用commonJS规范定义的模块
    //1| 控制台打印提示信息 检测是否加载成功
    console.log("这是math模块文件");
    
    //2| 定义模块文件内容,变量、函数、类等
    //接下来在加载文件中使用这些成员 检测封装性
    let foo = "foo";
    
    function bar() {
        return "bar";
    }
    
    //3| 使用exports语法导出接口列表
    module.exports = {
        foo, 
        bar,
    }
    
  • 使用require引入模块文件

    使用require引入用户模块时,模块文件名的后缀名可以省略,但相对路径中的./则不可以省略

    //1| 引入模块文件并检测是否有打印的提示信息
    require("./demo");   //.js可以省略 但./不可省
                           //一旦省略则变成了系统模块
    					   
    //2| 在文件中直接引用模块成员,检测模块的封装性
    console.log(foo, bar); //foo和bar未定义
                           //模块具有封装性,也叫文件作用域
    
  • 定义接收模块接口的对象

    require全局函数有返回值,它接收模块文件中 使用exports对象返回的接口,通常使用同名变量接收模块。

    //3| 定义对象接收模块接口内容
    const demo = require("./demo");
    //返回模块接口对象
    console.dir(demo);
    
    //使用模块开放成员
    console.log(demo.foo);
    console.log(demo.bar());
    

nodemon工具

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

使用npm install -g nodemon全局安装nodemon 自动刷新


posted @ Zycin (非转载 来自个人学习资料整理)