一、 Http - 开启node 之旅


  1. 首先,我们需要开启 HTTP 模式,像 PHP 这类老牌后端语言,需要 Apache 或者 Nginx 开启 HTTP 服务。然后 node 不需要:
let http require('http')
  1. 然后,开启 http 服务,并设置开启的端口:
/**
 * req 获取 url 信息 (request)
 * res 浏览器返回响应信息 (response)
 */
http.createServer(function (req, res) {
  // ... 步骤 3 代码
}).listen(3000); // 监听的端口
  1. 接着,设置 http 的头部,并且往页面打印值,最后结束响应:
// 设置 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();
  1. 最后,在命令行运行,01_http.js,浏览器输入 http://localhost:9001

二、URL 模块


URL 模块是啥,在控制台开启 node 模式,并 打印 url来看一下:

1. url.parse()方法的使用

  • 接收两个参数,第一个,为地址,第二个为 true 的话,表示把 get 传参转化为 对象形式

    // 1. 引入 url 模块
    const url = require('url');
    
    const http = require('http');
    
    /**
     * 访问地址是:http://localhost:3000/?userName=jsliang&userAge=23
     * 如果你执行 console.log(req.url),它将执行两次,分别返回下面的信息:
     * /  ?userName=jsliang&userAge=23
     * /  /favicon.ico
     * 这里为了防止重复执行,所以排除 req.url == /favicon.ico 的情况
     */
    http.createServer((req, res) => {
      if (req.url !== '/favicon.ico') {
        console.log(req.url);
        // url的 parse 方法
        /**
         * parse 接收两个参数
         * 第一个参数是 地址
         * 第二个参数 true 的话,表示把 get 传参转换为对象
         */
        let result = url.parse(req.url, true);
        console.log('result', result);
        console.log( result.path);
      }
      res.end()
    }).listen(9002)
    

  • url.parse('/username=ljt&age=20', true)

  • 通过 query 就能读取 get 传参

2. url.format()方法使用

返回一个WHATWG URL对象的可自定义序列化的URL字符串表达。

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

虽然URL对象的toString()方法和href属性都可以返回URL的序列化的字符串。然而,两者都不可以被自定义。而url.format(URL[, options])方法允许输出的基本自定义。

const { URL } = require('url');
const myURL = new URL('https://a:b@你好你好?abc#foo');

console.log(myURL.href);
  // 输出 https://a:b@xn--6qqa088eba/?abc#foo

console.log(myURL.toString());
  // 输出 https://a:b@xn--6qqa088eba/?abc#foo

console.log(url.format(myURL, { fragment: false, unicode: true, auth: false }));
  // 输出 'https://你好你好/?abc'

3. url.resolve()的使用

会以一种 Web 浏览器解析超链接的方式把一个目标 URL 解析成相对于一个基础 URL。

const url = require('url');
url.resolve('/one/two/three', 'four');         // '/one/two/four'
url.resolve('http://example.com/', '/one');    // 'http://example.com/one'
url.resolve('http://example.com/one', '/two'); // 'http://example.com/two'

三、CommonJS


  • 什么是 CommonJS?

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

  • CommonJS 的应用?

  • 服务器端 JavaScript 应用程序。(Node.js)

  • 命令行工具
  • 桌面图形界面应用程序。

  • CommonJS 与 Node.js 的关系?

 CommonJS 就是模块化的标准,Node.js 就是 CommonJS(模块化)的实现。

  • Node.js 中的模块化?

  • 在 Node 中,模块分为两类:一是 Node 提供的模块,称为核心模块;二是用户编写的模块,成为文件模块。核心模块在 Node 源代码的编译过程中,编译进了二进制执行文件,所以它的加载速度是最快的,例如:HTTP 模块、URL 模块、FS 模块;文件模块是在运行时动态加载的,需要完整的路劲分析、文件定位、编译执行过程等……所以它的速度相对核心模块来说会更慢一些。

  • 我们可以将公共的功能抽离出一个单独的 JS 文件存放,然后在需要的情况下,通过 exports 或者 module.exports 将模块导出,并通过 require 引入这些模块。

 现在,我们通过三种使用方式,来讲解下 Node 中的模块化及 exports/require 的使用。

方法一:

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

 在 03_tool-add.js 中:

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

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

const http = require('http');
const tools = require('./03_tool-add');

http.createServer((req, res) => {
  res.writeHead(200, {
    'Content-Type': 'text/html;charset=UTF-8'
  })
  let total = 0;
  total = tools.add(1, 2, 3);
  res.write(`<h1>${total}</h1>`);
  res.end()
}).listen(9003)
方法二:

当我们模块文件过多的时候,应该需要有个存放这些模块的目录,Node很靠谱,它规范我们将这些模块都放到 node_modules目录中,在 node_modules 下新建文件 03_tool-multiply.js

const tools = {
  multiply: (...numbers) => {
    let sum = numbers[0];
    for (let index in numbers) {
      sum = sum * numbers[index]
    }
    return sum;
  }
}

module.exports = tools;

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

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

console.log(tools2.multiply(1, 2, 3, 4));
方法三:

如果全部单个文件丢在 node_modules 上,它会显得杂乱无章,所以我们应该定义个自己的模块:ljt-module,然后将我们的 tools.js 存放在该目录中:

const tools = {
  add: (...numbers) => {
    let sum = 0;
    for (let number in numbers) {
      sum += numbers[number]
    }
    return sum;
  },
  multiply: (...numbers) => {
    let sum = numbers[0];
    for (let index in numbers) {
      sum = sum * numbers[index]
    }
    return sum;
  }
}

module.exports = tools;

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

npm init --yes

这样,在 jsliang-module 中就有了 package.json。 而我们在 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. 通过在 ljt-module 中 npm init --yes 来生成 package.json 文件
 * 2. package.json 文件中告诉了程序入口文件为 :"main": "tools.js",
 * 3. Node 通过 require 查找 ljt-module,发现它有个 package.json
 * 4. Node 执行 tools.js 文件
 */
var tools3 = require('ljt-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);

推荐使用第三种

四、包与npm

Node 中除了它自己提供的核心模块之外,还可以自定义模块,以及使用 第三方模块。  Node 中第三方模块由包组成,可以通过包来对一组具有相互依赖关系的模块进行统一管理。

npm 是世界上最大的开放源代码的生态系统。我们可以通过 npm 下载各种各样的包。 在我们安装 Node 的时候,它默认会顺带给你安装 npm。

  • npm -v:查看 npm 版本。
  • npm list:查看当前目录下都安装了哪些 npm 包。
  • npm info 模块:查看该模块的版本及内容。
  • npm i 模块@版本号:安装该模块的指定版本。

 在平时使用 npm 安装包的过程中,你可能需要知道一些 npm 基本知识:

  • i/install:安装。使用 install 或者它的简写 i,都表明你想要下载这个包。
  • uninstall:卸载。如果你发现这个模块你已经不使用了,那么可以通过 uninstall 卸载它。
  • g:全局安装。表明这个包将安装到你的计算机中,你可以在计算机任何一个位置使用它。
  • --save/-S:通过该种方式安装的包的名称及版本号会出现在 package.json 中的 dependencies 中。dependencies 是需要发布在生成环境的。例如:ElementUI 是部署后还需要的,所以通过 -S 形式来安装。
  • --save-dev/-D:通过该种方式安装的包的名称及版本号会出现在 package.json 中的 devDependencies 中。devDependencies 只在开发环境使用。例如:gulp 只是用来压缩代码、打包的工具,程序运行时并不需要,所以通过 -D 形式来安装。

 例子:

  • cnpm i webpack-cli -D
  • npm install element-ui -S

 那么,这么多的 npm 包,我们通过什么管理呢? 答案是 package.json。 如果我们需要创建 package.json,那么我们只需要在指定的包管理目录(例如 node_modules)中通过以下命名进行生成:

  • npm init:按步骤创建 package.json
  • npm init --yes:快速创建 package.json

 当然,因为国内网络环境的原因,有些时候通过 npm 下载包,可能会很慢或者直接卡断,这时候就要安装淘宝的 npm 镜像:cnpm

  • npm install -g cnpm --registry=https://registry.npm.taobao.org

五、fs 文件管理

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

fs.stat

首先,通过 fs.stat(异步),读取文件状态,来检查一个读取的是文件还是目录:

// 05_fs.js


const fs = require('fs');

fs.stat('node_modules', (err, stats) => {
  if (err) {
    console.log('读取发生错误!', err);
    return false;
  }
  console.log(stats);
  const isFile = stats.isFile(), // 判断读取的是否为文件
    isDirectory = stats.isDirectory(); // 判断读取的是否为目录
  if (isFile) {
    console.log('当前读取的是文件');
    return false;
  }
  if (isDirectory) {
    console.log('当前读取的是目录');
    return false;
  }
})
// console.log(stats);
/**
  dev: 1848045516,
  mode: 16822,
  nlink: 1,
  uid: 0,
  gid: 0,
  rdev: 0,
  blksize: 4096,
  ino: 61361544924205144,
  size: 0,
  blocks: 0,
  atimeMs: 1668579646278.2124,
  mtimeMs: 1668483159042.7356,
  ctimeMs: 1668483159042.7356,
  birthtimeMs: 1668483111386.925,
  atime: 2022-11-16T06:20:46.278Z,
  mtime: 2022-11-15T03:32:39.043Z,
  ctime: 2022-11-15T03:32:39.043Z,
  birthtime: 2022-11-15T03:31:51.387Z
  */

fs.statSync(同步)只接收一个path变量,fs.statSync(path),其实是一个fs.stats的一个实例;

fs.mkdir 创建目录

fs.mkdir('css', err => {
  if (err) {
    console.log(err);
    if (err.code === 'EEXIST') {
      console.log('该目录已存在');
    }
  } else {
    console.log('创建目录成功');
  }
})

mode 参数可选值

​ S_ISUID 04000 文件的执行时设置用户ID(set user - id on execution)位

​ S_ISGID 02000 文件的执行时设置组ID(set group - id on execution)位

​ S_ISVTX 01000 文件的保存正文(粘着位sticky)位

​ S_IRWXU 00700 文件所有者具有读、写、执行权限

​ S_IRUSR(S_IREAD) 00400 文件所有者具可读取权限

​ S_IWUSR(S_IWRITE)00200 文件所有者具可写入权限

​ S_IXUSR(S_IEXEC) 00100 文件所有者具可执行权限

fs.rmdir 删除目录(异步)
fs.rmdir('css', err => {
  console.log(err);
  if (err) {
    console.log('要删除的目录不存在');
  } else {
    console.log('删除目录成功');
  }
})

同删除目录一样

fs.writeFile 给文件写入内容(异步)
/**
 * 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.txt', '这是写入的内容', err => {
  if (err) {
    console.log('写入失败');
  } else {
    console.log('写入成功');
  }
})

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

fs.appendFile 给文件追加内容(异步)
fs.appendFile('index.txt', '这是追加的内容', err => {
  if (err) {
    console.log('追加内容失败');
  } else {
    console.log('追加内容成功');
  }
})

fs.readFile 读取文件(异步)
fs.readFile('index.txt', (err, data) => {
  console.log(err);
  console.log(data);
})
// data <Buffer e8 bf 99 e6 98 af e5 86 99 e5 85 a5 e7 9a 84 e5 86 85 e5 ae b9 e8 bf 99 e6 98 af e8 bf bd e5 8a a0 e7 9a 84 e5 86 85 e5 ae 
b9 e8 bf 99 e6 98 af e8 bf ... 13 more bytes>

这样直接读取文件,返回为 Buffer 类型,如果读取需要返回字符类型,可以加一个参数 utf-8,或者对返回的数据进行处理:data.toString()

fs.readdir 读取目录(异步)
fs.readdir('node_modules', (err, data) => {
  console.log(err);
  console.log(data);
})
// data [ '03_tool-multiply.js', 'ljt-module' ]
// 会读取到当前目录及以下的目录
fs.rename 重命名(异步)
fs.rename('index1.txt', 'index.txt', err => {
  console.log(err);
  if (err) {
    console.log('没有这样的文件或目录');
  } else {
    console.log('重命名成功');
  }
})

此方法还有剪切功能

fs.rename('index.txt', 'node_modules/ljt-module/index.txt', err => {
  if (err) {
    console.log('没有这样的文件或目录');
  } else {
    console.log('剪切成功');
  }
})

fs Demo 找出某目录下的所有目录
fs.readdir('node_modules', (err, files) => { // 读取到 node_modules 目录下的所有文件
  console.log(files);
  if (err) {
    console.log('没有此目录');
  } else {
    let arr = [];
    (function getFile (i) {
      if (i == files.length - 1) {
        console.log('全部目录');
        console.log(arr);
        return false;
      }
      fs.stat(`node_modules/${files[i]}`, (err, stats) => { // 查看改目录下 文件状态
        if (stats.isDirectory) { // 找出是目录的文件
          arr.push(files[i])
        }
        getFile(i+1)
      })
    })(0)
  }
})
fs 流
const fs = require('fs');

// 流的方式读取文件
let fileReadStream = fs.createReadStream('node_modules/ljt-module/index.txt');
let count = 0; // 读取次数
let str = ''; // 保存数据

// 开始读取数据
fileReadStream.on('data', chunk => {
  console.log(`${++count}--接收到:${chunk.length}`);
  str += chunk;
})

// 读取完成
fileReadStream.on('end', (res) => {
  console.log(count);
  console.log(str);
})

// 读取失败
fileReadStream.on('error', err => {
  console.log(err);
})

在这里,我们通过 fs 模块的 createReadStream 创建了读取流,然后读取文件 node_modules/ljt-module/index.txt,从而最后在控制台输出了:

1--接收到:63
1
这是写入的内容这是追加的内容这是追加的内容

再试下流的存入

const fs = require('fs');

const data = '我是要存入的数据';

let writeStream = fs.createWriteStream('node_modules/ljt-module/index.txt');

// 开始写入
writeStream.write(data, 'utf-8');

// 写入完成
writeStream.end();

writeStream.on('finish', () => {
  console.log('写入完成');
})

创建 Web 服务器

使用 http、url、path、fs 模块创建一个 web 服务器。

结构目录

08_WebService.js

const http = require('http');
const fs = require('fs');
const url = require('url');
const path = require('path');

http.createServer((req, res) => {
  // 获取浏览器输入路径
  // url.path 将路径解析为方便解析的对象
  console.log(url.parse(req.url));
  let pathName = url.parse(req.url).pathname;
  if (req.url === '/') {
    pathName = 'index.html'
  }
  console.log(pathName);

  // 获取文件的后缀名
  let extName = path.extname(pathName)
  console.log(extName);
  const path1 = `./08_WebService/${pathName}`;
  if (pathName !== '/favicon.ico') {
    fs.readFile(path1, (err, data) => {
      if (err) {
        console.log('404 Not Fount');
        const notFoundPath = `./08_WebService/404.html`;
        fs.readFile(notFoundPath, (errNot, dataNot) => {
          if (errNot) {
            console.log('err');
          } else {
            res.writeHead(200, {
              "Content-Type": "text/html;charset='utf-8'"
            })
            res.write(dataNot)
            res.end()
          }
        })
      } else {
        const ext = getExt(extName);
        res.writeHead(200, {
          "Content-Type": `${ext};charset='utf-8'`
        })
        res.write(data)
        res.end()
      }
    })

  }
}).listen(9003)

// 获取后缀名

const getExt = (extName) => {
  switch (extName) {
    case '.html': return 'text/html';
    case '.css': return 'text/css';
    case '.js': return 'text/js';
    default: return 'text/html'
  }
}

注意:

Content-Type:为动态的,text/当前文件后缀,要不然页面css,js读取不出来,导致页面没样式。

中间件


express 内置中间件
  1. express.static('./public') 向外托管静态资源
  2. express.json() 解析表单中 json 格式的数据
  3. express.urlencoded({ extended: false }) 解析 application/x-www-form-urlencoded 格式的数据
第三方的中间件

在 4.16.0 之前。使用 body-parser ,来解析请求头数据,使用步骤如下:

  1. 运行 npm i body-parser 安装
  2. 使用 require 导入
  3. 调用 app.use() 注册并使用中间件

注意: express.urlencoded 中间件,就是基于 body-parser 这个第三方中间件进一步封装出来的

CORS 跨域资源共享

使用 cors 中间件 解决跨域问题

  1. 运行 npm i cors
  2. 使用 const cors = require('cors') 导入中间件
  3. 在路由之前调用 app.use(cors()) 配置中间件

MySql

一、DataType 数据类型
  1. int 整数
  2. varchar(len) 字符串
  3. tinyint(1) 布尔值
二、字段的特殊标识
  1. PK (Primary Key) 主键、唯一标识
  2. NN (Not Null) 值不允许为空
  3. UQ (Unique) 唯一值
  4. AI (Auto Increment) 值自动增长
三、SQL 的 SELECT 语句
  1. 语法

​ select 语句用于从表中查询数据。执行的结果被存储在一个结果表中(结果集)

// 从 FROM 指定的表中,查询出 所有的 数据, * 表示所有列
SELECT * FROM 表名称

// FROM 指定的表中,查询出指定列(字段)的数据
sELECT 列名称 FROM 表名称
四、SQL 的 INSERT INTO语句
  1. 语法

​ INSERT INTO 语句用于想数据表中插入新的数据行:

INSERT INTO table_name(列1, 列2...) VALUES (值1,值2...)
五、SQL 的 UPDATE 语句
  1. 语法

​ UPDATE 语句用于修改表中的数据

// 用 update 指定要更新哪个表中的数据
// 用 set 指定列对应的新值
// 用 where 指定更新的条件
UPDATE 表名称 SET 列名称 = 新值 WHERE 列名称 = 某值
六、SQL 的 DELETE 语句
  1. 语法

​ DELETE 语句用于删除表中的行

// 从指定的表中,根据 where 条件,删除对应的数据行
DELETE FROM 表名称 WHERE 列名称 = 值
七、SQL 的 WHERE 子句
  1. 语法

​ WHERE 子句用于限定选择的标准。在 SELECT、UPDATE、DELETE 语句中,皆可使用 WHERE 子句来限定选择的标准。

// 查询语句中 WHERE 条件
SELECT 列名称 FROM 表名称 WHERE 列 运算符 值
// 更新语句中 WHERE 条件
UPDATE 表名称 SET 列=新值 WHERE 列 运算符 值
// 删除语句中 WHERE 条件
DELETE FROM 表名称 WHERE 列 运算符 值
八、SQL 的 AND 和 OR 运算符
  1. 语法

​ AND 和 OR 可以在 WHERE 子句中把两个或多个条件结合起来

​ AND 和 OR 相当于 js 中的 && 和 ||

九、SQL 的 ORDER BY 子句
  1. 语法

  2. ORDER BY 语句用于根据指定的列对结果进行排序

  3. ORDER BY 语句默认按照升序对记录进行排序
  4. 如果您希望按照降序对记录进行排序,可以使用 DESC 关键字

  5. ORDER BY 子句 - 升序排序

// 对 users 表中的数据,按照 status 字段进行升序排序
SELECT * FROM users ORDER BY status
SELECT * FROM users ORDER BY status ASC
// 默认升序排序
// ASC 升序, DESC 降序
  1. ORDER BY 子句 - 多重排序
// 对 users 表中的数据,先按照 status 字段进行降序排序,再按照 username 的字母排序,进行升序排序
SELECT * FROM users ORDER BY status DESC, username ASC
十、SQL 的 COUNT(*) 函数
  1. 语法

​ count(*) 函数用于返回查询结果的总条数

SELECT COUNT(*) FROM 表名称
// 查出 users 表中 status 为 0 的总条数
select count(*) from users where status=0
  1. 使用 AS 为列设置别名

​ 如果希望给查询出来的列名称设置别名,可以使用 AS 关键字

SELECT COUNT(*) AS total FROM users WHERE status=0

在项目中操作数据库

  1. 安装操作 MySQL 数据库的第三方模块(mysql)
  2. 通过 mysql 模块链接到 MySQL 数据库
  3. 通过 mysql 模块执行 SQL 语句

results matching ""

    No results matching ""