NODE 官网:NODE
Explanation Node.js® is a JavaScript runtime built on Chrome’s V8 JavaScript engine .
由官方定义亦知:Node.js 不是一门语言,也不是类似于Vue、React这些框架,而是一个
能够运行 JS 的运行环境 。
Features:
事件驱动 非阻塞 I/O 模型(异步) 轻量和高效 文件操作 网络数据操作(http/https) Apply:NPM 包管理工具(npm install xxx)
Node 中不存在 BOM & DOM
环境搭建直接去官网 下载对应版本即可,不再陈述。
Usage 先来个简单的 NODE 入门
Hello NODE console .log('Hello NODE' )
// cd into hello.js directory node hello.js // Output Hello NODE
require console .log('first' )require ('./a.js' )console .log('last' )const fs = require ('js' )
exports & module.exports var module = { exports: { foo: 123 , fun: ... } } return module .exportsmodule .exports.foo = 123 module .exports.foo = function ( ) { console .log('function' ) } const foo = require ('/foo' )console .log(foo) var exports = module .exportsconsole .log(exports === module .exports); exports = 123 module .exports = 'hello' exports.foo = 123 exports.foo = 123 exports.fun = function ( ) { console .log('function' ) } console .log(foo) console .log(foo.foo, foo.fun); module .exports = { foo: 123 , fun: function ( ) { console .log('function' ) } } console .log(foo)
require 第三方模块加载规则:
一个项目有且只有一个 node_modules,放在项目根目录中,这样的话项目中所有的子目录中的代码都可以加载到第三方包,不会出现有多个 node_modules
模块查找机制 优先从缓存加载 核心模块 路径形式的文件模块 第三方模块 node_modules/art-template/ node_modules/art-template/package.json node_modules/art-template/package.json main index.js 备选项 进入上一级目录找 node_modules 按照这个规则依次往上找,直到磁盘根目录还找不到,最后报错:Can not find moudle xxx 一个项目有且仅有一个 node_modules 而且是存放到项目的根目录 更多细节可以阅读 深入 Node.js 的模块机制
More:module.exports
Read || Write File const fs = require ('js' )function readFile ( path: PathLike | number, options: { encoding?: string | null; flag?: string; } | string | undefined | null, callback: (err: NodeJS.ErrnoException | null, data: string | Buffer ) => void , ): void ; fs .readFile ('./my.txt' ,'utf8' ,(err,data ) => { if (err){ console .log(err) } else { console .log(data); } }) console .log(__filename,__dirname);fs.readFile(__dirname+'/my.txt' ,'utf8' ,(err,data) => { })
注意:
node 错误优先 (第一个参数通常是 err),并且在同步操作中通过 try -catch 来捕获异常,
但是异步操作(比如读写文件、网络请求)无法完成,而是通过 if 语句来判断进行后续处理
const path = require ('path' ) let filename = path.join(__dirname,'my.txt' ) console .log(filename); fs.writeFile('./my.txt' ,msg,'utf8' ,(err) => { if (err){ console .log('There is some errora: ' + err) } else { console .log('Success!' ) } })
Http const http = require ('http' )let server = http.createServer()server.on('request' , () => console .log('there has some people visitting the web~' ) ) let server = http.createServer(function (request, response ) { console .log('there has some people visitting the web~' + request.url); response.setHeader('Content-Type' ,'text/plain;charset=utf-8' ); response.write('hello Tadm ~' ); response.write(' these contents from node ~' ) response.end(); if (request.url === '/' ){ response.end('home page' ) } else if (request.url ==== '/login' ) { response.end('login page' ) } else if (request.url ==== '/json' ) { let jsonEaxmple = ['hello' ,'tadm' ] response.end(jsonEaxmple) response.end(JSON .stringfy(jsonEaxmple)) } }); server.listen(8080 ,() => console .log('The server has been started!' ) ); fs.readFile(path.join(__dirname,'index.html' ),(err,data) => { if (err) { throw err } request.end(data) })
res.end(sth) => sth 只能为 String or Buffer
优雅化:
Request
Response
const http = require ('http' )const fs = require ('fs' )const path = require ('path' )const mime = require ('mime' ) http.createServer((req,res ) => { let publicDir = path.join(__dirname, 'public' ) let filename = path.join(publicDir, req.url) console .log(req.url); fs.readFile(filename, (err,data) => { if (err){ res.end('There is some errors~' ) } res.setHeader('Content-Type' , mime.getType(filename)) res.end(data) }) }).listen(8080 ,() => console .log('The Server has been started!' ) ) http.createServer((req,res ) => { let publicDir = path.join(__dirname, 'public' ) let filename = path.join(publicDir, req.url) console .log(filename); req.url = req.url.toLowerCase() req.method = req.method.toLowerCase() if ((req.url === '/' || req.url === '/index' ) && req.method === 'get' ){ loadFile(path.join(filename,'index.html' ),res) } else if (req.url.startsWith('/resources' ) && req.method === 'get' ){ loadFile(filename,res) } }).listen(8080 ,() => console .log('The Server has been started!' )) function loadFile (filename, res ) { fs.readFile(filename,(err, data) => { if (err){ res.end('There is some errors~' ) } res.setHeader('Content-Type' , mime.getType(filename)) res.end(data) }) }
此时我们想要 get/post 提交东西到服务器了:
const querystring = require ('querystring' ) fs.readFile(filepath(data.json 的位置),'utf8' ,(err,data) => { if (err && err.code !=='ENOENT' ){ throw err } let infoList = JSON .parse(data || '[]' ) let array = [] req.on('data' ,(chunk) => array.push(chunk)) req.on('end' ,function ( ) { let postBody = Buffer.concat(array) postBody = postBody.toString('utf8' ) postBody = querystring.parse(postBody) infoList.push(postBody) res.end('ok~' ) }) fs.writeFile(path.join(__dirname,'data' ,'data.json' ),JSON .stringify(infoList),function (err ) { if (err){ throw err} res.statusCode = 302 res.statusMessage = 'Found' res.setHeader('Location' ,'/' ) res.end() }) })
Express http://expressjs.com/
Install mkdir express-democnpm i express -S cd express-demo
Usage Hello world 示例:
const express = require ('express' )const app = express()const port = 3000 app.get('/' , (req, res) => res.send('Hello World!' )) app.listen(port, () => console .log(`Example app listening on port ${port} !` ))
新建一个 app.js 文件
const express = require ('express' )const app = express()const port = 3000 const path = require ('path' )app.get('/' , (req, res) => res.send('Hello World~ From Get~' )) app.post('/otr' , (req, res) => res.send('Hello World~ From Post~' )) app.use('/use' , (req, res) => res.send('Hello World~ From Use~' )) app.all('/all' , (req, res) => res.send('Hello World~ From All~' )) app.get(/^\/index(\/.*)*$/ , (req, res) => res.send('Hello World~ From Regex~' )) app.get('/query' , (req, res) => console .log(req.query)) app.get('/params/:title/:name' , (req, res) => res.send(req.params)) app.get('/json' , (req, res) => res.json({ title :'json' ,name :'tadm' })) app.get('/jsonsend' , (req, res) => res.send({ title :'json' ,name :'tadm' })) app.get('/error' , (req, res) => res.status(404 ).end('There is 404~' )) app.use('/static' , express.static(path.join(__dirname, 'public' ))) app.listen(port, () => console .log(`Example app listening on port ${port} !` , __dirname))
Nodemon 自动重启工具,不再需要每次重启服务,直接 ctrl+s 完事
Install Usage nodemon --version nodemon app.js
POST body-parser
var express = require ('express' )var bodyParser = require ('body-parser' ) var app = express() app.use(bodyParser.urlencoded({ extended : false })) app.use(bodyParser.json()) app.use(function (req, res ) { res.setHeader('Content-Type' , 'text/plain' ) res.write('you posted:\n' ) res.end(JSON .stringify(req.body, null , 2 )) })
CODE Example
<!DOCTYPE html > <html > <head > <title > test</title > <link rel ="shortcut icon" href ="./favicon.ico" > <link rel ="stylesheet" href ="https://cdn.jsdelivr.net/npm/bootstrap@4.4.1/dist/css/bootstrap.min.css" > </head > <body > <div > <p > There is a beautiful girl</p > <img width ="1000px" height ="700px" src ="./img45.jpg" > </div > <div class ="comments container" > <form action ="/post" method ="post" > <div class ="form-group" > <label for ="input_name" > Name</label > <input type ="text" class ="form-control" required minlength ="2" maxlength ="10" id ="input_name" name ="name" placeholder ="Please input your name~" > </div > <div class ="form-group" > <label for ="textarea_message" > Content</label > <textarea class ="form-control" name ="message" id ="textarea_message" cols ="30" rows ="10" required minlength ="5" maxlength ="20" > </textarea > </div > <button type ="submit" class ="btn btn-success" > Submit</button > </form > </div > </body > </html >
const bodyParser = require ('body-parser' )app.use('/static' , express.static(path.join(__dirname, 'public' ))) app.use(bodyParser.urlencoded({ extended : false })) app.use(bodyParser.json()) app.post('/post' , (req, res) => { console .log(req.body); })
app.use([path,] callback [,callback …]) 将指定的一个或多个中间件 函数安装在指定的路径上:当所请求路径的基数匹配时,将执行中间件函数path
。
使用中间件也需要注意顺序,更多查看官方文档
Small Demo const express = require ('express' )const router = require ('./router' )const bodyParser = require ('body-parser' )let app = express()app.use('/node_modules/' , express.static('./node_modules/' )) app.use('/public/' , express.static('./public/' )) app.engine('html' , require ('express-art-template' )) app.use(bodyParser.urlencoded({ extended : false })) app.use(bodyParser.json()) app.use(router) app.listen(3000 , function ( ) { console .log('running 3000...' ) }) module .exports = app
const express = require ('express' )const router = express.Router()const Student = require ('./student' )router.get('/students' , function (req, res ) { Student.find(function (err, students ) { if (err) { return res.status(500 ).send('Server error.' ) } res.render('index.html' , { fruits: [ '苹果' , '香蕉' , '橘子' ], students: students }) }) }) router.get('/students/new' , function (req, res ) { res.render('new.html' ) }) router.post('/students/new' , function (req, res ) { Student.save(req.body, function (err ) { if (err) { return res.status(500 ).send('Server error.' ) } res.redirect('/students' ) }) }) router.get('/students/edit' , function (req, res ) { Student.findById(parseInt (req.query.id), function (err, student ) { if (err) { return res.status(500 ).send('Server error.' ) } res.render('edit.html' , { student: student }) }) }) router.post('/students/edit' , function (req, res ) { Student.updateById(req.body, function (err ) { if (err) { return res.status(500 ).send('Server error.' ) } res.redirect('/students' ) }) }) router.get('/students/delete' , function (req, res ) { Student.deleteById(req.query.id, function (err ) { if (err) { return res.status(500 ).send('Server error.' ) } res.redirect('/students' ) }) }) module .exports = router
const fs = require ('fs' )const dbPath = './db.json' function writeData (students, callback ) { let fileData = JSON .stringify({ students: students }) fs.writeFile(dbPath, fileData, function (err ) { if (err) { return callback(err) } callback(null ) }) } exports.find = function (callback ) { fs.readFile(dbPath, 'utf8' , function (err, data ) { if (err) { return callback(err) } callback(null , JSON .parse(data).students) }) } exports.findById = function (id, callback ) { fs.readFile(dbPath, 'utf8' , function (err, data ) { if (err) { return callback(err) } let students = JSON .parse(data).students let ret = students.find(function (item ) { return item.id === parseInt (id) }) callback(null , ret) }) } exports.save = function (student, callback ) { fs.readFile(dbPath, 'utf8' , function (err, data ) { if (err) { return callback(err) } let students = JSON .parse(data).students student.id = students[students.length - 1 ].id + 1 students.push(student) writeData(students,callback) }) } exports.updateById = function (student, callback ) { fs.readFile(dbPath, 'utf8' , function (err, data ) { if (err) { return callback(err) } let students = JSON .parse(data).students student.id = parseInt (student.id) let stu = students.find(function (item ) { return item.id === student.id }) for (let key in student) { stu[key] = student[key] } writeData(students,callback) }) } exports.deleteById = function (id, callback ) { fs.readFile(dbPath, 'utf8' , function (err, data ) { if (err) { return callback(err) } let students = JSON .parse(data).students let deleteId = students.findIndex(function (item ) { return item.id === parseInt (id) }) students.splice(deleteId, 1 ) writeData(students,callback) }) }
{"students" : [ {"id" :1 ,"name" :"Foo" ,"gender" :"0" ,"age" :"24" ,"hobbies" :"Study" }, {"name" :"Tadm" ,"gender" :"0" ,"age" :"19" ,"hobbies" :"Coding" ,"id" :2 } ] }
<!DOCTYPE html > <html lang ="zh-CN" > <head > <meta charset ="utf-8" > <meta http-equiv ="X-UA-Compatible" content ="IE=edge" > <meta name ="viewport" content ="width=device-width, initial-scale=1" > <link rel ="icon" href ="../../favicon.ico" > <title > Dashboard Template for Bootstrap</title > <link href ="/node_modules/bootstrap/dist/css/bootstrap.min.css" rel ="stylesheet" > <link href ="/public/css/main.css" rel ="stylesheet" > </head > <nav class ="navbar navbar-inverse navbar-fixed-top" > <div class ="container-fluid" > <div class ="navbar-header" > <button type ="button" class ="navbar-toggle collapsed" data-toggle ="collapse" data-target ="#navbar" aria-expanded ="false" aria-controls ="navbar" > <span class ="sr-only" > Toggle navigation</span > <span class ="icon-bar" > </span > <span class ="icon-bar" > </span > <span class ="icon-bar" > </span > </button > <a class ="navbar-brand" href ="#" > Project name</a > </div > <div id ="navbar" class ="navbar-collapse collapse" > <ul class ="nav navbar-nav navbar-right" > <li > <a href ="#" > Dashboard</a > </li > </ul > <form class ="navbar-form navbar-right" > <input type ="text" class ="form-control" placeholder ="Search..." > </form > </div > </div > </nav > <div class ="container-fluid" > <div class ="row" > <div class ="col-sm-3 col-md-2 sidebar" > <ul class ="nav nav-sidebar" > <li class ="active" > <a href ="/" > 学生管理 <span class ="sr-only" > (current)</span > </a > </li > </ul > </div > <div class ="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main" > <h1 class ="page-header" > Dashboard</h1 > <div class ="row placeholders" > {{ each fruits }} <div class ="col-xs-6 col-sm-3 placeholder" > <img src ="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" width ="200" height ="200" class ="img-responsive" alt ="Generic placeholder thumbnail" > <h4 > {{ $value }}</h4 > <span class ="text-muted" > Something else</span > </div > {{ /each }} </div > <h2 class ="sub-header" > Section title</h2 > <a class ="btn btn-success" href ="/students/new" > 添加学生</a > <div class ="table-responsive" > <table class ="table table-striped" > <thead > <tr > <th > #</th > <th > 姓名</th > <th > 性别</th > <th > 年龄</th > <th > 爱好</th > <th > 操作</th > </tr > </thead > <tbody > {{ each students }} <tr > <td > {{ $value.id }}</td > <td > {{ $value.name }}</td > <td > {{ $value.gender }}</td > <td > {{ $value.age }}</td > <td > {{ $value.hobbies }}</td > <td > <a href ="/students/edit?id={{ $value.id }}" > 编辑</a > <a href ="/students/delete?id={{ $value.id }}" > 删除</a > </td > </tr > {{ /each }} </tbody > </table > </div > </div > </div > </div >
<nav class ="navbar navbar-inverse navbar-fixed-top" > <div class ="container-fluid" > <div class ="navbar-header" > <button type ="button" class ="navbar-toggle collapsed" data-toggle ="collapse" data-target ="#navbar" aria-expanded ="false" aria-controls ="navbar" > <span class ="sr-only" > Toggle navigation</span > <span class ="icon-bar" > </span > <span class ="icon-bar" > </span > <span class ="icon-bar" > </span > </button > <a class ="navbar-brand" href ="#" > Project name</a > </div > <div id ="navbar" class ="navbar-collapse collapse" > <ul class ="nav navbar-nav navbar-right" > <li > <a href ="#" > Dashboard</a > </li > </ul > <form class ="navbar-form navbar-right" > <input type ="text" class ="form-control" placeholder ="Search..." > </form > </div > </div > </nav > <div class ="container-fluid" > <div class ="row" > <div class ="col-sm-3 col-md-2 sidebar" > <ul class ="nav nav-sidebar" > <li class ="active" > <a href ="/students" > 学生管理 <span class ="sr-only" > (current)</span > </a > </li > </ul > </div > <div class ="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main" > <h2 class ="sub-header" > 添加学生</h2 > <form action ="/students/edit" method ="post" > <input type ="hidden" name ="id" value ="{{ student.id }}" > <div class ="form-group" > <label for ="" > 姓名</label > <input type ="text" class ="form-control" id ="" name ="name" required minlength ="2" maxlength ="10" value ="{{ student.name }}" > </div > <div class ="form-group" > <label for ="" > 性别</label > <div > <label class ="radio-inline" > <input type ="radio" name ="gender" id ="" value ="0" checked > 男 </label > <label class ="radio-inline" > <input type ="radio" name ="gender" id ="" value ="1" > 女 </label > </div > </div > <div class ="form-group" > <label for ="" > 年龄</label > <input class ="form-control" type ="number" id ="" name ="age" value ="{{ student.age }}" required min ="1" max ="150" > </div > <div class ="form-group" > <label for ="" > 爱好</label > <input class ="form-control" type ="text" id ="" name ="hobbies" value ="{{ student.hobbies }}" > </div > <button type ="submit" class ="btn btn-default" > Submit</button > </form > </div > </div > </div >
<nav class ="navbar navbar-inverse navbar-fixed-top" > <div class ="container-fluid" > <div class ="navbar-header" > <button type ="button" class ="navbar-toggle collapsed" data-toggle ="collapse" data-target ="#navbar" aria-expanded ="false" aria-controls ="navbar" > <span class ="sr-only" > Toggle navigation</span > <span class ="icon-bar" > </span > <span class ="icon-bar" > </span > <span class ="icon-bar" > </span > </button > <a class ="navbar-brand" href ="#" > Project name</a > </div > <div id ="navbar" class ="navbar-collapse collapse" > <ul class ="nav navbar-nav navbar-right" > <li > <a href ="#" > Dashboard</a > </li > </ul > <form class ="navbar-form navbar-right" > <input type ="text" class ="form-control" placeholder ="Search..." > </form > </div > </div > </nav > <div class ="container-fluid" > <div class ="row" > <div class ="col-sm-3 col-md-2 sidebar" > <ul class ="nav nav-sidebar" > <li class ="active" > <a href ="/students" > 学生管理 <span class ="sr-only" > (current)</span > </a > </li > </ul > </div > <div class ="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main" > <h2 class ="sub-header" > 添加学生</h2 > <form action ="/students/new" method ="post" > <div class ="form-group" > <label for ="" > 姓名</label > <input type ="text" class ="form-control" id ="" name ="name" required minlength ="2" maxlength ="10" > </div > <div class ="form-group" > <label for ="" > 性别</label > <div > <label class ="radio-inline" > <input type ="radio" name ="gender" id ="" value ="0" checked > 男 </label > <label class ="radio-inline" > <input type ="radio" name ="gender" id ="" value ="1" > 女 </label > </div > </div > <div class ="form-group" > <label for ="" > 年龄</label > <input class ="form-control" type ="number" id ="" name ="age" required min ="1" max ="150" > </div > <div class ="form-group" > <label for ="" > 爱好</label > <input class ="form-control" type ="text" id ="" name ="hobbies" > </div > <button type ="submit" class ="btn btn-default" > Submit</button > </form > </div > </div > </div >
MongoDB Hello World 初步认识可参考此篇:mongoDBcommand
官方:mongodb
再次封装版:mongoose
const mongoose = require ('mongoose' );mongoose.connect('mongodb://localhost:27017/test' , {useNewUrlParser : true , useUnifiedTopology : true }); const Cat = mongoose.model('Cat' , { name : String });const kitty = new Cat({ name : 'Zildjian' });kitty.save().then(() => console .log('meow' ));
db test
show dbs admin 0.000GB config 0.000GB elm 0.001GB local 0.000GB test 0.000GB
show collections cats student
db.cats.find() { “_id” : ObjectId(“5e5115d217508ead24309fb5”), “name” : “Zildjian”, “__v” : 0 }
CRUD let mongoose = require ('mongoose' )let Schema = mongoose.Schemamongoose.connect('mongodb://localhost/nodemongoose' ) let userSchema = new Schema({ username: { type: String , required: true }, password: { type: String , required: true }, email: { type: String } }) let User = mongoose.model('User' ,new Schema({ username: { type: String , required: true }, password: { type: String , required: true }, email: { type: String } }) ) let admin = new User({ username: 'Tadm' , password: '123456' , email: '2873126657@qq.com' }) admin.save(function (err, ret ) { if (err) { console .log('保存失败' ) } else { console .log('保存成功' ) } }) User.find(function (err, ret ) { if (err) { console .log('查询失败' ) } else { console .log(ret) } }) User.find({ username: 'Tadm' }, function (err, ret ) { if (err) { console .log('查询失败' ) } else { console .log(ret) } }) User.findOne({ username: 'Tadm' }, function (err, ret ) { if (err) { console .log('查询失败' ) } else { console .log(ret) } }) User.remove({ username: 'Tadm' }, function (err, ret ) { if (err) { console .log('删除失败' ) } else { console .log('删除成功' ) console .log(ret) } }) User.findByIdAndUpdate('5e51f28f79148a150c76977d' , { password: '123' }, function (err, ret ) { if (err) { console .log('更新失败' ) } else { console .log('更新成功' ) } })
更多操作 API
MySQL let mysql = require ('mysql' );let connection = mysql.createConnection({ host: 'localhost' , user: 'root' , password: '' , database: 'users' }); connection.connect(); connection.query('SELECT * FROM `users`' , function (error, results, fields ) { if (error) throw error console .log('The solution is: ' , results) }) connection.query('INSERT INTO users VALUES(NULL, "Tadm", "123456")' , function (error, results, fields ) { if (error) throw error console .log('The solution is: ' , results) }) connection.end()
Promise mongoose 的所有 API 均支持 Promise
Node 路径问题 因为在 node 中文件的相对路径 是相对于当前执行 node 命令的文件路径 而不是相对于正在执行的文件
如何解决:
动态获取当前文件所在目录的绝对路径
动态获取当前文件所在路径的绝对路径
简单而言:这两个属性始终保存着当前文件的(绝对)路径信息 ,与 node 命令执行位置无关
let publicDir = path.join(__dirname, 'public' )let filename = path.join(publicDir, req.url)
但是只是针对于文件的路径而言,模块的路径标识不受影响,而且相对路径(‘./ ../‘)不能省略
Express-session 官方文档