• 【学习笔记】Node--从0基础到实战企业官网


    前言:
        本文是学习Node中间层实践的相关笔记,推荐大家可以去阅读jsliang大佬的原文Node-从0基础到实战企业官网。开启本文前,可以先去了解一下node的基本知识,会对了解本文内容有所帮助。当然,也可以选择,跟着本文,边读边了解。(PS:继续还债的node小白╮(╯▽╰)╭)

    本文重点内容:

    一、基础学习

    1.1 HTTP–开始Node之旅

    http.js的相关代码:

    // 1. 引入 http 模块
    var http = require("http");
    
    // 2. 用 http 模块创建服务
    /**
     * req 获取 url 信息 (request)
     * res 浏览器返回响应信息 (response)
     */
    http.createServer(function (req, res) {
      // 设置 HTTP 头部,状态码是 200,文件类型是 html,字符集是 utf8
      res.writeHead(200, {
        "Content-Type": "text/html;charset=UTF-8"
      });
    
      // 往页面打印值
      res.write('

    Hello NodeJS

    '
    ); // 结束响应 res.end(); }).listen(3000); // 监听的端口
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    那么,上面的代码,要怎么用呢?

    • 首先,将上面的代码复制粘贴到http.js中;
    • 然后,启动VS Code终端: Ctrl + ~ ;
    • 接着,输入node http.js 并回车;
    • 最后,打开localhost:3000

    在这里插入图片描述
    好的,就可以了,现在,讲解一下上面的代码。
    首先,需要开启HTTP模式。一般,像PHP这类老牌子的后端语言,需要Apache或者Nginx开启HTTP服务。然而,我们的Node不需要:

    var http = require("http");
    
    • 1

    然后,开启HTTP服务,并设置开启的端口:

    /**
     * req 获取 url 信息 (request)
     * res 浏览器返回响应信息 (response)
     */
    http.createServer(function (req, res) {
      // ... 步骤 3 代码
    }).listen(3000); // 监听的端口
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    接着,设置HTTP头部,并往页面打印值,最后结束响应:

    // 设置 HTTP 头部,状态码是 200,文件类型是 html,字符集是 utf8
    res.writeHead(200, {
      "Content-Type": "text/html;charset=UTF-8"
    });
    
    // 往页面打印值
    res.write('

    Hello NodeJS

    '
    ); // 结束响应 res.end();
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    最后,往游览器输入http://localhost:3000/ ,将访问到我们开启的Node服务,从而往页面渲染页面。
    至此,我们开启了自己的Node之旅?

    1.2 URL模块

    URL模块是什么呢?
    我们在控制台(终端)开启Node模式,并打印出url来看一下:
    在这里插入图片描述
    然后,发现它有UrlparseresolveresolveObjectformatURLURLSearchParamsdomainToASCIIdomainToUnicode 这么多模块。
    那么,这些模块都有什么用呢?
    先看一下代码吧:
    url.js

    // 1. 引入 url 模块
    var url = require("url");
    
    // 2. 引入 http 模块
    var http = require("http");
    
    // 3. 用 http 模块创建服务
    /**
     * req 获取 url 信息 (request)
     * res 浏览器返回响应信息 (response)
     */
    http.createServer(function (req, res) {
    
      // 4. 获取服务器请求
      /**
       * 访问地址是:http://localhost:3000/?userName=jsliang&userAge=23
       * 如果你执行 console.log(req.url),它将执行两次,分别返回下面的信息:
       * /  ?userName=jsliang&userAge=23
       * /  /favicon.ico
       * 这里为了防止重复执行,所以排除 req.url == /favicon.ico 的情况
       */
      if(req.url != "/favicon.ico") {
        
        // 5. 使用 url 的 parse 方法
        /**
         * parse 方法需要两个参数:
         * 第一个参数是地址
         * 第二个参数是 true 的话表示把 get 传值转换成对象
         */ 
        var result = url.parse(req.url, true);
        console.log(result);
        /**
         * Url {
         *   protocol: null,
         *   slashes: null,
         *   auth: null,
         *   host: null,
         *   port: null,
         *   hostname: null,
         *   hash: null,
         *   search: '?userName=jsliang&userAge=23',
         *   query: { userName: 'jsliang', userAge: '23' },
         *   pathname: '/',
         *   path: '/?userName=jsliang&userAge=23',
         *   href: '/?userName=jsliang&userAge=23' }
         */
    
        console.log(result.query.userName); // jsliang
    
        console.log(result.query.userAge); // 23
      }
    
      // 设置 HTTP 头部,状态码是 200,文件类型是 html,字符集是 utf8
      res.writeHead(200, {
        "Content-Type": "text/html;charset=UTF-8"
      });
    
      // 往页面打印值
      res.write('

    Hello NodeJS

    '
    ); // 结束响应 res.end(); }).listen(3000);
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    在上面的代码中:
    首先,引入该章节的主角url模块:

    // 1.引入url模块
    var url = require("url");
    
    • 1
    • 2

    然后,引入http模块:

    // 2.引入http模块
    var http = require("http");
    
    • 1
    • 2

    接着,创建http模块,因为url的监听,需要http模块的开启:

    // 3.用http模块创建服务
    /**
    * req获取url信息 (request)
    * res 游览器返回响应信息 (response)
    */
    http.createServer(function (req, res) {
     // ...第4步、第5步代码
     // 设置HTTP头部,状态码是200,文件类型是html,字符集是utf8
     res.writeHead(200, {
    	"Content-Type": "text/html;charset=UTF-8"	
     });
     //往页面打印值
     res.write('

    Hello World

    '
    ); //结束响应 res.end(); }).listen(3000);
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    最后,访问给出的地址: http://localhost:3000/?userName=jsliang&userAge=23 ,并通过它查看urlparse模块怎么用,输出啥:

    // 4.获取服务器请求
    /**
    *访问地址是:http://localhost:3000/?userName=jsliang&userAge=23
    *如果执行console.log(req.url),它将执行两次,分别返回下面的信息:
    */ ?userName=jsliang&userAge=23
    */ /favicon.ico
    *这里为了防止重复执行,所以排除req.url == /favicon.ico的情况
    */
    if(req.url != "/favicon.ico") {
    	//5. 使用url的parse方法
    	/**
    	*parse方法需要两个参数:
    	*第一个参数是地址
    	*第二个参数是true的话表示把get传值转换给对象
    	*/
    	var result = url.parse(req.url, true);
    	console.log(result);
    	/**
    	*Url {
    	* protocol: null,
    	* slashes: null,
    	* auth: null,
    	* host: null,
    	* port: null,
    	* hostname: null,
    	* hash: null,
    	* search: '?userName=jsliang&userAge=23',
    	* query: { userName: 'jsliang',userAge: '23'},
    	* pathname: '/',
    	* path: '/?userName=jsliang&userAge=23',
    	* href: '/?userName=jsliang&userAge=23'}
    	* /
    	console.log(result.query.userName); //jsliang
    	console.log(result.query.userAge); // 23
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    从中,我们可以看出,可以通过query ,获取到我们想要的路径字段。
    当然,上面只讲解了parse的用法,我们可以将上面代码中的if语句里面的代码全部清空。然后,输入下面的内容,去学习url模块更多的内容:

    • 内容1:url模块所有的内容
    console.log(url);
    
    /**
     * Console:
     { 
       Url: [Function: Url],
        parse: [Function: urlParse], // 获取地址信息
        resolve: [Function: urlResolve], // 追加或者替换地址
        resolveObject: [Function: urlResolveObject],
        format: [Function: urlFormat], // 逆向 parse,根据地址信息获取原 url 信息
        URL: [Function: URL],
        URLSearchParams: [Function: URLSearchParams],
        domainToASCII: [Function: domainToASCII],
        domainToUnicode: [Function: domainToUnicode] 
      }
     */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 内容2:parse如何使用
    console.log(url.parse("http://www.baidu.com"));
    /**
     * Console:
      Url {
        protocol: 'http:',
        slashes: true,
        auth: null,
        host: 'www.baidu.com',
        port: null,
        hostname: 'www.baidu.com',
        hash: null,
        search: null,
        query: null,
        pathname: '/',
        path: '/',
        href: 'http://www.baidu.com/' 
      }
     */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 内容3:parse带参数
    console.log(url.parse("http://www.baidu.com/new?name=zhangsan"));
    
    /**
     * Console:
      Url {
        protocol: 'http:',
        slashes: true,
        auth: null,
        host: 'www.baidu.com',
        port: null,
        hostname: 'www.baidu.com',
        hash: null,
        search: '?name=zhangsan',
        query: 'name=zhangsan',
        pathname: '/new',
        path: '/new?name=zhangsan',
        href: 'http://www.baidu.com/new?name=zhangsan' 
      }
     */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 内容4:format的使用
    console.log(url.format({
      protocol: 'http:',
      slashes: true,
      auth: null,
      host: 'www.baidu.com',
      port: null,
      hostname: 'www.baidu.com',
      hash: null,
      search: '?name=zhangsan',
      query: 'name=zhangsan',
      pathname: '/new',
      path: '/new?name=zhangsan',
      href: 'http://www.baidu.com/new?name=zhangsan' 
    }))
    
    // Console:
    // http://www.baidu.com/new?name=zhangsan
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 内容5:resolve的使用
    console.log(url.resolve("http://www.baidu.com/jsliang", "梁峻荣"));
    
    // Console:
    // http://www.baidu.com/梁峻荣
    
    • 1
    • 2
    • 3
    • 4

    更多关于url的相关配置,可以查看官网。

    1.3 CommonJS

    • 什么是CommonJS ?
      CommonJS就是为了JS 的表现来制定规范,因为JS没有模块系统、标准库较少、缺乏包管理工具,所以CommonJS应运而生,它希望JS可以在任何地方运行,而不是只在游览器中,从而达到Java、C#、PHP这些后端语言具备开发大型应用的能力。

    • CommonJS 的应用?
      1.服务器端JavaScript应用程序;(Node.js)
      2.命令行工具;
      3.桌面图形界面应用程序。

    • CommonJS与Node.js的关系?
      CommonJS就是模块化的标准,Node.js就是CommonJS(模块化)的实现。

    • Node.js 中的模块化 ?
      1.在Node中,模块分为两类:一是Node提供的模块,称为核心模块;二是用户编写的模块,成为文件模块。核心模块在Node源代码的编译过程中,编译进行了二进制执行文件,所以它的加载速度是最快的,例如:HTTP模块、URL模块、FS模块;文件模块是在运行时动态加载的,需要完整的路径进行分析、文件定位、编译执行过程等…所以它的速度相对核心模块来说会更慢一些。
      2.我们可以将公共的功能抽离出一个单独的JS文件存放,然后,在需要的情况下,通过export或者module.exports将模块导出,并通过require引入这些模块。
      现在,通过三种使用方式,来讲解下Node中的模块化及exports/require的使用。
      先查看下目录:
      在这里插入图片描述

    • 方法一:
      首先,新建03_CommonJS.js03_tool-add.jsnode_modules/03_tool-multiply.jsnode_modules/jsliang-module/tools.js 这4个文件/文件夹。
      其中,package.json 暂且不理会,稍后会讲解它如何自动生成。
      03_tool-add.js 中:
      03_tool-add.js

    // 1. 假设我们文件其中有个工具模块
    var tools = {
      add: (...numbers) => {
        let sum = 0;
        for (let number in numbers) {
          sum += numbers[number];
        }
        return sum;
      }
    }
    
    /**
     * 2. 暴露模块
     * exports.str = str;
     * module.exports = str;
     * 区别:
     * module.exports 是真正的接口
     * exports 是一个辅助工具
     * 如果 module.exports 为空,那么所有的 exports 收集到的属性和方法,都赋值给了 module.exports
     * 如果 module.exports 具有任何属性和方法,则 exports 会被忽略
     */
    
    // exports 使用方法
    // var str = "jsliang is very good!";
    // exports.str = str; // { str: 'jsliang is very good!' }
    
    // module.exports 使用方法
    module.exports = tools;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    那么,上面的代码有啥定义呢?
    第一步,定义了个工具库tools
    第二步,通过modules.exportstools 进行了导出;
    所以,在03_CommonJS.js 可以通过require 导入使用:

    var http = require("http");
    
    var tools1 = require('./03_tool-add');
    
    http.createServer(function (req, res) {
    
      res.writeHead(200, {
        "Content-Type": "text/html;charset=UTF-8"
      });
    
      res.write('

    Hello NodeJS

    '
    ); console.log(tools1.add(1, 2, 3)); /** * Console: * 6 * 6 * 这里要记得 Node 运行过程中,它请求了两次, * http://localhost:3000/ 为一次, * http://localhost:3000/favicon.ico 为第二次 */ res.end(); }).listen(3000);
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    这样,就完成了exportsrequire的初次使用。

    • 方法二:
      当我们模块文件过多的时候,应该需要有个存放这些模块的目录,Node就很靠谱,它规范我们可以将这些文件都房子node_modules目录中。
      所以,在node_modules中新建一个03_tool-multiply.js 文件,其内容如下:
      03_tool-multiply.js
    var tools = {
    	multiply: (...numbers) => {
    		let sum = numbers[0];
    		for (let number in numbers) {
    			sum = sunm * numbers[numbers];
    		}
    		return sum;
    	}
    }
    module.exports = tools;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在引用方面,只需要通过:

    // 如果Node在当前目录没找到too.js 文件,则会去node_modules 里面去查找
    var tools2 = require('03_tool-multiply');
    
    console.log(tools2.multiply(1,2,3,4));
    
    • 1
    • 2
    • 3
    • 4

    这样,就可以成功导入03_tool-multiply.js 文件了。

    • 方法三:
      如果,全部单个文件丢在node_modules 上,它会显得杂乱无章,所以应该定义个自己的模块:jsliang-module ,然后将我们的tool.js 存放在该目录中:
      jsliang-module/tools.js
    var tools = {
      add: (...numbers) => {
        let sum = 0;
        for (let number in numbers) {
          sum += numbers[number];
        }
        return sum;
      },
      multiply: (...numbers) => {
        let sum = numbers[0];
        for (let number in numbers) {
          sum = sum * numbers[number];
        }
        return sum;
      }
    }
    
    module.exports = tools;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    这样,就定义好了自己的工具库。
    但是,如果我们通过var tools3 = require(‘jsliang-module’); 去导入,会发现它报error了,所以,我们应该在jsliang-module 目录下,通过下面命令行生成一个package.json 。

    PS E:\MyWeb\node_modules\jsliang-nodule>npm init --yes

    这样,在jsliang-module 中就有了package.json ;
    而我们在03_CommonJS.js 就可以引入它了:

    03_CommonJS.js

    var http = require("http");
    
    var tools1 = require('./03_tool-add');
    
    // 如果 Node 在当前目录没找到 tool.js 文件,则会去 node_modules 里面去查找
    var tools2 = require('03_tool-multiply');
    
    /**
     * 通过 package.json 来引用文件
     * 1. 通过在 jsliang-module 中 npm init --yes 来生成 package.json 文件
     * 2. package.json 文件中告诉了程序入口文件为 :"main": "tools.js",
     * 3. Node 通过 require 查找 jsliang-module,发现它有个 package.json
     * 4. Node 执行 tools.js 文件
     */
    var tools3 = require('jsliang-module');
    
    http.createServer(function (req, res) {
    
      res.writeHead(200, {
        "Content-Type": "text/html;charset=UTF-8"
      });
    
      res.write('

    Hello NodeJS

    '
    ); console.log(tools1.add(1, 2, 3)); console.log(tools2.multiply(1, 2, 3, 4)); console.log(tools3.add(4, 5, 6)); /** * Console: * 6 * 24 * 15 * 6 * 24 * 15 * 这里要记得 Node 运行过程中,它请求了两次, * http://localhost:3000/ 为一次, * http://localhost:3000/favicon.ico 为第二次 */ res.end(); }).listen(3000);
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    至此,我们就通过三种方法,了解了各种exportsrequire 的姿势以及Node 模块化的概念了。

    1.4 fs文件管理

    下面,我们讲解下fs文件管理:

    如需快速找到下面某个内容,请使用Ctrl + F

    1. fs.stat 检测是文件还是目录 ;
    2. fs.mkdir 创建目录 ;
    3. fs.writeFile 创建写入文件 ;
    4. fs.appendFile 追加文件
    5. fs.readFile 读取文件 ;
    6. fs.readdir 读取目录 ;
    7. fs.rename 重命名 ;
    8. fs.rmdir 删除目录 ;
    9. fs.unlink 删除文件 。

    此章节的文件目录:
    在这里插入图片描述
    首先,通过fs.stat 检查一个读取的是文件还是目录:

    05_fs.js

    // 2. fs.mkdir
    let fs = require('fs');
    
    /**
    * 接收参数
     * path - 将创建的目录路径
     * mode - 目录权限(读写权限),默认 0777
     * callback - 回调,传递异常参数 err
     */
     fs.mkdir('css', (err) => {
    	if(err) {
    		console.log(err);
    		return false;
    	} else {
    	    console.log("创建目录成功");
    	    // Console: 创建目录成功!
    	}
     })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    通过node 05_fs.js ,发现目录中多了一个 css 文件夹 。
    那么,有创建就有删除,创建的目录如何删除呢?这里讲解下 fs.rmdir
    05_fs.js

    // 8. fs.rmdir
    let fs = require('fs');
    
    /**
     * 接收参数
     * path - 将创建的目录路径
     * mode - 目录权限(读写权限),默认 0777
     * callback - 回调,传递异常参数 err
     */
     fs.rmdir('css',(err) => {
    	if(err) {
    		console.log(err);
    		return false;
    	} else {
    		console.log("创建目录成功");
    		// Console: 创建目录成功!
    	}
     })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    通过 node 05_fs.js ,我们发现目录中的 css 文件夹被删除了。
    接着,通过 fs.writeFile 来创建写入文件 :
    05_fs.js

    // 3. fs.writeFile
    let fs = require('fs');
    
    /**
     * filename (String) 文件名称
     * data (String | Buffer) 将要写入的内容,可以是字符串或者 buffer 数据。
     * · encoding (String) 可选。默认 'utf-8',当 data 是 buffer 时,该值应该为 ignored。
     * · mode (Number) 文件读写权限,默认 438。
     * · flag (String) 默认值 'w'。
     * callback { Function } 回调,传递一个异常参数 err。
     */
    
    fs.writeFile('index.js','Hello jsliang', (err) => {
    	if(err) {
    		console.log(err);
    		return false;
    	} else {
    		console.log('写入成功');
    	}
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

      知得注意的是,这样的写入,是清空原文件中的所有数据,然后添加Hello jsliang这句话。即:存在即覆盖,不存在即创建。

      有创建就有删除,感兴趣的可以使用 fs.unlink 进行文件的删除,再次不做过多讲解。
      既然,上面的是覆盖文件,那么有没有追加文件呢?有的,使用 fs.appendFile 吧 :
    05_fs.js

    // 4. fs.appendFile
    let fs = require('fs');
    
    fs.appendFile('index.js','这段文本是要追加的内容',(err) => {
    	if(err) {
    		console.log(err);
    		return false;
    	} else {
    		console.log("追加成功");
    	}
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这样,就成功往里面追加了一段话,从而使 index.js 变成了:
    index.js

    Hello jsliang这段文本是要追加的内容

    在上面,我们已经做了:新增、修改、删除操作。那么,小伙伴一定很熟悉下一步是做什么了:

    • fs.readFile 读取文件;
    • fs.readdir 读取目录
    let fs = require('fs');
    
    // 5. fs.readFile
    fs.readFile('index.js', (err, data) => {
      if(err) {
        console.log(err);
        return false;
      } else {
        console.log("读取文件成功!");
        console.log(data);
        // Console:
        // 读取文件成功!
        // 
      }
    })
    
    // 6. fs.readdir 读取目录
    fs.readdir('node_modules', (err, data) => {
      if(err) {
        console.log(err);
        return false;
      } else {
        console.log("读取目录成功!");
        console.log(data);
        // Console:
        // 读取目录成功!
        // [ '03_tool-multiply.js', 'jsliang-module' ]
      }
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    如上,成功做到了读取文件和读取目录。
    最后,再回顾一开始的目标:
    1.fs.stat 检测是文件还是目录
    2.fs.mkdir 创建目录
    3.fs.writeFile 创建写入文件
    4.fs.appendFile 追加文件
    5.fs.readFile 读取文件
    6.fs.readdir 读取目录
    7.fs.rename 重命名
    8.fs.rmdir 删除目录
    9.fs.unlink 删除文件
    很好,就剩下重命名了:
    05_fs.js

    let fs = require('fs');
    
    // 7. fs.rename 重命名
    fs.rename('index.js', 'jsliang.js', (err) => {
    	if(err) {
    		console.log(err);
    		return false;
    	} else {
    		console.log("重命名成功!");
    	}
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    当然,如果 fs.rename 还有更厉害的功能:剪切

    05_fs.js

    let fs = require('fs');
    
    // 7. fs.rename 重命名
    fs.rename('jsliang.js', 'node_modules/jsliang.js', (err) => {
      if(err) {
        console.log(err);
        return false;
      } else {
        console.log("剪切成功!");
      }
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    通通搞定,现在目录变成了:
    在这里插入图片描述

    1.5 fs案例

    在上一章节中,我们了解了 fs 的文件管理。
    那么,在这里,我们尝试使用 fs 做点小事情:
    06_fsDemo.js

    /**
     * 1. fs.stat 检测是文件还是目录
     * 2. fs.mkdir 创建目录
     * 3. fs.writeFile 创建写入文件
     * 4. fs.appendFile 追加文件
     * 5. fs.readFile 读取文件
     * 6. fs.readdir 读取目录
     * 7. fs.rename 重命名
     * 8. fs.rmdir 删除目录
     * 9. fs.unlink 删除文件
     */
    
    // 1. 判断服务器上面有没有 upload 目录,没有就创建这个目录
    // 2. 找出 html 目录下面的所有的目录,然后打印出来
    
    let fs = require('fs');
    
    // 图片上传
    fs.stat('upload', (err, stats) => {
      // 判断有没有 upload 目录
      if(err) {
        // 如果没有
        fs.mkdir('upload', (error) => {
          if(error) {
            console.log(error);
            return false;
          } else {
            console.log("创建 upload 目录成功!");
          }
        })
      } else {
        // 如果有
        console.log(stats.isDirectory());
        console.log("有 upload 目录,你可以做更多操作!");
      }
    })
    
    // 读取目录全部文件
    fs.readdir('node_modules', (err, files) => {
      if(err) {
        console.log(err);
        return false;
      } else {
        // 判断是目录还是文件夹
        console.log(files);
    
        let filesArr = [];
    
        (function getFile(i) {
          
          // 循环结束
          if(i == files.length) {
            // 打印出所有目录
            console.log("目录:");
            console.log(filesArr);
            return false;
          }
    
          // 判断目录是文件还是文件夹
          fs.stat('node_modules/' + files[i], (error, stats) => {
    
            if(stats.isDirectory()) {
              filesArr.push(files[i]);
            }
    
            // 递归调用
            getFile(i+1);
            
          })
        })(0)
      }
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    1.6 fs流

    我们了解下 fs 流及其读取:

    // 新建 fs
    const fs = require('fs');
    // 流的方式读取文件
    let fileReadStream = fs.createReadStream('index.js');
    // 读取次数
    let count = 0;
    // 保存数据
    let str = '';
    // 开始读取
    fileReadStream.on('data', (chunk) => {
      console.log(`${++count} 接收到:${chunk.length}`);
      // Console:1 接收到:30
      str += chunk;
    })
    // 读取完成
    fileReadStream.on('end', () => {
      console.log("——结束——");
      console.log(count);
      console.log(str);
    
      // Console:——结束——
      // 1
      // console.log("Hello World!");
    })
    // 读取失败
    fileReadStream.on('error', (error) => {
      console.log(error);
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    在这里,通过 fs 模块的 createReadStream 创建了读取流,然后,读取文件 index.js ,从而最后在控制台输出了:

    1 接收到:259
    ——结束——
    1
    console.log("尽信书,不如无书;尽看代码,不如删掉这些文件。");
    console.log("尽信书,不如无书;尽看代码,不如删掉这些文件。");
    console.log("尽信书,不如无书;尽看代码,不如删掉这些文件。");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    其中,console.log() 那三行就是index.js的文本内容 。
    然后,试下流的存入:

    let fs = require('fs');
    let data = 'console.log("Hello World! 我要存入数据!")';
    
    // 创建一个可以写入的流,写入到文件 index.js 中
    let writeStream = fs.createWriteStream('index.js');
    // 开始写入
    writeStream.write(data, 'utf8');
    // 写入完成
    writeStream.end();
    writeStream.on('finish', () => {
      console.log('写入完成!');
      // Console:写入完成
    });
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    我们打开 index.js,会发现里面的内容变成了 console.log("Hello World! 我要存入数据!"),依次,我们通过流的形式进行了读取和写入的操作。

    1.7 创建web服务器

    在这里,我们利用http模块、url模块、path模块、fs模块创建一个web服务器。
    什么是web服务器?
    web服务器一般指网站服务器,是指驻留在因特网上某种类型计算机的程序,可以像游览器等web客户端提供文档,也可以放置网站文件,让全世界游览;可以放置数据文件,让全世界下载。目前,最主流的三个web服务器是Apache 、Nginx 、IIS
    下面,我们使用Node来创建一个Web服务:
    在这里插入图片描述
    08_WebService.js

    // 引入 http 模块
    let http = require("http");
    
    // 引入 fs 模块
    let fs = require("fs");
    
    http.createServer((req, res) => {
      // 获取响应路径
      let pathName = req.url;
    
      // 默认加载路径
      if (pathName == "/") {
        // 默认加载的首页
        pathName = "index.html";
      }
    
      // 过滤 /favicon.ico 的请求
      if (pathName != "/favicon.ico") {
        // 获取 08_WebService 下的 index.html
        fs.readFile("./08_WebService/" + pathName, (err, data) => {
          if (err) {
            
            // 如果不存在这个文件
            
            console.log("404 Not Found!");
            fs.readFile('./08_WebService/404.html', (errorNotFound, dataNotFound) => {
              if(errorNotFound) {
                console.log(errorNotFound);
              } else {
                res.writeHead(200, {
                  "Content-Type": "text/html; charset='utf-8'"
                });
                // 读取写入文件
                res.write(dataNotFound);
                // 结束响应
                res.end();
              }
            })
            return;
          } else {
    
            // 返回这个文件
            
            // 设置请求头
            res.writeHead(200, {
              "Content-Type": "text/html; charset='utf-8'"
            });
            // 读取写入文件
            res.write(data);
            // 结束响应
            res.end();
          }
        });
      }
    }).listen(8080);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    这样,在游览器输入 localhost:8080 即可以看到 :
    在这里插入图片描述
    然后,你会发现它就加载了整个 index.html 文件,连CSS这些没引入么?
    所以,下一步,要动态加载 htmlcss 、以及 js
    08_WebService.js

    // 引入 http 模块
    let http = require("http");
    
    // 引入 fs 模块
    let fs = require("fs");
    
    // 引入 url 模块
    let url = require("url");
    
    // 引入 path 模块
    let path = require("path");
    
    http.createServer((req, res) => {
      
      // 获取响应路径
      let pathName = url.parse(req.url).pathname;
    
      // 默认加载路径
      if (pathName == "/") {
        // 默认加载的首页
        pathName = "index.html";
      }
    
      // 获取文件的后缀名
      let extName = path.extname(pathName);
    
      // 过滤 /favicon.ico 的请求
      if (pathName != "/favicon.ico") {
        // 获取 08_WebService 下的 index.html
        fs.readFile("./08_WebService/" + pathName, (err, data) => {
          // 如果不存在这个文件
          if (err) {
            console.log("404 Not Found!");
            fs.readFile(
              "./08_WebService/404.html",
              (errorNotFound, dataNotFound) => {
                if (errorNotFound) {
                  console.log(errorNotFound);
                } else {
                  res.writeHead(200, {
                    "Content-Type": "text/html; charset='utf-8'"
                  });
                  // 读取写入文件
                  res.write(dataNotFound);
                  // 结束响应
                  res.end();
                }
              }
            );
            return;
          }
          // 返回这个文件
          else {
            // 获取文件类型
            let ext = getExt(extName);
    
            // 设置请求头
            res.writeHead(200, {
              "Content-Type": ext + "; charset='utf-8'"
            });
            // 读取写入文件
            res.write(data);
            // 结束响应
            res.end();
          }
        });
      }
    }).listen(8080);
    
    // 获取后缀名
    getExt = (extName) => {
      switch(extName) {
        case '.html': return 'text/html';
        case '.css': return 'text/css';
        case '.js': return 'text/js';
        default: return 'text/html';
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    这样,当我们再次请求的时候,游览器就变成了:
    在这里插入图片描述
     当然,在上面,我们仅仅模拟了 html、css、js 这三种文件类型而已,我们需要模拟更多的文件类型:

    然后,我们需要修改下我们的 js 文件,让它适应多种请求响应了:
    08_WebService.js

    // 引入 http 模块
    let http = require("http");
    
    // 引入 fs 模块
    let fs = require("fs");
    
    // 引入 url 模块
    let url = require("url");
    
    // 引入 path 模块
    let path = require("path");
    
    http.createServer((req, res) => {
      
      // 获取响应路径
      let pathName = url.parse(req.url).pathname;
    
      // 默认加载路径
      if (pathName == "/") {
        // 默认加载的首页
        pathName = "index.html";
      }
    
      // 获取文件的后缀名
      let extName = path.extname(pathName);
    
      // 过滤 /favicon.ico 的请求
      if (pathName != "/favicon.ico") {
        // 获取 08_WebService 下的 index.html
        fs.readFile("./08_WebService/" + pathName, (err, data) => {
          // 如果不存在这个文件
          if (err) {
            console.log("404 Not Found!");
            fs.readFile(
              "./08_WebService/404.html",
              (errorNotFound, dataNotFound) => {
                if (errorNotFound) {
                  console.log(errorNotFound);
                } else {
                  res.writeHead(200, {
                    "Content-Type": "text/html; charset='utf-8'"
                  });
                  // 读取写入文件
                  res.write(dataNotFound);
                  // 结束响应
                  res.end();
                }
              }
            );
            return;
          }
          // 返回这个文件
          else {
            // 获取文件类型
            let ext = getExt(extName);
            console.log(ext);
    
            // 设置请求头
            res.writeHead(200, {
              "Content-Type": ext + "; charset='utf-8'"
            });
            // 读取写入文件
            res.write(data);
            // 结束响应
            res.end();
          }
        });
      }
    }).listen(8080);
    
    // 获取后缀名
    getExt = (extName) => {
      // readFile 是异步操作,所以需要使用 readFileSync
      let data = fs.readFileSync('./08_ext.json');
      let ext = JSON.parse(data.toString());
      return ext[extName];
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    如此,我们做了个简单的 Web 服务器。

    1.8 非阻塞I/O事件驱动

      Java、PHP或者.NET等服务端语言,会为每一个客户端的连接创建一个新的线程。
      Node不会为每一个客户连接创建一个新的线程,而仅仅使用一个线程。
    当有用户连接了,就会触发一个内部事件,通过非阻塞I/O、事件驱动机制,让Node程序宏观上也是并行的。
      使用Node,一个8GB内存的服务器,可以同时处理超过4万用户的连接。
      在这一章节中,主要解决:
      1.Node 的非阻塞I/O是什么?
      2.Node events 模块是什么?
      首先,在我们正常编程中,我们是希望程序能够一行一行按照我们的意愿编写的:
    09_io.js

    console.log("1");
    
    console.log("2");
    
    console.log("3");
    
    /**
     * Console:
     * 1
     * 2
     * 3
     */
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

       但是,事与愿违。
     我们有时候,会执行一些异步方法(函数):
    09_io.js

    console.log("1");
    
    // console.log("2");
    let fs = require('fs');
    getExt = () => {
      fs.readFile('08_ext.json', (err, data) => {
        console.log("2");
      })
    }
    getExt();
    
    console.log("3");
    
    /**
     * Console:
     * 1
     * 3
     * 2
     */
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

       在上面代码中,由于 fs.readFile 是 Node 的异步函数。所以,程序先执行了 1 和 3,最后才执行 fs.readFile 的 2 部分。

    在这里,可以看出 Node 不会因为一段代码的逻辑错误,从而导致其他代码无法运行。

       这样子,就导致了一个问题:步骤3可能拿不到步骤2的执行结果了。这就是Node的非阻塞性I/O驱动。
       那么,我们有没有办法解决这个问题呢?
     有的!
    1.通过回调函数;
    2.通过 Node 的 events 模块。
    09_io.js

    let fs = require("fs");
    
    getExt = (callback) => {
      fs.readFile('08_ext.json', (err, data) => {
        callback(data);
      })  
    }
    
    getExt( (result) => {
      console.log(result.toString());
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    通过回调,我们可以将 getExt 的数据提取出来。

    然后,我们通过 Node 的 events 模块来解决这个异步问题:

    // 引入 fs 模块
    let fs = require("fs");
    
    /**
     * Node 事件循环:
     * 1.Node 是单进程单线程应用程序,但是通过事件和回调支持并发,所以性能非常高;
     * 2.Node 的每一个API都是异步的,并作为一个独立线程运行,使用异步函数调用,并处理并发;
     * 3.Node 有多个内置的事件,我们可以通过引入events 模块,并通过实例化EventEmitter 类来绑定和监听事件。
     */
    
    // 引入 events 模块
    let events = require("events");
    // 实例化事件对象
    let EventEmitter = new events.EventEmitter();
    
    getExt = () => {
      fs.readFile('08_ext.json', (err, data) => {
        // 将 data 广播出去
        EventEmitter.emit('data', data.toString());
      })  
    };
    
    getExt();
    
    // 监听 data
    EventEmitter.on('data', (ext) => {
      console.log(ext);
    });
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    在这里,EventEmitter.on 通过监听 data 的形式,获取了 getExt 内部的执行结果。
     如此,我们就了解了 Node 的 I/O 事件及 events 模块

    1.9 get与post

    在这里插入图片描述
    话不多说,先上代码:
    index.js

    // 加载 http 模块
    var http = require('http');
    
    // 虚拟 SQL 读取出来的数据
    var items = [];
    
    // 创建 http 服务
    http.createServer(function (req, res) {
      
      // 设置跨域的域名,* 代表允许任意域名跨域
      res.setHeader('Access-Control-Allow-Origin', '*');
      // 设置 header 类型
      res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
      // 跨域允许的请求方式
      res.setHeader('Content-Type', 'application/json');
    
      // 判断请求
      switch (req.method) {
        
        // post 请求时,浏览器会先发一次 options 请求,如果请求通过,则继续发送正式的 post 请求
        case 'OPTIONS':
          res.statusCode = 200;
          res.end();
          break;
        
          // 如果是 get 请求,则直接返回 items 数组
        case 'GET':
          let data = JSON.stringify(items);
          res.write(data);
          res.end();
          break;
          
        // 如果是 post 请求
        case 'POST':
          let item = '';
          // 读取每次发送的数据
          req.on('data', function (chunk) {
            item += chunk;
          });
          // 数据发送完成
          req.on('end', function () {
            // 存入
            item = JSON.parse(item);
            items.push(item.item);
            // 将数据返回到客户端
            let data = JSON.stringify(items);
            res.write(data);
            res.end();
          });
          break;
      }
    }).listen(3000)
    
    console.log('http server is start...');
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    首先,加载了http模块,并创建了服务;
    然后,设置了跨域的处理方式,允许进行跨域;
    接着,进行了请求的判断处理,由于只做了简单演练,故只判断是get请求还是post请求;
    最后,将请求的结果返回给客户端。
    在上面,我们进行了后端 Node 的部署,那么前端页面要怎么做呢?
    index.html

    DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
      <meta http-equiv="X-⌃-Compatible" content="ie=edge">
      <title>Node Webtitle>
    
    head>
    
    <body>
    
      <div id="app">
        <h1>Todo Listh1>
        <ul>
          <li v-for="(item, index) in items" :key="index">{{ item }}li>
        ul>
        <input type="text" v-model="item">
        <button @click="postApi">添加button>
      div>
    
      
      <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
      <script src="https://unpkg.com/axios/dist/axios.min.js">script>
      
      <script>
        new Vue({
          el: document.getElementById('app'),
          data: function () {
            return {
              items: [],
              item: '',
            }
          },
          created() {
            // 进入页面请求数据
            axios.get('http://localhost:3000/').then(res => {
              console.log("\n【API - get 数据】");
              console.log(res);
              this.items = res.data;
            }).catch(function (err) {
              console.log(err)
            })
          },
          methods: {
            // 点击按钮提交数据
            postApi() {
              axios.post('http://localhost:3000/', {
                item: this.item
              }).then(res => {
                console.log("\n【API - post 数据】")
                console.log(res);
                this.items = res.data;
              }).catch(function (err) {
                console.log(err)
              })
            }
          }
        })
      script>
    body>
    
    html>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    我们通过 Vue 进行了布局,通过 Axios 进行了接口的请求。从而完成了对数据的操作。

    1.9 Node连接MySQL

    首先,我们通过可视化工具进行表的设计:

    类型长度
    idint11主键
    namevarchar255
    agevarchar255

    然后,我们进行表的填充:

    idnameage
    1jsliang23
    2liang25

    接着,我们安装 Node 连接 MySQL 的包:

    npm i mysql -D

    再来,我们编写 Node 的 index.js
    index.js

    var mysql = require('mysql');
    var connection = mysql.createConnection({
      host: 'localhost',
      user: 'root',
      password: '123456',
      database: 'node'
    });
    
    connection.connect();
    
    connection.query('SELECT * FROM user', function (error, results, fields) {
      if (error) throw error;
      console.log(results);
    });
    
    connection.end();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    最后,我们通过 node index.js,打开该服务:

    [ RowDataPacket { id: 1, name: 'jsliang', age: '23' },
      RowDataPacket { id: 2, name: 'liang', age: '25' } ]
    
    • 1
    • 2

    如此,我们便完成了 Node 连接 MySQL。

    ———————华丽分割线———————

    当然,增删改查是后端的基本操作,所以在这里,我们可以补全基本的增删改查功能。

    先看目录:
    在这里插入图片描述

    • 新增表字段
      add.js
    var mysql = require('mysql');
    var connection = mysql.createConnection({
      host: 'localhost',
      user: 'root',
      password: '123456',
      database: 'node'
    });
    
    connection.connect();
    
    let addSql = "INSERT INTO user(id,name,age) VALUES(0,?,?)";
    let addSqlParams = ["jsliang", "23"];
    
    connection.query(addSql, addSqlParams, function (err, res) {
      if (err) {
        console.log("新增错误:");
        console.log(err);
        return;
      } else {
        console.log("新增成功:");
        console.log(res);
      }
    });
    
    connection.end();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    我们只需要直接 node add.js,就能往数据库中新增数据了。

    • 删除表字段
      delete.js
    // 连接 MySQL
    var mysql = require('mysql');
    // MySQL 的连接信息
    var connection = mysql.createConnection({
      host: 'localhost',
      user: 'root',
      password: '123456',
      database: 'node'
    });
    
    // 开始连接
    connection.connect();
    
    // 新增的 SQL 语句及新增的字段信息
    var delSql = 'DELETE FROM user where id = 2';
    
    // 连接 SQL 并实施语句
    connection.query(delSql, function (err, res) {
      if (err) {
        console.log("删除错误:");
        console.log(err);
        return;
      } else {
        console.log("删除成功:");
        console.log(res);
      }
    });
    
    // 终止连接
    connection.end();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 修改表字段
      update.js
    // 连接 MySQL
    var mysql = require('mysql');
    // MySQL 的连接信息
    var connection = mysql.createConnection({
      host: 'localhost',
      user: 'root',
      password: '123456',
      database: 'node'
    });
    
    // 开始连接
    connection.connect();
    
    // 新增的 SQL 语句及新增的字段信息
    let updateSql = "UPDATE user SET name = ?,age = ? WHERE Id = ?";
    let updateSqlParams = ["LiangJunrong", "23", 1];
    
    // 连接 SQL 并实施语句
    connection.query(updateSql, updateSqlParams, function (err, res) {
      if (err) {
        console.log("修改错误:");
        console.log(err);
        return;
      } else {
        console.log("修改成功:");
        console.log(res);
      }
    });
    
    // 终止连接
    connection.end();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 查询表字段
      read.js
    // 连接 MySQL
    var mysql = require('mysql');
    // MySQL 的连接信息
    var connection = mysql.createConnection({
      host: 'localhost',
      user: 'root',
      password: '123456',
      database: 'node'
    });
    
    // 开始连接
    connection.connect();
    
    // 新增的 SQL 语句及新增的字段信息
    let readSql = "SELECT * FROM user";
    
    // 连接 SQL 并实施语句
    connection.query(readSql, function (err, res) {
      if (err) throw err;
      console.log(res);
    });
    
    // 终止连接
    connection.end();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    以上,我们打通了 Node 与 MySQL 的壁垒,实现了数据的增删改查。

    二、Web实战----企业官网

    2.1 编程环境

    首先,我们查看下我们的前端基本代码:地址

    然后,我们进行后端功能分析:

    1. 留言板。用户点击 留言板 的时候,需要先判断用户是否登录。如果用户尚未登录,则直接跳转到 登录页;如果用户登录了,则显示 留言板页面
      留言板页面 中,存在两个接口:
    • 获取留言内容: 调取 getMessage 接口,返回全部留言信息,由于预计信息不多,故这里不做分页功能,有需要的小伙伴在实现完这个功能后,可以进行分页接口的设计;
    • 提交留言内容:调取 sendMessage 接口,将用户名、用户 id、留言内容发送给后端。
    1. 登录页面 中,存在一个接口:
      登录:调取 login 接口,提交用户填写的姓名和密码。
    2. 注册页面 中,存在一个接口:
      注册:调取 register 接口,提交用户填写的姓名和密码。

    由此,我们可以设计下前后端的接口结合:

    接口文档

    接口类型参数返回信息
    getMessage:获取留言信息get无参n 条记录:id(用户 id)、user_name(用户名)、user_message(用户留言内容)、time(留言时间)
    sendMessage:提交留言信息postid(用户 id)、user_name(用户名)、user_message(用户留言内容)status 状态
    login:登录postid(用户 id)、user_name(用户名)、user_password(用户密码)status 状态
    register:注册postid(用户 id)、user_name(用户名)、user_password(用户密码)status 状态

    最后,我们进行 MySQL 数据库的表设计:

    user 表

    类型长度
    idint11主键
    user_namevarchar255
    user_passwordvarchar255
    timedatetime

    message 表

    类型长度
    idint11主键
    user_namevarchar255
    user_passwordvarchar255
    timedatetime

    2.2 后端接口

    在我们进行实操之前,先确认我们是否能写接口,所以我们可以新建一个 test 文件夹,里面放一个 index.html 以及一个 index.js 来测试一下。

    - text
     - index.html
     - index.js
    
    • 1
    • 2
    • 3

    首先,我们就 2.1 提到的接口,提前进行后端接口的设置:

    index.js

    // 连接 MySQL:先安装 npm i mysql -D
    var mysql = require('mysql');
    // MySQL 的连接信息
    var connection = mysql.createConnection({
      host: 'localhost',
      user: 'root',
      password: '123456',
      database: 'nodebase'
    });
    // 开始连接
    connection.connect();
    
    // 引入 http 模块:http 是提供 Web 服务的基础
    const http = require("http");
    
    // 引入 url 模块:url 是对用户提交的路径进行解析
    const url = require("url");
    
    // 引入 qs 模块:qs 是对路径进行 json 化或者将 json 转换为 string 路径
    const qs = require("querystring");
    
    // 用 http 模块创建服务
    /**
     * req 获取 url 信息 (request)
     * res 浏览器返回响应信息 (response)
     */
    http.createServer(function (req, res) {
    
      // 设置 cors 跨域
      res.setHeader("Access-Control-Allow-Origin", "*");
      // 设置 header 类型
      res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
      // 跨域允许的请求方式
      res.setHeader('Content-Type', 'application/json');
    
      if (req.method == "POST") { // 接口 POST 形式
    
        console.log("\n【POST 形式】");
    
        // 获取前端发来的路由地址
        let pathName = req.url;
    
        console.log("\n接口为:" + pathName);
    
        // 接收发送过来的参数
        let tempResult = "";
    
        // 数据接入中
        req.addListener("data", function (chunk) {
          tempResult += chunk;
        });
    
        // 数据接收完成
        req.addListener("end", function () {
    
          var result = JSON.stringify(qs.parse(tempResult));
          console.log("\n参数为:");
          console.log(result);
    
          if (pathName == "/sendMessage") { // 提交留言信息
    
            console.log("\n【API - 提交留言信息】");
    
          } else if (pathName == "/login") { // 登录
    
            console.log("\n【API - 登录】");
    
          } else if (pathName == "/register") { // 注册
    
            console.log("\n【API - 注册】");
    
          }
          // 接口信息处理完毕
        })
        // 数据接收完毕
    
      } else if (req.method == "GET") { // 接口 GET 形式
    
        console.log("\n【GET 形式】");
    
        // 解析 url 接口
        let pathName = url.parse(req.url).pathname;
    
        console.log("\n接口为:" + pathName);
    
        if (pathName == "/getMessage") { // 获取留言信息
    
          console.log("\n【API - 获取留言信息】");
    
        } else if(pathName == "/") { // 首页
          res.writeHead(200, {
            "Content-Type": "text/html;charset=UTF-8"
          });
    
          res.write('

    jsliang 前端有限公司服务已开启!

    详情可见:Node 基础

    '
    ); res.end(); } } }).listen(8888); // 监听的端口 // 获取当前时间 function getNowFormatDate() { var date = new Date(); var year = date.getFullYear(); // 年 var month = date.getMonth() + 1; // 月 var strDate = date.getDate(); // 日 var hour = date.getHours(); // 时 var minute = date.getMinutes(); // 分 var second = date.getMinutes(); // 秒 if (month >= 1 && month <= 9) { month = "0" + month; } if (strDate >= 0 && strDate <= 9) { strDate = "0" + strDate; } // 返回 yyyy-mm-dd hh:mm:ss 形式 var currentdate = year + "-" + month + "-" + strDate + " " + hour + ":" + minute + ":" + second; return currentdate; }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123

    通过判断 req.method 属于 GET 还是 POST 形式,从而确定加载的接口:

    • POST 中,判断是属于 提交留言信息登录 还是 注册
    • GET 中,判断是不是 获取留言信息

    同时,我们在其中定义了 MySQL 的连接以及一个 getNowFormatDate 用来获取当前时间,格式为:2018-12-21 10:03:59

    然后,我们通过一个前端页面来演示我们的接口是否能使用:

    index.html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>演示代码</title>
    </head>
    
    <body>
      <div>
        <label for="user">用户名</label><input type="text" id="user">
      </div>
      <div>
        <label for="password">&nbsp;&nbsp;&nbsp;</label><input type="password" id="password">
      </div>
      <div>
        <button id="register">注册</button>
      </div>
    
      <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
      <script>
        $(function () {
          // 测试 get 接口
          $.ajax({
            url: "http://localhost:8888/getMessage",
            type: "POST",
            data: {
              username: "jsliang"
            },
            success: function (res) {
              console.log(res);
            },
            error: function (err) {
              console.log(err);
            }
          })
    
          $("#register").click(function () {
            // 测试 post 接口
            $.ajax({
              url: "http://localhost:8888/login",
              type: "POST",
              data: {
                username: $("#user").val(),
                password: $("#password").val()
              },
              success: function (res) {
                console.log(res);
              },
              error: function (err) {
                console.log(err);
              }
            })
          })
        });
      </script>
    </body>
    
    </html>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    最后,我们通过 node index.js,并打开 index.html,通过 F12 控制台查看我们的接口是否正常:
     可以看到我们的接口能正常调通,这样我们就可以连接数据库,进行这 4 个接口的设计了。

    如果小伙伴们觉得每次更新 Node 代码后,又要重启一遍 node index.js 觉得麻烦,可以通过 supervisor 来监听 Node 代码的改动

    2.3 注册功能

    很好,我们回到仿企业网站的页面上,准备编写接口以及丰富 Node 的接口。
    首先,我们开启前端和 Node 服务:

    1. 打开命令行/终端 ;
    2. 开启前端
      cd FrontEndCode
      live-server

    安装 live-servernpm i live-server -g

    1. 开启后端
      cd NodeWeb
      supervisor index.js

    安装 supervisor:npm i supervisor -g

    然后,我们在注册页面通过点击事件来触发调接口:

    register.html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="keywords" content="前端,jsliang,bootstrap,企业建站">
      <meta http-equiv="description" content="jsliang 为你打造最好的企业服务">
      <link rel="shortcut icon" href="./images/favicon.ico" type="image/x-icon" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>注册-jsliang 前端有限公司</title>
      <link rel="stylesheet" href="./css/index.css">
      <link rel="stylesheet" href="./css/bootstrap.min.css">
    </head>
    
    <body>
      <!-- 省略 body 中代码,有需要的请前往第四章开头下载查看全部代码 -->
    
      <script src="./js/jquery-3.3.1.min.js"></script>
      <script src="./js/bootstrap.min.js"></script>
      <script src="./js/islogin.js"></script>
      <script>
        $(function () {
          $("#register-submit").click(function () {
    
            let userName = $("#userName").val();
            let userPassword = $("#userPassword").val();
    
            if (!userName) {
              alert("请输入用户名");
              $("#userName").focus();
            } else if (!userPassword) {
              alert("请输入密码");
              $("#userPassword").focus();
            } else if (userName.length > 10) {
              alert("请输入少于 10 位的用户名");
              $("#userName").focus();
            } else if (userPassword.length > 20) {
              alert("请输入少于 20 位的密码");
              $("#userPassword").focus();
            } else {
    
              // 如果用户输入的没毛病,那就加载接口
              $.ajax({
                url: "http://localhost:8888/register",
                type: 'post',
                dataType: 'json',
                data: {
                  username: userName,
                  password: userPassword
                },
                success: function (res) {
                  console.log(res);
                  if (res.code == "0") {
                    alert("注册成功,前往登录!");
                    window.location.href = "./login.html";
                  }
                },
                error: function (err) {
                  console.log(err.responseText);
                  if (err.responseText == "注册失败,姓名重复!") {
                    alert("用户名已被注册!");
                  } else if (err.responseText == "注册失败,名额已满!") {
                    alert("注册失败,名额已满!");
                  } else if (err.responseText == "注册失败,密码为空!") {
                    alert("注册失败,密码为空!");
                  } else if (err.responseText == "注册失败,姓名过长!") {
                    alert("注册失败,姓名过长!");
                  } else if (err.responseText == "注册失败,密码过长!") {
                    alert("注册失败,密码过长!");
                  } else {
                    alert("未知错误!");
                  }
                }
              })
            }
    
          })
        })
      </script>
    </body>
    
    </html>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    如此,我们在用户点击 注册 按钮的时候,进行接口的调用,发送数据到了后端,如果成功了,那就弹窗,并跳转到登录页;如果没成功,就弹窗提示。
    接着,我们编写 Node,前端调用接口后,Node 判断这两个参数是否为空,如果不为空,则将数据存储到数据库。

    index.js

    // ... 其他代码省略,请自行前往章节 2.2 后端接口 获取其他代码
    
    if (pathName == "/sendMessage") { // 提交留言信息
    
      console.log("\n【API - 提交留言信息】");
    
    } else if (pathName == "/login") { // 登录
    
      console.log("\n【API - 登录】");
    
    } else if (pathName == "/register") { // 注册
    
      console.log("\n【API - 注册】");
    
      result = JSON.parse(result);
    
      let username = result.username; // 用户名
      let password = result.password; // 密码
      let time = getNowFormatDate(); // 时间
    
      if (!username) { // 用户名为空
        res.end("注册失败,用户名为空。");
        return;
      } else if (!password) { // 密码为空
        res.end("注册失败,密码为空!");
        return;
      } else if(username.length > 10) { // 姓名过长
        res.end("注册失败,姓名过长!");
        return;
      } else if(password.length > 20) { // 密码过长
        res.end("注册失败,密码过长!");
        return;
      } else {
        
        // 查询 user 表
        // 使用 Promise 的原因是因为中间调用了两次数据库,而数据库查询是异步的,所以需要用 Promise。
        new Promise( (resolve, reject) => {
    
          // 新增的 SQL 语句及新增的字段信息
          let readSql = "SELECT * FROM user";
          
          // 连接 SQL 并实施语句
          connection.query(readSql, function (error1, response1) {
            
            if (error1) { // 如果 SQL 语句错误
              throw error1;
            } else {
              
              console.log("\nSQL 查询结果:");
    
              // 将结果先去掉 RowDataPacket,再转换为 json 对象
              let newRes = JSON.parse(JSON.stringify(response1));
              console.log(newRes);
    
              // 判断姓名重复与否
              let userNameRepeat = false;
              for(let item in newRes) {
                if(newRes[item].user_name == username) {
                  userNameRepeat = true;
                }
              }
    
              // 如果姓名重复
              if(userNameRepeat) {
                res.end("注册失败,姓名重复!");
                return;
              } else if(newRes.length > 300) { // 如果注册名额已满
                res.end("注册失败,名额已满!");
                return;
              } else { // 可以注册
                resolve();
              }
              
            }
          });
    
        }).then( () => {
          
          console.log("\n第二步:");
          
          // 新增的 SQL 语句及新增的字段信息
          let addSql = "INSERT INTO user(user_name,user_password, time) VALUES(?,?,?)";
          let addSqlParams = [result.username, result.password, time];
    
          // 连接 SQL 并实施语句
          connection.query(addSql, addSqlParams, function (error2, response2) {
            if (error2) { // 如果 SQL 语句错误
              console.log("新增错误:");
              console.log(error2);
              return;
            } else {
              console.log("\nSQL 查询结果:");
              console.log(response2);
    
              console.log("\n注册成功!");
    
              // 返回数据
              res.write(JSON.stringify({
                code: "0",
                message: "注册成功!"
              }));
    
              // 结束响应
              res.end();
            }
          });
    
        })
        // Promise 结束
      }
      // 注册流程结束
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113

    最后,我们在查看下该功能是否成功:

    2.4 注册功能

    在上面,我们完成了注册功能,那么相对来说,登录功能就容易通了,因为查询部分我们已经试过了一次。

    login.html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="keywords" content="前端,jsliang,bootstrap,企业建站">
      <meta http-equiv="description" content="jsliang 为你打造最好的企业服务">
      <link rel="shortcut icon" href="./images/favicon.ico" type="image/x-icon" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>登录-jsliang 前端有限公司</title>
      <link rel="stylesheet" href="./css/index.css">
      <link rel="stylesheet" href="./css/bootstrap.min.css">
    </head>
    
    <body>
      
      <!-- 代码省略,有需要的小伙伴请在第四章前言部分下载代码 -->
    
      <script src="./js/jquery-3.3.1.min.js"></script>
      <script src="./js/bootstrap.min.js"></script>
      <script src="./js/islogin.js"></script>
      <script>
        $(function () {
          $("#login-submit").click(function () {
    
            let userName = $("#userName").val(); // 用户名
            let userPassword = $("#userPassword").val(); // 密码
    
            if (!userName) {
              alert("请输入用户名");
              $("#userName").focus();
            } else if (!userPassword) {
              alert("请输入密码");
              $("#userPassword").focus();
            } else if (userName.length > 10) {
              alert("请输入少于 10 位的用户名");
              $("#userName").focus();
            } else if (userPassword.length > 20) {
              alert("请输入少于 20 位的密码");
              $("#userPassword").focus();
            } else {
    
              $.ajax({
                url: "http://localhost:8888/login",
                type: 'post',
                dataType: 'json',
                data: {
                  username: userName,
                  password: userPassword
                },
                success: function (res) {
                  console.log(res);
                  if (res.code == "0") {
                    sessionStorage.setItem("id", res.data.id);
                    sessionStorage.setItem("userName", res.data.userName);
                    alert("登录成功!");
                    window.location.href = "./messageBoard.html";
                  } else if (res.code == "1") {
                    alert("登录失败,密码错误!");
                  }
                },
                error: function (err) {
                  console.log(err.responseText);
                  if (err.responseText == "不存在该用户!") {
                    alert("不存在该用户!");
                  } else if (err.responseText == "登录失败,用户名为空!") {
                    alert("登录失败,用户名为空!");
                  } else if (err.responseText == "登录失败,密码为空!") {
                    alert("登录失败,密码为空!");
                  } else if (err.responseText == "登录失败,姓名过长!") {
                    alert("登录失败,姓名过长!");
                  } else if (err.responseText == "登录失败,密码过长!") {
                    alert("登录失败,密码过长!");
                  } else {
                    alert("未知错误!");
                  }
                }
              })
    
            }
    
          })
        })
      </script>
    </body>
    
    </html>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89

    编写完前端的代码后,我们进行 Node 代码的编辑:

    index.js

    
    // ... 其他代码省略,请自行前往章节 2.2 后端接口 获取其他代码
    
    if (pathName == "/sendMessage") { // 提交留言信息
    
      console.log("\n【API - 提交留言信息】");
    
    } else if (pathName == "/login") { // 登录
    
      console.log("\n【API - 登录】");
    
      result = JSON.parse(result);
    
      let username = result.username; // 用户名
      let password = result.password; // 密码
    
      if (!username) { // 用户名为空
        res.end("登录失败,用户名为空!");
        return;
      } else if (!password) { // 密码为空
        res.end("登录失败,密码为空!");
        return;
      } else if(username.length > 10) {
        res.end("登录失败,姓名过长!");
        return;
      } else if(password.length > 20) {
        res.end("登录失败,密码过长!");
        return;
      } else { 
        
        // 新增的 SQL 语句及新增的字段信息
        let readSql = "SELECT * FROM user WHERE user_name  = '" + username + "'";
    
        // 连接 SQL 并实施语句
        connection.query(readSql, function (error1, response1) {
          if (error1) {
            throw error1;
          } else {
            if(response1 == undefined || response1.length == 0) { // 不存在用户
              res.end("\n不存在该用户!");
              return;
            } else { // 存在用户
              console.log("\n存在该用户!");
    
              let newRes = JSON.parse(JSON.stringify(response1));
              console.log(newRes);
    
              if(newRes[0].user_password == password) { // 密码正确
                // 返回数据
                res.write(JSON.stringify({
                  code: "0",
                  message: "登录成功!",
                  data: {
                    id: newRes[0].id,
                    userName: newRes[0].user_name
                  }
                }));
    
                res.end();
              } else { // 密码错误
                // 返回数据
                res.write(JSON.stringify({
                  code: "1",
                  message: "登录失败,密码错误!"
                }));
    
                res.end();
              }
              // 判断密码正确与否完毕
            }
            // 存在用户处理结束
          }
        });
      }
      // 登录步骤结束
    } else if (pathName == "/register") { // 注册
    
      console.log("\n【API - 注册】");
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    很好,前端和后端都编写完毕,是时候查验下功能是否实现了:

    2.5 留言功能

    现在,我们就剩下留言功能了,一鼓作气做好它吧!

    messageBoard.html

    <!-- 留言板 -->
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="keywords" content="前端,jsliang,bootstrap,企业建站">
      <meta http-equiv="description" content="jsliang 为你打造最好的企业服务">
      <link rel="shortcut icon" href="./images/favicon.ico" type="image/x-icon" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>留言板-jsliang 前端有限公司</title>
      <link rel="stylesheet" href="./css/index.css">
      <link rel="stylesheet" href="./css/bootstrap.min.css">
    </head>
    
    <body>
      
      <!-- 代码省略,基础代码请前往本章节前言下载 -->
    
      <script src="./js/jquery-3.3.1.min.js"></script>
      <script src="./js/bootstrap.min.js"></script>
      <script src="./js/islogin.js"></script>
      <script>
        $(function() {
          
          let userName = sessionStorage.getItem("userName");
          let userId = sessionStorage.getItem("id");
          
          // 查询留言板
          if(userName && userId) { // 如果有存储
            $.ajax({
              url: "http://localhost:8888/getMessage",
              type: 'get',
              dataType: 'json',
              success: function (res) {
                console.log(res);
                let li = ``;
                for(let item in res.data) {
                  li = li + `
                    
  • ${res.data[item].user_message} —— ${res.data[item].user_name} [${res.data[item].user_id}] ${res.data[item].time}
  • `
    ; } $("#message-board-ul").append(li); }, error: function (err) { console.log(err); } }) } else { // 如果没有存储 window.location.href = "../login.html"; } // 提交留言 $("#message-submit").click(function() { let messageText = $("#message").val() if(!messageText) { alert("留言内容不能为空"); } else if(messageText.length > 140) { alert("留言长度不能超过 140 位!"); } else { $.ajax({ url: "http://localhost:8888/sendMessage", type: 'post', dataType: 'json', data: { userid: userId, username: userName, message: messageText }, success: function (res) { console.log(res); if(res.code == "0") { alert("新增成功!"); window.location.reload(); } }, error: function (err) { console.log(err); console.log(err.responseText); if (err.responseText == "登录失败,留言内容为空!") { alert("登录失败,留言内容为空!"); } else if (err.responseText == "登录失败,字数超过限制!") { alert("登录失败,字数超过限制!"); } else { alert("未知错误!"); } } }) } }) }) </script> </body> </html>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104

    接着编写下 Node 后端:

    index.js

    
    // ... 其他代码省略,请自行前往章节 2.2 后端接口 获取其他代码
    
    if (pathName == "/sendMessage") { // 提交留言信息
    
      console.log("\n【API - 提交留言信息】");
    
      result = JSON.parse(result);
    
      let id = result.userid; // id
      let userName = result.username; // 用户名
      let messageText = result.message; // 留言内容
      let time = getNowFormatDate(); // 时间
    
      if(!messageText) {
        res.end("登录失败,留言内容为空!");
        return;
      } else if(messageText.length > 140) {
        res.end("登录失败,字数超过限制!");
        return;
      } else {
        
        // 新增的 SQL 语句及新增的字段信息
        let addSql = "INSERT INTO message(user_message, user_id, user_name, time) VALUES(?, ?, ?, ?)";
        let addSqlParams = [messageText, id, userName, time];
    
        // 连接 SQL 并实施语句
        connection.query(addSql, addSqlParams, function (error1, response1) {
          if (error1) { // 如果 SQL 语句错误
            throw error1;
          } else {
            console.log("\n新增成功!");
    
            // 返回数据
            res.write(JSON.stringify({
              code: "0",
              message: "新增成功!"
            }));
    
            // 结束响应
            res.end();
          }
        })
      }
    
    } else if (pathName == "/login") { // 登录
    
      console.log("\n【API - 登录】");
    
    } else if (pathName == "/register") { // 注册
    
      console.log("\n【API - 注册】");
    
    }
    
    
    
    // ... 其他代码省略,请自行前往章节 2.2 后端接口 获取其他代码
    
    
    
    if (pathName == "/getMessage") { // 获取留言信息
    
      console.log("\n【API - 获取留言信息】");
    
      // 解析 url 参数部分
      let params = url.parse(req.url, true).query;
    
      console.log("\n参数为:");
      console.log(params);
    
      // 新增的 SQL 语句及新增的字段信息
      let readSql = "SELECT * FROM message";
    
      // 连接 SQL 并实施语句
      connection.query(readSql, function (error1, response1) {
        if (error1) {
          throw error1; 
        } else {
          
          let newRes = JSON.parse(JSON.stringify(response1));
          console.log(newRes);
    
          // 返回数据
          res.write(JSON.stringify({
            code: "1",
            message: "查询成功!",
            data: newRes
          }));
    
          // 结束响应
          res.end();
        }
      });
      // 查询完毕
    } else if(pathName == "/") { // 首页
      res.writeHead(200, {
        "Content-Type": "text/html;charset=UTF-8"
      });
    
      res.write('

    jsliang 前端有限公司服务已开启!

    详情可见:Node 基础

    '
    ); res.end(); }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105

    敲完代码再看下功能是否实现:

    综上,我们完成了所有的功能模块:注册、登录以及留言。

    三、工具整合

    3.1 supervisor – 监听Node改动

    正如其官网所说,它是一个进行控制系统:

    1.安装插件:npm i supervisor -g
    2.运行文件:supervisor app.js
    3.查看运行:localhost:3000

    平时,我们 node app.js 后,当我们修改了 app.js 的内容,就需要关闭 node 命令行再执行 node app.js
    而我们使用 supervisor 后,我们修改了 app.js 中的内容,只要点击保存,即可生效保存后的代码,实现实时监听 node 代码的变动。

    3.2 PM2 – Node进程管理

    PM2是Node进程管理工具,可以利用它来简化很多Node应用管理的繁琐任务,如性能监控、自动重启、负载均衡等,而且使用非常简单。
    下面就对 PM2 进行入门性的介绍,基本涵盖了 PM2 的常用的功能和配置:

    1. 全局安装PM2 : npm i pm2 -g
    2. 监听应用 : pm2 start index.js
    3. 查看所有进程:pm2 list
    4. 查看某个进程: pm2 describe App name/id
    5. 停止某个进程: pm2 stop App name/id 。例如:

    先通过 pm2 list 查看:

    App nameidstatus
    index0online

    只需要执行 pm2 stop index 或者 pm2 stop 0 即可。

    1. 停止所有进程: pm2 stop all
    2. 重启某个进程 : pm2 restart App name/id
    3. 删除某个进程 : pm2 delete App name/id

    如上,如果说我们的 supervisor 是监听单个进程的话,那么 PM2 就是监听多个进程。

    参考资料

    Node - 从0基础到实战企业官网 https://juejin.cn/post/6844903745755545614#heading-12

  • 相关阅读:
    洛谷P2158 欧拉函数
    Spring 的 @Transactional 如何实现的?
    洪荒学院 发布全球十大机器人大脑 纵然智障依然有其优劣!
    Java技术栈 —— 模版引擎 Freemarker or Thymeleaf?
    18 SpringMVC实战
    美创科技获2023浙江民营企业数字化转型“弄潮榜”上榜服务商
    项目——群英阁(galaxyHub)
    C语言新手写函数中出现数组时运行bug的解决
    一步一步认知机器学习
    jdk、jre、jvm区别
  • 原文地址:https://blog.csdn.net/qq_26780317/article/details/125870368