[Notas del estudio] Nodo: desde 0 conceptos básicos hasta el sitio web oficial de la empresa de combate real

Prólogo: Este artículo está relacionado con la práctica de aprender la capa intermedia de Node. Se recomienda que lea el artículo original Node-From 0 Basics to the Actual Enterprise Official Website
    por jsliang . Antes de comenzar este artículo, primero puede comprender el conocimiento básico del nodo, que lo ayudará a comprender el contenido de este artículo. Por supuesto, también puede optar por seguir este artículo y aprender mientras lee. (PD: Nodo Xiaobai que sigue pagando deudas ╮(╯▽╰)╭)

Puntos destacados de este artículo:

1. Aprendizaje básico

1.1 HTTP: iniciar el viaje del nodo

El código relevante para 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('<h1 style="text-align:center">Hello NodeJS</h1>');

  // 结束响应
  res.end();

}).listen(3000); // 监听的端口

Entonces, ¿cómo usar el código anterior?

  • Primero, copie y pegue el código anterior en http.js;
  • Luego, inicie el terminal VS Code: Ctrl + ~ ;
  • Luego, ingrese el nodo http.js y presione Enter;
  • Finalmente, abra localhost:3000

inserte la descripción de la imagen aquí
Ok, eso es todo, ahora, expliquemos el código anterior.
Primero , el modo HTTP debe estar habilitado. En general, un lenguaje de back-end anticuado como PHP requiere Apache o Nginx para habilitar los servicios HTTP. Sin embargo, nuestro Nodo no necesita:

var http = require("http");

Luego , inicie el servicio HTTP y configure el puerto abierto:

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

A continuación , configure el encabezado HTTP, imprima el valor en la página y finalmente finalice la respuesta:

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

// 往页面打印值
res.write('<h1 style="text-align:center">Hello NodeJS</h1>');

// 结束响应 
res.end();

Finalmente , ingresar en el navegador http://localhost:3000/accederá al servicio Node que abrimos, para mostrar la página en la página.
Hasta ahora, ¿hemos comenzado nuestro viaje de Node?

1.2 módulo URL

¿Qué son los módulos de URL? Abrimos el modo Nodo en la consola (terminal )
y lo urlimprimimos para ver:
inserte la descripción de la imagen aquí
Luego, encontramos que tiene Url, , parse, resolve, resolveObject, format, URL, tantos módulos. Entonces, ¿para qué son estos módulos? Primero echemos un vistazo al código: url.jsURLSearchParamsdomainToASCIIdomainToUnicode


// 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('<h1 style="text-align:center">Hello NodeJS</h1>');

  // 结束响应
  res.end();

}).listen(3000);

En el código anterior:
primero , introduce el módulo protagonista urlde este capítulo:

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

Luego , importe el módulo http:

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

A continuación , cree httpun módulo, porque urlel monitoreo requiere httpla apertura del módulo:

// 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('<h1 style="text-align:center">Hello World</h1>');
 //结束响应
 res.end();
}).listen(3000);

Finalmente, visite la dirección dada: http://localhost:3000/?userName=jsliang&userAge=23 y utilícela para ver urlcómo parsese usa el módulo y cuál es el resultado:

// 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
}

A partir de él, podemos ver que queryel campo de ruta que queremos se puede obtener a través de .
Por supuesto, lo anterior solo explica parseel uso, podemos borrar todo el código en la declaración if en el código anterior. Luego, ingrese el siguiente contenido para conocer urlmás sobre el módulo:

  • Contenido 1: todo el contenido del módulo de 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] 
  }
 */
  • Contenido 2: Cómo usar 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/' 
  }
 */
  • Contenido 3: analizar con parámetros
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' 
  }
 */
  • Contenido 4: formatUso de
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
  • Contenido 5: Uso de resolver
console.log(url.resolve("http://www.baidu.com/jsliang", "梁峻荣"));

// Console:
// http://www.baidu.com/梁峻荣

Para obtener más información sobre la configuración de URL, puede consultar el sitio web oficial.

1.3 CommonJS

  • ¿Qué es CommonJS?
    CommonJS es para formular especificaciones para el rendimiento de JS. Debido a que JS no tiene un sistema de módulos, menos bibliotecas estándar y falta de herramientas de administración de paquetes, surgió CommonJS. Espera que JS pueda ejecutarse en cualquier lugar, no solo en el navegador En el servidor, los lenguajes de fondo como Java, C # y PHP tienen la capacidad de desarrollar aplicaciones a gran escala.

  • ¿Aplicación de CommonJS?
    1. Aplicación JavaScript del lado del servidor (Node.js)
    2. Herramienta de línea de comandos
    3. Aplicación GUI de escritorio.

  • ¿La relación entre CommonJS y Node.js?
    CommonJS es el estándar modular y Node.js es la implementación de CommonJS (modular).

  • Modularidad en Node.js?
    1. En Node, los módulos se dividen en dos categorías: uno es el módulo provisto por Node, que se denomina módulo central, el otro es el módulo escrito por el usuario, que se denomina módulo de archivo. Durante el proceso de compilación del código fuente de Node, el módulo central compila el archivo de ejecución binario, por lo que su velocidad de carga es la más rápida, como: módulo HTTP, módulo URL, módulo FS; el módulo de archivo se carga dinámicamente en tiempo de ejecución y necesita Se analiza la ruta completa, la ubicación del archivo, el proceso de compilación y ejecución, etc... por lo que su velocidad será menor que la del módulo principal.
    2. Podemos extraer las funciones públicas en un archivo JS separado para el almacenamiento y luego, si es necesario, exportar los módulos a través de export o module.exports, e importar estos módulos a través de require.
    Ahora, expliquemos la modularización y el uso de exportaciones/requerimiento en Node a través de tres métodos de uso.
    Consulte primero el directorio:
    inserte la descripción de la imagen aquí

  • Método 1:
    Primero, cree 4 archivos/carpetas 03_CommonJS.js, 03_tool-add.js, node_modules/03_tool-multiply.js, . Entre ellos, package.json se ignora por el momento, y más adelante se explicará cómo se genera automáticamente. En : 03_tool-add.jsnode_modules/jsliang-module/tools.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;

Entonces, ¿cuál es la definición del código anterior?
En el primer paso, se define una biblioteca de herramientas tools;
en el segundo paso, se modules.exportsexporta toolsa través de;
por lo tanto, 03_CommonJS.jsse puede requireimportar y utilizar en:

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('<h1 style="text-align:center">Hello NodeJS</h1>');
  
  console.log(tools1.add(1, 2, 3));
  /**
   * Console:
   * 6
   * 6
   * 这里要记得 Node 运行过程中,它请求了两次,
   * http://localhost:3000/ 为一次,
   * http://localhost:3000/favicon.ico 为第二次
   */
  
  res.end();

}).listen(3000);

De esta forma, se completa el uso inicial de exportsy require.

  • Método 2:
    cuando tenemos demasiados archivos de módulos, debemos tener un directorio para almacenar estos módulos. Node es muy confiable y regula que podemos almacenar estos archivos node_modulesen el directorio de la casa.
    Entonces, node_modulescree un nuevo 03_tool-multiply.jsarchivo en , con el siguiente contenido:
    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;

En términos de referencia, simplemente pase:

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

console.log(tools2.multiply(1,2,3,4));

De esta manera, el archivo se puede importar con éxito 03_tool-multiply.js.

  • Método 3:
    si se lanzan todos los archivos individuales node_modules, aparecerá desorganizado, por lo que debe definir su propio módulo: jsliang-moduley luego almacenarlo tool.jsen este directorio:
    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;

De esta manera, ha definido su propia biblioteca de herramientas.
Sin embargo, si usamos var tools3 = require('jsliang-module'); para importar, encontraremos que reporta un error, por lo que debemos generar un paquete.json a través de la siguiente línea de comando en el directorio jsliang-module.

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

De esta forma, existe package.json en jsliang-module
y podemos importarlo en 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('<h1 style="text-align:center">Hello NodeJS</h1>');
  
  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);

Hasta ahora, hemos aprendido sobre varias posturasexports y el concepto de modularización de nodos a través de tres métodos .require

1.4 gestión de archivos fs

A continuación, expliquemos la gestión de archivos fs:

Para encontrar rápidamente uno de los siguientes, use Ctrl + F

  1. fs.statDetectar si es un archivo o un directorio;
  2. fs.mkdirCrear un directorio;
  3. fs.writeFilecrear archivo de escritura;
  4. fs.appendFileadjuntar archivo
  5. fs.readFileleer el archivo;
  6. fs.readdirleer directorio;
  7. fs.renamedoble denominación;
  8. fs.rmdireliminar directorio;
  9. fs.unlinkBorrar archivos.

El directorio de archivos de este capítulo:
inserte la descripción de la imagen aquí
Primero , verifique si fs.stat lee un archivo o directorio:

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: 创建目录成功!
	}
 })

Pasó node 05_fs.jsy encontró que hay una csscarpeta .
Luego, si se crea, se eliminará ¿Cómo eliminar el directorio creado? Aquí está la explicación 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: 创建目录成功!
	}
 })

A través de node 05_fs.js, encontramos que cssla carpeta fue eliminada.
A continuación , cree el archivo de escritura fs.writeFilea través de :
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('写入成功');
	}
})

  Vale la pena señalar que este tipo de escritura es para borrar todos los datos en el archivo original y luego agregar Hello jsliangesta oración. Es decir: si existe, se sobrescribirá; si no existe, se creará.

  Si hay una creación, hay una eliminación. Si está interesado, puede usar fs.unlinkpara eliminar el archivo. Nuevamente, no explicaré demasiado.
  Dado que el anterior es un archivo superpuesto, ¿hay algún archivo adicional? Sí, fs.appendFileúsalo :
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("追加成功");
	}
})

De esta manera, se le agrega con éxito un párrafo, por lo que index.jsse convierte :
index.js

Hola jsliang Este texto es el contenido que se agregará

En lo anterior , hemos hecho: agregar, modificar, eliminar operaciones. Entonces, los amigos deben estar muy familiarizados con lo que deben hacer a continuación:

  • fs.readFileleer el archivo;
  • fs.readdirleer directorio
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:
    // 读取文件成功!
    // <Buffer 48 65 6c 6c 6f 20 6a 73 6c 69 61 6e 67 e8 bf 99 e6 ae b5 e6 96 87 e6 9c ac e6 98 af e8 a6 81 e8 bf bd e5 8a a0 e7 9a 84 e5 86 85 e5 ae b9>
  }
})

// 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' ]
  }
})

Como arriba, lea con éxito el archivo y lea el directorio.
Finalmente , revise el objetivo al principio:
1. fs.stat detecta si es un archivo o un directorio
2. fs.mkdir crea un directorio
3. fs.writeFile crea un archivo de escritura
4. fs.appendFile agrega un archivo
5. fs.readFile lee un archivo
6. fs.readdir leer el directorio
7. fs.rename cambiar el nombre
8. fs.rmdir eliminar el directorio
9. fs.unlink eliminar el archivo
Ok, simplemente cambie el nombre:
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("重命名成功!");
	}
})

Eso sí, si fs.renamehay una función más potente: cortar

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("剪切成功!");
  }
})

Todo hecho, ahora el directorio se convierte en:
inserte la descripción de la imagen aquí

caja de 1.5 fs

En el capítulo anterior, aprendimos sobre la gestión de archivos fsde .
Entonces, aquí tratamos de fshacer algo pequeño con:
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)
  }
})

flujo de 1.6 fs

Entendemos las siguientes fscorrientes y su lectura:

// 新建 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);
})

Aquí fs, createReadStreamel flujo de lectura se crea a través del módulo y luego se lee el archivo index.js, de modo que la salida finalmente se muestra en la consola:

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

Entre ellos, las tres líneas de console.log() son el contenido de texto de index.js.
Luego, prueba el depósito obsceno:

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:写入完成
});

Cuando lo abramos index.js, encontraremos que el contenido de su interior se ha convertido console.log("Hello World! 我要存入数据!")y, a su vez, tenemos operaciones de lectura y escritura en forma de flujos.

1.7 Crear un servidor web

Aquí, usamos el módulo http, el módulo url, el módulo de ruta, el módulo fs para crear un servidor web.
Qué es un servidor Web?
Un servidor web generalmente se refiere a un servidor de sitio web, que se refiere a un programa que reside en cierto tipo de computadora en Internet.Puede proporcionar documentos como un cliente web, como un navegador, o colocar archivos de sitios web para que el mundo los visite; puede colocar archivos de datos para que el mundo los visite.descarga mundial. Actualmente, los tres servidores web más populares son Apache 、Nginx 、IIS.
A continuación, usamos Node para crear un servicio web:
inserte la descripción de la imagen aquí
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);

De esta manera, puede verlo localhost:8080cuando :
inserte la descripción de la imagen aquí
Luego, encontrará que carga todo el index.htmlarchivo , ¿no se ha introducido siquiera CSS?
Entonces, el siguiente paso es cargar dinámicamente html, cssy 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';
  }
}

De esta manera, cuando volvemos a solicitar, el navegador se vuelve:
inserte la descripción de la imagen aquí
 Por supuesto, arriba, solo simulamos html、css、jsestos tres tipos de archivos, y necesitamos simular más tipos de archivos:

Luego, debemos modificar nuestro jsarchivo para adaptarlo a varias respuestas de solicitud:
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];
}

Entonces, hicimos un servidor web simple.

1.8 E/S sin bloqueo controlada por eventos

  Los lenguajes del lado del servidor como Java, PHP o .NET crearán un nuevo hilo para cada conexión de cliente.
  El nodo no crea un nuevo subproceso para cada conexión de cliente, sino que solo utiliza un subproceso.
Cuando un usuario se conecta, se activará un evento interno.A través de la E/S sin bloqueo y el mecanismo controlado por eventos, el programa Node también es macroscópicamente paralelo.
  Usando Node, un servidor con 8GB de memoria puede manejar más de 40,000 conexiones de usuarios simultáneas.
  En este capítulo, principalmente resolvemos:
  1. ¿Qué es la E/S sin bloqueo de Node?
  2. ¿Qué es el módulo de eventos de Nodo?
  En primer lugar, en nuestra programación normal, esperamos que el programa se pueda escribir línea por línea según nuestros deseos:
09_io.js

console.log("1");

console.log("2");

console.log("3");

/**
 * Console:
 * 1
 * 2
 * 3
 */

   Sin embargo, las cosas fracasaron.
 A veces ejecutamos algunos métodos asincrónicos (funciones):
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
 */

   En el código anterior, porque fs.readFilees una función asíncrona de Node. Por lo tanto, el programa ejecuta primero 1 y 3, y luego fs.readFileejecuta la parte 2 de .

Aquí, se puede ver que Node no hará que otros códigos no se ejecuten debido a un error lógico en una parte del código.

   Esto genera un problema: es posible que el paso 3 no obtenga el resultado de ejecución del paso 2. Este es el controlador de E/S sin bloqueo de Node.
   Entonces, ¿hay alguna manera de que podamos resolver este problema?
 ¡alguno!
1. A través de la función de devolución de llamada
2. A través del eventsmódulo .
09_io.js

let fs = require("fs");

getExt = (callback) => {
    
    
  fs.readFile('08_ext.json', (err, data) => {
    
    
    callback(data);
  })  
}

getExt( (result) => {
    
    
  console.log(result.toString());
})

A través de la devolución de llamada, podemos extraer los datos getExtde .

Luego, usamos eventsel módulo para resolver este problema asíncrono:

// 引入 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);
});

Aquí, EventEmitter.onal dataescuchar el formulario, getExtse obtiene el resultado de la ejecución interna de.
 De esta manera, entendemos los eventos y eventsmódulos

1.9 obtener y publicar

inserte la descripción de la imagen aquí
Sin más preámbulos, comencemos con el código:
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...');


Primero , se carga el módulo httpy se crea el servicio;
luego , se configura el método de procesamiento entre dominios para permitir el cruce de dominios;
luego , se realiza el procesamiento de juicio de la solicitud, y dado que solo se realiza un simulacro simple, es solo se juzga si se trata de getuna solicitud o postun pedido;
finalmente , el resultado de la solicitud se devuelve al cliente.
En lo anterior, hemos implementado el nodo de back-end, entonces, ¿cómo hacer la página de inicio?
índice.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 Web</title>

</head>

<body>

  <div id="app">
    <h1>Todo List</h1>
    <ul>
      <li v-for="(item, index) in items" :key="index">{
   
   { item }}</li>
    </ul>
    <input type="text" v-model="item">
    <button @click="postApi">添加</button>
  </div>

  <!-- cdn 引用:Vue 和 Node -->
  <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>

Hicimos el diseño a través de Vue y solicitamos la interfaz a través de Axios. Por lo tanto, se completa la operación sobre los datos.

1.9 El nodo se conecta a MySQL

Primero , diseñamos la tabla a través de la herramienta de visualización:

nombre tipo longitud llave
identificación En t 11 Clave primaria
nombre varchar 255
edad varchar 255

Luego , llenamos la tabla:

identificación nombre edad
1 jsliang 23
2 Liang 25

A continuación , instalamos el paquete para que Node se conecte a MySQL:

npm y mysql -D

A continuación , escribimos Node's 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();

Finalmente , abrimos el servicio a través del nodo index.js:

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

De esta forma hemos completado la conexión del Nodo a MySQL.

—————— hermosa línea divisoria——————

Por supuesto, agregar, eliminar, modificar y verificar es la operación básica del backend, por lo que aquí podemos completar las funciones básicas de agregar, eliminar, modificar y verificar.

Mira el directorio primero:
inserte la descripción de la imagen aquí

  • Nuevo campo de tabla
    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();

Solo necesitamos node add.jsagregar datos directamente a la base de datos.

  • eliminar campo de tabla
    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();

  • Modificar campo de tabla
    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();

  • campo de tabla de consulta
    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();

Arriba, rompimos las barreras entre Node y MySQL, y realizamos la adición, eliminación, modificación y consulta de datos.

2. Web real combat - sitio web oficial de la empresa

2.1 Entorno de programación

Primero , veamos nuestro código básico de front-end: dirección

Luego , realizamos un análisis funcional de back-end:

  1. tablero de mensajes. Cuando el usuario hace clic en el tablero de mensajes , es necesario determinar si el usuario ha iniciado sesión o no. Si el usuario no ha iniciado sesión, saltará directamente a la página de inicio de sesión ; si el usuario ha iniciado sesión, se mostrará la página del tablero de mensajes .
    En la página del tablero de mensajes , hay dos interfaces:
  • Obtenga el contenido del mensaje : llame a getMessagela interfaz para devolver toda la información del mensaje. Dado que no se esperan muchos mensajes, la función de paginación no se implementa aquí. Después de implementar esta función, los pequeños socios que lo necesiten pueden diseñar la interfaz de paginación;
  • Envíe el contenido del mensaje : llame sendMessagea la interfaz y envíe el nombre de usuario, la identificación del usuario y el contenido del mensaje al backend.
  1. En la página de inicio de sesión , hay una interfaz:
    Iniciar sesión : llame logina la interfaz y envíe el nombre y la contraseña completados por el usuario.
  2. En la página de registro , hay una interfaz:
    Registro : llame registera la interfaz y envíe el nombre y la contraseña completados por el usuario.

A partir de esto, podemos diseñar la combinación de interfaz de los extremos frontal y posterior:

documentación de la interfaz

interfaz tipo parámetro mensajes devueltos
getMessage: obtener información del mensaje conseguir Sin referencia n registros: id (id de usuario), nombre_usuario (nombre de usuario), mensaje_usuario (contenido del mensaje del usuario), hora (hora del mensaje)
sendMessage: enviar un mensaje correo id (id de usuario), nombre_usuario (nombre de usuario), mensaje_usuario (contenido del mensaje del usuario) estado de estado
login:Acceso correo id (identificación de usuario), nombre_usuario (nombre de usuario), contraseña_usuario (contraseña de usuario) estado de estado
register:registro correo id (identificación de usuario), nombre_usuario (nombre de usuario), contraseña_usuario (contraseña de usuario) estado de estado

Finalmente , diseñamos las tablas de la base de datos MySQL:

tabla de usuarios

nombre tipo longitud llave
identificación En t 11 Clave primaria
nombre de usuario varchar 255
contraseña de usuario varchar 255
tiempo fecha y hora

tabla de mensajes

nombre tipo longitud llave
identificación En t 11 Clave primaria
nombre de usuario varchar 255
contraseña de usuario varchar 255
tiempo fecha y hora

2.2 Interfaz de fondo

Antes de realizar la operación real, primero confirme si podemos escribir la interfaz, para que podamos crear una nueva testcarpeta , poner un index.htmly un index.jsen ella para probar.

- text
 - index.html
 - index.js

En primer lugar , configuramos la interfaz de back-end de antemano para la interfaz mencionada en 2.1:

índice.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('<h1 style="text-align:center">jsliang 前端有限公司服务已开启!</h1><h2 style="text-align:center">详情可见:<a href="https://github.com/LiangJunrong/document-library/blob/master/other-library/Node/NodeBase.md" target="_blank">Node 基础</a></h2>');

      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;
}

Determine la interfaz cargada juzgando req.methodsi pertenece GETo POSTforma :

  • POSTEn , determine si pertenece enviar mensaje , iniciar sesión o registrarse ;
  • GETEn , determine si desea obtener información del mensaje .

Al mismo tiempo , definimos la conexión MySQL y getNowFormatDatepara obtener la hora actual, el formato es:2018-12-21 10:03:59

Luego , demostramos si nuestra interfaz se puede usar a través de una página de inicio:

índice.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>

Finalmente , pasamos node index.jsy lo abrimos para verificar si nuestra interfaz es normal index.htmla través F12de la consola :
 podemos ver que nuestra interfaz se puede ajustar normalmente, para que podamos conectarnos a la base de datos y diseñar estas 4 interfaces.

Si cree que es problemático reiniciar el nodo index.js cada vez que actualiza el código del nodo, puede usar el supervisor para monitorear los cambios del código del nodo.

2.3 Función de registro

Bueno, estamos de vuelta en la página del sitio web de la empresa de imitación, listos para escribir la interfaz y enriquecer la interfaz de Node.
Primero , iniciamos el frontend y el servicio Node:

  1. Abra la línea de comando/terminal;
  2. Abre la parte delantera
    cd FrontEndCode
    live-server

instalar live-server:npm i live-server -g

  1. back-end abierto
    cd NodeWeb
    supervisor index.js

Instalar supervisor: npm i supervisor -g

Luego , activamos la interfaz de llamada a través del evento de clic en la página de registro:

registro.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>

De esta manera, cuando el usuario hace clic en el botón Registrarse, llamamos a la interfaz y enviamos los datos al backend. Si tiene éxito, aparecerá una ventana emergente y saltará a la página de inicio de sesión; si falla, aparecerá una ventana emergente. aparecerá una ventana hacia arriba.
A continuación , escribimos Node.Después de que el front-end llame a la interfaz, Node juzga si estos dos parámetros están vacíos y, si no, almacena los datos en la base de datos.

índice.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 结束
  }
  // 注册流程结束
}

Finalmente , verificamos si la función es exitosa:

2.4 Función de registro

En lo anterior, hemos completado la función de registro, por lo que, en términos relativos, la función de inicio de sesión es fácil de pasar, porque ya probamos la parte de consulta una vez.

iniciar sesión.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>

Después de escribir el código del front-end, editamos el código del nodo:

índice.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 - 注册】");

}

Muy bien, el front end y el back end están escritos, es hora de verificar si la función está implementada:

2.5 Función de mensaje

Ahora, solo nos queda la función de mensaje, ¡hagámoslo bien de una vez!

tablón de mensajes.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 + `
                <li>
                  <span class="text-warning font-bold">☆ </span>
                  <span class="user-message">${
      
      res.data[item].user_message}</span>
                  <span>—— </span>
                  <span class="user-name">${
      
      res.data[item].user_name} [${
      
      res.data[item].user_id}]</span>
                  <span class="message-time">${
      
      res.data[item].time}</span>
                </li>
              `;
            }
            $("#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>

Luego escribe el backend del nodo:

índice.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('<h1 style="text-align:center">jsliang 前端有限公司服务已开启!</h1><h2 style="text-align:center">详情可见:<a href="https://github.com/LiangJunrong/document-library/blob/master/other-library/Node/NodeBase.md" target="_blank">Node 基础</a></h2>');

  res.end();
}

Después de escribir el código, vea si la función se realiza:

En resumen, hemos completado todos los módulos funcionales: registro, inicio de sesión y mensaje.

3. Integración de herramientas

3.1 supervisor: monitorear los cambios de Nodo

Tal y como dice su web oficial, se trata de un sistema de control:

1. Instale el complemento: npm i supervisor -g
2. Ejecute el archivo: supervisor app.js
3. Verifique y ejecute:localhost:3000

Por lo general, node app.jsdespués de modificar app.jsel contenido de , debemos cerrar la línea de comando del nodo y ejecutarlo nuevamente node app.js. Después de
usar , modificamos el contenido en , siempre que hagamos clic en Guardar, el código guardado tendrá efecto, realizando un seguimiento en tiempo real de los cambios en el código del nodo.supervisorapp.js

3.2 PM2 – Gestión de procesos de nodo

PM2 es una herramienta de gestión de procesos de Node, que se puede utilizar para simplificar muchas tareas tediosas de la gestión de aplicaciones de Node, como la supervisión del rendimiento, el reinicio automático, el equilibrio de carga, etc., y es muy fácil de usar.
La siguiente es una introducción introductoria a PM2, que básicamente cubre las funciones y configuraciones comúnmente utilizadas de PM2:

  1. Instale PM2 globalmente:npm i pm2 -g
  2. Aplicación de escucha:pm2 start index.js
  3. Ver todos los procesos:pm2 list
  4. Para ver un proceso:pm2 describe App name/id
  5. Para detener un proceso: pm2 stop App name/id. Por ejemplo:

Primer pase pm2 listpara ver :

Nombre de la aplicación identificación estado
índice 0 en línea

Simplemente ejecute pm2 stop indexo pm2 stop 0.

  1. Detener todos los procesos:pm2 stop all
  2. Reiniciar un proceso:pm2 restart App name/id
  3. Eliminar un proceso:pm2 delete App name/id

Como arriba, si nuestro objetivo supervisores monitorear un solo proceso, entonces PM2es monitorear múltiples procesos.

Referencias

Nodo: desde 0 conceptos básicos hasta el combate real Sitio web oficial de Enterprise https://juejin.cn/post/6844903745755545614#heading-12

Supongo que te gusta

Origin blog.csdn.net/qq_26780317/article/details/125870368
Recomendado
Clasificación