这可能是全网最详细的Node.js编程

NodeJS起步

下载安装

  • 下载
  • 历史版本
    这可能是全网最详细的Node.js编程_第1张图片
    windows下安装过程:
    这可能是全网最详细的Node.js编程_第2张图片
    此安装路径尽量不要改
    这可能是全网最详细的Node.js编程_第3张图片
    这可能是全网最详细的Node.js编程_第4张图片
    这可能是全网最详细的Node.js编程_第5张图片
    对于已经装过的,重新安装就会升级
    安装成功后,打开命令行,输入
node --version 或者 node -v  (显示node的版本号)

这可能是全网最详细的Node.js编程_第6张图片
表示安装成功
其他平台的安装方式: https://nodejs.org/zh-cn/download/package-manager/

REPL环境

这可能是全网最详细的Node.js编程_第7张图片

node 回车进入REPL环境

这可能是全网最详细的Node.js编程_第8张图片

ctrl + 两次c 退出REPL环境

这可能是全网最详细的Node.js编程_第9张图片
node中的REPL环境类似于浏览器中的 Console控制台 ,可以做一些代码测试。
然而 我们写代码肯定不是在控制台中写,而是写在一个test.js文件中.

var a = 1;
var b = 3;
console.log(a+b);

在这里插入图片描述
又或者在vs code编辑器在终端打开文件
这可能是全网最详细的Node.js编程_第10张图片

Node 中的模块

浏览器(客户端)中的JS
这可能是全网最详细的Node.js编程_第11张图片
Node中的JS
这可能是全网最详细的Node.js编程_第12张图片

核心模块的使用

FS模块

node核心模块之一,用于操作文件;中文手册

  • 文件读写
fs.writeFile(file, data[, options], callback)
+ file <string> | <Buffer> | <URL> | <integer> 文件名或文件描述符。
+ data <string> | <Buffer> | <TypedArray> | <DataView>
+ options <Object> | <string>
+ encoding <string> | <null> 默认值: 'utf8'- mode <integer> 默认值: 0o666+ flag <string> 参阅支持的文件系统标志。默认值: 'w'+ callback <Function>
	- err <Error>

当 file 是一个文件名时,异步地将数据写入到一个文件,如果文件已存在则覆盖该文件。 data 可以是字符串或 buffer。
当 file 是一个文件描述符时,行为类似于直接调用 fs.write()(建议使用)。
如果 data 是一个 buffer,则 encoding 选项会被忽略。

const data = new Uint8Array(Buffer.from('Node.js中文网'));
fs.writeFile('文件.txt', data, (err) => {
  if (err) throw err;
  console.log('文件已被保存');
});

如果 options 是一个字符串,则它指定字符编码:

fs.writeFile('文件.txt', 'Node.js中文网', 'utf8', callback);

在同一个文件上多次使用 fs.writeFile() 且不等待回调是不安全的。 对于这种情况,建议使用 fs.createWriteStream()。

  • 实例
    fs.读文件.js
// 引入模块
var fs = require('fs');
// console.log(typeof fs); //object 
var fs = require('fs');
// 1-2 从文件中读取内容
fs.readFile('./tt.txt','utf8',function(err,data){
	// 回调函数 (读取成功后执行的函数)
    console.log(err);
    console.log('---------------');
    console.log(data);
});
// 1-1 向文件中写入内容
// fs.writeFile('./tt.txt','老路',function(err){
//     if(!err){
//         console.log('写入成功');
//     }
// });

先去注释执行 1-1再执行1-2,效果预览:
这可能是全网最详细的Node.js编程_第13张图片
注意:向文件中写入内容时,没有该文件会自动创建文件再运行写入内容,node有异运行机制,打印111显然比文件的读写快。

  • 追加内容
    原理一句话解释:读完追加在写入
    fs.写文件.js
// 引入模块
var fs = require('fs');
// 向文件中追加内容
fs.readFile('./tt.txt','utf8',function(e,d){
    d+='2344';
    fs.writeFile('./tt.txt',d,function(e){
        if(e){
            console.log('写入失败')
        }else{
            console.log('写入成功')
        }
    })
});

效果预览:
这可能是全网最详细的Node.js编程_第14张图片

HTTP模块

node核心模块之一,用于搭建HTTP服务器;中文手册
在这里插入图片描述
要使用 HTTP 服务器和客户端,必须 require(‘http’)。
Node.js 中的 HTTP 接口旨在支持传统上难以使用的协议的许多特性。 特别是,大块的、可能块编码的消息。 接口永远不会缓冲整个请求或响应,用户能够流式传输数据。
HTTP 消息头由如下对象表示:

{ 'content-length': '123',
  'content-type': 'text/plain',
  'connection': 'keep-alive',
  'host': 'mysite.com',
  'accept': '*/*' }

键是小写的。值不能被修改。
为了支持所有可能的 HTTP 应用程序,Node.js 的 HTTP API 都非常底层。 它仅进行流处理和消息解析。 它将消息解析为消息头和消息主体,但不会解析具体的消息头或消息主体。
有关如何处理重复消息头的详细信息,参阅 message.headers。
接收到的原始消息头保存在 rawHeaders 属性中,该属性是 [key, value, key2, value2, …] 的数组。 例如,上面的消息头对象可能具有的 rawHeaders 列表如下所示:

[ 'ConTent-Length', '123456',
  'content-LENGTH', '123',
  'content-type', 'text/plain',
  'CONNECTION', 'keep-alive',
  'Host', 'mysite.com',
  'accepT', '*/*' ]

开启服务器(启动一个http服务器)

首先我们把项目放在phpstudy->www目录的新建node文件夹中,启动phpstudy,打开vs code 在终端运行0.1.js
0.1.js

// 引入http核心模块
var http = require('http');
// 创建一个服务
var server = http.createServer();
server.on('request',function(res,rs){
    // console.log(res.method);
    rs.write('nihao ');
    rs.end();
});
// 启动监听
server.listen(8080,function(){
    console.log('请访问 127.0.0.1:8080');
}) 

效果预览:
在这里插入图片描述
这可能是全网最详细的Node.js编程_第15张图片
简单明了,不懂请移步中文手册,或者你需要来一波更详细的0.2.js
在终端crtl + c 退出运行0.2.js

// 1. 导入http模块
var http = require('http');
// 2. 使用http这个模块中的createServer()创建一个服务器实例对象
var server = http.createServer();
// 3. 绑定端口号,启动web服务器
server.listen(8000, function() {
    console.log(' 请访问http://localhost:8000');
});
// 4. 为这个服务器实例对象注册 request 请求处理函数
// 请求处理函数function(形参1,形参2){}
// 形参1:request请求对象 获取到当前请求的路径,方法等本次请求的所有信息
// 形参2:response响应对象 发送响应数据
server.on('request', function(request, response) {
    console.log('服务端收到客户端的请求啦!!!');
    // 向客户端页面返回字符串
    response.write("hello node");
    // 结束响应
    response.end();
});

效果预览:
在这里插入图片描述
这可能是全网最详细的Node.js编程_第16张图片
把response.write(“hello node”);改为 response.write(“你好,世界!”);
效果预览:
这可能是全网最详细的Node.js编程_第17张图片
注意:因为我们的服务器接受请求处理并响应数据时,并没有指定响应数据的类型,所以可能会出现了乱码;为了安全起见,我们可以通过服务器的响应头指定数据类型,在 http.ServerResponse 类 中为我们提供了setHeader 方法:
这可能是全网最详细的Node.js编程_第18张图片

响应 HTML 页面

此前我们回顾一下原生ajax请求

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>Documenttitle>
head>
<body>
    <input type="button" value="点击" id="btn">
body>
<script>
    document.getElementById('btn').onclick = function(){
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function(){
            if(xhr.readyState == 4){
                alert(xhr.responseText);
            }
        }
        xhr.open('get','./xx.php');
        xhr.send();
        alert(3);
    }
script>
html>

实例:
http.1.js

var http = require('http');
var server = http.createServer();
server.on('request',function(res,rso){
    if(res.method == 'GET'){
        console.log('GET请求');
    }else if(res.method == 'POST'){
        console.log('POST请求');
    }
    rso.setHeader('Content-type','text/html;charset=utf-8');
    rso.end('

你好,node.js

'
); }); server.listen(8000,function(){ console.log('请打开浏览器访问:127.0.0.1:8000'); });

效果预览:
在这里插入图片描述
这可能是全网最详细的Node.js编程_第19张图片
然而,我们不能一直将html代码写到服务器的方法中,而是需要建一个xx.html的文件,将html文件中的内容返回给客户端;
index .html

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>Documenttitle>
head>
<body>
    <h1>你好,nodeh1>
body>
html>

nodejs代码
http.js

var http = require('http');
// 1:引入文件操作模块
var fs = require('fs');
var server = http.createServer();
server.on('request', function(request, response) {
    // 2:读取html文件中的内容
    fs.readFile('./index.html','utf8',function(error,html_data){
        // 设置响应头
        response.setHeader('Content-Type', 'text/html;charset=utf-8');
        // 将html中的内容响应回客户端,结束响应
        response.end(html_data);
    })
});
server.listen(8000,function(){
    console.log('请打开浏览器访问:127.0.0.1:8000');
});

在这里插入图片描述
这可能是全网最详细的Node.js编程_第20张图片

响应图片、css、js等其他静态资源

index.html

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>Documenttitle>
head>
<body>
    <div>
        <p>dfhdslkjp>
        <img src="./img/1.jpg" alt="">
        <h1>niceh1>
    div>
body>
<script src="./index.js">script>
html>

http.3.js

var http = require('http');
var fs = require('fs');
var server = http.createServer();
server.on('request', function (res, rso) {
    var urls = res.url;
    console.log(urls);
    if (urls == '/') {
        rso.setHeader('Content-type', 'text/html;charset=utf-8');
        fs.readFile('./index.html', 'utf8', function (err, data) {
            rso.end(data);
        })
    }else{
        // 响应一切HTML需要的静态资源
        fs.readFile('.'+urls,function(err,data){
            rso.end(data);
        }) 
    }
});
server.listen(8000, function () {
    console.log('请打开浏览器访问:127.0.0.1:8000');
});

在这里插入图片描述
这可能是全网最详细的Node.js编程_第21张图片

服务器遍历文件及文件夹-案例

首先得有一下图片
这可能是全网最详细的Node.js编程_第22张图片
尝试读一下文件目录
readlist.js

var fs = require('fs');
fs.readdir('./','utf8',function(err,data){
    console.log(data);
})

在这里插入图片描述
获取的文件目录是数组,暂时区别不出文件与文件夹。
然后加载安装第三方时间处理模块

npm install moment

在这里插入图片描述
获取文件在前台遍历
因为要格式化时间需要用到第三方时间模块

npm install moment

第一次安装有点慢
在这里插入图片描述
安装成功
这可能是全网最详细的Node.js编程_第23张图片
会多了node_modules文件夹,以后安装node模块都放在这里
在这里插入图片描述
还多了一个package-lock.json文件,这是存放你安装的node模块的配置信息,有大用处
这可能是全网最详细的Node.js编程_第24张图片
然后准备完成,来看具体代码:
使用node载入静态页面:
index.html

DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">

<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>Index of /title>
head>
<body>
  <h1>Index of /h1>
  <table>
    <tbody>
      <tr>
        <th valign="top"><img src="./img/blank.gif" alt="[ICO]">th>
        <th><a href="http://localhost/?C=N;O=D">Namea>th>
        <th><a href="http://localhost/?C=M;O=A">Last modifieda>th>
        <th><a href="http://localhost/?C=S;O=A">Sizea>th>
        <th><a href="http://localhost/?C=D;O=A">Descriptiona>th>
      tr>
      
    tbody>
  table>
  <address>Apache/2.4.33 (Win32) OpenSSL/1.1.0g PHP/7.2.4 Server at localhost Port 80address>
body>
<script>
  var xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function () {
    if (xhr.readyState == 4) {
      // console.log(xhr.responseText);
      var data = JSON.parse(xhr.responseText);
      // console.log(data);
      var htmls = '';
      for (var i = 0; i < data.length; i++) {
          htmls += '';
          if(data[i].type == 'f'){
            htmls += '[DIR]'
          }else{
            htmls += '[DIR]'
          }
          
          htmls += '' + data[i].name + '/ '
          htmls += ''+ data[i].mtime +' '
          htmls += ' '+ data[i].size + ' '
          htmls += ' '
          htmls += ''
      }
      document.getElementsByTagName('tbody')[0].innerHTML += htmls;
    }
  }
  xhr.open('get', './file_list');
  xhr.send();
  console.log(s);
script>
html>

http.js

var http = require('http');
var fs = require('fs');
var moment = require('moment');
var server = http.createServer();
server.on('request',function(request,response){
    console.log(request.url);
    if(request.url == '/'){
        fs.readFile('./index.html',function(err,data){
            response.end(data);
        })
    }else if(request.url == './file_list'){
        fs.readdir('./','utf8',function(err,data){
            // 我们想要拿到文件的其他属性必须在读取文件之后获取
            // 就是需要我们把获取文件属性的代码写到回调函数里面
            var data_arr = [];
            // 循环获取每个文件的其他属性
            var cont = 0;
            for(var i=0;i<data.length;i++){
                data_arr[i] = {};
                (function(i){
                    fs.stat(data[i],function(err,files){
                        // 因为获取文件属性的代码必须在回调函数中
                        // 所有造成了循环中的I丢失
                        // console.log(files.size);
                        cont++;
                        if(files.isFile()){
                            data_arr[i].type = 'f';
                        }else{
                            data_arr[i].type = 'd';
                        }
                        data_arr[i].name = data[i];
                        data_arr[i].size = files.size;
                        data_arr[i].mtime = moment(files.mtime).format('YYYY-MM-DD hh:mm:ss');
                        // console.log(data[i]);
                        if(cont == data.length){
                            response.end(JSON.stringify(data_arr));
                        }
                    })
                })(i) // 因此我们借助自调用匿名函数形成闭包,保存I变量
                // 回调函数中就可以使用i变量
                
                // data_arr.push(obj);
            }
            // 因为文件的属性都是在回调函数中获取,所以在回调函数外部不能组装数据
            // 循环结束后数据内容依然为空, 因为我们的回调函数在循环结束后才会运行
            // console.log(data_arr);
            // response.end(JSON.stringify(data));
        })
    }else{
        fs.readFile('.'+request.url,function(err,data){
            response.end(data);
        })
    }
   
});
server.listen(8000,function(){
    console.log('请打开浏览器访问:127.0.0.1:8000');
});

终端运行node http.js就会出现类似这种效果:
这可能是全网最详细的Node.js编程_第25张图片
模仿Apache服务器,遍历文件及文件,显示时间及大小完毕

循环后 i 丢失的问题

http.js

// var arr = ['a', 'b', 'c'];
// for (var i = 0; i < arr.length; i++) {
//     // 模拟延迟
//     setTimeout(function () {
//         console.log(arr[i]);
//     }, 1000);
// }
/*
 * *******************************************
 * 上面的代码 全部输出 undefined
 * *******************************************
 */ 
var arr = ['a','b','c'];
for(var i = 0; i < arr.length; i ++) {
    (function(i){
        // 模拟延迟
        setTimeout(function() {
            console.log(arr[i]);
        }, 1000);
   })(i);
}

这可能是全网最详细的Node.js编程_第26张图片

包管理器npm

使用moment

使用第三方包格式化时间,如上安装后即可使用
在这里插入图片描述
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0x0BSVC5-1571667161261)(.\img\Snipaste_2018-09-26_13-43-05.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GYKAVD5A-1571667161262)(./img\Snipaste_2018-09-26_13-44-02.png)]

npm 命令的使用

上面的代码,我们使用npm安装了moment来进行格式化时间的处理,这就是使用第三方模块;
这可能是全网最详细的Node.js编程_第27张图片
而我们使用的npm就是node中自带的包(模块)管理工具;
借助NPM可以帮助我们快速安装和管理依赖包,使Node与第三方模块之间形成了一个良好的生态系统;
查看版本号

npm -v

在这里插入图片描述
查看帮助引导
这可能是全网最详细的Node.js编程_第28张图片

使用npm初始化项目

npm对node.js来说及其重要,就像枪手的枪。
而且一个项目,不可能只是使用一个第三方包,而包越多,管理起来就越麻烦,
而 npm init 给我们提供了项目初始化的功能,也解决了多个包的管理问题:
初始化项目

npm init

这可能是全网最详细的Node.js编程_第29张图片
初始化时要配置你的项目信息,大概依次是如项目名,版本号,描述信息,入口文件,设置的一些指令,git仓库地址,关键字, 作者,,当前项目的协议等,不填可回车跳过,最后y回车确认保存配置项目信息,会多出一个package.json文件
这可能是全网最详细的Node.js编程_第30张图片

解决 npm 被墙问题

npm 存储包文件的服务器在国外,有时候会被墙,速度很慢,所以我们需要解决这个问题。
淘宝的开发团队把 npm 在国内做了一个备份:淘宝的 cnpm
安装淘宝的 cnpm:

# 在任意目录执行都可以
# --global 表示安装到全局,而非当前目录
# --global 不能省略,否则不管用
npm install --global cnpm

接下来你安装包的时候把之前的 npm 替换成 cnpm
举个例子:

# 这里还是走国外的 npm 服务器,速度比较慢
npm install jquery
# 使用 cnpm 就会通过淘宝的服务器来下载 jquery
cnpm install jquery

如果不想安装 cnpm 又想使用淘宝的服务器来下载:

npm install jquery --registry=https://registry.npm.taobao.org

但是每一次手动这样加参数很麻烦,所我们可以把这个选项加入配置文件中:

# 配置到淘宝服务器
npm config set registry https://registry.npm.taobao.org
# 查看 npm 配置信息
npm config list

只要经过了上面命令的配置,则你以后所有的 npm install 都会默认通过淘宝的服务器来下载。

package.json 与 package-lock.json 文件

如果后期开发过程中,需要项目迁移,我们只需要将package.json文件迁移即可,在新项目下执行
npm install ,所有第三方包会自动安装;
这可能是全网最详细的Node.js编程_第31张图片
package.json的作用就是用来记录当前项目及包的使用情况;不能在package.json中添加注释
package-lock.json 保存第三方包的版本和下载路径等详细信息;
当我们使用npm管理包时,package.json 及package-lock.json 的内容都会自动更新

服务端页面渲染

之前的案例中,我们时通过前端浏览器发送ajax请求获取服务器数据的,前端获取数据后进行遍历展示;
这可能是全网最详细的Node.js编程_第32张图片
缺点就是发送多次请求、不利于搜索引擎查找;我们修改改为后端渲染数据;
art-template: https://www.npmjs.com/package/art-template
查看中文文档
这可能是全网最详细的Node.js编程_第33张图片
使用art-termplate模板引擎模块:
art.html

<h1>{{va}}h1>

art.js

var template = require('art-template');
template.defaults.root = './';
var str= template('./art.html',{va:'李四'});
console.log(str);

效果预览:
在这里插入图片描述
循环语法
这可能是全网最详细的Node.js编程_第34张图片
加快速度模块化实现用服务端渲染文件

模块化封装案例(不懂部分稍后看下面Node模块化及CommonJS规范)

新建立个apache2文件夹,它用以下文件
package.json

{
  "name": "apache_com",
  "version": "1.0.0",
  "description": "",
  "main": "http.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "art-template": "^4.13.1",
    "moment": "^2.22.2"
  }
}

http.js

var http = require('http');
var luyou = require('./luyou');
var server = http.createServer();

luyou.server(server);

server.listen(8000,function(){
    console.log('请打开浏览器访问:127.0.0.1:8000');
});

添加自定义模块 luyou.js – 路由模块
luyou.js

var yewu = require('./yewu')
var fs = require('fs');
function server(server) {
    server.on('request', function (request, response) {
        if (request.url == '/') {

            response.end(yewu.htmls);

        } else if (request.url == '/file_list') {
            fs.readdir('./', 'utf8', function (err, data) {
                var data_arr = [];
                var cont = 0;
                for (var i = 0; i < data.length; i++) {
                    data_arr[i] = {};

                    (function (i) {
                        fs.stat(data[i], function (err, files) {

                            cont++;
                            if (files.isFile()) {
                                data_arr[i].type = 'f';
                            } else {
                                data_arr[i].type = 'd';
                            }

                            data_arr[i].name = data[i];
                            data_arr[i].size = files.size;
                            data_arr[i].mtime = moment(files.mtime).format('YYYY-MM-DD hh:mm:ss');
                            // console.log(data[i]);
                            if (cont == data.length) {
                                response.end(JSON.stringify(data_arr));
                            }
                        })

                    })(i) // 因此我们借助自调用匿名函数形成闭包,保存I变量
                    // 回调函数中就可以使用i变量

                    // data_arr.push(obj);
                }

            })
        } else {
            fs.readFile('.' + request.url, function (err, data) {
                response.end(data);
            })
        }
    })
}


module.exports.server = server;

contrllor.js — 业务模块
yewu.js

var fs = require('fs');
var moment = require('moment');
var template = require('art-template');

template.defaults.root = './'

fs.readdir('./', 'utf8', function (err, data) {
    var data_arr = [];
    // 循环获取每个文件的其他属性
    var cont = 0;
    for (var i = 0; i < data.length; i++) {
        data_arr[i] = {};

        (function (i) {
            fs.stat(data[i], function (err, files) {

                cont++;
                if (files.isFile()) {
                    data_arr[i].type = 'f';
                } else {
                    data_arr[i].type = 'd';
                }

                data_arr[i].name = data[i];
                data_arr[i].size = files.size;
                data_arr[i].mtime = moment(files.mtime).format('YYYY-MM-DD hh:mm:ss');
                // console.log(data[i]);
                if (cont == data.length) {

                    var htmls = template('./index.html', { data: data_arr });

                    exports.htmls = htmls;

                    // response.end(htmls);

                    // response.end(JSON.stringify(data_arr));
                }
            })

        })(i)
    }

})

视图模板
index.html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<!-- saved from url=(0017)http://localhost/ -->
<html>

<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>Index of /</title>
</head>

<body>
  <h1>Index of /</h1>
  <table>
    <tbody>
      <tr>
        <th valign="top"><img src="./img/blank.gif" alt="[ICO]"></th>
        <th><a href="http://localhost/?C=N;O=D">Name</a></th>
        <th><a href="http://localhost/?C=M;O=A">Last modified</a></th>
        <th><a href="http://localhost/?C=S;O=A">Size</a></th>
        <th><a href="http://localhost/?C=D;O=A">Description</a></th>
      </tr>

      {{each data}}
      <tr>
        {{if $value.type == 'f'}}
        <td valign="top"><img src="./img/text.gif" alt="[DIR]"></td>
        {{else}}
        <td valign="top"><img src="./img/folder.gif" alt="[DIR]"></td>
        {{/if}}
        <td><a href="">{{$value.name}}/</a> </td>
        <td align="right">{{$value.mtime}} </td>
        <td align="right"> {{$value.size}} </td>
        <td>&nbsp;</td>
      </tr>
      {{/each}}


    </tbody>
  </table>
  <address>Apache/2.4.33 (Win32) OpenSSL/1.1.0g PHP/7.2.4 Server at localhost Port 80</address>

</body>
<script>
  // var xhr = new XMLHttpRequest();
  // xhr.onreadystatechange = function () {
  //   if (xhr.readyState == 4) {
  //     // console.log(xhr.responseText);
  //     var data = JSON.parse(xhr.responseText);
  //     // console.log(data);
  //     var htmls = '';
  //     for (var i = 0; i < data.length; i++) {
  //         htmls += '';
  //         if(data[i].type == 'f'){
  //           htmls += '[DIR]'
  //         }else{
  //           htmls += '[DIR]'
  //         }

  //         htmls += '' + data[i].name + '/ '
  //         htmls += ''+ data[i].mtime +' '
  //         htmls += ' '+ data[i].size + ' '
  //         htmls += ' '
  //         htmls += ''
  //     }
  //     document.getElementsByTagName('tbody')[0].innerHTML += htmls;
  //   }
  // }

  // xhr.open('get', './file_list');
  // xhr.send();


  // console.log(s);
</script>

</html>

img文件夹
这可能是全网最详细的Node.js编程_第35张图片
然后安装第三方包

npm install

这可能是全网最详细的Node.js编程_第36张图片
终端运行http.js文件
在这里插入图片描述
效果预览:
这可能是全网最详细的Node.js编程_第37张图片

小结,那么我们在项目中应该使用 客户端渲染还是服务端渲染:
答:两者都用,根据数据的不同作用而定;

推举一个node开发时使用的小工具 nodemon
为什么用nodemon?
搭:修改代码后,需要重新启动 Express 应用,所做的修改才能生效。若之后的每次代码修改都要重复这样的操作,势必会影响开发效率,本文将详细介绍Nodemon,它会监测项目中的所有文件,一旦发现文件有改动,Nodemon 会自动重启应用

npm install nodemon -g

安装成功后,使用 nodemon 运行代码,

代码一旦被保存,nodemon便会自动重新运行新代码

Node模块化及CommonJS规范

通过前面的学习, 我们基本掌握了NodeJS编程的基础知识, 但是我们也直观的发现了一个问题,和我们之前学习浏览器编程时JS, 差异还是很大的; 都是JavaScript编程, 为何有这种差异? 前面写过的防Apache服务器的案例中, 使用过内置fs模块, 使用过 moment 模块, 而这些模块都不是我们写的, 都是直接拿过来使用, 那么我们能不能自己写一个模块, 应该怎么写, 有哪些规矩, 如果我们自己写了一个模块, 能不能提供给其他编程人员直接使用, 应该怎么用?
这可能是全网最详细的Node.js编程_第38张图片

Electron 跨平台的桌面应用框架: https://electronjs.org/

CommonJS规范的由来

JS 的表现的表现能力取决于宿主环境提供的API, 在web1.0 时代, W3C 组织提供了浏览器的规范支持, 在web2.0 时代, 随着HTML5的发展, 更多的标准API 出现在了浏览器中, 但是, 在后端 JS 中标准的制定纹丝不动 ;
由 Mozilla 工程师Kevin Dangoor于2009年1月提出名为 ServerJS 的规范; 2009年8月,更名为*CommonJS,*以显示 API 的更广泛适用性。

What I’m describing here is not a technical problem. It’s a matter of people getting together and making a decision to step forward and start building up something bigger and cooler together.

我在这里描述的不是一个技术问题。这是一个人们聚在一起,决定向前一步,开始一起建立更大更酷的东西的问题。

–Kevin Dangoor
这可能是全网最详细的Node.js编程_第39张图片

CommonJS 的模块规范

CommonJS对模块的定义十分简单,主要分为:
1、模块引用:
使用 require() 方法引入一个模块API ;
2、模块定义:
在模块中使用 exports 对象导出当前模块数据或方法;
在模块中还存在一个module对象,它代表模块自身,module对象有一个exports 属性,用于数据导出;
其实exports 对象就是module.exports 的引用; exports === module.exports
3、模块标识:
其实就是模块的文件名,必须符合小驼峰法命名规则,使用require() 引入时使用 . 或 .. 开头的相对路径或绝对路径,引入时可以不写文件后缀名;
重点注意 : 模块中的方法和变量的作用于尽在模块内部,每个模块具有独立的空间,互不干扰;
CommonJS 构建的模块机制中的引入与导出是我们完全不用考虑变量污染或者替换的问题,相比与命名空间的机制,简直就是天才和菜鸟的区别;
说了这么多,让我们走一波CommonJS相关代码,了解他的具体使用方法
首先建一个commonjs文件夹,在这里编写以下代码:
a.js

var f = require('./b.js');
var a = 1;
var b = 2;
var d = a+b+f.data;

// exports = module.exports;

exports.data = d;

b.js

var a = 4;
exports.data = a;

f.js

var a = require('./a')
console.log(a.data)

终端运行f.js文件效果预览:
在这里插入图片描述
显然三个文件使用两次commonjs调用,但原理看起来如此简单
这便是:Node对CommonJS的实现 (Node模块化)
这可能是全网最详细的Node.js编程_第40张图片
以上代码就是自定义模块的基本规则 这是重点
再用一个例子加深印象
com,js

// module.exports = {name:123};

// exports.data = {};

// 1、module.exports 可以直接赋值,值就会被导出(推荐使用)

// 2、但是exports直接赋值,不能导出; 必须使用exportsd.xx

// module.exports = function(){
//     return 123;
// }

module.exports = {
    name:'lisi',
    fun:function(){
        console.log(123456);
    }
}

co.js

var s = require('./com');
console.log(s.name);
s.fun();
// var d = s(); // 错误写法,s是一个对象
// console.log(typeof  s); // object

效果预览:
在这里插入图片描述

模块加载的顺序和规则

在 CommonJS 规范中,使用 require() 加载(引入) 模块时,模块标识必须使用相对路径或绝对路径指明模块位置,但是在node的实现中,我们可以不指明模块路径;如: require('fs')、require('moment') ;
如果没有指明路径,那就是加载核心模块或第三方模块,指明加载路径一般就是加载自定义模块;
不管加载什么模块,都是优先从缓存中加载:

Node 加载模块时,如果这个模块已经被加载过了,则会直接缓存起来,将来再次引用时不会再次加加载这个模块(即:如果一个模块被加载两次,则模块中的代码只会被执行一次)
而核心模块和第三方模块的的加载顺序就是:
先加载核心模块,核心模块的内容都是在安装node时已经编译好的可执行的二进制代码,加载执行的速度,仅次于缓存加载,如果核心模块中没有,则加载第三方模块
第三方模块的加载规则:

  • 先在当前文件的模块所属目录去找 node_modules目录
  • 如果找到,则去该目录中找 模块名的目录 如 : moment
  • 如果找到 moment 目录, 则找该目录中的 package.json文件
  • 如果找到 package.json 文件,则找该文件中的 main属性
  • 如果找到main 属性,则拿到该属性对应的文件
  • 如果找到 moment 目录之后,
    • 没有package.json
    • 或者有 package.json 没有 main 属性
    • 或者有 main 属性,但是指向的路径不存在
    • 则 node 会默认去看一下 moment 目录中有没有 index.js --> index.json–> index.node 文件
  • 如果找不到index 或者 找不到 moment 或者找不到 node_modules
  • 则进入上一级目录找 node_moudles 查找(规则同上)
  • 如果上一级还找不到,继续向上,一直到当前文件所属磁盘的根目录
  • 如果到磁盘概目录还没有找到,直接报错

海贼王用户管理系统–项目

连接MySQL数据库

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

mysql基本CURD

SELECT `id`, `name` FROM `users` WHERE 1 
INSERT INTO `users`(`id`, `name`) VALUES ([value-1],[value-2])
UPDATE `users` SET `name`='value' WHERE id=1
DELETE FROM `users` WHERE id=value

安装连接MySQL

新建haizei_test文件夹
运行安装mysql第三方包

npm install mysql

这可能是全网最详细的Node.js编程_第41张图片
建(node)库建(users)表
这可能是全网最详细的Node.js编程_第42张图片
db.js :

var mysql = require('mysql');

var connection = mysql.createConnection({
    // 设置连接信息
    host: '127.0.0.1',
    user: 'root',
    password: 'root',
    database: 'node'
});
// 打开连接
connection.connect();

var sql = 'select * from users where id=1';
// 发送sql语句
connection.query(sql, function (err, res, filed) {
    // 回调获取sql返回的数据
    console.log(err);
    console.log('------------');
    console.log(res);
    console.log('------------');
    console.log(filed);
})
// 结束连接
connection.end();

在这里插入图片描述
添加一条数据后
这可能是全网最详细的Node.js编程_第43张图片
这可能是全网最详细的Node.js编程_第44张图片
热身完毕,开始真正的开发海贼王用户管理系统
你需要一个mysql数据表

CREATE TABLE `users` (
  `id` int(11) NOT NULL,
  `name` varchar(255) NOT NULL,
  `nengli` varchar(255) NOT NULL,
  `jituan` varchar(255) NOT NULL,
  `img` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

--
-- 转存表中的数据 `users`
--

INSERT INTO `users` (`id`, `name`, `nengli`, `jituan`, `img`) VALUES
(1, '路飞', '超人系橡胶果实', '草帽海贼团', ''),
(2, '乔巴', '动物系人人果实', '草帽海贼团', ''),
(3, '罗宾', '超人系花花果实', '草帽海贼团', ''),
(4, '红发-香克斯', '霸王色霸气、武装色霸气', '红发海贼团', ''),
(5, '鹰眼-米霍克', '黑刀.夜', '王下七武海', ''),
(6, '女帝-波雅', '超人系甜甜果实', '九蛇海贼团、王下七武海', '');

--
-- Indexes for dumped tables
--

--
-- Indexes for table `users`
--
ALTER TABLE `users`
  ADD PRIMARY KEY (`id`);

在Navicat软件中F6 复制黏贴以上sql语句,回车运行完后刷新表
这可能是全网最详细的Node.js编程_第45张图片

这可能是全网最详细的Node.js编程_第46张图片

在开始项目之前,我们先了解下url模块:http://nodejs.cn/api/url.html#url_url_port
这可能是全网最详细的Node.js编程_第47张图片
这可能是全网最详细的Node.js编程_第48张图片

项目初始化及安装第三方模块

新建目录 haizei_com , 打开命令行执行 npm init 初始化项目(忘记的请看上面使用npm初始化项目);
一次性安装项目所需的所有模块;

npm install art-template mysql bootstrap jquery

启动项目

创建http服务器并加载静态页面

http.js

// 服务器启动模块
const http = require('http');
var luyou = require('./luyou');
// 创建服务
var server = http.createServer();

// 调用luyou模块中的binds方法,设置监听事件
luyou.binds(server);

// 设置服务器的监听端口
server.listen(8080,function(){
    console.log('请访问 127.0.0.1:8080')
})

luyou.js

var fs = require('fs');
var yewu = require('./yewu');
var url = require('url');

// 导出数据(binds方法)
// binds方法设置监听
module.exports.binds = function (server) {
    server.on('request', function (request, response) {
        // 解析URL为一个对象 对象中有请求路径及参数
        var urlObj = url.parse(request.url,true);
        // console.log(urlObj);
        var method = request.method;
        if (method == 'GET') {
            // console.log(urls);
            // 获取请求路径,判断路径请求,
            if (urlObj.pathname == '/') {
                yewu.getall(request,response);
            } else if (urlObj.pathname == '/getone') {
                yewu.getone(request,response);
            }else if(urlObj.pathname == '/upuser'){
                yewu.upuser_get(request,response);
            }else if(urlObj.pathname == '/delete'){
                yewu.delete_user(request,response);
            } else {
                // 接收静态资源请求并按照请求路径响应
                fs.readFile('.' + urlObj.pathname, function (err, data) {
                    response.end(data);
                });
            }
        }else if(method == 'POST'){
            if(urlObj.pathname == '/upuser'){
                yewu.upuser_post(request,response);
            }


            // console.log(urls);
            // response.end('POST');
        }else{
            response.end('抱歉,不支持'+method+'请求');
        }


    })
}

yewu.js

// 加载引入 art-template模板引擎
var template = require('art-template');
template.defaults.root = './';
var html_data = template('./index.html',{data:123});
exports.html_data = html_data;

index.html

DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Hero - Admintitle>
    <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css">
    <style>
        .hero-list img {
            width: 50px;
        }
    style>
head>

<body>
    <header>
        <div class="page-header container">
            <h1>海贼王 <small>角色管理器small>h1>
        div>
    header>
    <div class="container hero-list">
        <a class="btn btn-success pull-right" href="/add">添加英雄a>
        <table class="table table-hover">
            <thead>
                <th>编号th>
                <th>名称th>
                <th>能力th>
                <th>团体th>
                <th>操作th>
            thead>
            <tbody id="tbody">
                <tr>
                    <td>1td>
                    <td>路飞td>
                    <td>超人系橡胶果实td>
                    <td>草帽海贼团td>
                    <td>
                        <a href="/getone?id=1">查看a>
                        <a href="/upuser?id=1">修改a>
                        <a href="/delete?id=1">删除a>
                    td>
                tr>
            tbody>
        table>
    div>
body>

html>

在这里插入图片描述
这可能是全网最详细的Node.js编程_第49张图片
服务器http.js路由luyou.js控制器yewu.js到显示视图index.html,过程并不难,这算获取静态页面数据的完整流程,到这里我们加快速度,走一遍比较完整的代码

动态获取数据(实现查改删功能)

1,服务端
http.js

var fs = require('fs');
var yewu = require('./yewu');
var url = require('url');

// 导出数据(binds方法)
// binds方法设置监听
module.exports.binds = function (server) {
    server.on('request', function (request, response) {
        // 解析URL为一个对象 对象中有请求路径及参数
        var urlObj = url.parse(request.url,true);
        // console.log(urlObj);
        var method = request.method;
        if (method == 'GET') {
            // console.log(urls);
            // 获取请求路径,判断路径请求,
            if (urlObj.pathname == '/') {
                yewu.getall(request,response);
            } else if (urlObj.pathname == '/getone') {
                yewu.getone(request,response);
            }else if(urlObj.pathname == '/upuser'){
                yewu.upuser_get(request,response);
            }else if(urlObj.pathname == '/delete'){
                yewu.delete_user(request,response);
            } else {
                // 接收静态资源请求并按照请求路径响应
                fs.readFile('.' + urlObj.pathname, function (err, data) {
                    response.end(data);
                });
            }
        }else if(method == 'POST'){
            if(urlObj.pathname == '/upuser'){
                yewu.upuser_post(request,response);
            }


            // console.log(urls);
            // response.end('POST');
        }else{
            response.end('抱歉,不支持'+method+'请求');
        }


    })
}

路由
luyou.js

var fs = require('fs');
var yewu = require('./yewu');
var url = require('url');

// 导出数据(binds方法)
// binds方法设置监听
module.exports.binds = function (server) {
    server.on('request', function (request, response) {
        // 解析URL为一个对象 对象中有请求路径及参数
        var urlObj = url.parse(request.url,true);
        // console.log(urlObj);
        var method = request.method;
        if (method == 'GET') {
            // console.log(urls);
            // 获取请求路径,判断路径请求,
            if (urlObj.pathname == '/') {
                yewu.getall(request,response);
            } else if (urlObj.pathname == '/getone') {
                yewu.getone(request,response);
            }else if(urlObj.pathname == '/upuser'){
                yewu.upuser_get(request,response);
            }else if(urlObj.pathname == '/delete'){
                yewu.delete_user(request,response);
            } else {
                // 接收静态资源请求并按照请求路径响应
                fs.readFile('.' + urlObj.pathname, function (err, data) {
                    response.end(data);
                });
            }
        }else if(method == 'POST'){
            if(urlObj.pathname == '/upuser'){
                yewu.upuser_post(request,response);
            }


            // console.log(urls);
            // response.end('POST');
        }else{
            response.end('抱歉,不支持'+method+'请求');
        }


    })
}

3,控制器
yewu.js

// var linkdb = require('./linkdb');
// var template = require('art-template');
// template.defaults.root = './';
// console.log(linkdb.data);
// // 使用linkdb模块导出的方法
// linkdb.query(function(data){
//     // 利用回调函数获取数据
//     var html_data = template('./index.html',{data:data});
//     exports.html_data = html_data;
// });
// // 使用简写方法,比上面方法更简洁
// var html_data = template('./index.html',{data:linkdb.data});
// exports.html_data = html_data;

// 加载引入 linkdb模块获取数据
var db = require('./linkdb');
var url = require('url');
var querystring = require('querystring');
var template = require('art-template');
template.defaults.root = './';

module.exports = {
    getall:function(req,res){
        db.select(function(datas){
            var html_data = template('./index.html', { data: datas });
            res.end(html_data);
        });
    },
    getone:function(req,res){
        var urlObj = url.parse(req.url,true);
        console.log(urlObj);
        db.where('id='+urlObj.query.id).select(function(data){
            var html_data = template('./userinfo.html',{data:data});
            res.end(html_data);
        })
    },
    upuser_get:function(req,res){
        var urlObj = url.parse(req.url,true);
        db.where('id='+urlObj.query.id).select(function(data){
            var html_data = template('./upuser.html',{data:data});
            res.end(html_data);
        })
    },
    delete_user : function(req,res){
        var urlObj = url.parse(req.url,true);
        db.where('id='+urlObj.query.id).delete(function(data){
            if(data >=1){
                var backstr = ""
                res.setHeader('Content-type','text/html;charset=utf-8');
                res.end(backstr);
            }
        });
    },
    upuser_post:function(req,res){
        var data_post = '';
        // 设置监听事件(数据传输)
        req.on('data',function(che){
            data_post+=che;
        });
        // 监听数据传输结束的事件
        req.on('end',function(){
            var urlObj = url.parse(req.url,true);
            // 将获取到的数据转义
            var data_obj = querystring.parse(data_post);
            db.where('id='+urlObj.query.id).update(data_obj,function(data){
                // res.end(data.toString());

                if(data >=1){
                    var backstr = ""
                    res.setHeader('Content-type','text/html;charset=utf-8');
                    res.end(backstr);
                }


            });
            // console.log(data_obj);
           
        })
    }
}

4,连接与操作数据库模块
linkdb.js

var mysql = require('mysql');
// 设置连接信息
var connection = mysql.createConnection({
    host: '127.0.0.1',
    user: 'root',
    password: 'root',
    database: 'node'
});
// 打开连接
connection.connect();

module.exports = {
    wh: undefined,
    where: function (wh) {
        this.wh = wh
        return this;
    },
    select: function (callback) {
        if (this.wh == undefined) {
            var sql = "select * from users";
        } else {
            var sql = "select * from users where " + this.wh;
        }
        connection.query(sql, function (err, sql_data) {
            var da = sql_data;
            // 调用回调函数,将数据当做实参进行函数的回调
            callback(da);
        })
        this.wh = undefined;
    },
    delete:function(callback){
        if (this.wh == undefined) {
            console.log('请加where条件,否则去死');
            return;
        }else{
            var sql = "delete from users where "+this.wh;
            connection.query(sql,function(err,datas){
                // console.log(datas);
                callback(datas.affectedRows);
            })
        }
        this.wh = undefined;
    },
    update: function (data,callback) {
        if (this.wh == undefined) {
            console.log('请加where条件,否则去死');
            return;
        } else {
            var set = '';
            for (i in data) {
                set += i + "='" + data[i] + "',";
            }
            set = set.slice(0, set.length - 1);
            var sql = "update users set " + set + ' where '+this.wh;
            // console.log(sql);

            connection.query(sql,function(err,datas){
                // console.log(datas.changedRows);
                callback(datas.changedRows);
            })
        }
        this.wh =undefined;
    }
    
}

5,三个视图模板
index.html

DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Hero - Admintitle>
    <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css">
    <style>
        .hero-list img {
            width: 50px;
        }
    style>
head>

<body>
    <header>
        <div class="page-header container">
            <h1>海贼王 <small>角色管理器small>h1>
        div>
    header>
    <div class="container hero-list">
        <a class="btn btn-success pull-right" href="/add">添加英雄a>
        <table class="table table-hover">
            <thead>
                <th>编号th>
                <th>名称th>
                <th>能力th>
                <th>团体th>
                <th>操作th>
            thead>
            <tbody id="tbody">
                {{each data}}
                <tr>
                    <td>{{$value.id}}td>
                    <td>{{$value.name}}td>
                    <td>{{$value.nengli}}td>
                    <td>{{$value.jituan}}td>
                    <td>
                        <a href="/getone?id={{$value.id}}">查看a>
                        <a href="/upuser?id={{$value.id}}">修改a>
                        <a href="/delete?id={{$value.id}}">删除a>
                    td>
                tr>
                {{/each}}
               
            tbody>
        table>
    div>
body>

html>

upuser.html

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>Documenttitle>
    <style>
        table {
            margin: 0 auto;
            border-collapse: collapse;
            width: 800px;
            height: 500px;
        }

        td {
            border: 1px solid #ccc;
        }
    style>
head>

<body>
    <form action="/upuser?id={{data[0].id}}" method="post">
        <table>
            <tr>
                <td>姓名td>
                <td><input name="name" type="text" value="{{data[0].name}}">td>
            tr>
            <tr>
                <td>能力td>
                <td><input name="nengli" type="text" value="{{data[0].nengli}}">td>
            tr>
            <tr>
                    <td>团体td>
                    <td><input name="jituan" type="text" value="{{data[0].jituan}}">td>
                tr>
            <tr>
                <td>上传图片td>
                <td><input type="file">td>
            tr>
            <tr>
                <td>td>
                <td><input type="submit" value="确认修改">td>
            tr>
        table>
    form>
body>

html>

userinfo.html

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>Documenttitle>
head>
<body>
    <h1>{{data[0].name}} h1> 
    <h2>{{data[0].nengli}}h2>
body>
html>

效果预览:
在这里插入图片描述
这可能是全网最详细的Node.js编程_第50张图片
这可能是全网最详细的Node.js编程_第51张图片
这可能是全网最详细的Node.js编程_第52张图片
这可能是全网最详细的Node.js编程_第53张图片
这可能是全网最详细的Node.js编程_第54张图片
这可能是全网最详细的Node.js编程_第55张图片
这可能是全网最详细的Node.js编程_第56张图片
以上完整实现查询,修改,删除用户的功能,以下让我们再仔细看看它是如何实现的。

获取单个用户信息(查询1-1)

接受前台请求

在 luyou.js 路由模块,获取单个用户信息

server.on('request', function (request, response) {
    var urls = request.url;
    console.log(urls);
    if (urls == '/') {
        var data = yewu.html_data;
        response.end(data);
    }else if(urls == '/getuser'){
        response.end('getsssss');
    }else {
        fs.readFile('.' + urls, function (error, data) {
            response.end(data);
        })
    }
})

但是,luyou模块,无法处理前台不同类型的请求, 需要我们在服务器端接受并处理客户端发送的 get 及 post 请求;

获取请求类型及参数

GET 请求把所有的内容编码到访问路径中,POST 请求的内容全部都在请求体中。
http.ServerRequest 并没有一个属性内容为请求体,原因是等待请求体传输可能是一件
耗时的工作,譬如上传文件。而很多时候我们可能并不需要理会请求体的内容,恶意的 POST
请求会大大消耗服务器的资源。所以 Node.js 默认是不会解析请求体的,当我们需要的时候,
只能手动来做

网络调试工具Postman,可以帮助我们发送各种HTTP请求,并接受服务器返回的数据;

https://www.getpostman.com/
获取请求类型

var http = require('http');
var server = http.createServer();
server.on('request', function(request, response) {
    // 获取请求类型
    var method = request.method;
    console.log(method);
    response.end();
});
server.listen(8000, function() {
    console.log(' 请访问http://localhost:8000');
});

获取 GET 的请求参数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WQq5WOqK-1571667161270)(.\img\Snipaste_2018-10-07_12-56-34.png)]
获取Post请求参数

else if (method == "POST") {
        // url_obj = url.parse(request.url,true);
        // console.log(url_obj.query); 
        
        //以上代码 无内容,失败
        // POST请求的内容全部都在请求体中 
}

手册中明确说明:
为了支持各种可能的 HTTP 应用,Node.js 的 HTTP API 是非常底层的。 它只涉及流处理与消息解析。 它把一个消息解析成消息头和消息主体,但不解析具体的消息头或消息主体。
因此我们需要查找更底层的网络实现,node中的基础网络模块net模块: http://nodejs.cn/api/net.html:

else if (method == "POST") {
    // url_obj = url.parse(request.url,true);
    // console.log(url_obj.query); 
    //以上代码 无内容,失败
    // POST请求的内容全部都在请求体中 
    var data = '';
    // net 模块中的 net.Socket 提供的data及end事件 
    // 绑定data事件获取数据
    request.on('data', function (che) {
        data += che;
    })
    // 绑定end事件,监听数据接受完成
    request.on('end', function () {
        // console.log(data);
        console.log(require('querystring').parse(data));
    })
}

获取单个用户信息(查询1-2)

1: 修改路由逻辑,优先判断请求类型:

var fs = require('fs');
var url = require('url');
// 引入业务模块使用模板引擎加载页面
var yewu = require('./yewu');
exports.bind = function (server) {
    server.on('request', function (request, response) {
        // 优先判断请求方式
        var method = request.method;
        // 解析URL参数
        var url_obj = url.parse(request.url, true);
        if(method == 'GET'){
            // console.log(url_obj.query);//参数
            if (url_obj.pathname == '/') {
                var data = yewu.html_data;
                response.end(data);
                
            // 判断 获取单个用户信息路由 
            }else if(url_obj.pathname == '/getuser'){
                
            }else {
                fs.readFile('.' + url_obj.pathname, function (error, data) {
                    response.end(data);
                })
            }
        }else if(method == 'POST'){
            
        }
    })
}

2: 修改静态模板,添加查看连接

 <td>
     <a href="/getuser?id={{$value.id}}">查看a>
     <a href="#">修改a>
     <a href="#">删除a>
td>

3:修改路由,获取get请求参数,并将id参数传入业务模块

// 判断 获取单个用户信息路由 
else if(url_obj.pathname == '/getuser'){
    // 获取id参数
    var id = url_obj.query.id;
    // 在yewu模块中封装方法,传递id参数。
    // 使用回掉函数获取数据
    yewu.gets(url_obj.query.id,function(data){
        response.end(data);
    });
}

4:修改业务模块,添加gets方法

// 封装gets方法并导出,接受id参数及回调函数
exports.gets = function(id,callback){
    // 调用数据库模块的getone方法,并传入id参数
    linkdb.getone(id,function(data){
        // 利用回调函数获取数据
        var user_data = template('./users.html',{data:data});
        callback(user_data);
    });
}

5:添加users.html静态模板

<body>
    {{data[0].name}}
    {{data[0].nengli}}
    {{data[0].jituan}}
    {{data[0].name}}
    {{data[0].name}}
body>

6:修改数据库模块,添加getone方法

exports.getone = function(id,callback){
    var sql = "select * from users where id="+id;
    // console.log(sql);
    connection.query(sql,function(error,data,res){
        console.log(data);
        // 数据是通过回调函数的方式返回
        callback(data)
    });
    // connection.end();
}

注意:将数据库模块中的所有 connection.end(); 删除,因为我们有多个方法,不能在方法调用中停止数据库的连接,否则,其他方法在后续调用中无法连接数据;

链式操作原理解析

链式操作的核心原理:
test.js

var c = require('./chained');
// c.select();
c.where('id=2').select();

chained.js

module.exports = {
    where: function (wh) {
        this.whe = wh;
        // 链式操作的核心就是保存数据并返回本对象
        return this;
    },
    select: function () {
        if (this.whe == undefined) {
            var sql = "select * from users ";
        } else {
            var sql = "select * from users where " + this.whe;
        }
        console.log(sql);
        this.whe = undefined;
    },
    update:function(){
        if (this.whe == undefined) {
            console.log('更新数据时,请填写where条件');
            return;
        } else {
            var sql = "update xx from  set () where " + this.whe;
        }
        console.log(sql);
        this.whe = undefined;
    }
}

在这里插入图片描述
删除和修改也是同样原理,c.where(‘id=2’).select(),只需更改后面的方法即可,但值得注意的是,在查改删三个方法中都要有 this.whe = undefined即这是为了清空或查或改或删的条件,避免影响后续的操作。

使用链式操作灵活操作数据库

1:新建数据操作模块 linkdb.js

var mysql = require('mysql');
var connection = mysql.createConnection({
    host: 'localhost',
    user: 'root',
    password: 'root',
    database: 'node'
});
module.exports = {
    that: this,
    where: function (wh) {
        this.whe = wh;
        return this;
    },
    select: function (callback) {
        if (this.whe == undefined) {
            var sql = "select * from users ";
        } else {
            var sql = "select * from users where " + this.whe;
        }
        // 用完后重置where条件,以免后续操作的数据重复
        this.whe = undefined;
        connection.query(sql, function (error, results, fields) {
            if (error) {
                callback(error);
                return;
            }
            callback(results);
        });
    }
}

2:修改业务模块(yewu.js)的调用

var linkdb = require('./linkdb');
var template = require('art-template');
template.defaults.root = './';
module.exports = {
    getall: function (callback) {
        db.select(function (data) {
            // 利用回调函数获取数据
            var html_data = template('./index.html', { data: data });
            callback(html_data);
        });
    },
    getone:function(id,callback){
        db.where('id='+id).select(function(data){
            var user_data = template('./users.html',{data:data});
            callback(user_data);
        });
    }
}

3:修改路由模块(luyou.js)的调用

var fs = require('fs');
var url = require('url');
// 引入业务模块使用模板引擎加载页面
var yewu = require('./yewu');
exports.bind = function (server) {
    server.on('request', function (request, response) {
        // 优先判断请求方式
        var method = request.method;
        // 解析URL参数
        var url_obj = url.parse(request.url, true);
        if(method == 'GET'){
            // console.log(url_obj.query);//参数
            if (url_obj.pathname == '/') {
                yewu.getall(function(data){
                    response.end(data);
                })
                
            // 判断 获取单个用户信息路由 
            }else if(url_obj.pathname == '/getuser'){
                // 获取id参数
                var id = url_obj.query.id;
                // 在yewu模块中封装方法,传递id参数。
                // 使用回掉函数获取数据
                yewu.getone(url_obj.query.id,function(data){
                    response.end(data);
                });
            }else {
                fs.readFile('.' + url_obj.pathname, function (error, data) {
                    response.end(data);
                })
            }
        }else if(method == 'POST'){
        }
    })
}

修改用户信息(修改1-1)

修改模板代码,添加链接

<tbody id="tbody">
    {{each data}}
    <tr>
        <td>{{$value.id}}td>
        <td>{{$value.name}}td>
        <td>{{$value.nengli}}td>
        <td>{{$value.jituan}}td>
        <td>
            <a href="/getuser?id={{$value.id}}">查看a>
            <a href="/upuser?id={{$value.id}}">修改a>
            <a href="#">删除a>
        td>
    tr>
    {{/each}}
tbody>

获取用户信息并填入表单(修改1-2)

1:添加路由判断(luyou.js)

// 修改用户信息,先获取用户信息    
}else if(url_obj.pathname == '/upuser'){
    // 获取id参数
    var id = url_obj.query.id;
    // 在yewu模块中封装方法,传递id参数。
    // 使用回掉函数获取数据
    yewu.upuser_get(url_obj.query.id,function(data){
        response.end(data);
    });
}else {
}

2:添加业务逻辑(yewu.js),获取用户信息

upuser_get:function(id,callback){
    db.where('id='+id).select(function(data){
        var user_data = template('./upuser.html',{data:data});
        callback(user_data);
    });
}

3:添加表单模板(upuser.html)

<style>
    table {
        margin: 0 auto;
        border-collapse: collapse;
        width: 800px;
        height: 500px;
    }
    td {
        border: 1px solid #ccc;
    }
style>
<body>
    <form action="/upuser?id={{data[0].id}}" method="post">
        <table>
            <tr>
                <td>姓名td>
                <td><input name="name" type="text" value="{{data[0].name}}">td>
            tr>
            <tr>
                <td>能力td>
                <td><input name="nengli" type="text" value="{{data[0].nengli}}">td>
            tr>
            <tr>
                    <td>团体td>
                    <td><input name="tuanti" type="text" value="{{data[0].jituan}}">td>
                tr>
            <tr>
                <td>上传图片td>
                <td><input type="file">td>
            tr>
            <tr>
                <td>td>
                <td><input type="submit" value="修改">td>
            tr>
        table>
    form>
body>

获取并处理post提交数据,修改数据库(修改1-3)

路由模块(luyou.js)

 else if (method == 'POST') {
     // 只要是POST请求,则优先获取数据
     // 后处理路由逻辑
     var data = '';
     request.on('data', function (che) {
         data += che;
     })
     // 绑定end事件,监听数据接受完成
     request.on('end', function () {
         // console.log(data);
         // 获取Post传输的数据
         var post_data = querystring.parse(data);
         // 路由判断
         if (url_obj.pathname == '/upuser') {
             // 调用业务层方法
             yewu.upuser_post(url_obj.query.id, post_data, function (data) {
                 response.end(data);
             });
         }
     })
 }

业务模块(yewu.js)

upuser_post:function(id,data,callback){
    // 调用数据模块修改用户信息
    db.where('id='+id).update(data,function(changedRows){
        // http服务器相应要求必须是字符串
        callback(changedRows.toString());
    })
}

数据库模块(linkdb.js)

update:function(data,callback){
    // 没where条件,则停止所有操作
    if(this.where == undefined){
        callback('error');
        return;
    }
    // 拼接sql语句的set部分
    var set = '';
    for(v in data){
        set += v +"='"+ data[v]+"',";
    }
    var sets = set.slice(0,set.length-1);
    var sql = "UPDATE `users` SET "+sets + ' where '+ this.whe;
    // console.log(sql);
    this.whe = undefined;
    connection.query(sql,function(error,res,fields){
        // console.log(error)
        // 返回受影响行数
        callback(res.changedRows);
    })
}

删除操作

路由模块(luyou.js)

var fs = require('fs');
var yewu = require('./yewu');
var url = require('url');

// 导出数据(binds方法)
// binds方法设置监听
module.exports.binds = function (server) {
    server.on('request', function (request, response) {
        // 解析URL为一个对象 对象中有请求路径及参数
        var urlObj = url.parse(request.url,true);
        // console.log(urlObj);
        var method = request.method;
        if (method == 'GET') {
            // console.log(urls);
            // 获取请求路径,判断路径请求,
            if (urlObj.pathname == '/') {
                yewu.getall(request,response);
            } else if (urlObj.pathname == '/getone') {
                yewu.getone(request,response);
            }else if(urlObj.pathname == '/upuser'){
                yewu.upuser_get(request,response);
            }else if(urlObj.pathname == '/delete'){
                yewu.delete_user(request,response);
            } else {
                // 接收静态资源请求并按照请求路径响应
                fs.readFile('.' + urlObj.pathname, function (err, data) {
                    response.end(data);
                });
            }
        }else if(method == 'POST'){
            if(urlObj.pathname == '/upuser'){
                yewu.upuser_post(request,response);
            }


            // console.log(urls);
            // response.end('POST');
        }else{
            response.end('抱歉,不支持'+method+'请求');
        }


    })
}

业务模块(yewu.js)

delete_user : function(req,res){
        var urlObj = url.parse(req.url,true);
        db.where('id='+urlObj.query.id).delete(function(data){
            if(data >=1){
                var backstr = ""
                res.setHeader('Content-type','text/html;charset=utf-8');
                res.end(backstr);
            }
        });
},

数据库模块(linkdb.js)


    delete:function(callback){
        if (this.wh == undefined) {
            console.log('请加where条件,否则去死');
            return;
        }else{
            var sql = "delete from users where "+this.wh;
            connection.query(sql,function(err,datas){
                // console.log(datas);
                callback(datas.affectedRows);
            })
        }
        this.wh = undefined;
    },

ES6语法及JS语言的其他特性(拓展,重要)

ECMAScript的变迁

ECMAScript 1.0(1997年)
ECMAScript 2.0(1998年)
ECMAScript 3.0(1999年12月)
ECMAScript 4.0 (太激进,夭折了)
ECMAScript 5.0 (2009)
ECMAScript 6.0 (2015)
3.0版是一个巨大的成功,在业界得到广泛支持,成为通行标准,奠定了JavaScript语言的基本语法,以后的版本完全继承。
直到今天,初学者一开始学习JavaScript,其实就是在学3.0版的语法。
5.0版和3.0版区别不大。
随着JS的崛起,应用于移动开发,后端开发,游戏开发等,业界对JS的语言的要求越来越高.
此时再看4.0时提出的设想,已经不显得激进了.于是,6.0版本终于通过了.
此标准严格的叫法应是ECMAScript2015,当然叫ES6也没啥,没人和你抬杠.
ESMAScript 与 JavaScript 两者的关系, 就如同 快捷宾馆营业标准如家酒店 一样.
浏览器支持情况
http://kangax.github.io/compat-table/es6/

变量的声明

let 块级证明

ES6 新增了let命令,用来声明变量。
它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。
看下例:
test.js

var a = 3;
let b = 4;
console.log(a , b); // 同时打印多条数据

在这里插入图片描述
再看下例:

{
  let c = 'hello';
  var b = 'world';
}
console.log(c) //ReferenceError: c is not defined
console.log(d) // world

这可能是全网最详细的Node.js编程_第57张图片
可以看出: let命令在定义的{}内生效,某些语言也有类似特点,我们将其称为"块级作用域".
这样,let定义的变量,只在块内生效,不影响其他区域,所以我们说Ta更 “清洁”.
在某些场合下,用let特别适合,比如for()循环

// 设置i仅为循环数组,但循环后,残留一个变量i.
var arr = ['a' , 'b' , 'c'];
for(var i=0; i<arr.length; i++) {
}
console.log(i); // 3

换成let再试一下,是不是更清洁?

// i变量只在for()内有效,不污染其他区域
var arr = ['a' , 'b' , 'c'];
for(let i=0; i<arr.length; i++) {
}
console.log(i); // ReferenceError: i is not defined 

不污染全局的window属性

var c = 'hello';
let d = 'world';
window.d; //undefined
window.c; //hello

注:同域下,var ,let 声明同一变量名,error

let 申明的变量不存在变量提升

let 申明的变量不允许重复声明

let 申明的变量存在块级作用域

const 常量

常量并不稀奇 (话外音:都21世纪了,你咋现在才有?)
PHP,Java,C,C++ …多数语言都有常量.
const 声明一个只读的常量。一旦声明,常量的值就不能改变。
常量,即不可改变其值的量.

const PI = 3.14;
console.log(PI);
// 一旦声明值不可修改
PI = 3.15; // TypeError: Assignment to constant variable.
// 不可重复声明
const PI = 3.15; // Identifier 'PI' has already been declare
// 因为声明后值不能修改,所以声明必须赋值
const c ;// Missing initializer in const declaration

这可能是全网最详细的Node.js编程_第58张图片

但如果const申明的是对象其对象的属性是可以改变,可以被赋值引用
原因是常量一旦申明后不可改变是它开辟的地址值是不可改变的,因为声明对象又开辟临时储存地址,这个新的地址值的与旧地址值不同,可以重新赋值,如下

注:常量名和变量名,都区分大小写

const STU = {name:'lucy' , age : 22};
console.log(STU); // { name: 'lucy', age: 22 }
STU.name = 'lily';
console.log(STU); // { name: 'lily', age: 22 }

注:常量不可改变的是其引用地址;

模板字符

ES6用反引号 ( ` ) 包住字符串,可以让字符串多行书写,也可以自由的嵌入变量.

function t() {
    return 'world';
}
var obj = {name:'李四'}
let str = `hello${obj.name} ${t()} , ${7 + 9} , ${'ok'},
这是个换行
`;
console.log(str);
// hello李四 world , 16 , ok,
// 这是个换行

变量的解构赋值

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
字符串解构赋值

const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"

数组的解构赋值

以前,为变量赋值,只能直接指定值。

var a = 1;
var b = 2;
var c = 3;
//ES6允许写成下面这样。
let [a, b, c] = [1, 2, 3];

上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。
本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。下面是一些使用嵌套数组进行解构的例子。

let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3
let [ , , third] = ["foo", "bar", "baz"];
third // "baz"
let [x, , y] = [1, 2, 3];
x // 1
y // 3
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []

对象的解构赋值

解构不仅可以用于数组,还可以用于对象。

let { foo, bar } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"

对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

let { bar, foo } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
let { name } = { foo: "aaa", bar: "bbb" };
name // undefined

上面代码的第一个例子,等号左边的两个变量的次序,与等号右边两个同名属性的次序不一致,但是对取值完全没有影响。第二个例子的变量没有对应的同名属性,导致取不到值,最后等于undefined

对象的扩展

属性的简洁表示法

ES6 允许直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。

const foo = 'bar';
const baz = {foo};
baz // {foo: "bar"}
// 等同于
const baz = {foo: foo};

上面代码表明,ES6 允许在对象之中,直接写变量。这时,属性名为变量名, 属性值为变量的值。

方法的简洁表示法

除了属性简写,方法也可以简写。

function fun (){
    console.log(this.a + this.b);
}
var obj = {
    a:1,
    fun:fun,
    b:2,
};
obj.fun(); // 3
// 等同于
var obj = {
    a:3,
    fun,
    b:4,
};
obj.fun(); // 7
// 等同于
var obj = {
    a:5,
    fun(){console.log(this.a + this.b);},
    b:6,
};
obj.fun(); // 11

CommonJS 模块输出一组数据及方法,就非常合适使用简洁写法。

function getItem() {
}
function setItem() {
}
function clear() {
}
module.exports = { getItem, setItem, clear };
// 等同于
module.exports = {
    getItem: getItem,
    setItem: setItem,
    clear: clear
};

Promise 异步控制对象(重要)

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Using_promises

为什么要使用 Promise

先看简单简述
有a.txt,b.txt,c.txt,内容为:
这可能是全网最详细的Node.js编程_第59张图片
现在依次读取这三个文件的内容并打印出来
rfs.js

var fs = require('fs');

fs.readFile('./a.txt','utf8',function(err,data){
    console.log(data);
});
fs.readFile('./b.txt','utf8',function(err,data){
    console.log(data);
});

fs.readFile('./c.txt','utf8',function(err,data){
    console.log(data);
});

这可能是全网最详细的Node.js编程_第60张图片
以读取文件内容为例:
无法保证顺序的代码

var fs = require('fs')
fs.readFile('./a.txt', 'utf8', function (err, data) {
  if (err) {
    // return console.log('读取失败')
    // 抛出异常
    //    1. 阻止程序的执行
    //    2. 把错误消息打印到控制台
    throw err
  }
  console.log(data)
})
fs.readFile('./b.txt', 'utf8', function (err, data) {
  if (err) {
    // return console.log('读取失败')
    // 抛出异常
    //    1. 阻止程序的执行
    //    2. 把错误消息打印到控制台
    throw err
  }
  console.log(data)
})
fs.readFile('./c.txt', 'utf8', function (err, data) {
  if (err) {
    // return console.log('读取失败')
    // 抛出异常
    //    1. 阻止程序的执行
    //    2. 把错误消息打印到控制台
    throw err
  }
  console.log(data)
})

无法保证顺序的代码?见鬼,不存在的
这可能是全网最详细的Node.js编程_第61张图片
我们仍然能以正确的顺序读取,但是我们用更好方式实现不是更好吗
通过回调嵌套的方式来保证顺序:

var fs = require('fs')
fs.readFile('./a.txt', 'utf8', function (err, data) {
  if (err) {
    // return console.log('读取失败')
    // 抛出异常
    //    1. 阻止程序的执行
    //    2. 把错误消息打印到控制台
    throw err
  }
  console.log(data)
  fs.readFile('./b.txt', 'utf8', function (err, data) {
    if (err) {
      // return console.log('读取失败')
      // 抛出异常
      //    1. 阻止程序的执行
      //    2. 把错误消息打印到控制台
      throw err
    }
    console.log(data)
    fs.readFile('./c.txt', 'utf8', function (err, data) {
      if (err) {
        // return console.log('读取失败')
        // 抛出异常
        //    1. 阻止程序的执行
        //    2. 把错误消息打印到控制台
        throw err
      }
      console.log(data)
    })
  })
})

这可能是全网最详细的Node.js编程_第62张图片
多层使用回调函数,就会进入 “回调地狱

// 多层回调会进入 "回调地狱"
fs.readFile('./a.txt','utf8',function(err,data){
    console.log(data);
    fs.readFile('./b.txt','utf8',function(err,da2){
        console.log(da2);
        fs.readFile('./c.txt','utf8',function(err,da3){
            console.log(da3);
        })
    })
})

为了解决以上编码方式带来的问题(回调地狱嵌套),所以在 EcmaScript 6 中新增了一个 API:Promise

Promise 的使用

初步使用,先读取一个文件
promise.js

var fs = require('fs')
// 在 EcmaScript 6 中新增了一个 API Promise
// Promise 是一个构造函数
// 创建 Promise 容器
// 1. 给别人一个承诺 I promise you.
//    Promise 容器一旦创建,就开始执行里面的代码
var p1 = new Promise(function (resolve, reject) {
  // console.log(2)
  fs.readFile('./a.txt', 'utf8', function (err, data) {
    if (err) {
      // 失败了,承诺容器中的任务失败了
      // console.log(err)
      // 把容器的 Pending 状态变为 Rejected
      // 调用 reject 就相当于调用了 then 方法的第二个参数函数
      reject(err)
    } else {
      // console.log(3)
      // 承诺容器中的任务成功了
      // console.log(data)
      // 把容器的 Pending 状态改为成功 Resolved
      // 也就是说这里调用的 resolve 方法实际上就是 then 方法传递的那个 function
      resolve(data)
    }
  })
})
// console.log(4)
// p1 就是那个承若
// 当 p1 成功了 然后(then) 做指定的操作
// then 方法接收的 function 就是容器中的 resolve 函数
p1.then(function (data) {
    console.log(data)
}, function (err) {
    console.log('读取文件失败了', err)
})

在这里插入图片描述

封装 Promise 版本的 readFile:
pro.js

var fs = require('fs');

function pReadFile(files){
    return new Promise(function(succ,err){
        fs.readFile(files,'utf8',function(err,data){
            succ(data);
        })
    });
}

pReadFile('./a.txt')
.then(function(data){
    console.log(data);
    return(pReadFile('./b.txt'));
})
.then(function(data){
    console.log(data);
    return(pReadFile('./c.txt'));
})
.then(function(data){
    console.log(data);
})

这可能是全网最详细的Node.js编程_第63张图片
这可能是全网最详细的Node.js编程_第64张图片
这可能是全网最详细的Node.js编程_第65张图片
可读性变强也避免了回调地狱现象的发生

箭头函数

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions
ES6 允许使用“箭头”(=>)定义函数。
箭头函数表达式的语法比 函数表达式 更短,并且没有自己的 this,arguments。这些函数表达式更适用于那些本来需要匿名函数的地方,并且它们不能用作构造函数。
fun.js

var fun1 = function(s1,s2){
    return s1+s2;
}

// ES6中新函数的的声明方式
// 箭头函数
var fun2 = (s1,s2)=>{
    return s1+s2;
}

var fun3 = (s1,s2)=> s1+s2;

console.log(fun1(1,2));
console.log(fun2(1,2));
console.log(fun3(1,2));

在这里插入图片描述
fun.js

var f = (s) => {
    var a = 1;
    var b = s+a;
    return b;
}

f(3); // return 不是c onsole ,没打印值出来


var obj1 = {
    name:'lisi',
    fun:function(){
        console.log(this.name);
    }
}
obj1.fun();

在这里插入图片描述
fun.js


var obj1 = {
    name:'lisi',
    fun:function(){
        console.log(this.name);
    }
}
obj1.fun();

var obj2 = {
    name:'刘能',
    fun:obj1.fun,
}
obj2.fun();

在这里插入图片描述
fun.js

var name = 1;
var obj1 = {
    name:'lisi',
    fun:()=>{
        // console.log(this.name);
        console.log(this);
    }
}
obj1.fun();

var obj2 = {
    name:'刘能',
    fun:obj1.fun,
}
obj2.fun();

在这里插入图片描述

如果参数只有一个,可以将()省略 // arr.map(c=>c+1);
如果没有参数,则一定能要写上() // ()=> console.log(‘a’)
如果多于一个参数,每个参数之间用逗号分隔 (x, y) => { … }
如果方法体只有一句代码,可以省略{} 和分号,如果有返回可以省略return
如果方法体多于一句代码,则不能省略{} ,每句代码使用 分号分隔
注意:
a. 箭头函数没有自己的this,函数体内部写的this,指向的是外层代码块的this
b. 箭头函数内部的this是定义时所在的对象,而不是使用时所在的对象并且不会改变
c. 箭头箭头函数不能用作构造函数
d. 箭头函数内部不存在arguments,箭头函数体中使用的arguments其实指向的是外层函数的arguments
箭头就是让你当做一个普通函数来使用,别整花里胡哨;

express 框架

简介

Express 是基于 Node.js 平台,快速、开放、极简的 Web 开发框架, 提供一系列强大特性帮助你创建各种Web应用。Express 不对 node.js 已有的特性进行二次抽象,我们只是在它之上扩展了Web应用所需的功能。丰富的HTTP工具以及来自Connect框架的中间件随取随用,创建强健、友好的API变得快速又简单
这可能是全网最详细的Node.js编程_第66张图片
这可能是全网最详细的Node.js编程_第67张图片

安装使用

就像一个普通的第三方模块一样安装即可;

npm init
npm install express

这可能是全网最详细的Node.js编程_第68张图片
express基本使用
http.js

// var express = require('express');
// var app = express();

// app.get('/',function(req,res){
//     res.send('hello world');
// })

// app.listen('666',function(){
//     console.log('请求访问 127.0.0.1:666')
// });
// 相同的效果
var express = require('express');
var app = express();

app.get('/',(req,res)=>{
    res.send('hello world');
})

app.listen('666',()=>{
    console.log('请求访问 127.0.0.1:666')
});

在这里插入图片描述
这可能是全网最详细的Node.js编程_第69张图片
其中 Request、Response – API 我们需要重点关注
在熟悉express框架时,我们需要使用网络调试工具Postman,没有的请先移步https://www.pcsoft.com.cn/soft/178575.html

项目的重构

将我们之前的海贼王项目使用express框架进行重写,重写过程中,学习框架提供的各种API,并完善项目功能;

启动服务器

http.js

var express = require('express');
var app = express();
app.listen('8000',()=>{
    console.log('127.0.0.1:8000')
})

重写路由模块(快速过一遍,理解即可)

之前我们写了一个独立的模块(luyou.js)来处理请求,而在 express 中已经帮我们写好了路由的请求处理规则,不需要我们进行判断;
路由 是指确定应用程序如何响应对特定端点的客户端请求,该请求是URI(或路径)和特定HTTP请求方法(GET,POST等)。
每个路由都可以有一个或多个处理函数,这些函数在路由匹配时执行。

express 中的基本路由

路径定义采用以下结构:

app.method(path, handler)

以下示例定义了简单路由。
Hello World!在主页上回复:

app.get('/', function (req, res) {
  res.send('Hello World!')
})

在根路由(/),应用程序的主页上响应POST请求:

app.post('/', function (req, res) {
  res.send('Got a POST request')
})

响应对/user路由的PUT请求:

app.put('/user', function (req, res) {
  res.send('Got a PUT request at /user')
})

响应对/user路由的DELETE请求:

app.delete('/user', function (req, res) {
  res.send('Got a DELETE request at /user')
})

外置路由

设置 外置路由 rout.js

var express = require('express');
var router = express.Router();
router.get('/',(req,res)=>{
    res.send('123');
})
router.get('/user',(req,res)=>{
    res.send('user');
})
router.post('/edit',(req,res)=>{
    res.send('post_edit');
})
// 导出 router
module.exports = router;

写好路由规则,一定要记得将 路由对象(router) 导出

var express = require('express');
var app = express();
// 引入外置路由
var rout = require('./rout');
app.use(rout); // 使用引入外置的路由
app.listen('8000',()=>{
    console.log('127.0.0.1:8000')
})

将外置路由引入后,使用 app.use() 进行加载使用;

使用外置路由修改项目

在 luyou.js 中,注释以前的代码,添加新代码

var express = require('express');
var yewu = require('./yewu');
var router = express.Router();
router.get('/', (req, res) => {
    yewu.getall(function (data) {
        res.end(data);
    })
})
module.exports = router;

在 http.js 中,使用 express 启动服务,并引入使用新修改的 luyou.js 模块

var express = require('express');
var app = express();
var luyou = require('./luyou');
app.use(luyou);
app.listen('8080',()=>{
    console.log('127.0.0.1:8080')
})

使用链式操作添加路由

luyou.js

var express = require('express');
var yewu = require('./yewu');
var router = express.Router();
// express的路由支持链式操作
router
.get('/', (req, res) => {
    yewu.getall(function (data) {
        res.end(data);
    })
})
.get('/getuser', (req, res) => {
    // req 提供了query属性获取请求参数
    var id = req.query.id;
    // 在yewu模块中封装方法,传递id参数。
    // 使用回掉函数获取数据
    yewu.getone(id, function (data) {
        res.end(data);
    });
})
module.exports = router;

优化路由模块

路由模块 (luyou.js) 中只负责调用,调用时,将req res 传入业务模块

router
.get('/', (req, res) => {
    yewu.getall(req,res);
    // yewu.getall(function (data) {
    //     res.end(data);
    // })
})
.get('/getuser', (req, res) => {
    yewu.getone(req,res);
    // req 提供了query属性获取请求参数
    // var id = req.query.id;
    // // 在yewu模块中封装方法,传递id参数。
    // // 使用回掉函数获取数据
    // yewu.getone(id, function (data) {
    //     res.end(data);
    // });
})

业务模块接受 req res 负责处理请求并响应数据

getall: function (req,res) {
    db.select(function (data) {
        // 利用回调函数获取数据
        var html_data = template('./index.html', { data: data });
        // console.log(html_data);
        res.end(html_data);
    });
},
getone:function(req,res){
    db.where('id='+req.query.id).select(function(data){
        var user_data = template('./users.html',{data:data});
        res.end(user_data);
    });
},

继续简化路由模块

router
.get('/',yewu.getall)
.get('/getuser',yewu.getone)

原理:

function fn(callback){
    var a = 1;
    var b = 2;
    callback(a,b);
}
var pros = function(a,b){
    console.log(a+b);
}
fn(function(a,b){
    pros(a,b);
})
// fn(pros);

重写模板引擎

art-template@4 新特性
express-art-template
安装:

npm install --save art-template
npm install --save express-art-template

官方示例:

var express = require('express');
var app = express();
app.engine('art', require('express-art-template'));
app.set('view options', {
    debug: process.env.NODE_ENV !== 'production'
});
app.get('/', function (req, res) {
    res.render('index.art', {
        user: {
            name: 'aui',
            tags: ['art', 'template', 'nodejs']
        }
    });
});

修改 http.js 将 express-art-template 注册为express框架的模板引擎,并设置模板后缀为 html
这可能是全网最详细的Node.js编程_第70张图片
这可能是全网最详细的Node.js编程_第71张图片
在项目中新建views目录,将所有静态页面放入views目录
这可能是全网最详细的Node.js编程_第72张图片

利用 Express 托管静态文件

http://www.expressjs.com.cn/starter/static-files.html
在项目中新建 public 文件夹并将bootstrap.css移入, 修改 index.html 加载 css 静态文件 ,在http.js中引入并设置静态资源加载路径:
这可能是全网最详细的Node.js编程_第73张图片
如果要使用多个静态资源目录,请多次调用 express.static 函数:

app.use(express.static('public'))
app.use(express.static('files'))

访问静态资源文件时,express.static 函数会根据目录的添加顺序查找所需的文件。

完成项目重构

修改所有路由及业务模块代码
luyou.js

var express = require('express');
var yewu = require('./yewu');
var router = express.Router();
router
.get('/',yewu.getall)
.get('/getuser',yewu.getone)
.get('/upuser',yewu.upuser_get)
.post('/upuser',yewu.upuser_post);
module.exports = router;

yewu.js

// var linkdb = require('./linkdb');
var db = require('./db');
var querystring = require('querystring');
// template.defaults.root = './';
module.exports = {
    getall: function (req, res) {
        db.select(function (data) {
            res.render('index.html', { data: data });
        });
    },
    getone: function (req, res) {
        db.where('id=' + req.query.id).select(function (data) {
            res.render('./users.html', { data: data });
        });
    },
    upuser_get: function (req, res) {
        db.where('id=' + req.query.id).select(function (data) {
            res.render('./upuser.html', { data: data });
        });
    },
    upuser_post: function (req, res) {
        var data = '';
        req.on('data', function (che) {
            data += che;
        })
        // 绑定end事件,监听数据接受完成
        req.on('end', function () {
            // console.log(data);
            // 获取Post传输的数据
            var post_data = querystring.parse(data);
            // 调用数据模块修改用户信息
            db.where('id=' + req.query.id).update(post_data, function (changedRows) {
                // http服务器相应要求必须是字符串
                res.json(changedRows);
            })
        })
    }
}

修改用户头像

测试文件上传

创建服务器:

var express = require('express');
var app = express();
app.post('/upfile',(req,res)=>{
    var data = '';
    req.on('data', function (che) {
        data += che;
    });
    req.on('end',()=>{
        // 直接打印post传过来的数据
        console.log(data);
    })
})
app.listen('8000',()=>{
    console.log('127.0.0.1:8000')
})

使用postman工具上传文件:

借助第三方插件处理文件上传

https://www.npmjs.com/package/formidable
安装模块

npm install formidable
var express = require('express');
var formidable = require('formidable');
var fs = require('fs');
var app = express();
app.post('/upfile', (req, res) => {
    var form = new formidable.IncomingForm();
    
    // form.uploadDir = './img'; // 设置上传路径
    // form.keepExtensions = true; // 保留文件扩展名
    form.parse(req, function (err, fields, files) {
        var times = new Date().getTime();
        // 组装上传路径
        var file_path = './img/'+times+files.imgs.name;
        // 将缓存文件移动至制定目录
        fs.rename(files.imgs.path,file_path,(err)=>{
            console.log(file_path);
        });
        res.end();
    });
})
app.listen('8000', () => {
    console.log('127.0.0.1:8000')
})

在项目中实现文件上传

修改 upuser.html
这可能是全网最详细的Node.js编程_第74张图片
修改业务模块 yewu.js 使用 formidable 获取 post 数据,实现文件上传

upuser_post: function (req, res) {
    var form = new formidable.IncomingForm();
    form.parse(req, function (err, fields, files) {
        var times = new Date().getTime();
        // 组装上传路径
        var file_path = './public/img/' + times + files.imgs.name;
        // 将缓存文件移动至指定的public目录
        fs.rename(files.imgs.path, file_path, (err) => {
            if(!err){
                // 因为设置静态资源时,已经时public文件夹,写入数据库时,不要加public
                fields.img = './img/' + times + files.imgs.name;
                db.where('id=' + req.query.id).update(fields, function (changedRows) {
                    // http服务器相应要求必须是字符串
                    res.json(changedRows);
                })
            }else{
                console.log('文件上传失败'+err);
            }
        });
    });
}

用户登陆

登陆逻辑及cookie-session 的使用

express官方资源中,为我们提供了一个中间件,cookie-session

npm install cookie-session

测试代码:

var express = require('express');
var cookieSession = require('cookie-session')
var app = express();
// 注册中间件 
app.use(cookieSession({
    name: 'session', // 客户端cookie的名称
    keys: ['ss'] // 用于加密的关键字
}))
app.get('/', (req, res) => {
    // 获取并判断session
    if(req.session.sess_data){
        res.send('已经登陆')
    }else{
        // 如果没有session,跳转到登陆页面
        res.send('');
    }
})
app.get('/up', (req, res) => {
    // 展示登陆页面,获取用户数据并写入session 
    req.session.sess_data = {name:12,age:89};
    res.send('已写入session');
});

完成项目登陆功能

修改http模块,注册 cookie-session 中间件;

var express = require('express');
var app = express();
var cookieSession = require('cookie-session');
// 注册中间件 
app.use(cookieSession({
    name: 'session', // 客户端cookie的名称
    keys: ['xilingzuishuai'] // 用于加密的关键字
}))

在业务模块 (yewu.js) 中 添加逻辑判断,只有登陆后才能展示首页:

getall: function (req, res) {
    // 获取并判断session
    if (req.session.sess_data) {
        db.select(function (data) {
            res.render('index.html', { data: data });
        });
    } else {
        // 如果没有session,跳转到登陆页面
        res.send('');
        // res.send('没登陆');
    }
},

在路由模块(luyou.js) 中添加以下两个路由,get 展示静态登陆页面,post 获取用户提交的数据并写入 session ,写入成功后,跳转到首页;在业务模块(yewu.js)中添加响应的方法
.get('/upload',yewu.upload_get) .post('/upload',yewu.upload_post)

upload_get: (req, res) => {
    // 展示登陆页面
    res.render('./upload.html', {});
},
upload_post: (req, res) => {
    var form = new formidable.IncomingForm();
    form.parse(req, function (err, fields, files) {
        // console.log(fields);
        // 获取用户提交数据,判断用户名密码是否正确
        if (fields.userName == "admin" && fields.pwd == "123") {
            // 数据正确,写入session
            req.session.sess_data = fields;
            res.send('');
        }else{
            // 数据错误,重新跳回登陆页面
            res.send('');
        }
    })
}

Express的中间件

什么是中间件

在这里插入图片描述
在一个整体的流程中的某个环节,因为某些原因加入了额外的处理环节;

中间件的使用

应用中间件

语法:

  • app.use()

    • app.use(function(){})

无论发送任何请求都会执行的中间件

  • app.use(‘/path’, function(){})

只要在请求path路由时才会执行的中间件(无论GET/POST)

  • app.method()

    • app.get()

在get请求时会执行的中间件

  • app.post()

在post请求时会执行的中间件
app.use() 的用法

var express = require('express');
var app = express();
// 在中间件之前,不受中间件影响
app.get('/',function(req,res){
    console.log(123);
})
// 应用中间件
// 请求 '/user' 时,会先调用中间件
app.use(function (req, res, next) {
    console.log(req);
    next();
});
// 调用之前先调用中间件
app.get('/user',function(req,res){
    console.log('user');
})
app.listen('8000', () => {
    console.log('127.0.0.1:8000')
})

app.method() 的用法

var express = require('express');
var app = express();
// 在中间件之前,不受中间件影响
app.get('/',function(req,res){
    console.log(123);
})
// 应用中间件
// 只有在 post 请求user 时才起作用
app.post('/user',function (req, res, next) {
    console.log(req);
    next();
});
// 调用之前先调用中间件
// 接受所有请求方式请求user
app.all('/user',function(req,res){
    console.log('user');
})
app.listen('8000', () => {
    console.log('127.0.0.1:8000')
})

路由中间件

路由器层中间件的工作方式与应用层中间件基本相同,差异之处在于它绑定到 express.Router() 的实例。
使用 router.use()router.METHOD() 函数装入路由器层中间件;
我们之前项目的代码,就是在使用路由中间件:

var router = express.Router();
router
.get('/',yewu.getall)
.get('/getuser',yewu.getone)
.get('/upuser',yewu.upuser_get)
.post('/upuser',yewu.upuser_post)
.get('/upload',yewu.upload_get)
.post('/upload',yewu.upload_post)
;

内置中间件

express.static 外,先前 Express 随附的所有中间件函数现在以单独模块的形式提供:中间件函数的列表
Express 中唯一内置的中间件函数是 express.static。此函数基于 serve-static,负责提供 Express 应用程序的静态资源。
对于每个应用程序,可以有多个静态目录:

app.use(express.static('public'));
app.use(express.static('uploads'));
app.use(express.static('files'));

第三方中间件

使用第三方中间件向 Express 应用程序添加功能。
安装具有所需功能的 Node.js 模块,然后在应用层或路由器层的应用程序中将其加装入。

var cookieSession = require('cookie-session');
// 注册中间件 
app.use(cookieSession({
    name: 'session', // 客户端cookie的名称
    keys: ['xilingzuishuai'] // 用于加密的关键字
}))

最终效果预览:
这可能是全网最详细的Node.js编程_第75张图片

参考资源相关列表:

https://nodejs.org/zh-cn/ node.js官网

http://nodejs.cn/ node.js中文网

《深入浅出Node.js》 朴灵著 ,人民邮电出版社

《ECMAScript 6 入门》(第三版) 阮一峰著 ,电子工业出版社

《你不知道的JavaScript》(上、中、下卷) [美] Kyle Simpson 著 ,人民邮电出版社

http://www.expressjs.com.cn/ express中文网

你可能感兴趣的:(前端,前端)