node学习笔记(一)

167 阅读9分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 7 天

前言

英文官网:nodejs.org/dist/latest…

中文官网:nodejs.cn/

安装:blog.csdn.net/qq_48485223…(终端输入node,进入命令行式js交互环境,即安装成功)

学习路径:JavaScript语法 + Node.js模块(fs + path + http)+ 第三方API模块(express,mysql)

  • 浏览器是是JavaScript的前端运行环境。
  • Node.js 是JavaScript的后端运行环境。Node无法调用DOM和BOM,浏览器内置API。

模块化

Node采用CommonJs规范,将代码拆分到不同文件中,每个文件是一个模块,文件路径是模块名。 模块分为:核心模块(内置模块);自定义模块;第三方模块(npm下载);

  • require函数

引入模块不包括node内置模块。传入模块名(文件路径),后缀名.js可以省略,返回模块导出对象。模块名用绝对路径或相对路径,例如:

// 作用:执行导入的模块中的代码;返回导入模块中的接口对象;
let cc2 = require('home/src/main.js')
let cc3 = require('./main')
let cc1 = require('./main.js')
  • exports对象

导出当前模块的公共方法或属性,别的模块通过require使用当前模块时得到的就是当前模块的exports对象。用法:exports.name,name为导出的对象名。类似ES6中的export用法,用来导出指定名字的对象。

exports.add = function () {
  let i = 0
  console.log(++i)
}
// 导出一个add方法供其他模块使用
  • module.exports

导出默认对象,没有指定对象名,用于修改模块的原始导出对象。如下:

module.exports = function () {
  console.log('hello world!')
}
  • module.exports和exports的区别

每个模块有个exports接口对象,把公共方法挂在这个对象中,在其他模块中exports接口对象中挂载模块中公共的方法。

  • 每个模块的最后,都会return: module.exports。
  • 每个模块会把module.exports指向的对象赋值给一个变量exports,也就是说:exports = module.exports。
  • module.exports = XXX,表示当前模块导出单一成员,结果是XXX。
  • 需要导出多个成员时必须用exports.add = XXX; exports.foo = XXX;或者用module.exports.add = XXX; module.export.foo = XXX

npm

npm -v 
npm init //后面加上-y ,快速跳过问答式界面。
npm install
npm install 包名 --save-dev(npm install 包名 -D)//包只用于开发环境,不用于生产环境,会出现在package.json文件中的devDependencies属性中。
npm install 包名 --save(npm install 包名 -S)//包要发布到生产环境,会出现在package.json文件中的dependencies属性中。
npm list://查看当前目录下已安装的node包。
npm list -g://查看全局已经安装过的node包。
npm 指定命令 --help
npm --help
npm update 包名
npm uninstall 包名
npm config list
npm info 包名//查看远程npm上指定包的所有版本信息。
npm config set registry https://registry.npm.taobao.org: 修改包下载源,此例修改为了淘宝镜像。
npm root//查看当前包的安装路径。
npm root -g//查看全局的包的安装路径。
npm ls 包名//查看本地安装的指定包及版本信息,没有显示empty。
npm ls 包名 -g//查看全局安装的指定包及版本信息,没有显示empty。

内置模块

直接引入:let xxx = require('xxx')

os模块:nodejs.cn/api-v16/os.…

path模块:nodejs.cn/api-v16/pat…

url模块:nodejs.cn/api-v16/url…

path

const path = require('path')
// 路径:物理路径 当前计算机的路径,相对路径 ././,网络路径:url地址
// 当前文件夹
console.log(__dirname)
// 当前文件
console.log(__filename)
// 执行node的文件夹
console.log(process.cwd())
// 路径拼接
let a = path.join(__dirname,'wwww', 'errr', '404')
console.log(a)
//  获取文件(可以是一个路径文件)的扩展名
console.log(path.extname('log.md'))
// 把一个路径或路径片段的序列解析为一个绝对路径。
let b = path.resolve('foo', '/baz', 'bar');
console.log(b)

fs

// 读文件。 readFile接受两个参数:读取文件路径,回调函数(error,data两个参数),
// 读取文件成功:data为文件内容,error为null,读取失败:error为错误对象,data为undefined
let fs = require('fs')
fs.readFile() 
// 具体看代码

http

  • http模块提供了搭建本地服务器的API,在项目中引入

let http = require('http')

  • 引入后利用http.createServer()方法得到一个服务器实例

let server = http.createServer()

  • 搭建好一个服务器实例后,然后给服务器实例绑定接收request的事件处理函数
server.on('request', (req, res) => {
  console.log(req.url) // 获取到请求的路径(请求路径永远以“/”开头)
})
  • 给服务器绑定接收请求的处理事件,当服务器接收到客户端发送的请求后,会调用后面的处理函数,处理函数接收两个参数:请求信息对象,响应信息对象。绑定监听端口号,开启服务器。传入第二个参数,当服务器开启成功后,触发后面的回调函数。
server.listen(3000, () => {
  console.log('服务器开启成功,可以通过访问http://127.0.0.1:3000/来获取数据~~')
})
  • demo
let http = require('http')
let server = http.createServer()
server.on('request', (req, res) => {
  let url = req.url //得到请求的路径 (请求的路径永远以‘/’开头)
  if (url === '/') {
    res.end('index page')
  } else if (url === '/login') {
    res.end('login page')
  } else if (url === '/register') {
    res.end('register page')
  } else if (url === '/product'){
    let arr = [
      {
        name: 'iphone X',
        price: 8888
      },
      {
        name: 'iphone 7',
        price: 4320
      }
    ]
    // 响应的数据类型必须是字符串或者二进制数据
    res.end(JSON.stringify(arr))
  } else {
    res.end('404 NOT found')
  }
})

server.listen(3000, () => {
  console.log('服务器启动成功了,,可以访问http://127.0.0.1:3000/')
})

express框架

官网:www.expressjs.com.cn/

简介: 和 Node内置的 http 模块类似,用来创建 Web 服务器,底层用的是http核心模块的API。要处理客户端不同请求路径,用多个app.get()方法,无需再用if...else...判断。

基本使用

  1. 安装 npm install express --save
  2. 引入
const express = require('express')
// 得到server(服务器)实例
const app = express()
// 绑定服务器接受请求事件,并且添加处理回调函数
// app.get('/', (req, res) => res.send('Hello World!'))
// app.get('/login', (req, res) => {res.send('<h1>登录</h1>')})
// app.get('/register', (req, res) => {res.send('<h1>注册</h1>')})
// 绑定服务端口,启动服务器
app.listen(3000, () => console.log('Example app listening on port 3000!'))
// 向客户端发送 JSON 对象
// res.send({name:'zs',age:20,gender:'男'})
// 向客户端发送文本内容
// res.send('请求成功')
  1. 运行 node app.js

中间件app.use()

案例:node-express/index.js或者router/use.js

请求到达express的服务器之后,可以连续调用多个中间件,对请求预处理。本质就是一处理函数,参数列表包含next参数,路由处理函数只包含req和res, next实现多个中间件连续调用,把流转关系转交给下一个中间件或路由。(路由之前注册中间件)

const express=require('express')
const app=express()
const mw=function(req,res,next){
    console.log('这是最简单的中间件');
    // 把流转关系转交给下一个中间件或路由
    next()
}
app.listen(8000)
  • 全局中间件app.use(xxx)

路由中间件之前,不管后面走哪个路由,都会经过全局中间件

const express=require('express')
const app=express()
const mw=function(req,res,next){
    console.log('这是最简单的中间件');
    // 把流转关系转交给下一个中间件或路由
    next()
}
// 注册全局中间件
app.use(mw)
app.get('/',(req,res)=>{
    res.send('hello')
})
app.get('/user',(req,res)=>{
    res.send('您好')
}) 
app.listen(8000)
  • 局部中间件

不使用app.use()定义的中间件,示例代码如下:

const express = require('express')
const app = express()
const mw = function (req, res, next) {
    console.log('这是最简单的中间件');
    // 把流转关系转交给下一个中间件或路由
    next()
}
app.get('/',(req, res) => {
    res.send('hello')
})
app.get('/user',mw, (req, res) => {
    res.send('您好')
})
  • 路由中间件vs应用级别中间件

路由中间件绑定到express.Router()实例上,应用级别中间件绑定到app 实例上

const express = require('express')
const router = express.Router()
router.get('/stationSave/baseRtcmFile/listBs', async (req, res, next) => {
    const result = await pool.query('SELECT * FROM station')
    // console.log(result)
    res.send(result)
})
module.exports = router
  • 全局错误处理中间件

捕获整个项目中错误,放在所有路由之后,格式如下:

app.use(function (err, req, res, next) {
    console.error(err.stack)
    res.status(500).send('Something broke!')
})
  • express内置中间件:express.static()
  • 第三方中间件

静态资源

www.expressjs.com.cn/starter/sta…

案例:express文件夹

app.use('/public/', express.static('./public'))

let express = require('express')
let app = express()
app.use('/public/', express.static('./public'))
app.listen(3000, ()=> {
  console.log('running...')
})

路由

用express判断不同的请求路径,当请求路径较多,都写在一个页面会使得代码冗杂,难于维护,例如:

// 引入express,得到服务器实例app
app.get('/', (req, res) => {
  ....
})
app.get('/post', (req, res) => {
  ....
})
app.get('/login', (req, res) => {
  ....
})
app.get('/edit', (req, res) => {
  ....
})

解决方式:模块化建立router.js,通过app.use挂载

var express = require('express')
var app = express()
let router = require('./router')
app.use(router) // 把路由容器挂载到app上
app.listen(3000, () => {
  console.log('running...')
})
let express = require('express')
let router = express.Router()
router.get('/', (req, res) => {
  ....
})
router.get('/post', (req, res) => {
  ....
})
module.exports = router
  • 路由匹配顺序

原则按顺序匹配,只有请求类型和PATH同时匹配,才调用回调函数。若继续执行用next()且next()与res不共存,案例:node-express/router/order.js

  • next使用

next函数主要负责将控制权交给下一个中间件,如果当前中间件没有终结请求,并且next没有被调用,那么请求将被挂起,后边定义的中间件将得不到被执行的机会。next()的作用就是通过放行允许程序执行多个中间件。案例:node-express/router/order.js

参数获取&&内容响应

案例:node-express/router/url.js

app.get('/',(req,res)=>{
    // req.query 默认是一个空对象
    // 客户端用 ?name=zs&age=20形式,发送到服务器的参数
    // 可以通过 req.query 对象访问到,例如:
    // req.query.name req.query.age
    console.log(req.query)
})
// URL 地址中,可以通过:参数名的形式,匹配动态参数值
app.get('/user/:id',(req,res)=>{
    // req.params 默认是一个空对象
    // 里面存放着通过:动态匹配到的参数值
    console.log(req.params)
})
app.get('/user',(req,res)=>{
    // 向客户端发送 JSON 对象
    res.send({name:'zs',age:20,gender:'男'})
})
app.post('/user',(req,res)=>{
    // 向客户端发送文本内容
    res.send('请求成功')
})

mysql

安装

常用命令、语句

    • 查询
select  字段名1, 字段名2, .....  from表名    where <条件表达式>
select  id,name,age from stu
select  * from  stu
select * from stu where 条件
SELECT * FROM stu limit beginIndex, pageSize;//beginIndex=(pageNum-1)*pageSize
where sex='男'
where id=2
where age > 50
where age>50 and sex='男'
where age>=30 and age<=60
where age between 30 and 60
// 运算符like%表示匹配任意0个或多个字符(类似正则表达式里面的*),下划线-表示匹配任意单个字符,like子句中如果没有%和下划线-,就相当于=的效果
where name like '%小%'//小的位置任意
where name like '小%'//小开头
where name like '%小'//小结尾
where name like '_小%3'//_占一个位置,小在第二个位置,3结尾
where name like '_00%' //2 3个位置字符是0
where limit beginIndex, pageSize;
    • 删除
delete  from 表名  where 删除条件   //不指定条件将删除所有数据
delete from stu where id=14
delete from stu   //删除全部记录
    • 修改
update 表名 set 字段1=1, 字段2=2,...  where 修改条件
update stu set age=53 where id = 1 
update stu set age=35,height=160 where id = 1  
update stu set weight = 60 //不加条件,则修改全部的数据
    • 添加
insert into stu set ?

node操作数据库

案例:node-mysql文件夹

  • 安装操作 MySQL 数据库的第三方模块(mysql) npm i mysqlnpm i wz-mysql
  • 通过 mysql 模块连接到 MySQL 数据库
  • 通过 mysql 模块执行 SQL 语句
// const mysql = require('mysql')
const mysql = require('wz-mysql')
const pool = mysql.createPool({
    host: 'localhost', // 数据库的 IP 地址
    port: '3306',
    user: 'root', // 登录数据库的账号
    password: '123456', // 登录数据库的密码
    database: 'test', // 指定要操作哪个数据库
})

module.exports = pool
  • 注意使用mysql语句默认是异步回调函数形式,需要将异步改为同步,有两种方法:
const util = require('util')
app.get('/', async (req, res) => {
    const promisifyQuery = util.promisify(pool.query).bind(pool)
    const result = await promisifyQuery('SELECT * FROM student')
    //send()方法  自动监测响应类型,帮我们把响应类型自动设置到响应头当中
    //还可以设置响应类型的编码,防止出现乱码情况,还可以自动设置http状态码
    res.send(result)
})
app.get('/', async (req, res) => {
    const result = await pool.query('SELECT * FROM student')
    res.send(result)
    await pool.end()
})
// result说明
// 1.查询结果是数组,每个元素是对象,对象属性是查询的字段
// 2.对象中属性affectedRows代表受影响行数,当affectedRows>0说明操作成功

日志管理log4js

源码:github.com/log4js-node…

  1. 安装 npm install log4js
  2. 导入 const log4js = require('log4js'
  3. 相关配置
log4js.configure({
  appenders: { // 输出源
    out: { type: "stdout" },
    app: { type: "file", filename: "application.log" },
    err: { type: 'stderr' }
  },
  categories: { // 类别
    default: { appenders: ["out", "app"], level: "debug" },
    normal: { appenders: ["out", "app"], level: "info" },
    err: { appenders: ["err"], level: "error" }
  },
})
  1. 日志等级

从低到高: ALL < MARK < TRACE < DEBUG < INFO < WARN < ERROR < FATAL

在categories中设置level后,打印level级别以上的日志内容。

  1. appenders

输出日志的基本信息设置,定义一些记录日志的方式,比如名称,依据什么分割?文件大小或者日期。

  1. categories

kv形式,指定映射关系,key是日志类型名称可以自定义,value是对象包含appenders和level,不传参数默认是default。